private unifyCommonProperty()

in lib/generator/exampleGenerator.ts [271:367]


  private unifyCommonProperty(example: any) {
    if (!example || !example.parameters || !example.responses) {
      return;
    }
    type pathNode = string | number;
    type pathNodes = pathNode[];

    const requestPaths = _.paths(example.parameters, { pathFormat: "array" }).map((v) =>
      (v as pathNode[]).reverse()
    );

    /**
     * construct a inverted index , the key is leaf property key, value is reverse of the path from the root to the leaf property.
     */
    const invertedIndex = new Map<string | number, pathNodes[]>();
    requestPaths.forEach((v) => {
      if (v.length && typeof v[0] === "string") {
        const parentPaths = invertedIndex.get(v[0]);
        if (!parentPaths) {
          invertedIndex.set(v[0], [v.slice(1)]);
        } else {
          parentPaths.push(v.slice(1));
        }
      }
    });

    /**
     * get two paths' common properties' count
     */
    const getMatchedNodeCnt = (baseNode: pathNodes, destNode: pathNodes) => {
      let count = 0;
      baseNode.some((v, k) => {
        if (k < destNode.length && destNode[k] === v) {
          count++;
          return false;
        } else {
          return true;
        }
      });
      return count;
    };

    /**
     * update the property value of response using the same value which is found in the request
     */
    const res = _.mapValuesDeep(
      example.responses,
      (value, key, parentValue, context) => {
        if (!parentValue) {
          log.warn(`parent is null`);
        }
        if (
          ["integer", "number", "string"].some((type) => typeof value === type) &&
          typeof key === "string"
        ) {
          const possiblePaths = invertedIndex.get(key);
          if (context.path && possiblePaths) {
            const basePath = (context.path as pathNodes).slice().reverse().slice(1);

            /**
             * to find out the most matchable path in the parameters
             */
            const candidates = possiblePaths.filter(
              (apiPath) => getMatchedNodeCnt(basePath, apiPath) > 1
            );
            if (candidates.length === 0) {
              return value;
            }
            /**
             * if only one matched one path, just use it.
             */
            if (candidates.length === 1) {
              const pathOfParameter = _.pathToString([key, ...candidates[0]].reverse());
              const parameterValue = _.get(example.parameters, pathOfParameter);
              // console.debug(`use path ${pathOfParameter} ,value :${parameterValue}
              // -- original path:${_.pathToString(context.path as pathNodes)},value:${value}`);
              return parameterValue;
            }
            const mostMatched = candidates.reduce((previous, current) => {
              const countPrevious = getMatchedNodeCnt(basePath, previous);
              const countCurrent = getMatchedNodeCnt(basePath, current);
              return countPrevious < countCurrent ? current : previous;
            });
            return _.get(example.parameters, _.pathToString([key, ...mostMatched].reverse()));
          }
        }
        return value;
      },
      {
        leavesOnly: true,
        pathFormat: "array",
      }
    );
    // console.debug(`unify common properties end!`);
    example.responses = res;
    return example;
  }