raml-org / webapi-parser

API Spec parser based on AMF. Currently supports RAML 0.8, RAML 1.0, OAS 2.0 and OAS 3.0(beta).
Other
68 stars 24 forks source link

Unresolved reference when converting from OAS 2.0 to RAML 1.0 #73

Open benthurley82 opened 4 years ago

benthurley82 commented 4 years ago

I have been using this API to convert an API spec from OAS 2.0 to RAML 1.0 like so.

WebApiDocument doc = (WebApiDocument) Oas20.parse(oas20).get(); 
raml10 = Raml10.generateString(doc).get();

However, one of the types is not being converted correctly and attempting to use the RAML shows there are errors.

e.g. OAS 2.0

  "definitions": {
    "BaseResponse": {
      "type": "object",
      "properties": {
        "errors": {
          "type": "array",
          "xml": {
            "name": "errors",
            "attribute": false,
            "wrapped": false
          },
          "items": {
            "$ref": "#/definitions/LedgerEntry"
          }
        }
      },
      "title": "BaseResponse"
    },
...
    "LedgerEntry": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "severity": {
          "type": "string",
          "enum": [
            "INFO",
            "WARN",
            "VALIDATION",
            "ERROR",
            "FATAL",
            "CONSTRAINT"
          ]
        }
      },
      "title": "LedgerEntry"
    },

Results in RAML 1.0

  LedgerEntry:
    displayName: LedgerEntry
    type: object
    properties:
      code:
        type: string
        required: false
      description:
        type: string
        required: false
      severity:
        enum:
          - INFO
          - WARN
          - VALIDATION
          - ERROR
          - FATAL
          - CONSTRAINT
        type: string
        required: false
...
  BaseResponse:
    displayName: BaseResponse
    type: object
    properties:
      errors:
        xml:
          attribute: false
          wrapped: false
          name: errors
        type: array
        items: "#/definitions/LedgerEntry"
        required: false

Using the API designer in the Anypoint Design Centre I was able to correct the above RAML to the following:

items: LedgerEntry

My expectation is that the RAML is generated without errors.

Thanks Ben

postatum commented 4 years ago

Hi @benthurley82.

Thanks for reporting this bug. It will be resolved when we update our dependency lib - AMF to 4.0.5. At the moment I can't give you ETA of when this will be done.

For now you can use an API called model resolution. It flattens the model/document but fixes this issues.

It may be performed in two ways, both of which have different pros and cons:

  1. Official way, like so which produces [1]
  2. Unofficial way, as in the code snippet below, which produces [2]:
import amf.client.model.document.Document;
import amf.client.resolve.Resolver;
...
WebApiDocument oasDoc = (WebApiDocument) Oas20.parse("somefile").get();

Resolver res = new Resolver("RAML 1.0");
Document resolvedDoc = (Document) res.resolve(oasDoc, "editing");
WebApiDocument wapDoc = new WebApiDocument();
wapDoc.withEncodes((WebApi) resolvedDoc.encodes());
wapDoc.withDeclares(resolvedDoc.declares());
// Copy other things from the model if necessary 

String ramlStr = Raml10.generateString(wapDoc).get();
System.out.println("Generated RAML string:\n" + ramlStr);

[0] Input OAS

{
  "swagger": "2.0",
  "info": {
    "title": "API with Types",
    "version": ""
  },
  "paths": {
    "/users": {
      "get": {
        "responses": {
          "200": {
            "schema": {
              "$ref": "#/definitions/BaseResponse"
            }
          }
        }
      }
    }
  },
  "definitions": {
    "BaseResponse": {
      "type": "object",
      "properties": {
        "errors": {
          "type": "array",
          "xml": {
            "name": "errors",
            "attribute": false,
            "wrapped": false
          },
          "items": {
            "$ref": "#/definitions/LedgerEntry"
          }
        }
      },
      "title": "BaseResponse"
    },
    "LedgerEntry": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "severity": {
          "type": "string",
          "enum": [
            "INFO",
            "WARN",
            "VALIDATION",
            "ERROR",
            "FATAL",
            "CONSTRAINT"
          ]
        }
      },
      "title": "LedgerEntry"
    }
  }
}

[1]

#%RAML 1.0
title: API with Types
version: ""
/users:
  get:
    responses:
      "200":
        body:
          displayName: BaseResponse
          type: object
          additionalProperties: true
          properties:
            errors:
              xml:
                attribute: false
                wrapped: false
                name: errors
              type: array
              items:
                displayName: LedgerEntry
                type: object
                additionalProperties: true
                properties:
                  code:
                    type: string
                    required: false
                  description:
                    type: string
                    required: false
                  severity:
                    enum:
                      - INFO
                      - WARN
                      - VALIDATION
                      - ERROR
                      - FATAL
                      - CONSTRAINT
                    type: string
                    required: false
              required: false

[2]

#%RAML 1.0
types:
  BaseResponse:
    displayName: BaseResponse
    type: object
    additionalProperties: true
    properties:
      errors:
        xml:
          attribute: false
          wrapped: false
          name: errors
        type: array
        items:
          displayName: LedgerEntry
          type: object
          additionalProperties: true
          properties:
            code:
              type: string
              required: false
            description:
              type: string
              required: false
            severity:
              enum:
                - INFO
                - WARN
                - VALIDATION
                - ERROR
                - FATAL
                - CONSTRAINT
              type: string
              required: false
        required: false
  LedgerEntry:
    displayName: LedgerEntry
    type: object
    additionalProperties: true
    properties:
      code:
        type: string
        required: false
      description:
        type: string
        required: false
      severity:
        enum:
          - INFO
          - WARN
          - VALIDATION
          - ERROR
          - FATAL
          - CONSTRAINT
        type: string
        required: false
title: API with Types
version: ""
/users:
  get:
    responses:
      "200":
        body:
          displayName: BaseResponse
          type: object
          additionalProperties: true
          properties:
            errors:
              xml:
                attribute: false
                wrapped: false
                name: errors
              type: array
              items:
                displayName: LedgerEntry
                type: object
                additionalProperties: true
                properties:
                  code:
                    type: string
                    required: false
                  description:
                    type: string
                    required: false
                  severity:
                    enum:
                      - INFO
                      - WARN
                      - VALIDATION
                      - ERROR
                      - FATAL
                      - CONSTRAINT
                    type: string
                    required: false
              required: false