Skip to content

Commit 23a0df8

Browse files
shenbensonstarfy84
authored andcommitted
fix: fix no-$ref-siblings error
Fixes the no-$ref-siblings error seen in this [PR](BitGo/dev-portal#2508) Ticket: DX-1580
1 parent c75b51b commit 23a0df8

File tree

2 files changed

+129
-14
lines changed

2 files changed

+129
-14
lines changed

packages/openapi-generator/src/openapi.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,29 @@ export function schemaToOpenAPI(
5454
const { example, minItems, maxItems, ...rest } = defaultOpenAPIObject;
5555
const isArrayExample = example && Array.isArray(example);
5656

57+
// Handle case where innerSchema is a $ref
58+
const wrappedInnerSchema =
59+
'$ref' in innerSchema
60+
? // When there's a $ref, we need to wrap it in allOf to preserve other properties
61+
{
62+
allOf: [innerSchema],
63+
}
64+
: {
65+
...innerSchema,
66+
};
67+
68+
const items = {
69+
...wrappedInnerSchema,
70+
...rest,
71+
...(!isArrayExample && example ? { example } : {}),
72+
};
73+
5774
return {
5875
type: 'array',
5976
...(minItems ? { minItems } : {}),
6077
...(maxItems ? { maxItems } : {}),
6178
...(isArrayExample ? { example } : {}),
62-
items: {
63-
...innerSchema,
64-
...rest,
65-
...(!isArrayExample && example ? { example } : {}),
66-
},
79+
items,
6780
};
6881
case 'object':
6982
return {
@@ -153,23 +166,25 @@ export function schemaToOpenAPI(
153166
if (oneOf.length === 0) {
154167
return undefined;
155168
} else if (oneOf.length === 1) {
156-
if (
157-
Object.keys(
158-
oneOf[0] as OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject,
159-
)[0] === '$ref'
160-
)
169+
const singleSchema = oneOf[0];
170+
if (singleSchema === undefined) {
171+
return undefined;
172+
}
173+
// Check if the schema is a $ref
174+
if ('$ref' in singleSchema) {
161175
// OpenAPI spec doesn't allow $ref properties to have siblings, so they're wrapped in an 'allOf' array
162176
return {
163177
...(nullable ? { nullable } : {}),
164-
allOf: oneOf,
178+
allOf: [singleSchema],
165179
...defaultOpenAPIObject,
166180
};
167-
else
181+
} else {
168182
return {
169183
...(nullable ? { nullable } : {}),
170-
...oneOf[0],
184+
...singleSchema,
171185
...defaultOpenAPIObject,
172186
};
187+
}
173188
} else {
174189
return { ...(nullable ? { nullable } : {}), oneOf, ...defaultOpenAPIObject };
175190
}

packages/openapi-generator/test/openapi/ref.test.ts

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ const SimpleRouteResponse = t.type({
336336
* @title Human Readable Invalid Error Schema
337337
*/
338338
const InvalidError = t.intersection([
339-
ApiError,
339+
ApiError,
340340
t.type({ error: t.literal('invalid') })]);
341341
/**
342342
* Human readable description of the ApiError schema
@@ -441,3 +441,103 @@ testCase('route with api error schema', ROUTE_WITH_SCHEMA_WITH_COMMENT, {
441441
},
442442
},
443443
});
444+
445+
const ROUTE_WITH_ARRAY_OF_INNER_SCHEMA_REF_AND_DESCRIPTION = `
446+
import * as t from 'io-ts';
447+
import * as h from '@api-ts/io-ts-http';
448+
/**
449+
* A simple route with type descriptions for references
450+
*
451+
* @operationId api.v1.test
452+
* @tag Test Routes
453+
*/
454+
export const route = h.httpRoute({
455+
path: '/foo',
456+
method: 'GET',
457+
request: h.httpRequest({}),
458+
response: {
459+
200: SimpleRouteResponse
460+
},
461+
});
462+
463+
464+
/**
465+
* Human readable description of the Simple Route Response
466+
* @title Human Readable Simple Route Response
467+
*/
468+
const SimpleRouteResponse = t.type({
469+
/** List of test refs */
470+
test: t.array(TestRef)
471+
});
472+
473+
const TestRefBase = t.string;
474+
475+
const TestRef = TestRefBase;
476+
`;
477+
478+
testCase(
479+
'inner schema ref in array with description',
480+
ROUTE_WITH_ARRAY_OF_INNER_SCHEMA_REF_AND_DESCRIPTION,
481+
{
482+
openapi: '3.0.3',
483+
info: {
484+
title: 'Test',
485+
version: '1.0.0',
486+
},
487+
paths: {
488+
'/foo': {
489+
get: {
490+
summary: 'A simple route with type descriptions for references',
491+
operationId: 'api.v1.test',
492+
tags: ['Test Routes'],
493+
parameters: [],
494+
responses: {
495+
'200': {
496+
description: 'OK',
497+
content: {
498+
'application/json': {
499+
schema: {
500+
$ref: '#/components/schemas/SimpleRouteResponse',
501+
},
502+
},
503+
},
504+
},
505+
},
506+
},
507+
},
508+
},
509+
components: {
510+
schemas: {
511+
SimpleRouteResponse: {
512+
description: 'Human readable description of the Simple Route Response',
513+
properties: {
514+
test: {
515+
type: 'array',
516+
items: {
517+
allOf: [{ $ref: '#/components/schemas/TestRefBase' }],
518+
description: 'List of test refs',
519+
},
520+
},
521+
},
522+
required: ['test'],
523+
title: 'Human Readable Simple Route Response',
524+
type: 'object',
525+
},
526+
TestRefBase: {
527+
title: 'TestRefBase',
528+
type: 'string',
529+
},
530+
TestRef: {
531+
allOf: [
532+
{
533+
title: 'TestRef',
534+
},
535+
{
536+
$ref: '#/components/schemas/TestRefBase',
537+
},
538+
],
539+
},
540+
},
541+
},
542+
},
543+
);

0 commit comments

Comments
 (0)