@@ -8,7 +8,7 @@ const _ = require('lodash')
8
8
const HashMap = require ( 'hashmap' )
9
9
const logger = require ( '../util/logger' )
10
10
const helper = require ( '../util/helper' )
11
- const { Challenge, ChallengeType, ChallengeTimelineTemplate } = require ( '../models' )
11
+ const { Challenge, ChallengeType, ChallengeTimelineTemplate, AuditLog } = require ( '../models' )
12
12
const { getESClient, getV4ESClient, getM2MToken, forceV4ESFeeder } = require ( '../util/helper' )
13
13
const challengeInformixService = require ( './challengeInformixService' )
14
14
const resourceService = require ( './resourceService' )
@@ -34,17 +34,25 @@ const challengePropertiesToOmitFromDynamo = [
34
34
'track'
35
35
]
36
36
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
+
37
47
async function save ( challenge ) {
38
48
// Check if challenge is already created
49
+ let challengesInES
39
50
if ( ! challenge . id ) {
40
51
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
48
56
}
49
57
} catch ( e ) {
50
58
logger . error ( `Error fetching V5 challenge ${ JSON . stringify ( e ) } ` )
@@ -54,7 +62,7 @@ async function save (challenge) {
54
62
if ( challenge . id ) {
55
63
// logger.debug(`Update Challenge ${challenge.id}`)
56
64
// return
57
- return updateChallenge ( challenge )
65
+ return updateChallenge ( challenge , challengesInES && challengesInES . length ? challengesInES [ 0 ] : null )
58
66
}
59
67
// logger.debug(`Create Challenge ${challenge.id}`)
60
68
// return
@@ -91,14 +99,119 @@ async function createChallenge (challenge) {
91
99
* Update challenge data to new system
92
100
* @param {Object } challenge challenge data
93
101
*/
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
+
95
109
try {
96
110
if ( challenge . task && _ . isUndefined ( challenge . task . memberId ) && ( challenge . status === constants . challengeStatuses . Completed || _ . get ( challenge , 'winners.length' ) > 0 ) ) {
97
111
_ . unset ( challenge , 'task' )
98
112
}
99
113
const updateChallenge = new Challenge ( _ . omit ( challenge , [ 'created' , 'createdBy' , 'name' ] ) )
100
114
// numOfSubmissions and numOfRegistrants are not stored in dynamo, they're calclated by the ES processor
101
115
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
+ }
102
215
await getESClient ( ) . update ( {
103
216
index : config . get ( 'ES.CHALLENGE_ES_INDEX' ) ,
104
217
type : config . get ( 'ES.CHALLENGE_ES_TYPE' ) ,
@@ -195,7 +308,7 @@ async function getChallengesFromES (legacyIds) {
195
308
/**
196
309
* Get existing challenges from ES using legacyId
197
310
*/
198
- async function getChallengeFromES ( legacyId ) {
311
+ async function getChallengeFromES ( legacyId , full ) {
199
312
const esQuery = {
200
313
index : config . get ( 'ES.CHALLENGE_ES_INDEX' ) ,
201
314
type : config . get ( 'ES.CHALLENGE_ES_TYPE' ) ,
@@ -228,7 +341,7 @@ async function getChallengeFromES (legacyId) {
228
341
}
229
342
}
230
343
// 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 => ( {
232
345
legacyId : item . _source . legacyId ,
233
346
legacy : {
234
347
screeningScorecardId : _ . get ( item . _source , 'legacy.screeningScorecardId' ) ,
0 commit comments