Skip to content

Commit

Permalink
fix: figure out a way to uniquely identify shallow copies produced by…
Browse files Browse the repository at this point in the history
… `merge`
  • Loading branch information
brotheroftux committed Sep 26, 2024
1 parent add2c31 commit 65b497d
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 4 deletions.
288 changes: 288 additions & 0 deletions src/__snapshots__/recursiveReferences.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Recursive references in schemas resulting in a cycle between two components are able to be handled by the includer 1`] = `
"<div class="openapi">
# recursiveReferences
## Request
<div class="openapi__requests">
<div class="openapi__request__wrapper" style="--method: var(--dc-openapi-methods-post);margin-bottom: 12px">
<div class="openapi__request">
POST {.openapi__method}
\`\`\`
http://localhost:8080/test
\`\`\`
</div>
Generated server url
</div>
</div>
## Responses
<div class="openapi__response__code__200">
## 200 OK
<div class="openapi-entity">
### Body
{% cut "application/json" %}
\`\`\`json
{
"A": {
"B": {}
}
}
\`\`\`
{% endcut %}
#|||
**Name**
|
**Description**
||
||
A
|
**Type:** [RecurseMiddle](#recursemiddle)
|||#
</div>
<div class="openapi-entity">
### RecurseMiddle
#|||
**Name**
|
**Description**
||
||
B
|
**Type:** [RecurseTop](#recursetop)
|||#
</div>
<div class="openapi-entity">
### RecurseTop
#|||
**Name**
|
**Description**
||
||
A
|
**Type:** [RecurseMiddle](#recursemiddle)
|||#
</div>
</div>
<!-- markdownlint-disable-file -->
</div>"
`;

exports[`Recursive references in schemas resulting in a trivial self-referential cycle are able to be handled by the includer 1`] = `
"<div class="openapi">
# recursiveReferences
## Request
<div class="openapi__requests">
<div class="openapi__request__wrapper" style="--method: var(--dc-openapi-methods-post);margin-bottom: 12px">
<div class="openapi__request">
POST {.openapi__method}
\`\`\`
http://localhost:8080/test
\`\`\`
</div>
Generated server url
</div>
</div>
## Responses
<div class="openapi__response__code__200">
## 200 OK
<div class="openapi-entity">
### Body
{% cut "application/json" %}
\`\`\`json
{
"A": {}
}
\`\`\`
{% endcut %}
#|||
**Name**
|
**Description**
||
||
A
|
**Type:** [RecurseTop](#recursetop)
|||#
</div>
<div class="openapi-entity">
### RecurseTop
#|||
**Name**
|
**Description**
||
||
A
|
**Type:** [RecurseTop](#recursetop)
|||#
</div>
</div>
<!-- markdownlint-disable-file -->
</div>"
`;

exports[`Recursive references in schemas where the cycle itself is not trivially referenced are able to be handled by the includer 1`] = `
"<div class="openapi">
# recursiveReferences
## Request
<div class="openapi__requests">
<div class="openapi__request__wrapper" style="--method: var(--dc-openapi-methods-post);margin-bottom: 12px">
<div class="openapi__request">
POST {.openapi__method}
\`\`\`
http://localhost:8080/test
\`\`\`
</div>
Generated server url
</div>
</div>
## Responses
<div class="openapi__response__code__200">
## 200 OK
<div class="openapi-entity">
### Body
{% cut "application/json" %}
\`\`\`json
{
"A": {}
}
\`\`\`
{% endcut %}
#|||
**Name**
|
**Description**
||
||
A
|
**Type:** [RecurseTop](#recursetop)
|||#
</div>
<div class="openapi-entity">
### RecurseTop
#|||
**Name**
|
**Description**
||
||
A
|
**Type:** [RecurseTop](#recursetop)
|||#
</div>
</div>
<!-- markdownlint-disable-file -->
</div>"
`;
73 changes: 73 additions & 0 deletions src/__tests__/recursiveReferences.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {DocumentBuilder, run} from './__helpers__/run';

const name = 'recursiveReferences';

describe('Recursive references in schemas', () => {
it('resulting in a cycle between two components are able to be handled by the includer', async () => {
const spec = new DocumentBuilder(name)
.component('RecurseTop', {
type: 'object',
properties: {
A: DocumentBuilder.ref('RecurseMiddle'),
},
})
.component('RecurseMiddle', {
type: 'object',
properties: {
B: DocumentBuilder.ref('RecurseTop'),
},
})
.response(200, {
schema: DocumentBuilder.ref('RecurseTop'),
})
.build();

const fs = await run(spec);

const page = fs.match(name);

expect(page).toMatchSnapshot();
});

it('resulting in a trivial self-referential cycle are able to be handled by the includer', async () => {
const spec = new DocumentBuilder(name)
.component('RecurseTop', {
type: 'object',
properties: {
A: DocumentBuilder.ref('RecurseTop'),
},
})
.response(200, {
schema: DocumentBuilder.ref('RecurseTop'),
})
.build();

const fs = await run(spec);

const page = fs.match(name);

expect(page).toMatchSnapshot();
});

test('where the cycle itself is not trivially referenced are able to be handled by the includer', async () => {
const spec = new DocumentBuilder(name)
.component('RecurseTop', {
type: 'object',
properties: {
A: DocumentBuilder.ref('RecurseTop'),
},
})
.response(200, {
schema: {
allOf: [DocumentBuilder.ref('RecurseTop', 'Description override')],
},
})
.build();

const fs = await run(spec);

const page = fs.match(name);

expect(page).toMatchSnapshot();
});
});
1 change: 1 addition & 0 deletions src/includer/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ export type OpenApiIncluderParams = {
export type OpenJSONSchema = JSONSchema6 & {
_runtime?: true;
_emptyDescription?: true;
_shallowCopyOf?: OpenJSONSchema;
example?: unknown;
properties?: {
[key: string]: JSONSchema6Definition & {
Expand Down
7 changes: 6 additions & 1 deletion src/includer/services/refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ function find(value: OpenJSONSchema): string | undefined {
return undefined;
}

const markedShallowCopy = (source: OpenJSONSchema): OpenJSONSchema => ({
...source,
_shallowCopyOf: source._shallowCopyOf ?? source,
});

// unwrapping such samples
// custom:
// additionalProperties:
Expand Down Expand Up @@ -81,7 +86,7 @@ function merge(schema: OpenJSONSchemaDefinition, needToSaveRef = true): OpenJSON
const combiners = value.oneOf || value.allOf || [];

if (combiners.length === 0) {
return {...value};
return markedShallowCopy(value);
}

if (needToSaveRef && combiners.length === 1) {
Expand Down
Loading

0 comments on commit 65b497d

Please sign in to comment.