Skip to content

Commit 40628db

Browse files
author
vikasrohit
authored
Merge pull request #298 from topcoder-platform/feature/project-product-templates-final-fixes
Feature/project product templates final fixes
2 parents 29ed9d4 + 25474df commit 40628db

File tree

9 files changed

+275
-213
lines changed

9 files changed

+275
-213
lines changed

src/routes/productTemplates/create.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ const schema = {
2323
brief: Joi.string().max(45).required(),
2424
details: Joi.string().max(255).required(),
2525
aliases: Joi.array().required(),
26-
template: Joi.object(),
27-
form: Joi.object(),
26+
template: Joi.object().empty(null),
27+
form: Joi.object().keys({
28+
key: Joi.string().required(),
29+
version: Joi.number(),
30+
}).empty(null),
2831
disabled: Joi.boolean().optional(),
2932
hidden: Joi.boolean().optional(),
3033
isAddOn: Joi.boolean().optional(),
@@ -34,7 +37,9 @@ const schema = {
3437
createdBy: Joi.any().strip(),
3538
updatedBy: Joi.any().strip(),
3639
deletedBy: Joi.any().strip(),
37-
}).required(),
40+
})
41+
.xor('form', 'template')
42+
.required(),
3843
},
3944
};
4045

src/routes/productTemplates/create.spec.js

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,18 @@ describe('CREATE product template', () => {
8989
},
9090
};
9191

92-
const bodyWithForm = _.cloneDeep(body);
93-
bodyWithForm.param.form = {
92+
const bodyDefinedFormTemplate = _.cloneDeep(body);
93+
bodyDefinedFormTemplate.param.form = {
9494
version: 1,
9595
key: 'dev',
9696
};
97+
98+
const bodyWithForm = _.cloneDeep(bodyDefinedFormTemplate);
9799
delete bodyWithForm.param.template;
98100

101+
const bodyMissingFormTemplate = _.cloneDeep(bodyWithForm);
102+
delete bodyMissingFormTemplate.param.form;
103+
99104
const bodyInvalidForm = _.cloneDeep(body);
100105
bodyInvalidForm.param.form = {
101106
version: 1,
@@ -277,5 +282,27 @@ describe('CREATE product template', () => {
277282
.send(bodyInvalidForm)
278283
.expect(422, done);
279284
});
285+
286+
it('should return 422 if both form or template field are defined', (done) => {
287+
request(server)
288+
.post('/v4/projects/metadata/productTemplates')
289+
.set({
290+
Authorization: `Bearer ${testUtil.jwts.admin}`,
291+
})
292+
.send(bodyDefinedFormTemplate)
293+
.expect('Content-Type', /json/)
294+
.expect(422, done);
295+
});
296+
297+
it('should return 422 if both form or template field are missing', (done) => {
298+
request(server)
299+
.post('/v4/projects/metadata/productTemplates')
300+
.set({
301+
Authorization: `Bearer ${testUtil.jwts.admin}`,
302+
})
303+
.send(bodyMissingFormTemplate)
304+
.expect('Content-Type', /json/)
305+
.expect(422, done);
306+
});
280307
});
281308
});

src/routes/productTemplates/update.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ const schema = {
2626
brief: Joi.string().max(45),
2727
details: Joi.string().max(255),
2828
aliases: Joi.array(),
29-
template: Joi.object(),
30-
form: Joi.object(),
29+
template: Joi.object().empty(null),
30+
form: Joi.object().keys({
31+
key: Joi.string().required(),
32+
version: Joi.number(),
33+
}).empty(null),
3134
disabled: Joi.boolean().optional(),
3235
hidden: Joi.boolean().optional(),
3336
isAddOn: Joi.boolean().optional(),
@@ -37,7 +40,9 @@ const schema = {
3740
createdBy: Joi.any().strip(),
3841
updatedBy: Joi.any().strip(),
3942
deletedBy: Joi.any().strip(),
40-
}).required(),
43+
})
44+
.xor('form', 'template')
45+
.required(),
4146
},
4247
};
4348

src/routes/productTemplates/update.spec.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,18 @@ describe('UPDATE product template', () => {
133133
},
134134
};
135135

136-
const bodyWithForm = _.cloneDeep(body);
137-
bodyWithForm.param.form = {
136+
const bodyDefinedFormTemplate = _.cloneDeep(body);
137+
bodyDefinedFormTemplate.param.form = {
138138
version: 1,
139139
key: 'dev',
140140
};
141141

142+
const bodyWithForm = _.cloneDeep(bodyDefinedFormTemplate);
143+
delete bodyWithForm.param.template;
144+
145+
const bodyMissingFormTemplate = _.cloneDeep(bodyWithForm);
146+
delete bodyMissingFormTemplate.param.form;
147+
142148
const bodyInvalidForm = _.cloneDeep(body);
143149
bodyInvalidForm.param.form = {
144150
version: 1,
@@ -312,5 +318,25 @@ describe('UPDATE product template', () => {
312318
.send(bodyInvalidForm)
313319
.expect(422, done);
314320
});
321+
322+
it('should return 422 if both form or template field are defined', (done) => {
323+
request(server)
324+
.patch(`/v4/projects/metadata/productTemplates/${templateId}`)
325+
.set({
326+
Authorization: `Bearer ${testUtil.jwts.admin}`,
327+
})
328+
.send(bodyDefinedFormTemplate)
329+
.expect(422, done);
330+
});
331+
332+
it('should return 422 if both form or template field are missing', (done) => {
333+
request(server)
334+
.patch(`/v4/projects/metadata/productTemplates/${templateId}`)
335+
.set({
336+
Authorization: `Bearer ${testUtil.jwts.admin}`,
337+
})
338+
.send(bodyMissingFormTemplate)
339+
.expect(422, done);
340+
});
315341
});
316342
});

src/routes/projectTemplates/create.js

Lines changed: 23 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,20 @@ const schema = {
2222
question: Joi.string().max(255).required(),
2323
info: Joi.string().max(255).required(),
2424
aliases: Joi.array().required(),
25-
scope: Joi.object().optional().allow(null),
26-
phases: Joi.object().optional().allow(null),
27-
form: Joi.object().optional().allow(null),
28-
planConfig: Joi.object().optional().allow(null),
29-
priceConfig: Joi.object().optional().allow(null),
25+
scope: Joi.object().empty(null),
26+
phases: Joi.object().empty(null),
27+
form: Joi.object().keys({
28+
key: Joi.string().required(),
29+
version: Joi.number(),
30+
}).empty(null),
31+
planConfig: Joi.object().keys({
32+
key: Joi.string().required(),
33+
version: Joi.number(),
34+
}).empty(null),
35+
priceConfig: Joi.object().keys({
36+
key: Joi.string().required(),
37+
version: Joi.number(),
38+
}).empty(null),
3039
disabled: Joi.boolean().optional(),
3140
hidden: Joi.boolean().optional(),
3241
createdAt: Joi.any().strip(),
@@ -35,7 +44,11 @@ const schema = {
3544
createdBy: Joi.any().strip(),
3645
updatedBy: Joi.any().strip(),
3746
deletedBy: Joi.any().strip(),
38-
}).required(),
47+
})
48+
.xor('form', 'scope')
49+
.xor('phases', 'planConfig')
50+
.nand('priceConfig', 'scope')
51+
.required(),
3952
},
4053
};
4154

@@ -47,54 +60,12 @@ module.exports = [
4760
const param = req.body.param;
4861
const { form, priceConfig, planConfig } = param;
4962

50-
const checkModel = (keyInfo, modelName, model) => {
51-
let errorMessage = '';
52-
if (keyInfo == null) {
53-
return Promise.resolve(null);
54-
}
55-
if ((keyInfo.version != null) && (keyInfo.key != null)) {
56-
errorMessage = `${modelName} with key ${keyInfo.key} and version ${keyInfo.version}`
57-
+ ' referred in the project template is not found';
58-
return (model.findOne({
59-
where: {
60-
key: keyInfo.key,
61-
version: keyInfo.version,
62-
},
63-
})).then((record) => {
64-
if (record == null) {
65-
return Promise.resolve(errorMessage);
66-
}
67-
return Promise.resolve(null);
68-
});
69-
} else if ((keyInfo.version == null) && (keyInfo.key != null)) {
70-
errorMessage = `${modelName} with key ${keyInfo.key}`
71-
+ ' referred in the project template is not found';
72-
return model.findOne({
73-
where: {
74-
key: keyInfo.key,
75-
},
76-
}).then((record) => {
77-
if (record == null) {
78-
return Promise.resolve(errorMessage);
79-
}
80-
return Promise.resolve(null);
81-
});
82-
}
83-
return Promise.resolve(null);
84-
};
85-
8663
return Promise.all([
87-
checkModel(form, 'Form', models.Form, next),
88-
checkModel(priceConfig, 'PriceConfig', models.PriceConfig, next),
89-
checkModel(planConfig, 'PlanConfig', models.PlanConfig, next),
64+
util.checkModel(form, 'Form', models.Form, 'project template'),
65+
util.checkModel(priceConfig, 'PriceConfig', models.PriceConfig, 'project template'),
66+
util.checkModel(planConfig, 'PlanConfig', models.PlanConfig, 'project template'),
9067
])
91-
.then((errorMessages) => {
92-
const errorMessage = errorMessages.find(e => e && e.length > 0);
93-
if (errorMessage) {
94-
const apiErr = new Error(errorMessage);
95-
apiErr.status = 422;
96-
throw apiErr;
97-
}
68+
.then(() => {
9869
const entity = _.assign(req.body.param, {
9970
createdBy: req.authUser.userId,
10071
updatedBy: req.authUser.userId,

src/routes/projectTemplates/create.spec.js

Lines changed: 82 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,51 @@ import testUtil from '../../tests/util';
1212
const should = chai.should();
1313

1414
describe('CREATE project template', () => {
15-
before((done) => {
16-
testUtil.clearDb()
17-
.then(() => models.ProjectType.bulkCreate([
18-
{
19-
key: 'generic',
20-
displayName: 'Generic',
21-
icon: 'http://example.com/icon1.ico',
22-
question: 'question 1',
23-
info: 'info 1',
24-
aliases: ['key-1', 'key_1'],
25-
metadata: {},
26-
createdBy: 1,
27-
updatedBy: 1,
28-
},
29-
]))
30-
.then(() => done());
31-
});
15+
before(() => testUtil.clearDb()
16+
.then(() => models.ProjectType.bulkCreate([
17+
{
18+
key: 'generic',
19+
displayName: 'Generic',
20+
icon: 'http://example.com/icon1.ico',
21+
question: 'question 1',
22+
info: 'info 1',
23+
aliases: ['key-1', 'key_1'],
24+
metadata: {},
25+
createdBy: 1,
26+
updatedBy: 1,
27+
},
28+
]))
29+
.then(() => models.Form.create({
30+
key: 'test',
31+
config: {
32+
test: 'test1',
33+
},
34+
version: 1,
35+
revision: 1,
36+
createdBy: 1,
37+
updatedBy: 1,
38+
}))
39+
.then(() => models.PlanConfig.create({
40+
key: 'test',
41+
config: {
42+
test: 'test1',
43+
},
44+
version: 1,
45+
revision: 1,
46+
createdBy: 1,
47+
updatedBy: 1,
48+
}))
49+
.then(() => models.PriceConfig.create({
50+
key: 'test',
51+
config: {
52+
test: 'test1',
53+
},
54+
version: 1,
55+
revision: 1,
56+
createdBy: 1,
57+
updatedBy: 1,
58+
})),
59+
);
3260

3361
describe('POST /projects/metadata/projectTemplates', () => {
3462
const body = {
@@ -80,34 +108,29 @@ describe('CREATE project template', () => {
80108
disabled: true,
81109
hidden: true,
82110
form: {
83-
scope1: {
84-
subScope1A: 1,
85-
subScope1B: 2,
86-
},
87-
scope2: [1, 2, 3],
111+
key: 'test',
112+
version: 1,
88113
},
89114
priceConfig: {
90-
first: '$800',
115+
key: 'test',
91116
},
92117
planConfig: {
93-
phase1: {
94-
name: 'phase 1',
95-
details: {
96-
anyDetails: 'any details 1',
97-
},
98-
others: ['others 11', 'others 12'],
99-
},
100-
phase2: {
101-
name: 'phase 2',
102-
details: {
103-
anyDetails: 'any details 2',
104-
},
105-
others: ['others 21', 'others 22'],
106-
},
118+
key: 'test',
107119
},
108120
},
109121
};
110122

123+
const bodyDefinedFormScope = _.cloneDeep(body);
124+
bodyDefinedFormScope.param.form = {
125+
scope1: {
126+
subScope1A: 1,
127+
subScope1B: 2,
128+
},
129+
scope2: [1, 2, 3],
130+
};
131+
const bodyMissingFormScope = _.cloneDeep(body);
132+
delete bodyMissingFormScope.param.scope;
133+
111134
it('should return 403 if user is not authenticated', (done) => {
112135
request(server)
113136
.post('/v4/projects/metadata/projectTemplates')
@@ -270,5 +293,27 @@ describe('CREATE project template', () => {
270293
done();
271294
});
272295
});
296+
297+
it('should return 422 if both scope and form are defined', (done) => {
298+
request(server)
299+
.post('/v4/projects/metadata/projectTemplates')
300+
.set({
301+
Authorization: `Bearer ${testUtil.jwts.admin}`,
302+
})
303+
.send(bodyDefinedFormScope)
304+
.expect('Content-Type', /json/)
305+
.expect(422, done);
306+
});
307+
308+
it('should return 422 if both scope and form are missing', (done) => {
309+
request(server)
310+
.post('/v4/projects/metadata/projectTemplates')
311+
.set({
312+
Authorization: `Bearer ${testUtil.jwts.admin}`,
313+
})
314+
.send(bodyMissingFormScope)
315+
.expect('Content-Type', /json/)
316+
.expect(422, done);
317+
});
273318
});
274319
});

0 commit comments

Comments
 (0)