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

fix: generated $ref schemas when $refed with different descriptions #252

Merged
merged 3 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/itchy-seas-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openapi-ts-json-schema": patch
---

Fix generated `$ref` schemas when `$ref`ed with different descriptions
59 changes: 33 additions & 26 deletions src/openapiToTsJsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,17 @@ export async function openapiToTsJsonSchema(

await clearFolder(outputPath);

const schemaParser = new $RefParser();
const bundledOpenApiSchema = await schemaParser.bundle(openApiSchemaPath);
const openApiParser = new $RefParser();
const jsonSchemaParser = new $RefParser();

// Resolve and inline external $ref definitions
const bundledOpenApiSchema = await openApiParser.bundle(openApiSchemaPath);
// Convert oas definitions to JSON schema
const initialJsonSchema = convertOpenApiToJsonSchema(bundledOpenApiSchema);

const inlinedRefs: Map<string, JSONSchema> = new Map();
const dereferencedJsonSchema = await schemaParser.dereference(
// Inline and collect internal $ref definitions
const dereferencedJsonSchema = await jsonSchemaParser.dereference(
initialJsonSchema,
{
dereference: {
Expand All @@ -88,29 +93,31 @@ export async function openapiToTsJsonSchema(

// Keep track of inlined refs
if (!inlinedRefs.has(id)) {
// Make a shallow copy of the ref schema to save it from the mutations below
inlinedRefs.set(id, { ...inlinedSchema });

/**
* "import" refHandling support:
* mark inlined ref objects with a "SCHEMA_ID_SYMBOL" to retrieve their
* original $ref value once inlined
*/
inlinedSchema[SCHEMA_ID_SYMBOL] = id;

/**
* "inline" refHandling support:
* add a $ref comment to each inlined schema with the original ref value.
* See: https://github.com/kaelzhang/node-comment-json
*/
if (refHandling === 'inline') {
inlinedSchema[Symbol.for('before')] = [
{
type: 'LineComment',
value: ` $ref: "${ref}"`,
},
];
}
// Shallow copy the ref schema to avoid the mutations below
inlinedRefs.set(id, {
// @ts-expect-error Spread types may only be created from object types
...jsonSchemaParser.$refs.get(ref),
});
}

/**
* mark inlined ref objects with a "SCHEMA_ID_SYMBOL"
* to retrieve their id once inlined
*/
inlinedSchema[SCHEMA_ID_SYMBOL] = id;

/**
* "inline" refHandling support:
* add a $ref comment to each inlined schema with the original ref value.
* See: https://github.com/kaelzhang/node-comment-json
*/
if (refHandling === 'inline') {
inlinedSchema[Symbol.for('before')] = [
{
type: 'LineComment',
value: ` $ref: "${ref}"`,
},
];
}
},
},
Expand Down
8 changes: 4 additions & 4 deletions src/utils/convertOpenApiToJsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ function convertToJsonSchema<Value extends unknown>(
}

/**
* Traverse the openAPI schema tree an brutally try to convert everything
* possible to JSON schema. We are probably overdoing since we process any object we find.
* Traverse the openAPI schema tree an brutally try to convert every oas definition
* to JSON schema. We are probably overdoing since we process any found object.
*
* - Is there a way to tell an OpenAPI schema objects convertible to JSON schema from the others?
* - Could we explicitly convert only the properties where we know conversion is needed?
* - Is there a way to tell an OpenAPI definition objects convertible to JSON schema from the others?
* - Could we explicitly convert only the properties that need it?
*
* @TODO Find a nicer way to convert convert all the expected OpenAPI schemas
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { JSONSchema, JSONSchemaWithPlaceholders } from '../../types';

/**
* Get any JSON schema node and:
* - Return ref placeholder is the entity is an inlined ref schema objects (with SCHEMA_ID_SYMBOL prop)
* - Return id placeholder if the entity is an inlined ref schema objects (with SCHEMA_ID_SYMBOL prop)
* - Return provided node in all other cases
*/
function replaceInlinedSchemaWithPlaceholder<Node extends unknown>(
Expand All @@ -21,7 +21,7 @@ function replaceInlinedSchemaWithPlaceholder<Node extends unknown>(
/**
* Iterate a JSON schema to replace inlined ref schema objects
* (marked with a SCHEMA_ID_SYMBOL property holding the original $ref value)
* with a string placeholder with a reference to the original $ref value ("_OTJS-START_/id/value_OTJS-END_")
* with a string placeholder with a reference to their internal id ("_OTJS-START_/id/value_OTJS-END_")
*/
export function replaceInlinedRefsWithStringPlaceholder(
schema: JSONSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { makeRelativeModulePath, PLACEHOLDER_REGEX } from '..';
import type { SchemaMetaDataMap } from '../../types';

/**
* Replace Refs placeholders with imported schemas
* Replace id placeholders with imported schemas
*/
export function replacePlaceholdersWithImportedSchemas({
schemaAsText,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/makeTsJsonSchema/replacePlaceholdersWithRefs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PLACEHOLDER_REGEX } from '..';

/**
* Replace Refs placeholders with original ref objects
* Replace id placeholders with their relevant $ref object
*/
export function replacePlaceholdersWithRefs({
schemaAsText,
Expand Down
2 changes: 2 additions & 0 deletions test/$idMapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ describe('$idMapper option', () => {
description: 'January description',
properties: {
isJanuary: {
description: 'isJanuary description',
enum: ['yes', 'no', null],
type: ['string', 'null'],
},
isFebruary: {
description: 'isFebruary description',
enum: ['yes', 'no', null],
type: ['string', 'null'],
},
Expand Down
4 changes: 2 additions & 2 deletions test/fixtures/ref-property/specs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ components:
- isJanuary
properties:
isJanuary:
# description: isJanuary description
description: isJanuary description
$ref: '#/components/schemas/Answer'
isFebruary:
# description: isFebruary description
description: isFebruary description
$ref: '#/components/schemas/Answer'
Answer:
type: string
Expand Down
2 changes: 2 additions & 0 deletions test/refHandling-inline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ describe('refHandling option === "inline"', () => {
properties: {
isJanuary: {
// $ref: "#/components/schemas/Answer"
description: "isJanuary description",
type: ["string", "null"],
enum: ["yes", "no", null],
},
isFebruary: {
// $ref: "#/components/schemas/Answer"
description: "isFebruary description",
type: ["string", "null"],
enum: ["yes", "no", null],
},
Expand Down
Loading