HiDeoo / starlight-openapi

Starlight plugin to generate documentation from OpenAPI/Swagger specifications
https://starlight-openapi.vercel.app
MIT License
101 stars 12 forks source link

Schema with recursion hangs building #41

Open inpt333 opened 1 month ago

inpt333 commented 1 month ago

Describe the bug

When I try to build a schema which includes recursion the process hangs.

To Reproduce

Build the following schema:

{
    "openapi": "3.1.0",
    "info": {
        "title": "Performance",
        "version": "1.0"
    },
    "paths": {
        "/treesversions": {
            "put": {
                "description": "Creates a new tree version.",
                "operationId": "PutTreeVersion",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/PutTreeVersionRequestContent"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "PutTreeVersion 200 response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/PutTreeVersionResponseContent"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "ValidationException 400 response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ValidationExceptionResponseContent"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UnauthorizedError 401 response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UnauthorizedErrorResponseContent"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "ForbiddenError 403 response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ForbiddenErrorResponseContent"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "ConflictError 409 response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ConflictErrorResponseContent"
                                }
                            }
                        }
                    },
                    "500": {
                        "description": "InternalServerError 500 response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/InternalServerErrorResponseContent"
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "ServiceUnavailableError 503 response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ServiceUnavailableErrorResponseContent"
                                }
                            }
                        }
                    }
                },
                "x-amazon-apigateway-integration": {
                    "type": "aws_proxy",
                    "httpMethod": "PUT",
                    "uri": ""
                }
            }
        }
    },
    "components": {
        "schemas": {
            "ConflictErrorResponseContent": {
                "type": "object",
                "properties": {
                    "message": {
                        "type": "string"
                    }
                },
                "required": [
                    "message"
                ]
            },
            "ForbiddenErrorResponseContent": {
                "type": "object",
                "properties": {
                    "message": {
                        "type": "string"
                    }
                },
                "required": [
                    "message"
                ]
            },
            "InternalServerErrorResponseContent": {
                "type": "object",
                "properties": {
                    "message": {
                        "type": "string"
                    }
                },
                "required": [
                    "message"
                ]
            },
            "PutTreeVersionRequestContent": {
                "type": "object",
                "properties": {
                    "treeVersionId": {
                        "type": "string",
                        "pattern": "^[A-Za-z0-9-]+$",
                        "description": "Tree version unique ID."
                    },
                    "version": {
                        "type": "string",
                        "description": "Tree version."
                    },
                    "treeId": {
                        "type": "string",
                        "pattern": "^[A-Za-z0-9-]+$",
                        "description": "Tree unique ID."
                    },
                    "treeVersionName": {
                        "type": "string",
                        "description": "Tree version name."
                    },
                    "startDate": {
                        "type": "number",
                        "description": "Tree version start date."
                    },
                    "otherNodeId": {
                        "type": "string",
                        "pattern": "^[A-Za-z0-9-]+$",
                        "description": "Tree version other node unique ID."
                    },
                    "nodes": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/TreeNodeInput"
                        },
                        "description": "Tree nodes."
                    }
                },
                "required": [
                    "nodes",
                    "otherNodeId",
                    "startDate",
                    "treeId",
                    "treeVersionId",
                    "version"
                ]
            },
            "PutTreeVersionResponseContent": {
                "type": "object",
                "properties": {
                    "success": {
                        "type": "boolean",
                        "description": "Tree version creation was successful."
                    }
                }
            },
            "ServiceUnavailableErrorResponseContent": {
                "type": "object",
                "properties": {
                    "message": {
                        "type": "string"
                    }
                },
                "required": [
                    "message"
                ]
            },
            "TreeNodeInput": {
                "type": "object",
                "properties": {
                    "treeNodeId": {
                        "type": "string",
                        "pattern": "^[A-Za-z0-9-]+$",
                        "description": "Tree node unique ID."
                    },
                    "treeNodeName": {
                        "type": "string",
                        "description": "Tree node name."
                    },
                    "nodes": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/TreeNodeInput"
                        },
                        "description": "Tree nodes."
                    },
                    "properties": {
                        "$ref": "#/components/schemas/TreeNodePropertiesInput"
                    }
                },
                "required": [
                    "treeNodeId"
                ]
            },
            "TreeNodePropertiesInput": {
                "type": "object",
                "properties": {
                    "investmentType": {
                        "type": "string",
                        "description": "Tree node investment type."
                    }
                }
            },
            "UnauthorizedErrorResponseContent": {
                "type": "object",
                "properties": {
                    "message": {
                        "type": "string"
                    }
                },
                "required": [
                    "message"
                ]
            },
            "ValidationExceptionField": {
                "type": "object",
                "description": "Describes one specific validation failure for an input member.",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "A JSONPointer expression to the structure member whose value failed to satisfy the modeled constraints."
                    },
                    "message": {
                        "type": "string",
                        "description": "A detailed description of the validation failure."
                    }
                },
                "required": [
                    "message",
                    "path"
                ]
            },
            "ValidationExceptionResponseContent": {
                "type": "object",
                "description": "A standard error for input validation failures.\nThis should be thrown by services when a member of the input structure\nfalls outside of the modeled or documented constraints.",
                "properties": {
                    "message": {
                        "type": "string",
                        "description": "A summary of the validation failure."
                    },
                    "fieldList": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/ValidationExceptionField"
                        },
                        "description": "A list of specific failures encountered while validating the input.\nA member can appear in this list more than once if it failed to satisfy multiple constraints."
                    }
                },
                "required": [
                    "message"
                ]
            }
        },
        "securitySchemes": {
            "aws.auth.sigv4": {
                "type": "apiKey",
                "description": "AWS Signature Version 4 authentication",
                "name": "Authorization",
                "in": "header",
                "x-amazon-apigateway-authtype": "awsSigv4"
            }
        }
    },
    "security": [
        {
            "aws.auth.sigv4": []
        }
    ]
}

The component TreeNodeInput has a child nodes which is an array of the same type.

Expected behavior

Maybe it'd make sense to detect the recursion and stop generating additional levels, so the process doesn't get into infinite recursion.

How often does this bug happen?

Every time

System Info

Version: 0.6.4

Additional Context

No response

HiDeoo commented 1 month ago

Thanks for the feedback 🙌

If possible, that would indeed be a nice enhancement. I remember that the parser we use has various options regarding circular references, so may need to play around with that and see if some of them could help :thumbsup: