Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 7edab84

Browse files
committed
DynamoDB updates
1 parent 33396a1 commit 7edab84

25 files changed

+835
-329
lines changed

TopcoderXDeploy.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Topcoder X overview
22

3-
Topcoder-X is a set of applications and services that allow a copilot or Topcoder customer to manage work directly through Gitlab or Github. When an issue is created in a Gitlab or Github project set up in Topcoder-X, Topcoder-X will create a Topcoder challenge to mirror the Gitlab or Github issue, and it will ensure that the challenge has the correct prize, copilot, assignee, description, and title. As the Gitlab or Github issue is updated, Topcoder-X will ensure that the Topcoder challenge associated with the issue is updated appropriately. When the Gitlab or Github issue is closed, Topcoder-X will activate and close the Topcoder challenge, ensuring that the members get paid as expected.
3+
Topcoder-X is a set of applications and services that allow a copilot or Topcoder customer to manage work directly through Gitlab or Github. When an issue is created in a Gitlab or Github project set up in Topcoder-X, Topcoder-X will create a Topcoder challenge to mirror the Gitlab or Github issue, and it will ensure that the challenge has the correct prize, copilot, assignee, description, and title. As the Gitlab or Github issue is updated, Topcoder-X will ensure that the Topcoder challenge associated with the issue is updated appropriately. When the Gitlab or Github issue is closed, Topcoder-X will activate and close the Topcoder challenge, ensuring that the members get paid as expected.
44

55
At each step of the process, Topcoder-X will add comments to the Gitlab or Github project, ensuring that the members know where the Topcoder challenge is and what the status of the challenge is.
66

@@ -10,7 +10,7 @@ The information is updated in real time based on webhook integrations with Gitla
1010

1111
## Dependencies
1212
* NodeJS 8+
13-
* MongoDB 3.2
13+
* DynamoDB
1414
* Kafka
1515
* nodemon (for local development)
1616

@@ -25,18 +25,21 @@ Topcoder-X comprises 3 pieces:
2525
All 3 pieces will be configured to use the same MongoDB and Kafka installations.
2626

2727

28-
## MongoDB
28+
## DynamoDB
2929

30-
The MongoDB can be created using default options. Just make sure that it is configured properly as `MONGODB_URI` in all 3 pieces:
30+
The DynamoDB can be created using default options. Just make sure that it is configured properly as `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `IS_LOCAL` in all 3 pieces:
3131

3232
* Topcoder-X processor
3333
* Topcoder-X receiver
3434
* Topcoder-X UI
3535

36-
Sample from our development environment:
37-
38-
`MONGODB_URI: mongodb://heroku_k366sw2n:<password>@ds135552.mlab.com:35552/heroku_k366sw2n`
39-
36+
Sample from our local development environment:
37+
```
38+
AWS_ACCESS_KEY_ID: 'FAKE_ACCESS_KEY_ID'
39+
AWS_SECRET_ACCESS_KEY: 'FAKE_SECRET_ACCESS_KEY'
40+
AWS_REGION: 'FAKE_REGION'
41+
IS_LOCAL: true
42+
```
4043
## Kafka
4144

4245
Installing Kafka can be done either locally or using a cloud service. You'll need to note how the service is configured and will have to update the configuration appropriately for the receiver, processor, and site.
@@ -87,7 +90,7 @@ To this:
8790

8891
For login to work, your local Topcoder-X-UI deployment needs to have a `*.topcoder-dev.com` DNS name. Our development environment uses `x.topcoder-dev.com`
8992

90-
You can make this change in your local `/etc/hosts` file.
93+
You can make this change in your local `/etc/hosts` file.
9194

9295
```
9396
127.0.0.1 x.topcoder-dev.com
@@ -100,7 +103,7 @@ You can login with one of these sample accounts:
100103

101104
## Local webhook setup
102105

103-
The hardest part of the setup may be ensuring that Gitlab and Github can make callbacks to your local environment. You will have to ensure that your Topcoder-X receiver is publicly accessible on the public internet.
106+
The hardest part of the setup may be ensuring that Gitlab and Github can make callbacks to your local environment. You will have to ensure that your Topcoder-X receiver is publicly accessible on the public internet.
104107

105108
If your ISP dynamically configures your IP address, you can use a dyndns service:
106109

@@ -118,7 +121,7 @@ http://<publicly accessible domain name>:<port>/webhooks/gitlab
118121
#### Github
119122

120123
```
121-
https://<publicly accessible domain name>:<port>/webhooks/github
124+
https://<publicly accessible domain name>:<port>/webhooks/github
122125
```
123126

124127
## Account setup
@@ -153,7 +156,7 @@ Contest https://www.topcoder-dev.com/challenges/30052039 has been updated - it h
153156

154157
## Sample development configs
155158

156-
For reference, this is what the sample configs look like in our development environment, which should closely match your local deployment environment.
159+
For reference, this is what the sample configs look like in our development environment, which should closely match your local deployment environment.
157160

158161
You can use the Gitlab and Github keys and secrets below, but you are also welcome to create your own.
159162

@@ -174,12 +177,15 @@ MAILGUN_SMTP_LOGIN: [email protected]
174177
MAILGUN_SMTP_PASSWORD: c8aefb446e76febdbc31d57ef30b9c10
175178
MAILGUN_SMTP_PORT: 587
176179
MAILGUN_SMTP_SERVER: smtp.mailgun.org
177-
MONGODB_URI: mongodb://heroku_k366sw2n:<password>@ds135552.mlab.com:35552/heroku_k366sw2n
178180
NODE_DEBUG: app
179181
NODE_ENV: development
180182
NODE_MODULES_CACHE: false
181183
NODE_TLS_REJECT_UNAUTHORIZED: 0
182184
TOPIC: topcoder-x
185+
AWS_ACCESS_KEY_ID: FAKE_ACCESS_KEY_ID
186+
AWS_SECRET_ACCESS_KEY: FAKE_SECRET_ACCESS_KEY
187+
AWS_REGION: FAKE_REGION
188+
IS_LOCAL: true
183189
```
184190

185191
#### Topcoder-X receiver
@@ -191,10 +197,13 @@ KAFKA_CLIENT_CERT: <cert>
191197
KAFKA_CLIENT_CERT_KEY: <key>
192198
KAFKA_HOST: silver-craft-01.srvs.cloudkafka.com:9093,silver-craft-01.srvs.cloudkafka.com:9094
193199
LOG_LEVEL: debug
194-
MONGODB_URI: mongodb://heroku_k366sw2n:<password>@ds135552.mlab.com:35552/heroku_k366sw2n
195200
NODE_ENV: development
196201
NODE_TLS_REJECT_UNAUTHORIZED: 0
197202
TOPIC: topcoder-x
203+
AWS_ACCESS_KEY_ID: FAKE_ACCESS_KEY_ID
204+
AWS_SECRET_ACCESS_KEY: FAKE_SECRET_ACCESS_KEY
205+
AWS_REGION: FAKE_REGION
206+
IS_LOCAL: true
198207
```
199208

200209
#### Topcoder-X UI
@@ -211,11 +220,14 @@ HOOK_BASE_URL: https://topcoder-x-receiver-dev.herokuapp.com
211220
KAFKA_CLIENT_CERT: <cert>
212221
KAFKA_CLIENT_CERT_KEY: <key>
213222
KAFKA_HOST: silver-craft-01.srvs.cloudkafka.com:9093,silver-craft-01.srvs.cloudkafka.com:9094
214-
MONGODB_URI: mongodb://heroku_k366sw2n:<password>@ds135552.mlab.com:35552/heroku_k366sw2n
215223
NPM_CONFIG_PRODUCTION: false
216224
SESSION_SECRET: kjsdfkj34857
217225
TC_LOGIN_URL: https://accounts.topcoder-dev.com/member
218226
TC_USER_PROFILE_URL: http://api.topcoder-dev.com/v2/user/profile
219227
TOPIC: topcoder-x
220228
WEBSITE: https://x.topcoder-dev.com
221-
```
229+
AWS_ACCESS_KEY_ID: FAKE_ACCESS_KEY_ID
230+
AWS_SECRET_ACCESS_KEY: FAKE_SECRET_ACCESS_KEY
231+
AWS_REGION: FAKE_REGION
232+
IS_LOCAL: true
233+
```

configuration.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ The following config parameters are supported, they are defined in `src/config.j
1515
| GITLAB_CLIENT_SECRET | the GitLab client secret | No default - needs to be set up using the instructions below |
1616
| WEBSITE | used as base to construct various URLs | http://topcoderx.topcoder-dev.com/ |
1717
| GITLAB_API_BASE_URL | The Gitlab API base URL | https://gitlab.com|
18-
| MONGODB_URI | The MongoDB URI. This needs to be the same MongoDB used by topcoder-x-receiver, topcoder-x-processor, and topcoder-x-site | mongodb://127.0.0.1:27017/topcoderx |
1918
|TOPIC | The Kafka topic where events are published. This must be the same as the configured value for topcoder-x-processor| |
2019
|KAFKA_OPTIONS | Kafka connection options| |
2120
|KAFKA_HOST | The Kafka host to connect to| localhost:9092 |
@@ -28,6 +27,10 @@ The following config parameters are supported, they are defined in `src/config.j
2827
|COPILOT_ROLE| The role to identify copilot|'copilot'|
2928
|HELP_LINK| The link for help| 'https://github.com/topcoder-platform/topcoder-x-ui/wiki'|
3029
|ADMINISTRATOR_ROLES| The array of roles to be considered as admin| `['administrator', 'admin']`|
30+
|AWS_ACCESS_KEY_ID | The Amazon certificate key to use when connecting. Use local dynamodb you can set fake value|FAKE_ACCESS_KEY_ID |
31+
|AWS_SECRET_ACCESS_KEY | The Amazon certificate access key to use when connecting. Use local dynamodb you can set fake value|FAKE_SECRET_ACCESS_KEY |
32+
|AWS_REGION | The Amazon certificate region to use when connecting. Use local dynamodb you can set fake value|FAKE_REGION |
33+
|IS_LOCAL | Use Amazon DynamoDB Local or server. |true |
3134

3235
## GitHub OAuth App Setup
3336

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"core-js": "^2.4.1",
5050
"cors": "^2.8.4",
5151
"debug": "~2.6.3",
52+
"dynamoose": "^1.1.0",
5253
"express": "^4.15.4",
5354
"express-jwt": "^5.3.0",
5455
"express-session": "^1.15.5",
@@ -66,7 +67,7 @@
6667
"jwt-decode": "^2.2.0",
6768
"lodash": "^4.17.4",
6869
"metismenu": "~2.0.2",
69-
"mongoose": "^4.11.9",
70+
"moment": "^2.22.2",
7071
"no-kafka": "^3.2.10",
7172
"octonode": "^0.9.2",
7273
"pace-js": "~1.0.2",
@@ -75,7 +76,7 @@
7576
"superagent": "^3.6.0",
7677
"superagent-promise": "^1.1.0",
7778
"typescript": "~2.3.3",
78-
"uuid": "^3.1.0",
79+
"uuid": "^3.3.2",
7980
"winston": "^2.3.1"
8081
},
8182
"devDependencies": {

src/common/db-helper.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright (c) 2018 TopCoder, Inc. All rights reserved.
3+
*/
4+
/**
5+
* This module contains the database helper methods.
6+
*
7+
* @version 1.0
8+
*/
9+
10+
/**
11+
* Get Data by model id
12+
* @param {Object} model The dynamoose model to query
13+
* @param {String} id The id value
14+
* @returns {Promise<void>}
15+
*/
16+
async function getById(model, id) {
17+
return await new Promise((resolve, reject) => {
18+
model.query('id').eq(id).exec((err, result) => {
19+
if (err) {
20+
reject(err);
21+
}
22+
23+
return resolve(result[0]);
24+
});
25+
});
26+
}
27+
28+
/**
29+
* Get data collection by scan parameters
30+
* @param {Object} model The dynamoose model to scan
31+
* @param {Object} scanParams The scan parameters object
32+
* @returns {Promise<void>}
33+
*/
34+
async function scan(model, scanParams) {
35+
return await new Promise((resolve, reject) => {
36+
model.scan(scanParams).exec((err, result) => {
37+
if (err) {
38+
reject(err);
39+
}
40+
41+
return resolve(result.count === 0 ? [] : result);
42+
});
43+
});
44+
}
45+
46+
/**
47+
* Get single data by scan parameters
48+
* @param {Object} model The dynamoose model to scan
49+
* @param {Object} scanParams The scan parameters object
50+
* @returns {Promise<void>}
51+
*/
52+
async function scanOne(model, scanParams) {
53+
return await new Promise((resolve, reject) => {
54+
model.scan(scanParams).exec((err, result) => {
55+
if (err) {
56+
reject(err);
57+
}
58+
59+
return resolve(result.count === 0 ? null : result[0]);
60+
});
61+
});
62+
}
63+
64+
/**
65+
* Create item in database
66+
* @param {Object} Model The dynamoose model to query
67+
* @param {Object} data The create data object
68+
* @returns {Promise<void>}
69+
*/
70+
async function create(Model, data) {
71+
return await new Promise((resolve, reject) => {
72+
const dbItem = new Model(data);
73+
dbItem.save((err) => {
74+
if (err) {
75+
reject(err);
76+
}
77+
78+
return resolve(dbItem);
79+
});
80+
});
81+
}
82+
83+
/**
84+
* Update item in database
85+
* @param {Object} Model The dynamoose model to update
86+
* @param {String} id The id of item
87+
* @param {Object} data The updated data object
88+
* @returns {Promise<void>}
89+
*/
90+
async function update(Model, id, data) {
91+
const dbItem = await getById(Model, id);
92+
Object.keys(data).forEach((key) => {
93+
dbItem[key] = data[key];
94+
});
95+
return await new Promise((resolve, reject) => {
96+
dbItem.save((err) => {
97+
if (err) {
98+
reject(err);
99+
}
100+
101+
return resolve(dbItem);
102+
});
103+
});
104+
}
105+
106+
/**
107+
* Delete item in database
108+
* @param {Object} Model The dynamoose model to delete
109+
* @param {Object} queryParams The query parameters object
110+
*/
111+
async function remove(Model, queryParams) {
112+
const dbItem = await scanOne(Model, queryParams);
113+
await new Promise((resolve, reject) => {
114+
dbItem.delete((err) => {
115+
if (err) {
116+
reject(err);
117+
}
118+
119+
resolve(dbItem);
120+
});
121+
});
122+
}
123+
124+
module.exports = {
125+
getById,
126+
scan,
127+
scanOne,
128+
create,
129+
update,
130+
remove,
131+
};

src/common/helper.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
* @author TCSCODER
99
* @version 1.0
1010
*/
11+
1112
const util = require('util');
1213
const _ = require('lodash');
14+
const uuid = require('uuid/v4');
1315
const Joi = require('joi');
1416
const getParams = require('get-parameter-names');
1517
const bluebird = require('bluebird');
16-
const uuid = require('uuid/v4');
1718
const bcrypt = require('bcryptjs');
1819
const parseDomain = require('parse-domain');
1920
const config = require('../config');
@@ -22,6 +23,7 @@ const errors = require('./errors');
2223
const constants = require('./constants');
2324
const NotFoundError = require('./errors').NotFoundError;
2425
const ValidationError = require('./errors').ValidationError;
26+
const dbHelper = require('./db-helper');
2527

2628
bluebird.promisifyAll(bcrypt);
2729
bluebird.promisifyAll(parseDomain);
@@ -85,7 +87,7 @@ function _decorateWithValidators(service) {
8587
const params = getParams(method);
8688
service[name] = async function serviceMethodWithValidation(...args) {
8789
const value = _combineObject(params, args);
88-
const normalized = Joi.attempt(value, method.schema, { abortEarly: false });
90+
const normalized = Joi.attempt(value, method.schema, {abortEarly: false});
8991
// Joi will normalize values
9092
// for example string number '1' to 1
9193
// if schema type is number
@@ -167,24 +169,25 @@ function convertGitLabError(err, message) {
167169
* Ensure entity exists for given criteria. Return error if no result.
168170
* @param {Object} Model the mongoose model to query
169171
* @param {Object|String|Number} criteria the criteria (if object) or id (if string/number)
172+
* @param {String} modelName the name of model
170173
* @returns {Object} the found entity
171174
*/
172-
async function ensureExists(Model, criteria) {
175+
async function ensureExists(Model, criteria, modelName) {
173176
let query;
174177
let byId = true;
175178
if (_.isObject(criteria)) {
176179
byId = false;
177-
query = Model.findOne(criteria);
180+
query = dbHelper.scanOne(Model, criteria);
178181
} else {
179-
query = Model.findById(criteria);
182+
query = dbHelper.getById(Model, criteria);
180183
}
181184
const result = await query;
182185
if (!result) {
183186
let msg;
184187
if (byId) {
185-
msg = util.format('%s not found with id: %s', Model.modelName, criteria);
188+
msg = util.format('%s not found with id: %s', modelName, criteria);
186189
} else {
187-
msg = util.format('%s not found with criteria: %j', Model.modelName, criteria);
190+
msg = util.format('%s not found with criteria: %j', modelName, criteria);
188191
}
189192
throw new NotFoundError(msg);
190193
}
@@ -212,19 +215,18 @@ async function getProviderType(repoUrl) {
212215
* @returns {Object} the owner/copilot for the project
213216
*/
214217
async function getProjectCopilot(models, project, provider) {
215-
const userMapping = await models.UserMapping.findOne({
218+
const userMapping = await dbHelper.scanOne(models.UserMapping, {
216219
topcoderUsername: project.copilot,
217220
});
218221

219222
if (!userMapping || (provider === 'github' && !userMapping.githubUserId) || (provider === 'gitlab' && !userMapping.gitlabUserId)) {
220223
throw new Error(`Couldn't find owner username for '${provider}' for this repository.`);
221224
}
222225

223-
const copilot = await models.User.findOne({
226+
return await dbHelper.scanOne(models.User, {
224227
username: provider === 'github' ? userMapping.githubUsername : userMapping.gitlabUsername,
225228
type: provider,
226229
});
227-
return copilot;
228230
}
229231

230232

0 commit comments

Comments
 (0)