Skip to content

Commit 77bc4ae

Browse files
Fix #699 serdes missed on items in a collection, with tests. (#704)
Thanks @Fabiencdp.
1 parent 2bbed6f commit 77bc4ae

File tree

3 files changed

+460
-2
lines changed

3 files changed

+460
-2
lines changed

src/middlewares/parsers/schema.preprocessor.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type Schema = ReferenceObject | SchemaObject;
5656
if (!Array.prototype['flatMap']) {
5757
// polyfill flatMap
5858
// TODO remove me when dropping node 10 support
59-
Array.prototype['flatMap'] = function (lambda) {
59+
Array.prototype['flatMap'] = function(lambda) {
6060
return Array.prototype.concat.apply([], this.map(lambda));
6161
};
6262
Object.defineProperty(Array.prototype, 'flatMap', { enumerable: false });
@@ -199,6 +199,9 @@ export class SchemaPreprocessor {
199199
const child = new Node(node, s, [...node.path, 'anyOf', i + '']);
200200
recurse(node, child, opts);
201201
});
202+
} else if (schema.type === 'array' && schema.items) {
203+
const child = new Node(node, schema.items, [...node.path, 'items']);
204+
recurse(node, child, opts);
202205
} else if (schema.properties) {
203206
Object.entries(schema.properties).forEach(([id, cschema]) => {
204207
const path = [...node.path, 'properties', id];
@@ -320,7 +323,7 @@ export class SchemaPreprocessor {
320323
...(o.properties ?? {}),
321324
...(newSchema.properties ?? {}),
322325
};
323-
if(Object.keys(newProperties).length > 0) {
326+
if (Object.keys(newProperties).length > 0) {
324327
newSchema.properties = newProperties;
325328
}
326329

test/699.spec.ts

+353
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
import * as path from 'path';
2+
import { expect } from 'chai';
3+
import * as request from 'supertest';
4+
import { createApp } from './common/app';
5+
6+
import { date, dateTime } from '../src/framework/base.serdes';
7+
8+
const apiSpecPath = path.join('test', 'resources', '699.yaml');
9+
10+
class ObjectID {
11+
id: string;
12+
13+
constructor(id: string = "5fdefd13a6640bb5fb5fa925") {
14+
this.id = id;
15+
}
16+
17+
toString() {
18+
return this.id;
19+
}
20+
21+
}
22+
23+
class BadDate extends Date {
24+
public toISOString(): string {
25+
return "oh no a bad iso date";
26+
}
27+
}
28+
29+
describe('699', () => {
30+
let app = null;
31+
32+
before(async () => {
33+
// set up express app
34+
app = await createApp(
35+
{
36+
apiSpec: apiSpecPath,
37+
validateRequests: {
38+
coerceTypes: true
39+
},
40+
validateResponses: {
41+
coerceTypes: true
42+
},
43+
serDes: [
44+
date,
45+
dateTime,
46+
{
47+
format: "mongo-objectid",
48+
deserialize: (s) => new ObjectID(s),
49+
serialize: (o) => o.toString(),
50+
},
51+
],
52+
unknownFormats: ['string-list'],
53+
},
54+
3005,
55+
(app) => {
56+
app.get([`${app.basePath}/users/:id?`], (req, res) => {
57+
if (typeof req.params.id !== 'object') {
58+
throw new Error("Should be deserialized to ObjectId object");
59+
}
60+
let date = new Date("2020-12-20T07:28:19.213Z");
61+
res.json({
62+
id: req.params.id,
63+
creationDateTime: date,
64+
creationDate: date,
65+
shortOrLong: 'a',
66+
history: [{ modificationDate: date }],
67+
historyWithoutRef: [{ modificationDate: date }],
68+
});
69+
});
70+
app.post([`${app.basePath}/users`], (req, res) => {
71+
if (typeof req.body.history[0].modificationDate !== 'object' || !(req.body.history[0].modificationDate instanceof Date)) {
72+
throw new Error("Should be deserialized to Date object");
73+
}
74+
if (typeof req.body.historyWithoutRef[0].modificationDate !== 'object' || !(req.body.historyWithoutRef[0].modificationDate instanceof Date)) {
75+
throw new Error("Should be deserialized to Date object");
76+
}
77+
res.json(req.body);
78+
});
79+
app.use((err, req, res, next) => {
80+
res.status(err.status ?? 500).json({
81+
message: err.message,
82+
code: err.status ?? 500,
83+
});
84+
});
85+
},
86+
false,
87+
);
88+
return app
89+
});
90+
91+
after(() => {
92+
app.server.close();
93+
});
94+
95+
it('should control GOOD id format and get a response in expected format', async () =>
96+
request(app)
97+
.get(`${app.basePath}/users/5fdefd13a6640bb5fb5fa925`)
98+
.expect(200)
99+
.then((r) => {
100+
expect(r.body.history[0].modificationDate).to.equal("2020-12-20");
101+
expect(r.body.historyWithoutRef[0].modificationDate).to.equal("2020-12-20");
102+
}));
103+
104+
it('should POST also works with deserialize on request then serialize en response', async () =>
105+
request(app)
106+
.post(`${app.basePath}/users`)
107+
.send({
108+
id: '5fdefd13a6640bb5fb5fa925',
109+
creationDateTime: '2020-12-20T07:28:19.213Z',
110+
creationDate: '2020-12-20',
111+
shortOrLong: 'ab',
112+
history: [{ modificationDate: '2020-12-20' }],
113+
historyWithoutRef: [{ modificationDate: '2020-12-20' }],
114+
})
115+
.set('Content-Type', 'application/json')
116+
.expect(200)
117+
.then((r) => {
118+
expect(r.body.history[0].modificationDate).to.equal("2020-12-20");
119+
expect(r.body.historyWithoutRef[0].modificationDate).to.equal("2020-12-20");
120+
}));
121+
122+
it('should POST throw error on invalid schema Date', async () =>
123+
request(app)
124+
.post(`${app.basePath}/users`)
125+
.send({
126+
id: '5fdefd13a6640bb5fb5fa925',
127+
creationDateTime: '2020-12-20T07:28:19.213Z',
128+
creationDate: '2020-12-20',
129+
history: [{ modificationDate: '2020-1f-20' }],
130+
historyWithoutRef: [{ modificationDate: '2020-12-20' }],
131+
})
132+
.set('Content-Type', 'application/json')
133+
.expect(400)
134+
.then((r) => {
135+
expect(r.body.message).to.equal('request/body/history/0/modificationDate must match format "date"');
136+
}));
137+
138+
it('should POST throw error on invalid schema Date', async () =>
139+
request(app)
140+
.post(`${app.basePath}/users`)
141+
.send({
142+
id: '5fdefd13a6640bb5fb5fa925',
143+
creationDateTime: '2020-12-20T07:28:19.213Z',
144+
creationDate: '2020-12-20',
145+
history: [{ modificationDate: '2020-12-20' }],
146+
historyWithoutRef: [{ modificationDate: '2020-1f-20' }],
147+
})
148+
.set('Content-Type', 'application/json')
149+
.expect(400)
150+
.then((r) => {
151+
expect(r.body.message).to.equal('request/body/historyWithoutRef/0/modificationDate must match format "date"');
152+
}));
153+
154+
});
155+
156+
157+
158+
describe('699 serialize response components only', () => {
159+
let app = null;
160+
161+
before(async () => {
162+
// set up express app
163+
app = await createApp(
164+
{
165+
apiSpec: apiSpecPath,
166+
validateRequests: {
167+
coerceTypes: true
168+
},
169+
validateResponses: {
170+
coerceTypes: true
171+
},
172+
serDes: [
173+
date.serializer,
174+
dateTime.serializer,
175+
{
176+
format: "mongo-objectid",
177+
serialize: (o) => o.toString(),
178+
},
179+
],
180+
unknownFormats: ['mongo-objectid', 'string-list'],
181+
},
182+
3005,
183+
(app) => {
184+
app.get([`${app.basePath}/users/:id?`], (req, res) => {
185+
debugger;
186+
if (typeof req.params.id !== 'string') {
187+
throw new Error("Should be not be deserialized to ObjectId object");
188+
}
189+
let date = new Date("2020-12-20T07:28:19.213Z");
190+
let result = {
191+
id: new ObjectID(req.params.id),
192+
creationDateTime: date,
193+
creationDate: date,
194+
shortOrLong: 'a',
195+
history: [{ modificationDate: undefined }],
196+
historyWithoutRef: [{ modificationDate: undefined }],
197+
};
198+
if (req.query.baddateresponse === 'functionNotExists') {
199+
result.history[0].modificationDate = new ObjectID();
200+
result.historyWithoutRef[0].modificationDate = date;
201+
}
202+
else if (req.query.baddateresponse === 'functionNotExistsWithoutRef') {
203+
result.history[0].modificationDate = date;
204+
result.historyWithoutRef[0].modificationDate = new ObjectID();
205+
}
206+
else if (req.query.baddateresponse === 'functionBadFormat') {
207+
result.history[0].modificationDate = new BadDate();
208+
result.historyWithoutRef[0].modificationDate = date;
209+
}
210+
else if (req.query.baddateresponse === 'functionBadFormatWithoutRef') {
211+
result.history[0].modificationDate = date;
212+
result.historyWithoutRef[0].modificationDate = new BadDate();
213+
}
214+
else {
215+
result.history[0].modificationDate = date;
216+
result.historyWithoutRef[0].modificationDate = date;
217+
}
218+
res.json(result);
219+
});
220+
app.post([`${app.basePath}/users`], (req, res) => {
221+
if (typeof req.body.id !== 'string') {
222+
throw new Error("Should NOT be deserialized to ObjectId object");
223+
}
224+
if (typeof req.body.history[0].modificationDate !== 'string') {
225+
throw new Error("Should NTO be deserialized to Date object");
226+
}
227+
if (typeof req.body.historyWithoutRef[0].modificationDate !== 'string') {
228+
throw new Error("Should NOT be deserialized to Date object");
229+
}
230+
req.body.id = new ObjectID(req.body.id);
231+
req.body.creationDateTime = new Date(req.body.creationDateTime);
232+
req.body.history[0].modificationDate = new Date(req.body.history[0].modificationDate);
233+
req.body.historyWithoutRef[0].modificationDate = new Date(req.body.historyWithoutRef[0].modificationDate);
234+
// We let creationDate et al as String and it should also work (either in Date Object ou String 'date' format)
235+
res.json(req.body);
236+
});
237+
app.use((err, req, res, next) => {
238+
res.status(err.status ?? 500).json({
239+
message: err.message,
240+
code: err.status ?? 500,
241+
});
242+
});
243+
},
244+
false,
245+
);
246+
return app
247+
});
248+
249+
after(() => {
250+
app.server.close();
251+
});
252+
253+
it('should control GOOD id format and get a response in expected format', async () =>
254+
request(app)
255+
.get(`${app.basePath}/users/5fdefd13a6640bb5fb5fa925`)
256+
.expect(200)
257+
.then((r) => {
258+
expect(r.body.id).to.equal('5fdefd13a6640bb5fb5fa925');
259+
expect(r.body.creationDate).to.equal('2020-12-20');
260+
expect(r.body.creationDateTime).to.equal("2020-12-20T07:28:19.213Z");
261+
expect(r.body.history[0].modificationDate).to.equal("2020-12-20");
262+
expect(r.body.historyWithoutRef[0].modificationDate).to.equal("2020-12-20");
263+
}));
264+
265+
it('should POST also works with deserialize on request then serialize en response', async () =>
266+
request(app)
267+
.post(`${app.basePath}/users`)
268+
.send({
269+
id: '5fdefd13a6640bb5fb5fa925',
270+
creationDateTime: '2020-12-20T07:28:19.213Z',
271+
creationDate: '2020-12-20',
272+
history: [{ modificationDate: '2020-12-20' }],
273+
historyWithoutRef: [{ modificationDate: '2020-12-20' }],
274+
})
275+
.set('Content-Type', 'application/json')
276+
.expect(200)
277+
.then((r) => {
278+
expect(r.body.history[0].modificationDate).to.equal("2020-12-20");
279+
expect(r.body.historyWithoutRef[0].modificationDate).to.equal("2020-12-20");
280+
}));
281+
282+
it('should POST throw error on invalid schema Date', async () =>
283+
request(app)
284+
.post(`${app.basePath}/users`)
285+
.send({
286+
id: '5fdefd13a6640bb5fb5fa925',
287+
creationDateTime: '2020-12-20T07:28:19.213Z',
288+
creationDate: '2020-12-20',
289+
history: [{ modificationDate: '2020-1f-20' }],
290+
historyWithoutRef: [{ modificationDate: '2020-12-20' }],
291+
})
292+
.set('Content-Type', 'application/json')
293+
.expect(400)
294+
.then((r) => {
295+
expect(r.body.message).to.equal('request/body/history/0/modificationDate must match format "date"');
296+
}));
297+
298+
it('should POST throw error on invalid schema Date', async () =>
299+
request(app)
300+
.post(`${app.basePath}/users`)
301+
.send({
302+
id: '5fdefd13a6640bb5fb5fa925',
303+
creationDateTime: '2020-12-20T07:28:19.213Z',
304+
creationDate: '2020-12-20',
305+
history: [{ modificationDate: '2020-12-20' }],
306+
historyWithoutRef: [{ modificationDate: '2020-1f-20' }],
307+
})
308+
.set('Content-Type', 'application/json')
309+
.expect(400)
310+
.then((r) => {
311+
expect(r.body.message).to.equal('request/body/historyWithoutRef/0/modificationDate must match format "date"');
312+
}));
313+
314+
it('should throw error 500 on invalid object type instead of Date expected', async () =>
315+
request(app)
316+
.get(`${app.basePath}/users/5fdefd13a6640bb5fb5fa925`)
317+
.query({ baddateresponse: 'functionNotExists' })
318+
.expect(500)
319+
.then((r) => {
320+
expect(r.body.message).to.equal(
321+
'/response/history/0/modificationDate format is invalid',
322+
);
323+
}));
324+
325+
it('should throw error 500 on invalid object type instead of Date expected', async () =>
326+
request(app)
327+
.get(`${app.basePath}/users/5fdefd13a6640bb5fb5fa925`)
328+
.query({ baddateresponse: 'functionNotExistsWithoutRef' })
329+
.expect(500)
330+
.then((r) => {
331+
expect(r.body.message).to.equal(
332+
'/response/historyWithoutRef/0/modificationDate format is invalid',
333+
);
334+
}));
335+
336+
// This FIXME from serdes.spec.ts
337+
/*
338+
FIXME Manage format validation after serialize ? I can serialize using a working serialize method but that respond a bad format
339+
it('should throw error 500 on an object that serialize to a bad string format', async () =>
340+
341+
request(app)
342+
.get(`${app.basePath}/users/5fdefd13a6640bb5fb5fa925`)
343+
.query({baddateresponse : 'functionBadFormat'})
344+
.expect(200)
345+
.then((r) => {
346+
expect(r.body.message).to.equal('Something saying that date is not date-time format');
347+
}));
348+
349+
*/
350+
351+
});
352+
353+

0 commit comments

Comments
 (0)