private async loadStepRestCall()

in lib/apiScenario/apiScenarioLoader.ts [422:680]


  private async loadStepRestCall(
    rawStep: RawStepOperation | RawStepExample,
    ctx: ApiScenarioContext
  ): Promise<StepRestCall> {
    if (rawStep.step !== undefined && ctx.stepTracking.has(rawStep.step)) {
      throw new Error(`Duplicated step name: ${rawStep.step}`);
    }

    const step: StepRestCall = {
      type: "restCall",
      step: rawStep.step ?? `_${ctx.stepIndex}`,
      description: rawStep.description,
      operationId: "",
      operation: {} as Operation,
      parameters: {} as SwaggerExample["parameters"],
      responses: {} as SwaggerExample["responses"],
      outputVariables: rawStep.outputVariables ?? {},
      ...convertVariables(rawStep.variables),
      authentication:
        this.loadAuthentication(rawStep.authentication) ??
        ctx.scenario?.authentication ??
        ctx.scenarioDef.authentication,
    };

    const getVariable = (
      name: string,
      ...scopes: Array<VariableScope | undefined>
    ): Variable | undefined => {
      if (!scopes || scopes.length === 0) {
        scopes = [step, ctx.scenario, ctx.scenarioDef];
      }
      for (const scope of scopes) {
        if (scope && scope.variables[name]) {
          return scope.variables[name];
        }
      }
      for (const scope of scopes) {
        if (scope && scope.requiredVariables.includes(name)) {
          return {
            type: "string",
            value: `$(${name})`,
          };
        }
      }
      if (
        (ctx.scenarioDef.scope === "ResourceGroup" &&
          ["subscriptionId", "resourceGroupName", "location"].includes(name)) ||
        (ctx.scenarioDef.scope === "Subscription" && ["subscriptionId"].includes(name))
      ) {
        return {
          type: "string",
          value: `$(${name})`,
        };
      }
      return undefined;
    };

    const requireVariable = (name: string) => {
      if (ctx.scenarioDef.scope === "ResourceGroup" && ["resourceGroupName"].includes(name)) {
        return;
      }
      const requiredVariables =
        ctx.scenario?.requiredVariables ?? ctx.scenarioDef.requiredVariables;
      if (!requiredVariables.includes(name)) {
        requiredVariables.push(`${name}`);
      }
    };

    let operation: Operation | undefined;

    if (!("exampleFile" in rawStep)) {
      // load operation step
      step.operationId = rawStep.operationId;

      operation = rawStep.readmeTag
        ? this.additionalMap.get(rawStep.readmeTag)?.operationsMap.get(step.operationId)
        : this.operationsMap.get(step.operationId);
      if (operation === undefined) {
        throw new Error(`Operation not found for ${step.operationId} in step ${step.step}`);
      }
      if (rawStep.readmeTag) {
        step.externalReference = true;
      }
      step.isManagementPlane = this.isManagementPlane(operation);
      if (this.opts.includeOperation) {
        step.operation = operation;
      }

      if (rawStep.variables) {
        for (const [name, value] of Object.entries(rawStep.variables)) {
          if (typeof value === "string") {
            step.variables[name] = { type: "string", value };
            continue;
          }

          if (value.type === "object" || value.type === "secureObject" || value.type === "array") {
            if (value.patches) {
              const variable = ctx.scenario
                ? getVariable(name, ctx.scenario, ctx.scenarioDef)
                : getVariable(name, ctx.scenarioDef);
              if (!variable) {
                throw new Error(`Variable ${name} not found in step ${step.step}`);
              }
              const obj = cloneDeep(variable);
              if (typeof obj !== "object") {
                // TODO dynamic json patch
                throw new Error(`Can not Json Patch on ${name}, type of ${typeof obj}`);
              }
              jsonPatchApply(obj.value, value.patches);
              step.variables[name] = obj;
              continue;
            }
          }
          step.variables[name] = value;
        }
      }

      operation.parameters?.forEach((param) => {
        param = this.jsonLoader.resolveRefObj(param);
        if (param.name === "api-version") {
          step.parameters["api-version"] = rawStep.readmeTag
            ? this.additionalMap.get(rawStep.readmeTag)?.apiVersionsMap.get(step.operationId)!
            : this.apiVersionsMap.get(step.operationId)!;
        } else if (rawStep.parameters?.[param.name]) {
          step.parameters[param.name] = rawStep.parameters[param.name];
        } else {
          const v = getVariable(param.name);
          if (v) {
            if (param.in === "body") {
              step.parameters[param.name] = v.value;
            } else {
              step.parameters[param.name] = `$(${param.name})`;
            }
          } else if (param.in === "path" || param.required) {
            step.parameters[param.name] = `$(${param.name})`;
            requireVariable(param.name);
          }
        }
      });

      const xHost = operation._path._spec["x-ms-parameterized-host"];
      if (xHost) {
        xHost.parameters.forEach((param) => {
          param = this.jsonLoader.resolveRefObj(param);
          if (rawStep.parameters?.[param.name]) {
            step.variables[param.name] = {
              type: "string",
              value: rawStep.parameters[param.name] as string,
            };
          }
        });
      }

      step.responseAssertion = rawStep.responses;
    } else {
      // load example step
      step.exampleFile = rawStep.exampleFile;

      const exampleFilePath = pathJoin(pathDirName(ctx.scenarioDef._filePath), step.exampleFile!);

      // Load example file
      const fileContent = await this.fileLoader.load(exampleFilePath);
      const exampleFileContent = JSON.parse(fileContent) as SwaggerExample;

      // Load Operation
      if (rawStep.operationId || exampleFileContent.operationId) {
        step.operationId = (rawStep.operationId ?? exampleFileContent.operationId)!;

        operation = this.operationsMap.get(step.operationId);
        if (operation === undefined) {
          throw new Error(`Operation not found for ${step.operationId} in step ${step.step}`);
        }
        step.isManagementPlane = this.isManagementPlane(operation);
        if (this.opts.includeOperation) {
          step.operation = operation;
        }
      } else {
        const opMap = this.exampleToOperation.get(exampleFilePath);
        if (opMap === undefined) {
          throw new Error(`Example file is not referenced by any operation: ${step.exampleFile}`);
        }
        const ops = Object.keys(opMap);
        if (ops.length > 1) {
          throw new Error(
            `Example file is referenced by multiple operation: ${Object.keys(opMap)} ${
              step.exampleFile
            }`
          );
        }
        step.operationId = ops[0];
        const exampleName = opMap[step.operationId];
        operation = this.operationsMap.get(step.operationId);
        if (operation === undefined) {
          throw new Error(`Operation not found for ${step.operationId} in step ${step.step}`);
        }
        step.isManagementPlane = this.isManagementPlane(operation);
        if (this.opts.includeOperation) {
          step.operation = operation;
        }
        step.description = step.description ?? exampleName;
      }

      step.parameters = exampleFileContent.parameters;

      // force update api-version
      if (step.parameters["api-version"]) {
        step.parameters["api-version"] = this.apiVersionsMap.get(step.operationId)!;
      }

      step.responses = exampleFileContent.responses;

      await this.applyPatches(step, rawStep, operation);

      this.templateGenerator.exampleParameterConvention(step, getVariable, operation);
    }

    const security = operation?.security || operation?._path._spec.security;
    if (!step.authentication && security) {
      if (security.length > 1) {
        logger.warn("Multiple security definitions found, only the first one will be used");
      }
      const security0 = security[0];
      const key = Object.keys(security0)[0];
      const securityDefinition = operation?._path._spec.securityDefinitions?.[key];
      if (!securityDefinition) {
        throw new Error(`Security definition not found for ${key}`);
      }
      if (securityDefinition.type === "oauth2") {
        const value = security0[key];
        if (value.length > 1) {
          throw new Error(`Multiple scopes are not supported yet: ${JSON.stringify(value)}`);
        }
        const scope = value[0];
        step.authentication = {
          type: "AADToken",
          scope: scope,
        };
      } else if (securityDefinition.type === "apiKey") {
        const def = securityDefinition as ApiKeySecurity;
        step.authentication = {
          type: "AzureKey",
          key: `$(${key})`,
          in: def.in === "query" ? "query" : "header",
          name: def.name ?? "Authorization",
        };
      }
    }

    if (!rawStep.step) {
      step.step = step.operationId;
      let i = 1;
      while (ctx.stepTracking.has(step.step)) {
        step.step += `_${i++}`;
      }
    }
    ctx.stepTracking.set(step.step, step);

    return step;
  }