private async validateOperation()

in lib/swaggerValidator/semanticValidator.ts [455:551]


  private async validateOperation(spec: SwaggerSpec, errors: SemanticErrorDetail[]) {
    const visitedOperationId = new Set<string>();
    const visitedPathTemplate = new Set<string>();
    const url = spec._filePath;
    const pathArgs = new Set<string>();
    let pathParams: Parameter[] | undefined;

    traverseSwagger(spec, {
      onPath: (path) => {
        pathArgs.clear();
        pathParams = path.parameters;
        const pathTemplate = path._pathTemplate;
        let normalizedPath = pathTemplate;

        const argMatches = normalizedPath.match(/\{.*?\}/g);
        let idx = 0;
        for (const arg of argMatches ?? []) {
          if (arg === "{}") {
            const meta = getOavErrorMeta("EMPTY_PATH_PARAMETER_DECLARATION", { pathTemplate });
            this.addErrorsFromErrorCode(errors, url, meta, path);
          } else {
            normalizedPath = normalizedPath.replace(arg, `arg${idx}`);
            ++idx;
            pathArgs.add(arg.substr(1, arg.length - 2));
          }
        }

        if (visitedPathTemplate.has(normalizedPath)) {
          const meta = getOavErrorMeta("EQUIVALENT_PATH", { pathTemplate });
          this.addErrorsFromErrorCode(errors, url, meta, path);
        }
        visitedPathTemplate.add(normalizedPath);
      },
      onOperation: (operation) => {
        let bodyParam: Parameter | undefined;
        const requiredPathArgs = new Set(pathArgs);
        const visitedParamName = new Set<string>();
        const { operationId, parameters, consumes } = operation;
        const mergedParameters = [...(parameters ?? []), ...(pathParams ?? [])];

        if (operationId !== undefined) {
          if (visitedOperationId.has(operationId)) {
            const meta = getOavErrorMeta("DUPLICATE_OPERATIONID", { operationId });
            this.addErrorsFromErrorCode(errors, url, meta, operation);
          } else {
            visitedOperationId.add(operationId);
          }
        }

        for (const p of mergedParameters) {
          const param = this.jsonLoader.resolveRefObj(p);
          const { name } = param;

          if (visitedParamName.has(name)) {
            const meta = getOavErrorMeta("DUPLICATE_PARAMETER", { name });
            this.addErrorsFromErrorCode(errors, url, meta, operation);
          }
          visitedParamName.add(name);

          if (param.in === "body" || param.in === "formData") {
            if (bodyParam !== undefined) {
              const meta = getOavErrorMeta(
                param.in === bodyParam.in
                  ? "MULTIPLE_BODY_PARAMETERS"
                  : "INVALID_PARAMETER_COMBINATION",
                {}
              );
              if (
                !(
                  meta.code === "MULTIPLE_BODY_PARAMETERS" &&
                  param.in === "formData" &&
                  consumes !== undefined &&
                  consumes.includes("multipart/form-data")
                )
              ) {
                this.addErrorsFromErrorCode(errors, url, meta, operation);
              }
            }
            bodyParam = param;
          }

          if (param.in === "path") {
            if (!requiredPathArgs.has(name)) {
              const meta = getOavErrorMeta("MISSING_PATH_PARAMETER_DECLARATION", { name });
              this.addErrorsFromErrorCode(errors, url, meta, operation);
            }
            requiredPathArgs.delete(name);
          }
        }

        for (const name of requiredPathArgs) {
          const meta = getOavErrorMeta("MISSING_PATH_PARAMETER_DEFINITION", { name });
          this.addErrorsFromErrorCode(errors, url, meta, operation);
        }
      },
    });
  }