json-schema-to-typescript icon indicating copy to clipboard operation
json-schema-to-typescript copied to clipboard

2 bugs in conversion of openapi json schema.

Open bvandenbon opened this issue 5 years ago • 4 comments

I tried to convert the json schema defintion for openapi to a schema.t.ts.

At first it looked succesful, but there are several details which are omitted from the final schema. Here are some examples:

The resulting main node is represented by the generated HttpsSpecOpenapisOrgOas30Schema20190402 interface. This interface describes paths, which is correct.

In reality in a json data file, the paths element could look like:

paths:  {
    "/some/random/text": {...}
    "/another/random/text": {...}
 }

By contrast, the generated interface for this paths field is just an empty type.

export interface Paths {}

It should have been more like:

export interface Paths {
  [path: string]: ...
}

The openapi json schema defines it using a reference:

"paths": {
  "$ref": "#/definitions/Paths"
},

This reference to #/definitions/Paths actually points to:

"Paths": {
  "type": "object",
  "patternProperties": {
    "^\\/": {
      "$ref": "#/definitions/PathItem"
    },
    "^x-": {
    }
  },
  "additionalProperties": false
},

I have to admit, there is something special about this case, because it uses a regex for its property names. The regex is used to define that its properties (e.g. /some/random/text) should start with a slash.

Well, right now, it just ommits those properties in the generated interface. But it would make more sense to just allow a string, right ?


Another issue is how the Parameter's are converted. The json schema doc defines a required name field, some other properties and finally an allOf of 3 other types.

"Parameter": {
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    ...
  }
  ...
  "patternProperties": {
    "^x-": {
    }
  },
  "additionalProperties": false,
  "required": [
    "name",
    "in"
  ],
  "allOf": [
    {
      "$ref": "#/definitions/ExampleXORExamples"
    },
    {
      "$ref": "#/definitions/SchemaXORContent"
    },
    {
      "$ref": "#/definitions/ParameterLocation"
    }
  ]
},

Unfortunately, it just generates the following union type:

export type Parameter = ExampleXORExamples & SchemaXORContent & ParameterLocation;

It only used the allOf section, but just forgot all about the properties which were defined on Parameter itself. As a result crucial properties like name are just missing.


I hope this is of use for somebody. 👍 Keep up the good work.

bvandenbon avatar Jun 26 '20 12:06 bvandenbon

Unfortunately many people already reported the second bug you are talking about with no answer/solution by those who contribute...

carlosvfernandez avatar Jun 29 '20 07:06 carlosvfernandez

Hey, thanks for the report! Could you whittle these down to minimal repro cases?

I'm adding this test case to the repo. Hopefully with a minimal repro case we can whittle things down a bit.

bcherny avatar Nov 29 '20 22:11 bcherny

Here's an attempt:

id: https://spec.openapis.org/oas/3.0/schema/2019-04-02
$schema: http://json-schema.org/draft-04/schema#
description: A minimal repro case with one local property and 2 included types.
type: object
properties:
  fooBar:
    $ref: '#/definitions/FooBar'
definitions:
  FooBar:
    type: object
    properties:
      dontForgetMe:
        type: string
    additionalProperties: false
    required:
      - dontForgetMe
    allOf:
      - $ref: '#/definitions/Foo'
      - $ref: '#/definitions/Bar'
  Foo:
    type: object
    properties:
      foo:
        type: string
    additionalProperties: false
  Bar:
    type: object
    properties:
      bar:
        type: string
    additionalProperties: false
  • I would assume that the output should include a Foo a Bar and a FooBar interface (and then one additional one for the root).
  • The FooBar uses an allOf to include the 2 other types, so I would assume that this means that the FooBar interface extends the 2 other interfaces.
  • Then finally, the FooBar also defines an additional property dontForgetMe: string.

So, the result should include these 3 interfaces:

interface Foo {
  foo?: string;
}

interface Bar {
  bar?: string;
}

interface FooBar extends Foo, Bar {
  dontForgetMe?: string;
}

bvandenbon avatar Nov 30 '20 08:11 bvandenbon

Quick update: Second issue should be fixed in v10.0.0.

First issue needs a bit more thought-

  • If we allow a string anytime a schema declares a patternProperty, that means you'll be able to assign invalid values to your schema. JSTT has erred on the side of being conservative here, so far.
  • It could be worth revisiting this, and being more lenient instead (eg. allow string keys, and emit a comment containing the original regex for documentation). Others have proposed this before too.
  • TS template types might change things -- could it be possible to emit TS types that represent some subset of simple regexes?

bcherny avatar Jan 05 '21 07:01 bcherny