swagger-api / swagger-parser

Swagger Spec to Java POJOs
http://swagger.io
Apache License 2.0
778 stars 525 forks source link

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

Open rojaranivutukuri opened 1 year ago

rojaranivutukuri commented 1 year ago

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

latyshas commented 1 year ago

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).

thboileau commented 7 months ago

Same issue here.

DavidShiel commented 6 months ago

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!