Skip to content

Commit e889c24

Browse files
author
vikasrohit
authored
Merge pull request #242 from topcoder-platform/feature/org-configs
winning submission from challenge 30081357 - Topcoder Connect - Organization configs
2 parents ecdea1f + a387540 commit e889c24

File tree

16 files changed

+1253
-2
lines changed

16 files changed

+1253
-2
lines changed

config/default.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"idleTimeout": 1000
3636
},
3737
"kafkaConfig": {
38+
"hosts": "localhost:9092"
3839
},
3940
"analyticsKey": "",
4041
"VALID_ISSUERS": "[\"https:\/\/topcoder-newauth.auth0.com\/\",\"https:\/\/api.topcoder-dev.com\"]",
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--
2+
-- CREATE NEW TABLE:
3+
-- org_config
4+
--
5+
CREATE TABLE org_config (
6+
id bigint NOT NULL,
7+
orgId character varying(45) NOT NULL,
8+
configName character varying(45) NOT NULL,
9+
configValue character varying(512),
10+
"deletedAt" timestamp with time zone,
11+
"createdAt" timestamp with time zone,
12+
"updatedAt" timestamp with time zone,
13+
"deletedBy" bigint,
14+
"createdBy" bigint NOT NULL,
15+
"updatedBy" bigint NOT NULL
16+
);
17+
18+
CREATE SEQUENCE org_config_id_seq
19+
START WITH 1
20+
INCREMENT BY 1
21+
NO MINVALUE
22+
NO MAXVALUE
23+
CACHE 1;
24+
25+
ALTER SEQUENCE org_config_id_seq OWNED BY org_config.id;
26+
27+
ALTER TABLE org_config
28+
ALTER COLUMN id SET DEFAULT nextval('org_config_id_seq');

postman.json

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3301,6 +3301,180 @@
33013301
}
33023302
]
33033303
},
3304+
{
3305+
"name": "Organization Config",
3306+
"description": "",
3307+
"item": [
3308+
{
3309+
"name": "Create organization config",
3310+
"request": {
3311+
"url": "{{api-url}}/v4/orgConfig",
3312+
"method": "POST",
3313+
"header": [
3314+
{
3315+
"key": "Content-Type",
3316+
"value": "application/json",
3317+
"description": ""
3318+
},
3319+
{
3320+
"key": "Authorization",
3321+
"value": "Bearer {{jwt-token}}",
3322+
"description": ""
3323+
}
3324+
],
3325+
"body": {
3326+
"mode": "raw",
3327+
"raw": "{\r\n \"param\":{\r\n \"orgId\": \"20000013\",\r\n \"configName\": \"project_catalog_url\",\r\n \"configValue\": \"/projects/1\"\r\n }\r\n}"
3328+
},
3329+
"description": ""
3330+
},
3331+
"response": []
3332+
},
3333+
{
3334+
"name": "List organization config",
3335+
"request": {
3336+
"url": "{{api-url}}/v4/orgConfig",
3337+
"method": "GET",
3338+
"header": [
3339+
{
3340+
"key": "Content-Type",
3341+
"value": "application/json",
3342+
"description": ""
3343+
},
3344+
{
3345+
"key": "Authorization",
3346+
"value": "Bearer {{jwt-token}}",
3347+
"description": ""
3348+
}
3349+
],
3350+
"body": {
3351+
"mode": "raw",
3352+
"raw": ""
3353+
},
3354+
"description": ""
3355+
},
3356+
"response": []
3357+
},
3358+
{
3359+
"name": "List organization config - filter",
3360+
"request": {
3361+
"url": {
3362+
"raw": "{{api-url}}/v4/orgConfig?filter=orgId=in(20000010,20000013,20000015)%26configName%3Dproject_catalog_url",
3363+
"host": [
3364+
"{{api-url}}"
3365+
],
3366+
"path": [
3367+
"v4",
3368+
"orgConfig"
3369+
],
3370+
"query": [
3371+
{
3372+
"key": "filter",
3373+
"value": "orgId=in(20000010,20000013,20000015)%26configName%3Dproject_catalog_url",
3374+
"equals": true,
3375+
"description": ""
3376+
}
3377+
],
3378+
"variable": []
3379+
},
3380+
"method": "GET",
3381+
"header": [
3382+
{
3383+
"key": "Content-Type",
3384+
"value": "application/json",
3385+
"description": ""
3386+
},
3387+
{
3388+
"key": "Authorization",
3389+
"value": "Bearer {{jwt-token}}",
3390+
"description": ""
3391+
}
3392+
],
3393+
"body": {
3394+
"mode": "raw",
3395+
"raw": ""
3396+
},
3397+
"description": ""
3398+
},
3399+
"response": []
3400+
},
3401+
{
3402+
"name": "Get organization config",
3403+
"request": {
3404+
"url": "{{api-url}}/v4/orgConfig/1",
3405+
"method": "GET",
3406+
"header": [
3407+
{
3408+
"key": "Content-Type",
3409+
"value": "application/json",
3410+
"description": ""
3411+
},
3412+
{
3413+
"key": "Authorization",
3414+
"value": "Bearer {{jwt-token}}",
3415+
"description": ""
3416+
}
3417+
],
3418+
"body": {
3419+
"mode": "raw",
3420+
"raw": ""
3421+
},
3422+
"description": ""
3423+
},
3424+
"response": []
3425+
},
3426+
{
3427+
"name": "Update organization config",
3428+
"request": {
3429+
"url": "{{api-url}}/v4/orgConfig/1",
3430+
"method": "PATCH",
3431+
"header": [
3432+
{
3433+
"key": "Content-Type",
3434+
"value": "application/json",
3435+
"description": ""
3436+
},
3437+
{
3438+
"key": "Authorization",
3439+
"value": "Bearer {{jwt-token}}",
3440+
"description": ""
3441+
}
3442+
],
3443+
"body": {
3444+
"mode": "raw",
3445+
"raw": "{\r\n \"param\":{\r\n \"configName\": \"project_catalog_url\"\r\n }\r\n}"
3446+
},
3447+
"description": ""
3448+
},
3449+
"response": []
3450+
},
3451+
{
3452+
"name": "Delete organization config",
3453+
"request": {
3454+
"url": "{{api-url}}/v4/orgConfig/1",
3455+
"method": "DELETE",
3456+
"header": [
3457+
{
3458+
"key": "Content-Type",
3459+
"value": "application/json",
3460+
"description": ""
3461+
},
3462+
{
3463+
"key": "Authorization",
3464+
"value": "Bearer {{jwt-token}}",
3465+
"description": ""
3466+
}
3467+
],
3468+
"body": {
3469+
"mode": "raw",
3470+
"raw": ""
3471+
},
3472+
"description": ""
3473+
},
3474+
"response": []
3475+
}
3476+
]
3477+
},
33043478
{
33053479
"name": "Product Category",
33063480
"item": [
@@ -5010,4 +5184,4 @@
50105184
]
50115185
}
50125186
]
5013-
}
5187+
}

src/models/orgConfig.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* eslint-disable valid-jsdoc */
2+
3+
/**
4+
* The Organization config model
5+
*/
6+
module.exports = (sequelize, DataTypes) => {
7+
const OrgConfig = sequelize.define('OrgConfig', {
8+
id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
9+
orgId: { type: DataTypes.STRING(45), allowNull: false },
10+
configName: { type: DataTypes.STRING(45), allowNull: false },
11+
configValue: { type: DataTypes.STRING(512) },
12+
deletedAt: DataTypes.DATE,
13+
createdAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
14+
updatedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
15+
deletedBy: DataTypes.BIGINT,
16+
createdBy: { type: DataTypes.BIGINT, allowNull: false },
17+
updatedBy: { type: DataTypes.BIGINT, allowNull: false },
18+
}, {
19+
tableName: 'org_config',
20+
paranoid: true,
21+
timestamps: true,
22+
updatedAt: 'updatedAt',
23+
createdAt: 'createdAt',
24+
deletedAt: 'deletedAt',
25+
});
26+
27+
return OrgConfig;
28+
};

src/permissions/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ module.exports = () => {
5454
Authorizer.setPolicy('projectType.delete', projectAdmin);
5555
Authorizer.setPolicy('projectType.view', true); // anyone can view project types
5656

57+
Authorizer.setPolicy('orgConfig.create', projectAdmin);
58+
Authorizer.setPolicy('orgConfig.edit', projectAdmin);
59+
Authorizer.setPolicy('orgConfig.delete', projectAdmin);
60+
Authorizer.setPolicy('orgConfig.view', true); // anyone can view project types
61+
5762
Authorizer.setPolicy('productCategory.create', projectAdmin);
5863
Authorizer.setPolicy('productCategory.edit', projectAdmin);
5964
Authorizer.setPolicy('productCategory.delete', projectAdmin);

src/routes/index.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ router.route('/v4/projects/metadata/projectTypes')
4242
router.route('/v4/projects/metadata/projectTypes/:key')
4343
.get(require('./projectTypes/get'));
4444

45+
router.route('/v4/orgConfig')
46+
.get(require('./orgConfig/list'));
47+
48+
router.route('/v4/orgConfig/:id(\\d+)')
49+
.get(require('./orgConfig/get'));
50+
4551
router.route('/v4/projects/metadata/productCategories')
4652
.get(require('./productCategories/list'));
4753
router.route('/v4/projects/metadata/productCategories/:key')
@@ -53,7 +59,7 @@ router.route('/v4/projects/metadata')
5359
.get(require('./metadata/list'));
5460

5561
router.all(
56-
RegExp(`\\/${apiVersion}\\/(projects|timelines)(?!\\/health).*`), (req, res, next) => (
62+
RegExp(`\\/${apiVersion}\\/(projects|timelines|orgConfig)(?!\\/health).*`), (req, res, next) => (
5763
// JWT authentication
5864
jwtAuth(config)(req, res, next)
5965
),
@@ -182,6 +188,13 @@ router.route('/v4/projects/:projectId(\\d+)/members/invite')
182188
.put(require('./projectMemberInvites/update'))
183189
.get(require('./projectMemberInvites/get'));
184190

191+
router.route('/v4/orgConfig')
192+
.post(require('./orgConfig/create'));
193+
194+
router.route('/v4/orgConfig/:id(\\d+)')
195+
.patch(require('./orgConfig/update'))
196+
.delete(require('./orgConfig/delete'));
197+
185198
// register error handler
186199
router.use((err, req, res, next) => { // eslint-disable-line no-unused-vars
187200
// DO NOT REMOVE next arg.. even though eslint

src/routes/orgConfig/create.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* API to add a organization config
3+
*/
4+
import validate from 'express-validation';
5+
import _ from 'lodash';
6+
import Joi from 'joi';
7+
import { middleware as tcMiddleware } from 'tc-core-library-js';
8+
import util from '../../util';
9+
import models from '../../models';
10+
11+
const permissions = tcMiddleware.permissions;
12+
13+
const schema = {
14+
body: {
15+
param: Joi.object().keys({
16+
id: Joi.any().strip(),
17+
orgId: Joi.string().max(45).required(),
18+
configName: Joi.string().max(45).required(),
19+
configValue: Joi.string().max(512),
20+
createdAt: Joi.any().strip(),
21+
updatedAt: Joi.any().strip(),
22+
deletedAt: Joi.any().strip(),
23+
createdBy: Joi.any().strip(),
24+
updatedBy: Joi.any().strip(),
25+
deletedBy: Joi.any().strip(),
26+
}).required(),
27+
},
28+
};
29+
30+
module.exports = [
31+
validate(schema),
32+
permissions('orgConfig.create'),
33+
(req, res, next) => {
34+
const entity = _.assign(req.body.param, {
35+
createdBy: req.authUser.userId,
36+
updatedBy: req.authUser.userId,
37+
});
38+
39+
// Check if duplicated key
40+
return models.OrgConfig.findOne({ where: { orgId: req.body.param.orgId, configName: req.body.param.configName } })
41+
.then((existing) => {
42+
if (existing) {
43+
const apiErr = new Error(`Organization config exists for orgId ${req.body.param.orgId}
44+
and configName ${req.body.param.configName}`);
45+
apiErr.status = 422;
46+
return Promise.reject(apiErr);
47+
}
48+
49+
// Create
50+
return models.OrgConfig.create(entity);
51+
}).then((createdEntity) => {
52+
// Omit deletedAt, deletedBy
53+
res.status(201).json(util.wrapResponse(
54+
req.id, _.omit(createdEntity.toJSON(), 'deletedAt', 'deletedBy'), 1, 201));
55+
})
56+
.catch(next);
57+
},
58+
];

0 commit comments

Comments
 (0)