Skip to content

Commit

Permalink
feat: improves indirect recursion detection (#46)
Browse files Browse the repository at this point in the history
Co-authored-by: HiDeoo <[email protected]>
  • Loading branch information
ribeirobreno and HiDeoo authored Oct 1, 2024
1 parent 957cae3 commit 506675d
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 18 deletions.
10 changes: 10 additions & 0 deletions docs/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ export default defineConfig({
label: 'Animals v2.0',
schema: '../schemas/v2.0/animals.yaml',
},
{
base: 'api/v3/recursive',
label: 'Recursion v3.0',
schema: '../schemas/v3.0/recursive.yaml',
},
{
base: 'api/v3/recursive-simple',
label: 'Simple Recursion v3.0',
schema: '../schemas/v3.0/recursive-simple.yaml',
},
]),
starlightOpenAPIDocsDemoPlugin(),
],
Expand Down
5 changes: 3 additions & 2 deletions packages/starlight-openapi/components/Items.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ interface Props {
items: Items
negated?: boolean | undefined
nullable?: boolean | undefined
parents?: SchemaObject[]
}
const { hideExample, items, negated, nullable } = Astro.props
const { hideExample, items, negated, nullable, parents = [] } = Astro.props
const enumItems = items.enum ?? items.items?.enum
---
Expand Down Expand Up @@ -46,7 +47,7 @@ const enumItems = items.enum ?? items.items?.enum
/>

{enumItems && <Tags label="Allowed values:" tags={enumItems} />}
{items.items && isSchemaObjectObject(items.items) && <SchemaObject nested schemaObject={items.items} {hideExample} />}
{items.items && isSchemaObjectObject(items.items) && <SchemaObject {parents} nested schemaObject={items.items} {hideExample} />}

<style>
.type {
Expand Down
11 changes: 6 additions & 5 deletions packages/starlight-openapi/components/schema/SchemaObject.astro
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ interface Props {
hideExample?: boolean | undefined
negated?: boolean
nested?: boolean
parents?: SchemaObject[]
schemaObject: SchemaObject
}
const { hideExample = false, negated, nested = false, schemaObject } = Astro.props
const { hideExample = false, negated, nested = false, parents = [], schemaObject } = Astro.props
const schemaObjects = getSchemaObjects(schemaObject)
Expand All @@ -32,19 +33,19 @@ const isNegated = schemaObject.not !== undefined

{
hasMany ? (
<SchemaObjects discriminator={schemaObject.discriminator} {nested} {schemaObjects} />
<SchemaObjects {parents} discriminator={schemaObject.discriminator} {nested} {schemaObjects} />
) : isNegated ? (
<Astro.self negated schemaObject={schemaObject.not} />
) : (
<>
<Md text={schemaObject.description} />
<ExternalDocs docs={schemaObject.externalDocs} />
{isSchemaObjectObject(schemaObject) ? (
<SchemaObjectObject {nested} {schemaObject} />
<SchemaObjectObject {parents} {nested} {schemaObject} />
) : isSchemaObjectAllOf(schemaObject) ? (
<SchemaObjectAllOf {schemaObject} />
<SchemaObjectAllOf {parents} {schemaObject} />
) : (
<Items items={schemaObject} {negated} nullable={getNullable(schemaObject)} />
<Items {parents} items={schemaObject} {negated} nullable={getNullable(schemaObject)} />
)}
{!hideExample && schemaObject.example && <Example raw={schemaObject.example} />}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,19 @@ import Items from '../Items.astro'
import SchemaObjectObjectProperties from './SchemaObjectObjectProperties.astro'
interface Props {
parents?: SchemaObject[]
schemaObject: SchemaObject
}
const { schemaObject } = Astro.props
const { schemaObject, parents = [] } = Astro.props
---

{
schemaObject.allOf &&
schemaObject.allOf.map((allOfSchemaObject) =>
isSchemaObjectObject(schemaObject) && isSchemaObject(allOfSchemaObject) ? (
<SchemaObjectObjectProperties
parent={schemaObject}
parents={[...parents, schemaObject]}
properties={getProperties(allOfSchemaObject)}
required={allOfSchemaObject.required}
/>
Expand All @@ -31,6 +32,7 @@ const { schemaObject } = Astro.props
items={allOfSchemaObject}
negated={allOfSchemaObject.not !== undefined}
nullable={getNullable(allOfSchemaObject)}
parents={[...parents, schemaObject]}
/>
) : null,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import SchemaObjectObjectProperties from './SchemaObjectObjectProperties.astro'
interface Props {
nested: boolean
parents?: SchemaObject[]
schemaObject: SchemaObject
}
const { nested, schemaObject } = Astro.props
const { nested, parents = [], schemaObject } = Astro.props
const properties = getProperties(schemaObject)
---
Expand All @@ -30,15 +31,15 @@ const properties = getProperties(schemaObject)
schemaObject.maxProperties && `<= ${schemaObject.maxProperties} properties`,
]}
/>
<SchemaObjectObjectProperties parent={schemaObject} {properties} required={schemaObject.required} />
<SchemaObjectAllOf {schemaObject} />
<SchemaObjectObjectProperties parents={[...parents, schemaObject]} {properties} required={schemaObject.required} />
<SchemaObjectAllOf {parents} {schemaObject} />
{
schemaObject.additionalProperties && (
<Key additional name="key">
{schemaObject.additionalProperties === true ? (
<div class="any">any</div>
) : isAdditionalPropertiesWithSchemaObject(schemaObject.additionalProperties) ? (
<Schema schemaObject={schemaObject.additionalProperties} />
<Schema parents={[...parents, schemaObject]} schemaObject={schemaObject.additionalProperties} />
) : null}
</Key>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@ import Tag from '../Tag.astro'
import Schema from './SchemaObject.astro'
interface Props {
parent: SchemaObject
parents: SchemaObject[]
properties: Properties
required: string[] | undefined
}
const { parent, properties, required } = Astro.props
const { parents, properties, required } = Astro.props
---

{
Object.entries(properties).map(([name, schema]) => (
<Key name={name} required={required?.includes(name)}>
{schema === parent ? (
{parents?.indexOf(schema) >= 0 ? (
<div>
<span class="type">object</span>
<Tag>recursive</Tag>
</div>
) : (
<Schema nested schemaObject={schema} />
<Schema {parents} nested schemaObject={schema} />
)}
</Key>
))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import SchemaObject from './SchemaObject.astro'
interface Props {
discriminator: Discriminator
nested: boolean
parents?: SchemaObject[]
schemaObjects: SchemaObjects
}
const {
discriminator,
nested,
parents = [],
schemaObjects: { schemaObjects, type },
} = Astro.props
Expand All @@ -40,7 +42,7 @@ const humanReadableType: Record<SchemaObjects['type'], string> = {
{
schemaObjects.map((schemaObject) => (
<TabItem label={schemaObject.title ?? getType(schemaObject) ?? 'unknown'}>
<SchemaObject {nested} schemaObject={schemaObject} />
<SchemaObject {parents} {nested} schemaObject={schemaObject} />
</TabItem>
))
}
Expand Down
19 changes: 19 additions & 0 deletions packages/starlight-openapi/tests/recursion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { expect, test } from './test'

test('displays the recursive tag for a recursive category schema', async ({ docPage }) => {
await docPage.goto('/v3/recursive/operations/listcategories')
const okResponse = docPage.getResponse('200')
await expect(okResponse.getByText('recursive')).toHaveCount(1)
})

test('displays the recursive tag for a recursive post schema', async ({ docPage }) => {
await docPage.goto('/v3/recursive/operations/listposts')
const okResponse = docPage.getResponse('200')
await expect(okResponse.getByText('recursive')).toHaveCount(1)
})

test('displays the recursive tag for a simpler recursive category schema', async ({ docPage }) => {
await docPage.goto('/v3/recursive-simple/operations/listcategories')
const okResponse = docPage.getResponse('200')
await expect(okResponse.getByText('recursive')).toHaveCount(1)
})
72 changes: 72 additions & 0 deletions schemas/v3.0/recursive-simple.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
openapi: 3.1.0
info:
title: Test Simple Recursion
description: Example of the simple recursion issue.
version: 1.0.0
servers:
- url: 'http://localhost'
paths:
/categories:
get:
summary: List all categories
operationId: listCategories
parameters:
- name: limit
in: query
description: How many categories to return at one time (max 100)
schema:
type: integer
maximum: 50
format: int32
nullable: true
- name: offset
in: query
description: The number of categories to skip before starting to collect the result set
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of categories
content:
application/json:
schema:
$ref: '#/components/schemas/Categories'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Category:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
parent:
$ref: '#/components/schemas/Category'
Categories:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/Category'
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
Loading

0 comments on commit 506675d

Please sign in to comment.