swagger-parser icon indicating copy to clipboard operation
swagger-parser copied to clipboard

[BUG] nested references to external schema are not resolved properly in swagger parser v3.

Open rojaranivutukuri opened this issue 1 year ago • 3 comments

I have defined schema in following structure.

├── schema
    ├── com
          ├── wf
               ├── test
                     ├── schema_a.yaml
                     ├── schema_b.yaml

schema_a.yaml:

openapi: 3.0.0
info:
  title: SCHEMA A
  version: 1.0.2
  description: Links
  x-package: com.workfusion.test
components:
  schemas:
    TypeFromB:
      $ref: 'schema/com/wf/test/schema_b.yaml#/components/schemas/MyType'
    UnrelatedType:
      type: object

schema_b.yaml:

openapi: 3.0.0
info:
  title: SCHEMA B
  version: 1.0.2
  description: Links
  x-package: com.workfusion.test
components:
  schemas:
    MyType:
      type: string

and I want to parse the below schema:

openapi: 3.0.0
info:
  title: REF SCHEMA A
  version: 1.0.1
components:
  schemas:
    in:
      type: object
      properties:
        schema_a_ref:
          $ref: 'schema/com/wf/test/schema_a.yaml#/components/schemas/TypeFromB'

parsing using

OpenAPIV3Parser parser = new OpenAPIV3Parser();
ParseOptions options = new ParseOptions();
options.setResolve(true);
return parser.readContents(yamlSchema, null, options, schemaDir.toAbsolutePath().toString());

and I got below error: Unable to load RELATIVE ref: schema\com\wf\test\schema\com\wf\test\schema_b.yaml

rojaranivutukuri avatar Jul 05 '23 11:07 rojaranivutukuri

Hi everyone, it looks the issue inside next method: io.swagger.v3.parser.processors.ExternalRefProcessor#processRefToExternalSchema:

if (isAnExternalRefFormat(ref)) {
                    if (!ref.equals(RefFormat.URL)) {
                        String schemaFullRef = schema.get$ref();
                        String parent = (file.contains("/")) ? file.substring(0, file.lastIndexOf('/')) : "";
                        if (!parent.isEmpty() && !schemaFullRef.startsWith("/")) {
                            if (schemaFullRef.contains("#/")) {
                                String[] parts = schemaFullRef.split("#/");
                                String schemaFullRefFilePart = parts[0];
                                String schemaFullRefInternalRefPart = parts[1];
                                schemaFullRef = Paths.get(parent, schemaFullRefFilePart).normalize().toString() + "#/" + schemaFullRefInternalRefPart;
                            } else {
                                schemaFullRef = Paths.get(parent, schemaFullRef).normalize().toString();
                            }
                        }
                        schema.set$ref(processRefToExternalSchema(schemaFullRef, ref));
                    }
                }

Problem is that schema.get$ref() return string with value: ./schema/com/wf/test/schema_b.yaml#/components/schemas/MyType

parent value will be : ./schema/com/wf/test/ schemaFullRefFilePart : ./schema/com/wf/test/schema_b.yaml and root cause: Paths.get(parent, schemaFullRefFilePart).normalize().toString() will return: schema\com\wf\test\schema\com\wf\test\schema_b.yaml

Looks like one check was missed here: if schemaFullRefFilePart contains parent path - use only schemaFullRefFilePart otherwise use Paths.get(parent, schemaFullRef).

latyshas avatar Jul 18 '23 14:07 latyshas

Same issue here.

thboileau avatar Jan 30 '24 12:01 thboileau

Same issue encountered here too; https://github.com/swagger-api/swagger-parser/blob/master/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ExternalRefProcessor.java#L122-L143

For me RefFormat ref is calculated as RefFormat.URL, but there's no logic to process these types of references.

Wondering if the code should be something like the following so that schema.set$ref(processRefToExternalSchema(schemaFullRef, ref)) is called for all external references?

if (schema.get$ref() != null) {
    RefFormat ref = computeRefFormat(schema.get$ref());
    if (isAnExternalRefFormat(ref)) {
        String schemaFullRef = schema.get$ref();
        if (!ref.equals(RefFormat.URL)) {
            String parent = (file.contains("/")) ? file.substring(0, file.lastIndexOf('/')) : "";
            if (!parent.isEmpty() && !schemaFullRef.startsWith("/")) {
                if (schemaFullRef.contains("#/")) {
                    String[] parts = schemaFullRef.split("#/");
                    String schemaFullRefFilePart = parts[0];
                    String schemaFullRefInternalRefPart = parts[1];
                    schemaFullRef = Paths.get(parent, schemaFullRefFilePart).normalize().toString() + "#/" + schemaFullRefInternalRefPart;
                } else {
                    schemaFullRef = Paths.get(parent, schemaFullRef).normalize().toString();
                }
            }
        }
        schema.set$ref(processRefToExternalSchema(schemaFullRef, ref));
    } else {
        processRefToExternalSchema(file + schema.get$ref(), RefFormat.RELATIVE);
    }
}

Any sign of this being fixed? Thanks!

DavidShiel avatar Feb 09 '24 15:02 DavidShiel