Skip to content

Commit

Permalink
fix non xOf serialisable object
Browse files Browse the repository at this point in the history
  • Loading branch information
Carmine DiMascio committed Dec 21, 2019
1 parent ad64307 commit dc2c827
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 32 deletions.
97 changes: 68 additions & 29 deletions src/middlewares/openapi.request.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,24 @@ export class RequestValidator {

const validator = this.ajv.compile(schema);
return (req: OpenApiRequest, res: Response, next: NextFunction): void => {

// forcing convert to object if scheme describes param as object + explode
// for easy validation, keep the schema but update whereabouts of its sub components
parameters.parseObjectExplode.forEach(item => {
if (req[item.reqField]) {
// check if there is at least one of the nested properties before create the parent
const atLeastOne = item.properties.some(p => req[item.reqField].hasOwnProperty(p));
const atLeastOne = item.properties.some(p =>
req[item.reqField].hasOwnProperty(p),
);
if (atLeastOne) {
req[item.reqField][item.name] = {};
item.properties.forEach(property => {
// const removeME = schema;
if (req[item.reqField][property]) {
req[item.reqField][item.name][property] = req[item.reqField][property];
const type =
schema.properties[item.reqField].properties[item.name]
.properties[property].type;
[req[item.reqField][item.name]][property] =
req[item.reqField][property];
delete req[item.reqField][property];
}
});
Expand Down Expand Up @@ -319,13 +325,13 @@ export class RequestValidator {
): string[] {
return usedSecuritySchema && securitySchema
? usedSecuritySchema
.filter(obj => Object.entries(obj).length !== 0)
.map(sec => {
const securityKey = Object.keys(sec)[0];
return securitySchema[securityKey];
})
.filter(sec => sec?.in === 'query')
.map(sec => sec.name)
.filter(obj => Object.entries(obj).length !== 0)
.map(sec => {
const securityKey = Object.keys(sec)[0];
return securitySchema[securityKey];
})
.filter(sec => sec?.in === 'query')
.map(sec => sec.name)
: [];
}

Expand Down Expand Up @@ -387,14 +393,11 @@ export class RequestValidator {
} else if ($in === 'query') {
// handle complex json types in schema
const schemaHasObject = schema =>
schema && (
schema.type === 'object' ||
[].concat(
schema.allOf,
schema.oneOf,
schema.anyOf,
).some(schemaHasObject)
);
schema &&
(schema.type === 'object' ||
[]
.concat(schema.allOf, schema.oneOf, schema.anyOf)
.some(schemaHasObject));

if (schemaHasObject(parameterSchema)) {
parseJson.push({ name, reqField });
Expand Down Expand Up @@ -422,24 +425,54 @@ export class RequestValidator {
// handle object serialization in query
if (parameter?.style === 'form' && parameter?.explode === true) {
// fetch the keys used for this kind of explode

const properties = ['allOf', 'oneOf', 'anyOf'].reduce(
(acc, key) => {
if (!parameter.schema.hasOwnProperty(key)) {
const hasXOf =
parameterSchema.allOf ||
parameterSchema.oneOf ||
parameterSchema.anyOf;

const xOfProperties = schema => {
return ['allOf', 'oneOf', 'anyOf'].reduce((acc, key) => {
if (!schema.hasOwnProperty(key)) {
return acc;
} else {
const found_properties = parameter.schema[key].reduce((acc2, obj) => {
return (obj.type === 'object')
const found_properties = schema[key].reduce((acc2, obj) => {
return obj.type === 'object'
? acc2.concat(...Object.keys(obj.properties))
: acc2;
}, []);
return (found_properties.length > 0)
return found_properties.length > 0
? acc.concat(...found_properties)
: acc;
}
},
[],
);
}, []);
};

const properties = hasXOf
? xOfProperties(parameterSchema)
: parameterSchema.type === 'object'
? Object.keys(parameterSchema.properties)
: [];
// const properties = hasXOf
// ? ['allOf', 'oneOf', 'anyOf'].reduce((acc, key) => {
// if (!parameter.schema.hasOwnProperty(key)) {
// return acc;
// } else {
// const found_properties = parameter.schema[key].reduce(
// (acc2, obj) => {
// return obj.type === 'object'
// ? acc2.concat(...Object.keys(obj.properties))
// : acc2;
// },
// [],
// );
// return found_properties.length > 0
// ? acc.concat(...found_properties)
// : acc;
// }
// }, [])
// : parameterSchema.type === 'object'
// ? Object.keys(parameterSchema.properties)
// : [];
parseObjectExplode.push({ reqField, name, properties });
}

Expand All @@ -459,6 +492,12 @@ export class RequestValidator {
}
});

return { schema, parseJson, parseArray, parseArrayExplode, parseObjectExplode };
return {
schema,
parseJson,
parseArray,
parseArrayExplode,
parseObjectExplode,
};
}
}
26 changes: 26 additions & 0 deletions test/resources/serialized.objects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@ servers:
- url: /v1/

paths:
/tags:
get:
summary: "Retrieve all tags"
operationId: getTags
parameters:
- in: query
style: form
name: settings
explode: true
schema:
type: object
properties:
tag_ids:
type: array
items:
type: integer
minimum: 0
minItems: 1
state:
type: string
enum: ["default", "validated", "pending"]
default: "default"
description: "Filter the tags by their validity. The default value ('default') stands for no filtering."
responses:
'200':
description: "An array of tag"
/serialisable:
get:
summary: "Retrieve something"
Expand Down
35 changes: 32 additions & 3 deletions test/serialized.objects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ describe(packageJson.name, () => {
`${app.basePath}`,
express
.Router()
.get(`/serialisable`, (req, res) => res.json(req.query)),
.get(`/serialisable`, (req, res) => res.json(req.query))
.get(`/tags`, (req, res) => res.json(req.query)),
),
);
});
Expand All @@ -31,14 +32,16 @@ describe(packageJson.name, () => {
.query({
onlyValidated: true,
timestamp: '2019-06-24T12:34:56.789Z',
onlySelected: [1, 2, 3],
fooBar: '{"foo":"bar"}',
})
.expect(200)
.then(response => {
console.log(response.body);
expect(response.body).to.deep.equal({
settings: {
onlyValidated: true,
onlySelected: [],
onlySelected: [1, 2, 3],
},
timestamp: '2019-06-24T12:34:56.789Z',
fooBar: {
Expand Down Expand Up @@ -70,6 +73,32 @@ describe(packageJson.name, () => {
})
.expect(400)
.then(response => {
expect(response.body.message).to.equal('request.query.settings should be object');
expect(response.body.message).to.equal(
'request.query.settings should be object',
);
}));

it('should explode query param object e.g. tag_ids, state as query params', async () =>
request(app)
.get(`${app.basePath}/tags`)
.query({
tag_ids: 1,
})
.expect(400)
.then(r => {
expect(r.body)
.to.have.property('message')
.that.equals('request.query.settings.tag_ids should be array');
}));

it.only('should explode query param object e.g. tag_ids, state as query params', async () =>
request(app)
.get(`${app.basePath}/tags`)
.query({
tag_ids: [1],
})
.expect(200)
.then(r => {
console.log(r.body);
}));
});

0 comments on commit dc2c827

Please sign in to comment.