export function createComponentModifierPlugin()

in packages/extensions/core/src/lib/plugins/component-modifier.ts [22:103]


export function createComponentModifierPlugin(): PipelinePlugin {
  const noWireExtension = "x-ms-no-wire";

  return createPerFilePlugin(async (context) => async (fileIn, sink) => {
    const componentModifier = cloneDeep(context.config.raw.components);
    if (componentModifier) {
      const o = await fileIn.ReadObject<any>();

      o.components = o.components || {};

      // schemas:
      //  merge-override semantics, but decorate new properties so they're not serialized
      const schemasSource = componentModifier.schemas || {};
      const schemasTarget = (o.components.schemas = o.components.schemas || {});
      for (const schemaKey of Object.keys(schemasSource)) {
        const schemaSource = schemasSource[schemaKey];
        const schemaTarget = schemasTarget[schemaKey] || {};
        // decorate properties
        if (schemaSource.properties) {
          for (const propertyKey of Object.keys(schemaSource.properties)) {
            const propSource = schemaSource.properties[propertyKey];
            if (!schemaTarget.properties || !schemaTarget.properties[propertyKey]) {
              propSource[noWireExtension] = true;
            }
            decorateSpecialProperties(propSource);
          }
        }

        schemasTarget[schemaKey] = mergeOverwriteOrAppend(schemaSource, schemaTarget);
      }

      // parameters:
      //  merge-override semantics
      const paramsSource = componentModifier.parameters || {};
      const paramsTarget = (o.components.parameters = o.components.parameters || {});
      for (const paramKey of Object.keys(paramsSource)) {
        const paramSource = paramsSource[paramKey];
        const paramTarget = paramsTarget[paramKey] || {};
        paramsTarget[paramKey] = mergeOverwriteOrAppend(paramSource, paramTarget);
      }

      // operations:
      //  merge-override semantics based on operationId, but decorate operations so they're not targetting the wire
      const operationsSource = componentModifier.operations || [];
      const operationsTarget1 = (o["paths"] = o["paths"] || {});
      const operationsTarget2 = (o["x-ms-paths"] = o["x-ms-paths"] || {});
      const getOperationWithId = (operationId: string): { get(): any; set(x: any): void } | null => {
        for (const path of <any>[...Object.values(operationsTarget1), ...Object.values(operationsTarget2)]) {
          for (const method of Object.keys(path)) {
            if (path[method].operationId === operationId) {
              return { get: () => path[method], set: (x) => (path[method] = x) };
            }
          }
        }
        return null;
      };
      const getDummyPath = (): string => {
        let path = `/dummy?${Object.keys(operationsTarget2).length}`;
        while (path in operationsTarget2) {
          path += "0";
        }
        return path;
      };
      for (const newOperation of operationsSource) {
        const operationId: string | null = newOperation.operationId || null;
        const existingOperation = operationId && getOperationWithId(operationId);

        decorateSpecialProperties(newOperation);

        if (existingOperation) {
          existingOperation.set(mergeOverwriteOrAppend(newOperation, existingOperation.get()));
        } else {
          newOperation[noWireExtension] = true;
          operationsTarget2[getDummyPath()] = { get: newOperation };
        }
      }

      return sink.writeObject(fileIn.description, o, fileIn.identity);
    }
    return fileIn;
  });
}