Skip to content
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/thirty-taxis-write.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hey-api/shared": patch
---

**parser**: fix: encode special characters in JSON Pointer
96 changes: 96 additions & 0 deletions packages/shared/src/openApi/2.0.x/parser/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Logger } from '@hey-api/codegen-core';
import type { OpenAPIV2 } from '@hey-api/spec-types';

import { Context } from '../../../../ir/context';
import { parseV2_0_X } from '../index';

function createContext(spec: OpenAPIV2.Document) {
return new Context({
config: {
input: [],
logs: {},
// @ts-expect-error
output: {
case: undefined,
entryFile: false,
path: '',
},
// @ts-expect-error - partial config for testing
parser: {
transforms: {
enums: { case: 'PascalCase', enabled: false, mode: 'root', name: '{{name}}Enum' },
propertiesRequiredByDefault: false,
readWrite: {
enabled: false,
requests: { case: 'preserve', name: '{{name}}Writable' },
responses: { case: 'preserve', name: '{{name}}' },
},
},
},
pluginOrder: [],
plugins: {},
},
dependencies: {},
logger: new Logger(),
spec,
});
}

describe('parseV2_0_X', () => {
it('encodes $ref for definition name containing /', () => {
const spec: OpenAPIV2.Document = {
definitions: {
'node/type': {
properties: {
id: { type: 'string' },
},
type: 'object',
},
},
info: { title: 'Test', version: '1' },
paths: {},
swagger: '2.0',
};
const context = createContext(spec);
parseV2_0_X(context);
expect(context.ir.components?.schemas?.['node/type']).toBeDefined();
});

it('encodes $ref for definition name containing ~', () => {
const spec: OpenAPIV2.Document = {
definitions: {
'type~special': {
properties: {
id: { type: 'string' },
},
type: 'object',
},
},
info: { title: 'Test', version: '1' },
paths: {},
swagger: '2.0',
};
const context = createContext(spec);
parseV2_0_X(context);
expect(context.ir.components?.schemas?.['type~special']).toBeDefined();
});

it('encodes $ref for definition name containing / and ~', () => {
const spec: OpenAPIV2.Document = {
definitions: {
'node/type~special': {
properties: {
id: { type: 'string' },
},
type: 'object',
},
},
info: { title: 'Test', version: '1' },
paths: {},
swagger: '2.0',
};
const context = createContext(spec);
parseV2_0_X(context);
expect(context.ir.components?.schemas?.['node/type~special']).toBeDefined();
});
});
3 changes: 2 additions & 1 deletion packages/shared/src/openApi/2.0.x/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { buildGraph } from '../../../openApi/shared/utils/graph';
import { mergeParametersObjects } from '../../../openApi/shared/utils/parameter';
import { handleValidatorResult } from '../../../openApi/shared/utils/validator';
import { pathToJsonPointer } from '../../../utils/ref';
import { filterSpec } from './filter';
import { parsePathOperation } from './operation';
import { parametersArrayToObject } from './parameter';
Expand Down Expand Up @@ -60,7 +61,7 @@ export const parseV2_0_X = (context: Context<OpenAPIV2.Document>) => {

if (context.spec.definitions) {
for (const name in context.spec.definitions) {
const $ref = `#/definitions/${name}`;
const $ref = pathToJsonPointer(['definitions', name]);
const schema = context.spec.definitions[name]!;

parseSchema({
Expand Down
145 changes: 145 additions & 0 deletions packages/shared/src/openApi/3.0.x/parser/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { Logger } from '@hey-api/codegen-core';
import type { OpenAPIV3 } from '@hey-api/spec-types';

import { Context } from '../../../../ir/context';
import { parseV3_0_X } from '../index';

function createContext(spec: OpenAPIV3.Document) {
return new Context({
config: {
input: [],
logs: {},
// @ts-expect-error
output: {
case: undefined,
entryFile: false,
path: '',
},
// @ts-expect-error - partial config for testing
parser: {
pagination: { keywords: [] },
transforms: {
enums: { case: 'PascalCase', enabled: false, mode: 'root', name: '{{name}}Enum' },
propertiesRequiredByDefault: false,
readWrite: {
enabled: false,
requests: { case: 'preserve', name: '{{name}}Writable' },
responses: { case: 'preserve', name: '{{name}}' },
},
},
},
pluginOrder: [],
plugins: {},
},
dependencies: {},
logger: new Logger(),
spec,
});
}

describe('parseV3_0_X', () => {
it('encodes $ref for schema name containing /', () => {
const spec: OpenAPIV3.Document = {
components: {
schemas: {
'node/type': {
properties: {
id: { type: 'string' },
},
type: 'object',
},
},
},
info: { title: 'Test', version: '1' },
openapi: '3.0.3',
paths: {},
};
const context = createContext(spec);
parseV3_0_X(context);
expect(context.ir.components?.schemas?.['node/type']).toBeDefined();
});

it('encodes $ref for schema name containing ~', () => {
const spec: OpenAPIV3.Document = {
components: {
schemas: {
'type~special': {
properties: {
id: { type: 'string' },
},
type: 'object',
},
},
},
info: { title: 'Test', version: '1' },
openapi: '3.0.3',
paths: {},
};
const context = createContext(spec);
parseV3_0_X(context);
expect(context.ir.components?.schemas?.['type~special']).toBeDefined();
});

it('encodes $ref for schema name containing / and ~', () => {
const spec: OpenAPIV3.Document = {
components: {
schemas: {
'node/type~special': {
properties: {
id: { type: 'string' },
},
type: 'object',
},
},
},
info: { title: 'Test', version: '1' },
openapi: '3.0.3',
paths: {},
};
const context = createContext(spec);
parseV3_0_X(context);
expect(context.ir.components?.schemas?.['node/type~special']).toBeDefined();
});

it('encodes $ref for parameter name containing special characters', () => {
const spec: OpenAPIV3.Document = {
components: {
parameters: {
'param/special~name': {
in: 'query' as const,
name: 'special',
schema: { type: 'string' },
},
},
},
info: { title: 'Test', version: '1' },
openapi: '3.0.3',
paths: {},
};
const context = createContext(spec);
parseV3_0_X(context);
expect(context.ir.components?.parameters?.['param/special~name']).toBeDefined();
});

it('encodes $ref for requestBody name containing special characters', () => {
const spec: OpenAPIV3.Document = {
components: {
requestBodies: {
'body/special~name': {
content: {
'application/json': {
schema: { type: 'object' },
},
},
},
},
},
info: { title: 'Test', version: '1' },
openapi: '3.0.3',
paths: {},
};
const context = createContext(spec);
parseV3_0_X(context);
expect(context.ir.components?.requestBodies?.['body/special~name']).toBeDefined();
});
});
7 changes: 4 additions & 3 deletions packages/shared/src/openApi/3.0.x/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { buildGraph } from '../../../openApi/shared/utils/graph';
import { mergeParametersObjects } from '../../../openApi/shared/utils/parameter';
import { handleValidatorResult } from '../../../openApi/shared/utils/validator';
import { pathToJsonPointer } from '../../../utils/ref';
import { filterSpec } from './filter';
import { parsePathOperation } from './operation';
import { parametersArrayToObject, parseParameter } from './parameter';
Expand Down Expand Up @@ -63,7 +64,7 @@ export const parseV3_0_X = (context: Context<OpenAPIV3.Document>) => {
}

for (const name in context.spec.components.parameters) {
const $ref = `#/components/parameters/${name}`;
const $ref = pathToJsonPointer(['components', 'parameters', name]);
const parameterOrReference = context.spec.components.parameters[name]!;
const parameter =
'$ref' in parameterOrReference
Expand All @@ -78,7 +79,7 @@ export const parseV3_0_X = (context: Context<OpenAPIV3.Document>) => {
}

for (const name in context.spec.components.requestBodies) {
const $ref = `#/components/requestBodies/${name}`;
const $ref = pathToJsonPointer(['components', 'requestBodies', name]);
const requestBodyOrReference = context.spec.components.requestBodies[name]!;
const requestBody =
'$ref' in requestBodyOrReference
Expand All @@ -93,7 +94,7 @@ export const parseV3_0_X = (context: Context<OpenAPIV3.Document>) => {
}

for (const name in context.spec.components.schemas) {
const $ref = `#/components/schemas/${name}`;
const $ref = pathToJsonPointer(['components', 'schemas', name]);
const schema = context.spec.components.schemas[name]!;

parseSchema({
Expand Down
Loading
Loading