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

Commit 7860799

Browse files
authored
Merge branch 'develop' into master
2 parents 4d23cc2 + 644e50d commit 7860799

File tree

5 files changed

+178
-13
lines changed

5 files changed

+178
-13
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ Example:
8383
- TBD
8484

8585
## Running tests
86+
8687
- TBD
8788

8889
## Running tests in CI
90+
8991
- TBD

src/controllers/syncController.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ async function autoQueueChallenges () {
6969
* @param {Object} filter {startDate, endDate, legacyId, status}
7070
*/
7171
async function queueChallenges (filter) {
72+
logger.debug(`Inside queueChallenges with filter: ${JSON.stringify(filter)}`)
7273
// find challenges in es status
7374
let page = 1
7475
const perPage = 50

src/models/audit-log.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* This defines AuditLog model.
3+
*/
4+
5+
const dynamoose = require('dynamoose')
6+
7+
const Schema = dynamoose.Schema
8+
9+
const schema = new Schema({
10+
id: {
11+
type: String,
12+
hashKey: true,
13+
required: true
14+
},
15+
challengeId: {
16+
type: String,
17+
required: true
18+
},
19+
fieldName: {
20+
type: String,
21+
required: true
22+
},
23+
oldValue: {
24+
type: String,
25+
required: true
26+
},
27+
newValue: {
28+
type: String,
29+
required: true
30+
},
31+
created: {
32+
type: Date,
33+
required: true
34+
},
35+
createdBy: {
36+
type: String,
37+
required: true
38+
},
39+
memberId: {
40+
type: String
41+
}
42+
},
43+
{
44+
throughput: { read: 4, write: 2 }
45+
})
46+
47+
module.exports = schema
48+

src/models/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ module.exports = {
3434
Challenge: dynamoose.model('Challenge', require('./challenge')),
3535
ChallengeType: dynamoose.model('ChallengeType', require('./challenge-type')),
3636
ChallengeTrack: dynamoose.model('ChallengeTrack', require('./challenge-track')),
37-
ChallengeTimelineTemplate: dynamoose.model('ChallengeTimelineTemplate', require('./challenge-timeline-template'))
37+
ChallengeTimelineTemplate: dynamoose.model('ChallengeTimelineTemplate', require('./challenge-timeline-template')),
38+
AuditLog: dynamoose.model('AuditLog', require('./audit-log'))
3839
}

src/services/challengeService.js

Lines changed: 125 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const _ = require('lodash')
88
const HashMap = require('hashmap')
99
const logger = require('../util/logger')
1010
const helper = require('../util/helper')
11-
const { Challenge, ChallengeType, ChallengeTimelineTemplate } = require('../models')
11+
const { Challenge, ChallengeType, ChallengeTimelineTemplate, AuditLog } = require('../models')
1212
const { getESClient, getV4ESClient, getM2MToken, forceV4ESFeeder } = require('../util/helper')
1313
const challengeInformixService = require('./challengeInformixService')
1414
const resourceService = require('./resourceService')
@@ -34,17 +34,25 @@ const challengePropertiesToOmitFromDynamo = [
3434
'track'
3535
]
3636

37+
/**
38+
* Check whether given two PrizeSet Array are different.
39+
* @param {Array} prizeSets the first PrizeSet Array
40+
* @param {Array} otherPrizeSets the second PrizeSet Array
41+
* @returns {Boolean} true if different, false otherwise
42+
*/
43+
function isDifferentPrizeSets (prizeSets = [], otherPrizeSets = []) {
44+
return !_.isEqual(_.sortBy(prizeSets, 'type'), _.sortBy(otherPrizeSets, 'type'))
45+
}
46+
3747
async function save (challenge) {
3848
// Check if challenge is already created
49+
let challengesInES
3950
if (!challenge.id) {
4051
try {
41-
const challengesInES = await getChallengeFromES(challenge.legacyId)
42-
if (challengesInES.length === 1) {
43-
logger.info(`PREVENT DUPLICATE CHALLENGE - ${challenge.legacyId} - V5 already exists ${challengesInES[0].challengeId}`)
44-
challenge.id = challengesInES[0].challengeId
45-
} else if (challengesInES.length > 1) {
46-
// There are more than 1 duplicate challenges
47-
logger.warn(`Duplicate challenge found for legacy ID ${challenge.legacyId} has ${challengesInES.length} duplicates - ${challengesInES.toString()}`)
52+
challengesInES = await getChallengeFromES(challenge.legacyId, true)
53+
if (challengesInES.length > 0) {
54+
logger.debug(`PREVENT DUPLICATE CHALLENGE - ${challenge.legacyId} - V5 already exists ${challengesInES[0].id}`)
55+
challenge.id = challengesInES[0].id
4856
}
4957
} catch (e) {
5058
logger.error(`Error fetching V5 challenge ${JSON.stringify(e)}`)
@@ -54,7 +62,7 @@ async function save (challenge) {
5462
if (challenge.id) {
5563
// logger.debug(`Update Challenge ${challenge.id}`)
5664
// return
57-
return updateChallenge(challenge)
65+
return updateChallenge(challenge, challengesInES && challengesInES.length ? challengesInES[0] : null)
5866
}
5967
// logger.debug(`Create Challenge ${challenge.id}`)
6068
// return
@@ -91,14 +99,119 @@ async function createChallenge (challenge) {
9199
* Update challenge data to new system
92100
* @param {Object} challenge challenge data
93101
*/
94-
async function updateChallenge (challenge) {
102+
async function updateChallenge (challenge, previousState) {
103+
if (!previousState) {
104+
challengesInES = await getChallengeFromES(challenge.legacyId, true)
105+
previousState = challengesInES[0]
106+
}
107+
const auditLogs = []
108+
95109
try {
96110
if (challenge.task && _.isUndefined(challenge.task.memberId) && (challenge.status === constants.challengeStatuses.Completed || _.get(challenge, 'winners.length') > 0)) {
97111
_.unset(challenge, 'task')
98112
}
99113
const updateChallenge = new Challenge(_.omit(challenge, ['created', 'createdBy', 'name']))
100114
// numOfSubmissions and numOfRegistrants are not stored in dynamo, they're calclated by the ES processor
101115
await Challenge.update({ id: challenge.id }, _.omit(updateChallenge, challengePropertiesToOmitFromDynamo))
116+
117+
const updateDetails = {}
118+
let phasesHaveBeenModified = false
119+
_.each(updateChallenge, (value, key) => {
120+
let op
121+
if (key === 'metadata') {
122+
if (_.isUndefined(previousState[key]) || previousState[key].length !== value.length ||
123+
_.differenceWith(previousState[key], value, _.isEqual).length !== 0) {
124+
op = '$PUT'
125+
}
126+
} else if (key === 'phases') {
127+
// always consider a modification if the property exists
128+
phasesHaveBeenModified = true
129+
op = '$PUT'
130+
} else if (key === 'prizeSets') {
131+
if (isDifferentPrizeSets(previousState[key], value)) {
132+
op = '$PUT'
133+
}
134+
} else if (key === 'tags') {
135+
if (_.isUndefined(previousState[key]) || previousState[key].length !== value.length ||
136+
_.intersection(previousState[key], value).length !== value.length) {
137+
op = '$PUT'
138+
}
139+
} else if (key === 'attachments') {
140+
const oldIds = _.map(previousState.attachments || [], (a) => a.id)
141+
if (oldIds.length !== value.length ||
142+
_.intersection(oldIds, _.map(value, a => a.id)).length !== value.length) {
143+
op = '$PUT'
144+
}
145+
} else if (key === 'groups') {
146+
if (_.isUndefined(previousState[key]) || previousState[key].length !== value.length ||
147+
_.intersection(previousState[key], value).length !== value.length) {
148+
op = '$PUT'
149+
}
150+
} else if (key === 'winners') {
151+
if (_.isUndefined(previousState[key]) || previousState[key].length !== value.length ||
152+
_.intersectionWith(previousState[key], value, _.isEqual).length !== value.length) {
153+
op = '$PUT'
154+
}
155+
} else if (key === 'terms') {
156+
const oldIds = _.map(previousState.terms || [], (t) => t.id)
157+
const newIds = _.map(value || [], (t) => t.id)
158+
if (oldIds.length !== newIds.length ||
159+
_.intersection(oldIds, newIds).length !== value.length) {
160+
op = '$PUT'
161+
}
162+
} else if (key === 'billing' || key === 'legacy') {
163+
// make sure that's always being udpated
164+
op = '$PUT'
165+
} else if (_.isUndefined(previousState[key]) || previousState[key] !== value) {
166+
op = '$PUT'
167+
} else if (_.get(previousState, 'legacy.pureV5Task') && key === 'task') {
168+
// always update task for pureV5 challenges
169+
op = '$PUT'
170+
}
171+
172+
if (op) {
173+
if (_.isUndefined(updateDetails[op])) {
174+
updateDetails[op] = {}
175+
}
176+
if (key === 'attachments') {
177+
updateDetails[op].attachments = updateChallenge.attachments
178+
} else if (key === 'terms') {
179+
updateDetails[op].terms = updateChallenge.terms
180+
} else {
181+
updateDetails[op][key] = value
182+
}
183+
if (key !== 'updated' && key !== 'updatedBy') {
184+
let oldValue
185+
let newValue
186+
if (key === 'attachments') {
187+
oldValue = previousState.attachments ? JSON.stringify(previousState.attachments) : 'NULL'
188+
newValue = JSON.stringify(updateChallenge.attachments)
189+
} else if (key === 'terms') {
190+
oldValue = previousState.terms ? JSON.stringify(previousState.terms) : 'NULL'
191+
newValue = JSON.stringify(updateChallenge.terms)
192+
} else {
193+
oldValue = previousState[key] ? JSON.stringify(previousState[key]) : 'NULL'
194+
newValue = JSON.stringify(value)
195+
}
196+
// logger.debug(`Audit Log: Key ${key} OldValue: ${oldValue} NewValue: ${newValue}`)
197+
auditLogs.push({
198+
id: uuid(),
199+
challengeId: challenge.id,
200+
fieldName: key,
201+
oldValue,
202+
newValue,
203+
created: moment().utc(),
204+
createdBy: 'v5migration',
205+
memberId: null
206+
})
207+
}
208+
}
209+
})
210+
211+
if (auditLogs.length > 0) {
212+
// insert audit logs
213+
await AuditLog.batchPut(auditLogs)
214+
}
102215
await getESClient().update({
103216
index: config.get('ES.CHALLENGE_ES_INDEX'),
104217
type: config.get('ES.CHALLENGE_ES_TYPE'),
@@ -195,7 +308,7 @@ async function getChallengesFromES (legacyIds) {
195308
/**
196309
* Get existing challenges from ES using legacyId
197310
*/
198-
async function getChallengeFromES (legacyId) {
311+
async function getChallengeFromES (legacyId, full) {
199312
const esQuery = {
200313
index: config.get('ES.CHALLENGE_ES_INDEX'),
201314
type: config.get('ES.CHALLENGE_ES_TYPE'),
@@ -228,7 +341,7 @@ async function getChallengeFromES (legacyId) {
228341
}
229342
}
230343
// Extract data from hits
231-
return _.map(docs.hits.hits, item => ({
344+
return full ? _.map(docs.hits.hits, item => item._source) : _.map(docs.hits.hits, item => ({
232345
legacyId: item._source.legacyId,
233346
legacy: {
234347
screeningScorecardId: _.get(item._source, 'legacy.screeningScorecardId'),

0 commit comments

Comments
 (0)