Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8cc6c63

Browse files
committedJun 21, 2019
read challenges from ES
1 parent 2d435e0 commit 8cc6c63

File tree

9 files changed

+1348
-520
lines changed

9 files changed

+1348
-520
lines changed
 

‎README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ The following parameters can be set in config files or in env variables:
3030
- AMAZON.IS_LOCAL_DB: Use Amazon DynamoDB Local or server.
3131
- AMAZON.DYNAMODB_URL: The local url if using Amazon DynamoDB Local
3232
- AMAZON.ATTACHMENT_S3_BUCKET: the AWS S3 bucket to store attachments
33+
- ES: config object for Elasticsearch
34+
- ES.HOST: Elasticsearch host
35+
- ES.API_VERSION: Elasticsearch API version
36+
- ES.ES_INDEX: Elasticsearch index name
37+
- ES.ES_TYPE: Elasticsearch index type
3338
- FILE_UPLOAD_SIZE_LIMIT: the file upload size limit in bytes
3439
- CHALLENGES_API_URL: TC challenges API base URL
3540
- GROUPS_API_URL: TC groups API base URL
@@ -67,7 +72,7 @@ Go to `mock-api` folder and run command `npm run start` to start the mock-api li
6772
1. Drop/delete tables: `npm run drop-tables`
6873
2. Creating tables: `npm run create-tables`
6974
3. Seed/Insert data to tables: `npm run seed-tables`
70-
4. Initialize database in default environment: `npm run init-db`
75+
4. Initialize/Clear database in default environment: `npm run init-db`
7176
5. View table data in default environment: `npm run view-data <ModelName>`, ModelName can be `Challenge`, `ChallengeType`, `ChallengeSetting`, `AuditLog`, `Phase`, `TimelineTemplate`or `Attachment`
7277

7378
### Notes

‎config/default.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ module.exports = {
2929
ATTACHMENT_S3_BUCKET: process.env.ATTACHMENT_S3_BUCKET || 'my-testing-bucket-12345',
3030
S3_API_VERSION: process.env.S3_API_VERSION || '2006-03-01'
3131
},
32+
33+
ES: {
34+
// above AWS_REGION is used if we use AWS ES
35+
HOST: process.env.ES_HOST || 'localhost:9200',
36+
API_VERSION: process.env.ES_API_VERSION || '6.3',
37+
ES_INDEX: process.env.ES_INDEX || 'challenge',
38+
ES_TYPE: process.env.ES_TYPE || '_doc' // ES 6.x accepts only 1 Type per index and it's mandatory to define it
39+
},
40+
3241
// in bytes
3342
FILE_UPLOAD_SIZE_LIMIT: process.env.FILE_UPLOAD_SIZE_LIMIT
3443
? Number(process.env.FILE_UPLOAD_SIZE_LIMIT) : 50 * 1024 * 1024, // 50M

‎docs/swagger.yaml

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,39 +53,107 @@ paths:
5353
parameters:
5454
- $ref: '#/parameters/page'
5555
- $ref: '#/parameters/perPage'
56+
- name: id
57+
in: query
58+
description: Filter by id, exact match
59+
required: false
60+
type: string
61+
format: UUID
62+
- name: typeId
63+
in: query
64+
description: Filter by type id, exact match
65+
required: false
66+
type: string
67+
format: UUID
68+
- name: track
69+
in: query
70+
description: Filter by track, case-insensitive, partial matches are allowed.
71+
required: false
72+
type: string
5673
- name: name
5774
in: query
5875
description: Filter by name, case-insensitive, partial matches are allowed.
5976
required: false
6077
type: string
6178
- name: description
6279
in: query
63-
description: Filter by description. Partial matches are allowed.
80+
description: Filter by description, case-insensitive, partial matches are allowed.
81+
required: false
82+
type: string
83+
- name: timelineTemplateId
84+
in: query
85+
description: Filter by timeline template id, exact match
86+
required: false
87+
type: string
88+
format: UUID
89+
- name: reviewType
90+
in: query
91+
description: Filter by review type, case-insensitive, partial matches are allowed.
92+
required: false
93+
type: string
94+
- name: tag
95+
in: query
96+
description: Filter by tag name, case-insensitive, partial matches are allowed.
97+
required: false
98+
type: string
99+
- name: projectId
100+
in: query
101+
description: Filter by project id, exact match.
102+
required: false
103+
type: integer
104+
- name: forumId
105+
in: query
106+
description: Filter by forum id, exact match.
107+
required: false
108+
type: integer
109+
- name: legacyId
110+
in: query
111+
description: Filter by legacy id, exact match.
112+
required: false
113+
type: integer
114+
- name: status
115+
in: query
116+
description: Filter by status, case-insensitive, partial matches are allowed.
117+
required: false
118+
type: string
119+
enum: ['Draft', 'Canceled', 'Active', 'Completed']
120+
- name: group
121+
in: query
122+
description: Filter by group name, case-insensitive, partial matches are allowed.
64123
required: false
65124
type: string
66125
- name: createdDateStart
67126
in: query
68-
description: Filter by createdDate (lower bound of date range)
127+
description: Filter by created date (lower bound of date range)
69128
required: false
70129
type: string
130+
format: date-time
71131
- name: createdDateEnd
72132
in: query
73-
description: Filter by createdDate (upper bound of date range)
133+
description: Filter by created date (upper bound of date range)
74134
required: false
75135
type: string
136+
format: date-time
76137
- name: updatedDateStart
77138
in: query
78-
description: Filter by updatedDate (lower bound of date range)
139+
description: Filter by updated date (lower bound of date range)
79140
required: false
80141
type: string
142+
format: date-time
81143
- name: updatedDateEnd
82144
in: query
83-
description: Filter by updatedDate (upper bound of date range)
145+
description: Filter by updated date (upper bound of date range)
84146
required: false
85147
type: string
148+
format: date-time
86149
- name: createdBy
87150
in: query
88-
description: Filter by 'createdBy' field
151+
description: Filter by 'createdBy' field, case-insensitive, partial matches are allowed.
152+
required: false
153+
type: string
154+
- name: updatedBy
155+
in: query
156+
description: Filter by 'updatedBy' field, case-insensitive, partial matches are allowed.
89157
required: false
90158
type: string
91159
responses:

‎docs/topcoder-challenge-api.postman_collection.json

Lines changed: 682 additions & 28 deletions
Large diffs are not rendered by default.

‎docs/topcoder-challenge-api.postman_environment.json

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"id": "1313001a-a495-4416-b89b-89a914759267",
2+
"id": "e393fed9-558b-405c-a845-d454483173ba",
33
"name": "topcoder-challenge-api",
44
"values": [
55
{
@@ -29,27 +29,27 @@
2929
},
3030
{
3131
"key": "TYPEA_ID",
32-
"value": "0ce8bdb5-0d92-4eb0-a4d8-ce08c49c386d",
32+
"value": "0494bf18-93fb-4320-bd54-f23465b649b6",
3333
"enabled": true
3434
},
3535
{
3636
"key": "TYPEB_ID",
37-
"value": "82de3cff-6097-4cb0-a34b-3981e9e0bd28",
37+
"value": "c61db679-8b9c-4ddc-b41e-889dd035cd47",
3838
"enabled": true
3939
},
4040
{
4141
"key": "CHALLENGE_ID1",
42-
"value": "2d0b90b6-136d-4f4d-a970-5d25862c7a6a",
42+
"value": "870caf5f-3f54-4761-a0fe-a3e378d5ce0b",
4343
"enabled": true
4444
},
4545
{
4646
"key": "SETTINGA_ID",
47-
"value": "5f41fd63-a3fc-4395-970e-efd48614072a",
47+
"value": "7bb8a726-278b-498f-89e1-2c6caabd7932",
4848
"enabled": true
4949
},
5050
{
5151
"key": "SETTINGB_ID",
52-
"value": "26590cb7-cf9e-4f0e-8e8b-2fc508d1038d",
52+
"value": "2c6dfd54-38d2-4a42-a3b3-0bff809b71f3",
5353
"enabled": true
5454
},
5555
{
@@ -69,92 +69,92 @@
6969
},
7070
{
7171
"key": "TEST_SETTING_ID1",
72-
"value": "0e64b53b-d5fa-410f-971f-c6f0005f4dae",
72+
"value": "2a0868e3-9ee3-4d55-8ed4-079a7434e60d",
7373
"enabled": true
7474
},
7575
{
7676
"key": "TEST_SETTING_ID2",
77-
"value": "cb342da3-b8a1-4d24-9bda-60cb9ac379d4",
77+
"value": "6685bfcc-146c-49a6-889c-314f777d1a70",
7878
"enabled": true
7979
},
8080
{
8181
"key": "TEST_SETTING_ID3",
82-
"value": "53586963-3faa-47af-b750-3217a9a0f702",
82+
"value": "3d604840-e455-45a7-9b7e-f0064f690859",
8383
"enabled": true
8484
},
8585
{
8686
"key": "TEST_SETTING_ID4",
87-
"value": "8cd84a26-575f-4127-b147-99e59f309887",
87+
"value": "533d61ea-7664-4ee3-b022-bc822aa45760",
8888
"enabled": true
8989
},
9090
{
9191
"key": "TEST_SETTING_ID5",
92-
"value": "ec9b9d67-72c4-4483-8c86-5478cb436d4b",
92+
"value": "1ec89dfe-4ec1-4322-b072-229953686b11",
9393
"enabled": true
9494
},
9595
{
9696
"key": "TEST_TYPE_ID1",
97-
"value": "c60cc083-e4f5-44fd-a06c-6c9f16b76fe2",
97+
"value": "82cee579-5b69-42d1-8042-6f26dd3a3557",
9898
"enabled": true
9999
},
100100
{
101101
"key": "TEST_TYPE_ID2",
102-
"value": "9872c39e-5035-4b0a-819c-773ef8f80159",
102+
"value": "c422774e-10a8-436e-a9d9-919dec503e1b",
103103
"enabled": true
104104
},
105105
{
106106
"key": "TEST_TYPE_ID3",
107-
"value": "f938415a-3a5d-491b-bccf-2357b9f6fbba",
107+
"value": "23c78c3c-b3ee-425e-bfa6-125d3b569122",
108108
"enabled": true
109109
},
110110
{
111111
"key": "TEST_TYPE_ID4",
112-
"value": "aad0d068-27c5-4684-b762-ccf83963aca3",
112+
"value": "871a55af-fe91-4cd4-b6ac-36c81b7e445c",
113113
"enabled": true
114114
},
115115
{
116116
"key": "TEST_TYPE_ID5",
117-
"value": "60337999-a47b-430b-aad7-babe82e3c567",
117+
"value": "7e9e040a-b8b1-4acc-85ec-1b24cd03b150",
118118
"enabled": true
119119
},
120120
{
121121
"key": "PHASEA_ID",
122-
"value": "a1f0cc6d-fe19-497f-8b29-809a0e7a11b5",
122+
"value": "2050437f-7f3c-4bf6-ba50-657249da5523",
123123
"enabled": true
124124
},
125125
{
126126
"key": "PHASEB_ID",
127-
"value": "fc75d892-9d42-47d7-9f78-e039898e43da",
127+
"value": "14f9364b-da63-48f8-beb4-f6c4aa801b29",
128128
"enabled": true
129129
},
130130
{
131131
"key": "PHASEC_ID",
132-
"value": "0769e231-f609-4ff4-bfda-95d0395ba1ed",
132+
"value": "eb223cfb-fc81-493b-a081-a3c549057392",
133133
"enabled": true
134134
},
135135
{
136136
"key": "PHASED_ID",
137-
"value": "fe39da38-3666-43f6-a968-08829f08638c",
137+
"value": "27315009-e8c5-4dd5-8953-239c011df3d0",
138138
"enabled": true
139139
},
140140
{
141141
"key": "TEMPLATEA_ID",
142-
"value": "6a4d829d-0922-4e3b-8c77-4ef4cd15d1d7",
142+
"value": "fad5a22f-cb7d-4316-ba52-6e22934b960e",
143143
"enabled": true
144144
},
145145
{
146146
"key": "TEMPLATEB_ID",
147-
"value": "8ad1e168-feeb-4265-a2e2-5255a8e00b2a",
147+
"value": "8b9eb3a0-454a-43a3-a45e-67aa9a08be7d",
148148
"enabled": true
149149
},
150150
{
151151
"key": "TEMPLATEC_ID",
152-
"value": "ee6b3a27-f734-4610-9e93-ce484f310565",
152+
"value": "a640a736-875d-4af9-b7a8-7bab5fc6ff71",
153153
"enabled": true
154154
},
155155
{
156156
"key": "ATTACHMENT_ID1",
157-
"value": "59230def-0158-44c5-a9cc-c3cd5bd283ad",
157+
"value": "2193b392-394d-44c3-9257-1f3c4907558b",
158158
"enabled": true
159159
},
160160
{
@@ -259,22 +259,22 @@
259259
},
260260
{
261261
"key": "TEST_SETTING_M2M_ID",
262-
"value": "",
262+
"value": "cbac1bc8-749b-4e8b-9ff5-cfc129342e22",
263263
"enabled": true
264264
},
265265
{
266266
"key": "TEST_TYPE_M2M_ID",
267-
"value": "",
267+
"value": "e0d5202c-e9e5-4ec2-a218-3c08688813a5",
268268
"enabled": true
269269
},
270270
{
271271
"key": "TEST_PHASE_M2M_ID",
272-
"value": "",
272+
"value": "06f2e9f2-656e-4862-838c-75e793fd2f8c",
273273
"enabled": true
274274
},
275275
{
276276
"key": "TEST_TEMPLATE_M2M_ID",
277-
"value": "",
277+
"value": "ba402af0-b13a-4772-9550-d0988635672b",
278278
"enabled": true
279279
},
280280
{
@@ -286,9 +286,36 @@
286286
"key": "TEST_ATTACHMENT_M2M_ID",
287287
"value": "",
288288
"enabled": true
289+
},
290+
{
291+
"key": "ES_CHALLENGE_ID",
292+
"value": "173803d3-019e-4033-b1cf-d7205c7f774c",
293+
"description": {
294+
"content": "",
295+
"type": "text/plain"
296+
},
297+
"enabled": true
298+
},
299+
{
300+
"key": "ES_TYPE_ID",
301+
"value": "45415132-79fa-4d13-a9ac-71f50020dc10",
302+
"description": {
303+
"content": "",
304+
"type": "text/plain"
305+
},
306+
"enabled": true
307+
},
308+
{
309+
"key": "ES_TIMELINE_TEMPLATE_ID",
310+
"value": "8e17090c-465b-4c17-b6d9-dfa16300b0aa",
311+
"description": {
312+
"content": "",
313+
"type": "text/plain"
314+
},
315+
"enabled": true
289316
}
290317
],
291318
"_postman_variable_scope": "environment",
292-
"_postman_exported_at": "2019-06-06T05:22:00.094Z",
293-
"_postman_exported_using": "Postman/7.1.1"
319+
"_postman_exported_at": "2019-06-16T20:45:25.574Z",
320+
"_postman_exported_using": "Postman/6.4.4"
294321
}

‎package-lock.json

Lines changed: 362 additions & 429 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,25 @@
1919
"standard": "^12.0.1"
2020
},
2121
"dependencies": {
22-
"aws-sdk": "^2.445.0",
23-
"axios": "^0.18.0",
22+
"aws-sdk": "^2.466.0",
23+
"axios": "^0.19.0",
2424
"bluebird": "^3.5.1",
2525
"body-parser": "^1.15.1",
26-
"config": "^3.0.0",
26+
"config": "^3.0.1",
2727
"cors": "^2.7.1",
2828
"dynamoose": "^1.6.4",
29+
"elasticsearch": "^15.1.1",
2930
"express": "^4.14.0",
3031
"express-fileupload": "^1.1.4",
3132
"express-interceptor": "^1.2.0",
3233
"get-parameter-names": "^0.3.0",
34+
"http-aws-es": "^6.0.0",
3335
"http-status-codes": "^1.3.0",
3436
"joi": "^14.0.0",
3537
"jsonwebtoken": "^8.3.0",
3638
"lodash": "^4.17.11",
37-
"tc-core-library-js": "appirio-tech/tc-core-library-js.git#v2.4.1",
3839
"tc-bus-api-wrapper": "topcoder-platform/tc-bus-api-wrapper.git",
40+
"tc-core-library-js": "appirio-tech/tc-core-library-js.git#v2.6.2",
3941
"uuid": "^3.3.2",
4042
"winston": "^3.1.0"
4143
},

‎src/common/helper.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ const m2mAuth = require('tc-core-library-js').auth.m2m
1313
const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME']))
1414
const axios = require('axios')
1515
const busApi = require('tc-bus-api-wrapper')
16+
const elasticsearch = require('elasticsearch')
1617

1718
// Bus API Client
1819
let busApiClient
1920

21+
// Elasticsearch client
22+
let esClient
23+
2024
AWS.config.update({
2125
s3: config.AMAZON.S3_API_VERSION,
2226
accessKeyId: config.AMAZON.AWS_ACCESS_KEY_ID,
@@ -464,6 +468,35 @@ async function postBusEvent (topic, payload) {
464468
})
465469
}
466470

471+
/**
472+
* Get ES Client
473+
* @return {Object} Elasticsearch Client Instance
474+
*/
475+
function getESClient () {
476+
if (esClient) {
477+
return esClient
478+
}
479+
const esHost = config.get('ES.HOST')
480+
// AWS ES configuration is different from other providers
481+
if (/.*amazonaws.*/.test(esHost)) {
482+
esClient = elasticsearch.Client({
483+
apiVersion: config.get('ES.API_VERSION'),
484+
hosts: esHost,
485+
connectionClass: require('http-aws-es'), // eslint-disable-line global-require
486+
amazonES: {
487+
region: config.get('AMAZON.AWS_REGION'),
488+
credentials: new AWS.EnvironmentCredentials('AWS')
489+
}
490+
})
491+
} else {
492+
esClient = new elasticsearch.Client({
493+
apiVersion: config.get('ES.API_VERSION'),
494+
hosts: esHost
495+
})
496+
}
497+
return esClient
498+
}
499+
467500
module.exports = {
468501
wrapExpress,
469502
autoWrapExpress,
@@ -485,5 +518,6 @@ module.exports = {
485518
getChallengeResources,
486519
getUserGroups,
487520
ensureNoDuplicateOrNullElements,
488-
postBusEvent
521+
postBusEvent,
522+
getESClient
489523
}

‎src/services/ChallengeService.js

Lines changed: 117 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
const _ = require('lodash')
66
const Joi = require('joi')
77
const uuid = require('uuid/v4')
8+
const config = require('config')
89
const helper = require('../common/helper')
910
const logger = require('../common/logger')
1011
const errors = require('../common/errors')
1112
const constants = require('../../app-constants')
1213
const models = require('../models')
14+
const HttpStatus = require('http-status-codes')
1315

1416
/**
1517
* Filter challenges by groups access
@@ -56,18 +58,55 @@ async function ensureAccessibleByGroupsAccess (currentUser, challenge) {
5658
* @returns {Object} the search result
5759
*/
5860
async function searchChallenges (currentUser, criteria) {
59-
const list = await helper.scan('Challenge')
60-
let records = _.filter(list, e => helper.partialMatch(criteria.name, e.name) &&
61-
helper.partialMatch(criteria.description, e.description) &&
62-
(_.isUndefined(criteria.createdDateStart) || criteria.createdDateStart.getTime() <= e.created.getTime()) &&
63-
(_.isUndefined(criteria.createdDateEnd) || criteria.createdDateEnd.getTime() >= e.created.getTime()) &&
64-
(_.isUndefined(criteria.updatedDateStart) || (!_.isUndefined(e.updated) && criteria.updatedDateStart.getTime() <= e.updated.getTime())) &&
65-
(_.isUndefined(criteria.updatedDateEnd) || (!_.isUndefined(e.updated) && criteria.updatedDateEnd.getTime() >= e.updated.getTime())) &&
66-
(_.isUndefined(criteria.createdBy) || criteria.createdBy.toLowerCase() === e.createdBy.toLowerCase())
67-
)
68-
records = await filterChallengesByGroupsAccess(currentUser, records)
69-
const total = records.length
70-
const result = records.slice((criteria.page - 1) * criteria.perPage, criteria.page * criteria.perPage)
61+
// construct ES query
62+
const boolQuery = []
63+
_.forIn(_.omit(criteria, ['page', 'perPage', 'tag', 'group', 'createdDateStart', 'createdDateEnd',
64+
'updatedDateStart', 'updatedDateEnd']), (value, key) => {
65+
const filter = { match_phrase: {} }
66+
filter.match_phrase[key] = value
67+
boolQuery.push(filter)
68+
})
69+
if (criteria.tag) {
70+
boolQuery.push({ match_phrase: { tags: criteria.tag } })
71+
}
72+
if (criteria.group) {
73+
boolQuery.push({ match_phrase: { groups: criteria.group } })
74+
}
75+
if (criteria.createdDateStart) {
76+
boolQuery.push({ range: { created: { gte: criteria.createdDateStart } } })
77+
}
78+
if (criteria.createdDateEnd) {
79+
boolQuery.push({ range: { created: { lte: criteria.createdDateEnd } } })
80+
}
81+
if (criteria.updatedDateStart) {
82+
boolQuery.push({ range: { updated: { gte: criteria.updatedDateStart } } })
83+
}
84+
if (criteria.updatedDateEnd) {
85+
boolQuery.push({ range: { updated: { lte: criteria.updatedDateEnd } } })
86+
}
87+
88+
const esQuery = {
89+
index: config.get('ES.ES_INDEX'),
90+
type: config.get('ES.ES_TYPE'),
91+
size: criteria.perPage,
92+
from: (criteria.page - 1) * criteria.perPage, // Es Index starts from 0
93+
body: {
94+
query: {
95+
bool: {
96+
filter: boolQuery
97+
}
98+
},
99+
sort: [{ 'created': { 'order': 'asc' } }]
100+
}
101+
}
102+
103+
// get ES client
104+
const esClient = helper.getESClient()
105+
// Search with constructed query
106+
const docs = await esClient.search(esQuery)
107+
// Extract data from hits
108+
const total = docs.hits.total
109+
const result = _.map(docs.hits.hits, item => item._source)
71110

72111
const typeList = await helper.scan('ChallengeType')
73112
const typeMap = new Map()
@@ -87,13 +126,25 @@ searchChallenges.schema = {
87126
criteria: Joi.object().keys({
88127
page: Joi.page(),
89128
perPage: Joi.perPage(),
129+
id: Joi.optionalId(),
130+
typeId: Joi.optionalId(),
131+
track: Joi.string(),
90132
name: Joi.string(),
91133
description: Joi.string(),
134+
timelineTemplateId: Joi.optionalId(),
135+
reviewType: Joi.string(),
136+
tag: Joi.string(),
137+
projectId: Joi.number().integer().positive(),
138+
forumId: Joi.number().integer().positive(),
139+
legacyId: Joi.number().integer().positive(),
140+
status: Joi.string().valid(_.values(constants.challengeStatuses)),
141+
group: Joi.string(),
92142
createdDateStart: Joi.date(),
93143
createdDateEnd: Joi.date(),
94144
updatedDateStart: Joi.date(),
95145
updatedDateEnd: Joi.date(),
96-
createdBy: Joi.string()
146+
createdBy: Joi.string(),
147+
updatedBy: Joi.string()
97148
})
98149
}
99150

@@ -148,7 +199,7 @@ async function createChallenge (currentUser, challenge) {
148199
await ensureAccessibleByGroupsAccess(currentUser, challenge)
149200

150201
const ret = await helper.create('Challenge', _.assign({
151-
id: uuid(), created: new Date(), createdBy: currentUser.handle }, challenge))
202+
id: uuid(), created: new Date(), createdBy: currentUser.handle || currentUser.sub }, challenge))
152203

153204
// post bus event
154205
await helper.postBusEvent(constants.Topics.ChallengeCreated, ret)
@@ -188,6 +239,7 @@ createChallenge.schema = {
188239
tags: Joi.array().items(Joi.string().required()).min(1).required(), // tag names
189240
projectId: Joi.number().integer().positive().required(),
190241
forumId: Joi.number().integer().positive().required(),
242+
legacyId: Joi.number().integer().positive(),
191243
status: Joi.string().valid(_.values(constants.challengeStatuses)).required(),
192244
groups: Joi.array().items(Joi.string()) // group names
193245
}).required()
@@ -227,7 +279,24 @@ async function populateSettings (data) {
227279
* @returns {Object} the challenge with given id
228280
*/
229281
async function getChallenge (currentUser, id) {
230-
const challenge = await helper.getById('Challenge', id)
282+
const esClient = helper.getESClient()
283+
284+
// get challenge from Elasticsearch
285+
let challenge
286+
try {
287+
challenge = await esClient.getSource({
288+
index: config.get('ES.ES_INDEX'),
289+
type: config.get('ES.ES_TYPE'),
290+
id
291+
})
292+
} catch (e) {
293+
if (e.statusCode === HttpStatus.NOT_FOUND) {
294+
throw new errors.NotFoundError(`Challenge of id ${id} is not found.`)
295+
} else {
296+
throw e
297+
}
298+
}
299+
231300
// check groups authorization
232301
await ensureAccessibleByGroupsAccess(currentUser, challenge)
233302

@@ -346,7 +415,7 @@ async function update (currentUser, challengeId, data, isFull) {
346415
newAttachments = await helper.getByIds('Attachment', data.attachmentIds || [])
347416
}
348417

349-
if (challenge.createdBy.toLowerCase() !== currentUser.handle.toLowerCase() && !currentUser.isMachine && !helper.hasAdminRole(currentUser)) {
418+
if (!currentUser.isMachine && !helper.hasAdminRole(currentUser) && challenge.createdBy.toLowerCase() !== currentUser.handle.toLowerCase()) {
350419
throw new errors.ForbiddenError(`Only M2M, admin or challenge's copilot can perform modification.`)
351420
}
352421

@@ -366,7 +435,7 @@ async function update (currentUser, challengeId, data, isFull) {
366435
}
367436

368437
data.updated = new Date()
369-
data.updatedBy = currentUser.handle
438+
data.updatedBy = currentUser.handle || currentUser.sub
370439
const updateDetails = {}
371440
const auditLogs = []
372441
_.each(data, (value, key) => {
@@ -432,7 +501,7 @@ async function update (currentUser, challengeId, data, isFull) {
432501
oldValue,
433502
newValue,
434503
created: new Date(),
435-
createdBy: currentUser.handle
504+
createdBy: currentUser.handle || currentUser.sub
436505
})
437506
}
438507
}
@@ -447,9 +516,11 @@ async function update (currentUser, challengeId, data, isFull) {
447516
oldValue: JSON.stringify(challenge.challengeSettings),
448517
newValue: 'NULL',
449518
created: new Date(),
450-
createdBy: currentUser.handle
519+
createdBy: currentUser.handle || currentUser.sub
451520
})
452521
delete challenge.challengeSettings
522+
// send null to Elasticsearch to clear the field
523+
data.challengeSettings = null
453524
}
454525
if (isFull && _.isUndefined(data.attachmentIds) && challenge.attachments) {
455526
if (!updateDetails['$DELETE']) {
@@ -463,9 +534,11 @@ async function update (currentUser, challengeId, data, isFull) {
463534
oldValue: JSON.stringify(challenge.attachments),
464535
newValue: 'NULL',
465536
created: new Date(),
466-
createdBy: currentUser.handle
537+
createdBy: currentUser.handle || currentUser.sub
467538
})
468539
delete challenge.attachments
540+
// send null to Elasticsearch to clear the field
541+
data.attachments = null
469542
}
470543
if (isFull && _.isUndefined(data.groups) && challenge.groups) {
471544
if (!updateDetails['$DELETE']) {
@@ -479,9 +552,29 @@ async function update (currentUser, challengeId, data, isFull) {
479552
oldValue: JSON.stringify(challenge.groups),
480553
newValue: 'NULL',
481554
created: new Date(),
482-
createdBy: currentUser.handle
555+
createdBy: currentUser.handle || currentUser.sub
483556
})
484557
delete challenge.groups
558+
// send null to Elasticsearch to clear the field
559+
data.groups = null
560+
}
561+
if (isFull && _.isUndefined(data.legacyId) && challenge.legacyId) {
562+
if (!updateDetails['$DELETE']) {
563+
updateDetails['$DELETE'] = {}
564+
}
565+
updateDetails['$DELETE'].legacyId = null
566+
auditLogs.push({
567+
id: uuid(),
568+
challengeId,
569+
fieldName: 'legacyId',
570+
oldValue: JSON.stringify(challenge.legacyId),
571+
newValue: 'NULL',
572+
created: new Date(),
573+
createdBy: currentUser.handle || currentUser.sub
574+
})
575+
delete challenge.legacyId
576+
// send null to Elasticsearch to clear the field
577+
data.legacyId = null
485578
}
486579

487580
await models.Challenge.update({ id: challengeId }, updateDetails)
@@ -493,6 +586,7 @@ async function update (currentUser, challengeId, data, isFull) {
493586
_.assign(challenge, data)
494587
if (!_.isUndefined(newAttachments)) {
495588
challenge.attachments = newAttachments
589+
data.attachments = newAttachments
496590
}
497591

498592
// delete unused attachments
@@ -553,6 +647,7 @@ fullyUpdateChallenge.schema = {
553647
tags: Joi.array().items(Joi.string().required()).min(1).required(), // tag names
554648
projectId: Joi.number().integer().positive().required(),
555649
forumId: Joi.number().integer().positive().required(),
650+
legacyId: Joi.number().integer().positive(),
556651
status: Joi.string().valid(_.values(constants.challengeStatuses)).required(),
557652
attachmentIds: Joi.array().items(Joi.optionalId()),
558653
groups: Joi.array().items(Joi.string()) // group names
@@ -604,6 +699,7 @@ partiallyUpdateChallenge.schema = {
604699
tags: Joi.array().items(Joi.string().required()).min(1), // tag names
605700
projectId: Joi.number().integer().positive(),
606701
forumId: Joi.number().integer().positive(),
702+
legacyId: Joi.number().integer().positive(),
607703
status: Joi.string().valid(_.values(constants.challengeStatuses)),
608704
attachmentIds: Joi.array().items(Joi.optionalId()),
609705
groups: Joi.array().items(Joi.string()) // group names

0 commit comments

Comments
 (0)
Please sign in to comment.