Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cyclical dependency in oneOf causes generation to fatal #523

Closed
sey opened this issue Apr 24, 2023 · 1 comment
Closed

Cyclical dependency in oneOf causes generation to fatal #523

sey opened this issue Apr 24, 2023 · 1 comment

Comments

@sey
Copy link

sey commented Apr 24, 2023

I am trying to define a JSON schema for expressions (similar to what Mongo does).

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "$ref": "#/definitions/expression"
  },
  "definitions": {
    "fieldExpression": {
      "type": "object",
      "propertyNames": { "type": "string" },
      "patternProperties": {
        "^.*$": {
          "type": "object",
          "properties": {
            "$eq": { "type": ["boolean", "number", "string", "null"] },
            "$ne": { "type": ["boolean", "number", "string", "null"] },
            "$gt": { "type": ["number", "string"] },
            "$gte": { "type": ["number", "string"] },
            "$lt": { "type": ["number", "string"] },
            "$lte": { "type": ["number", "string"] }
          },
          "minProperties": 1,
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "logicalOperator": {
      "type": "object",
      "oneOf": [
        {
          "properties": {
            "$and": {
              "type": "array",
              "items": { "$ref": "#/definitions/expression" },
              "minItems": 1
            }
          },
          "required": ["$and"],
          "additionalProperties": false
        },
        {
          "properties": {
            "$or": {
              "type": "array",
              "items": { "$ref": "#/definitions/expression" },
              "minItems": 1
            }
          },
          "required": ["$or"],
          "additionalProperties": false
        },
        {
          "properties": {
            "$not": { "$ref": "#/definitions/expression" }
          },
          "required": ["$not"],
          "additionalProperties": false
        }
      ]
    },
    "expression": {
      "oneOf": [
        { "$ref": "#/definitions/fieldExpression" },
        { "$ref": "#/definitions/logicalOperator" }
      ]
    }
  }
}

It fails with

[
  TypeError: Converting circular structure to JSON
      --> starting at object with constructor 'Array'
      |     index 1 -> object with constructor 'Object'
      |     property 'oneOf' -> object with constructor 'Array'
      |     ...
      |     index 0 -> object with constructor 'Object'
      --- property 'oneOf' closes the circle
      at JSON.stringify (<anonymous>)
      at generateRawType (/Users/florian/Developer/repositories/proximus/digital-kyc-onboarding-app/node_modules/json-schema-to-typescript/dist/src/generator.js:171:25)

This is due to logical operator being recursive.

Are you aware of a way to express the recursive aspect in a different way supported by this package?

@sey sey changed the title Circular structure are not supported Circular structures are not supported Apr 24, 2023
@bcherny bcherny changed the title Circular structures are not supported Cyclical dependency in oneOf causes generation to fatal May 4, 2023
@alexmojaki
Copy link

I think your schema is wrong. This:

  "properties": {
    "$ref": "#/definitions/expression"
  },

translates to:

  "properties": {
      "oneOf": [
        { "$ref": "#/definitions/fieldExpression" },
        { "$ref": "#/definitions/logicalOperator" }
      ]
  },

which I think doesn't make sense. I tried three online JSON schema validators and they agreed. So I think this is a +1 for #48.

This works:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "title": "Expression",
  "oneOf": [
    {
      "$ref": "#/definitions/fieldExpression"
    },
    {
      "$ref": "#/definitions/logicalOperator"
    }
  ],
  "definitions": {
    "fieldExpression": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "patternProperties": {
        "^.*$": {
          "type": "object",
          "properties": {
            "$eq": {
              "type": [
                "boolean",
                "number",
                "string",
                "null"
              ]
            },
            "$ne": {
              "type": [
                "boolean",
                "number",
                "string",
                "null"
              ]
            },
            "$gt": {
              "type": [
                "number",
                "string"
              ]
            },
            "$gte": {
              "type": [
                "number",
                "string"
              ]
            },
            "$lt": {
              "type": [
                "number",
                "string"
              ]
            },
            "$lte": {
              "type": [
                "number",
                "string"
              ]
            }
          },
          "minProperties": 1,
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "logicalOperator": {
      "type": "object",
      "oneOf": [
        {
          "properties": {
            "$and": {
              "type": "array",
              "items": {
                "$ref": "#"
              },
              "minItems": 1
            }
          },
          "required": [
            "$and"
          ],
          "additionalProperties": false
        },
        {
          "properties": {
            "$or": {
              "type": "array",
              "items": {
                "$ref": "#"
              },
              "minItems": 1
            }
          },
          "required": [
            "$or"
          ],
          "additionalProperties": false
        },
        {
          "properties": {
            "$not": {
              "$ref": "#"
            }
          },
          "required": [
            "$not"
          ],
          "additionalProperties": false
        }
      ]
    }
  }
}

Output:

/* eslint-disable */
/**
 * This file was automatically generated by json-schema-to-typescript.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
 * and run json-schema-to-typescript to regenerate this file.
 */

export type Expression = FieldExpression | LogicalOperator;
export type LogicalOperator =
  | {
      /**
       * @minItems 1
       */
      $and: [Expression, ...Expression[]];
    }
  | {
      /**
       * @minItems 1
       */
      $or: [Expression, ...Expression[]];
    }
  | {
      $not: Expression;
    };

export interface FieldExpression {
  /**
   * This interface was referenced by `FieldExpression`'s JSON-Schema definition
   * via the `patternProperty` "^.*$".
   */
  [k: string]: {
    $eq?: boolean | number | string | null;
    $ne?: boolean | number | string | null;
    $gt?: number | string;
    $gte?: number | string;
    $lt?: number | string;
    $lte?: number | string;
  };
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants