Skip to content

Commit

Permalink
fix: edge case where component would sometimes be marked a string (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
erunion authored Apr 30, 2020
1 parent 35b8e3c commit 1254603
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 36 deletions.
84 changes: 63 additions & 21 deletions packages/tooling/__tests__/lib/parameters-to-json-schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -834,35 +834,77 @@ describe('type', () => {
});
});

it('should repair an invalid schema that has no `type` as just a simple string', () => {
const schema = parametersToJsonSchema(
{
requestBody: {
content: {
'application/json': {
schema: {
type: 'object',
properties: {
host: {
description: 'Host name to check validity of.',
describe('repair invalid schema that has no `type`', () => {
it('should repair an invalid schema that has no `type` as just a simple string', () => {
const schema = parametersToJsonSchema(
{
requestBody: {
content: {
'application/json': {
schema: {
type: 'object',
properties: {
host: {
description: 'Host name to check validity of.',
},
},
},
},
},
},
},
},
{}
);
{}
);

expect(schema[0].schema).toStrictEqual({
properties: {
host: {
description: 'Host name to check validity of.',
type: 'string',
expect(schema[0].schema).toStrictEqual({
properties: {
host: {
description: 'Host name to check validity of.',
type: 'string',
},
},
},
type: 'object',
type: 'object',
});
});

it('should not add a string type on a ref and component schema that are clearly objects', () => {
const schema = parametersToJsonSchema(
{
requestBody: {
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/NewUser',
},
},
},
},
},
{
components: {
schemas: {
ErrorResponse: {
properties: {
message: {
type: 'string',
},
},
},
NewUser: {
required: ['user_id'],
properties: {
user_id: {
type: 'integer',
},
},
},
},
},
}
);

expect(schema[0].schema.components.schemas.ErrorResponse.type).toBe('object');
expect(schema[0].schema.components.schemas.NewUser.type).toBe('object');
});
});
});
Expand Down
40 changes: 25 additions & 15 deletions packages/tooling/src/lib/parameters-to-json-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ function getBodyParam(pathOperation, oas) {

const cleanupSchemaDefaults = (obj, prevProp = false, prevProps = []) => {
Object.keys(obj).forEach(prop => {
// Since this method is recursive, let's reset our states when we're first processing a new property tree.
if (!prevProp) {
prevProps = [];
}

if (obj[prop] === null) {
// If the item is null, just carry on. Why do this in addition to `typeof obj[prop] == object`? Because
// `typeof null` equates to `object` for "legacy reasons" apparently.
Expand Down Expand Up @@ -77,15 +82,10 @@ function getBodyParam(pathOperation, oas) {
if (!prevProp) {
// If we have no previous prop, then we're processing a top-level title and description on a requestBody.
delete obj[prop];
} else if (
prevProp !== false &&
prevProps.includes('schemas') &&
'components' in oas &&
'schemas' in oas.components &&
prevProp in oas.components.schemas
) {
// If we have a previous prop, but we're parsing the immediate schemas tree in the components object,
// then we're processing a title and description on a component schema.
} else if (prevProp !== false && prevProps.length === 1) {
// If we have a previous prop, but we're parsing the immediate schemas tree (we know this if prevProps
// only has a single entry as that entry will be the name of the schema!) in the components object, then
// we're processing a title and description on a component schema.
delete obj[prop];
}
break;
Expand Down Expand Up @@ -128,13 +128,23 @@ function getBodyParam(pathOperation, oas) {
};

const type = schema.type === 'application/x-www-form-urlencoded' ? 'formData' : 'body';
let cleanedSchema;

const cleanedSchema = oas.components
? {
components: cleanupSchemaDefaults(oas.components),
...cleanupSchemaDefaults(schema.schema),
}
: cleanupSchemaDefaults(schema.schema);
if (oas.components) {
cleanedSchema = {
components: {},
...cleanupSchemaDefaults(schema.schema),
};

// Since cleanupSchemaDefaults is a recursive method, it's best if we start it at the `components.schemas` level
// so we have immediate knowledge of when we're first processing a component schema, and can reset our internal
// prop states that keep track of how we should treat certain prop edge cases.
Object.keys(oas.components).forEach(componentType => {
cleanedSchema.components[componentType] = cleanupSchemaDefaults(oas.components[componentType]);
});
} else {
cleanedSchema = cleanupSchemaDefaults(schema.schema);
}

// If there's not actually any data within this schema, don't bother returning it.
if (Object.keys(cleanedSchema).length === 0) {
Expand Down

0 comments on commit 1254603

Please sign in to comment.