arbs-io / vscode-openapi-viewer

The vscode-openapi-viewer extension is a valuable tool for developers and architects working with OpenAPI specifications, as it provides a convenient and interactive way to preview, test, and debug APIs within Visual Studio Code.
MIT License
5 stars 1 forks source link

Follow file refs #32

Open BertEzendam opened 1 month ago

BertEzendam commented 1 month ago

It seems that the tool is not following $ref: "".

This whould be apreciated. I have a javascript code that does just that, and I will include it here. Maybe it is possibel to add it to this tool somewhere: `// REPLACEREFS // Explanation: // Function Definition:

// replaceRefs(json) is the main function that takes the JSON object and replaces all $ref references with their corresponding content. // getRefContent(ref, root) is a helper function that takes a $ref string and the root of the JSON structure. It returns the content at the path specified by the $ref. // // Get Reference Content: // If the $ref starts with #, it is treated as an internal reference, and the function proceeds to traverse the path within the JSON structure. // If the $ref does not start with #, it is treated as a file path. The function checks the file extension: // For yaml and yml extensions, it reads and parses the file using yaml.load. // For json extensions, it reads and parses the file using JSON.parse. // If the file extension is unsupported, an error is logged. // The parsed content of the file is then processed with replaceRefs. // Logging statements print each step of the path traversal and file loading process. // // Recursive Traversal: // If the current object (obj) is an array, it iterates over each element and calls recurse on it, passing the array as the parent and the index as the key. // If the current object (obj) is a plain object, it iterates over its keys. // If the key is $ref, it retrieves the new content from the JSON structure or external file using the getRefContent function. // The retrieved new content replaces the entire object entry (parent[key]), and a deep copy of the new content is created to avoid reference issues. // The function then recursively scans the new content for further $ref replacements. // If the key is not $ref, it recursively calls recurse on the value of the key, passing the current object as the parent and the current key. // function replaceRefs(json) { function getRefContent(ref, root) { if (ref.startsWith("#")) { // Internal reference const path = ref.replace(/^#\/?/, "").split("/"); if (verbose) console.log(Resolving $ref: ${ref}); let acc = root; for (const part of path) { if (acc && acc[part] !== undefined) { //console.log(Accessing: ${part} ->, acc[part]); acc = acc[part]; } else { if (warn || verbose) console.warn( Path not found: ${part}, possibly forward referenced ); return undefined; } } return acc; } else { // External file reference try { const fileExtension = ref.split(".").pop(); let fileContent;

    if (fileExtension === "yaml" || fileExtension === "yml") {
      fileContent = yaml.load(fs.readFileSync(ref, "utf8"));
      if (verbose) console.log(`Resolving external $ref YAML file: ${ref}`);
    } else if (fileExtension === "json") {
      fileContent = JSON.parse(fs.readFileSync(ref, "utf8"));
      if (verbose) console.log(`Resolving external $ref JSON file: ${ref}`);
    } else {
      console.error(`Unsupported file extension: ${fileExtension}`);
      return undefined;
    }

    fileContent = replaceRefs(fileContent); // pass 1: Resolve file includes and non forward references
    fileContent = replaceRefs(fileContent); // pass 2: Resolve forward references
    return fileContent;
  } catch (error) {
    console.error(`Error reading or parsing file: ${ref}`, error);
    return undefined;
  }
}

} function recurse(obj, parent, key) { if (typeof obj === "object" && obj !== null) { if (Array.isArray(obj)) { obj.forEach((item, index) => recurse(item, obj, index)); } else { for (let k in obj) { if (obj.hasOwnProperty(k)) { if (k === "$ref") { const newContent = getRefContent(obj[k], json); if (newContent !== undefined) { parent[key] = JSON.parse(JSON.stringify(newContent)); // Deep copy to avoid reference issues recurse(parent[key], parent, key); // Recurse into the new content return; // Return early since the current object has been replaced } } else { recurse(obj[k], obj, k); } } } } } }

recurse(json, null, null); return json; } `

It should be called with the json structure of your openapi document in the following manner: // Read the input file if (fileExtension === ".yaml" || fileExtension === ".yml") { json = yaml.load(contents); } else if (fileExtension === ".json") { json = JSON.parse(contents); } else { console.error(Unsupported input file extension: ${fileExtension}`); process.exit(1); }

// Process the JSON content
json = replaceRefs(json); // pass 1: Resolve file includes and non forward references
json = replaceRefs(json); // pass 2: Resolve forward references
if (verbose) console.log(JSON.parse(JSON.stringify(json)));

`

The 2 pass structure copes with forward references.

The source code (in a txt doc): gendoc - Copy.txt

arbs-io commented 1 month ago

many thanks for raising the issue. Do you have an example openapi specification that demonstrate the issue. it would be very useful for testing