@@ -206,39 +206,38 @@ function validateUpdates(
206206
207207 metrics . increment ( `update.action.${ update . action } .count` ) ;
208208
209+ if ( update . action !== 'setting' && ! platformMembershipId ) {
210+ metrics . increment ( 'update.validation.platformMembershipIdMissing.count' ) ;
211+ result = {
212+ status : 'InvalidArgument' ,
213+ message : `${ update . action } requires platform membership ID to be set` ,
214+ } ;
215+ }
216+
209217 switch ( update . action ) {
210218 case 'setting' :
211219 case 'tag_cleanup' :
220+ case 'delete_loadout' :
221+ case 'track_triumph' :
222+ case 'delete_search' :
212223 // no special validation
213224 break ;
214225
215226 case 'loadout' :
216- result = validateUpdateLoadout ( platformMembershipId , update . payload , appId ) ;
217- break ;
218-
219- case 'delete_loadout' :
220- result = validateDeleteLoadout ( platformMembershipId ) ;
227+ result = validateUpdateLoadout ( update . payload , appId ) ;
221228 break ;
222229
223230 case 'tag' :
224- result = validateUpdateItemAnnotation ( platformMembershipId , update . payload , appId ) ;
231+ result = validateUpdateItemAnnotation ( update . payload ) ;
225232 break ;
226233
227234 case 'item_hash_tag' :
228- result = validateUpdateItemHashTag ( platformMembershipId , update . payload , appId ) ;
229- break ;
230-
231- case 'track_triumph' :
232- result = validateTrackTriumph ( platformMembershipId ) ;
235+ result = validateUpdateItemHashTag ( update . payload ) ;
233236 break ;
234237
235238 case 'search' :
236239 case 'save_search' :
237- result = validateSearch ( platformMembershipId , update . payload ) ;
238- break ;
239-
240- case 'delete_search' :
241- result = validateDeleteSearch ( platformMembershipId ) ;
240+ result = validateSearch ( update . payload ) ;
242241 break ;
243242
244243 default :
@@ -253,6 +252,14 @@ function validateUpdates(
253252 } ;
254253 }
255254 if ( result . status !== 'Success' ) {
255+ captureMessage ( `update ${ update . action } failed validation` , {
256+ extra : {
257+ update,
258+ result,
259+ platformMembershipId,
260+ appId,
261+ } ,
262+ } ) ;
256263 console . log ( 'Stately failed update' , update . action , result , appId ) ;
257264 }
258265 results . push ( result ) ;
@@ -282,11 +289,16 @@ async function statelyUpdate(
282289 return [ u ] ;
283290 } ) ;
284291
292+ const tagIds = new Set < string > ( ) ;
293+ for ( const update of sortedUpdates ) {
294+ if ( update . action === 'tag' ) {
295+ tagIds . add ( update . payload . id ) ;
296+ }
297+ }
298+
285299 for ( const updateChunk of chunk ( sortedUpdates , 25 ) ) {
286300 await client . transaction ( async ( txn ) => {
287301 for ( const [ action , group ] of Object . entries ( groupBy ( updateChunk , actionKey ) ) ) {
288- metrics . increment ( `update.action.${ action } .count` ) ;
289-
290302 switch ( action ) {
291303 case 'setting' : {
292304 // The DIM reducer already combines settings updates, but just in case...
@@ -328,7 +340,11 @@ async function statelyUpdate(
328340 case 'tag_cleanup' : {
329341 const instanceIds = ( group as TagCleanupUpdate [ ] )
330342 . flatMap ( ( u ) => u . payload )
331- . filter ( isValidItemId ) ;
343+ . filter (
344+ ( id ) =>
345+ // We've seen a problem where DIM sends a tag_cleanup and a tag for the same item in the same update
346+ ! tagIds . has ( id ) && isValidItemId ( id ) ,
347+ ) ;
332348 if ( instanceIds . length ) {
333349 await deleteItemAnnotationListStately (
334350 txn ,
@@ -388,8 +404,6 @@ async function pgUpdate(
388404) {
389405 return transaction ( async ( client ) => {
390406 for ( const update of updates ) {
391- metrics . increment ( `update.action.${ update . action } .count` ) ;
392-
393407 switch ( update . action ) {
394408 case 'setting' :
395409 await updateSetting ( client , appId , bungieMembershipId , update . payload ) ;
@@ -488,25 +502,8 @@ async function updateLoadout(
488502 metrics . timing ( 'update.loadout' , start ) ;
489503}
490504
491- function validateUpdateLoadout (
492- platformMembershipId : string | undefined ,
493- loadout : Loadout ,
494- appId : string ,
495- ) : ProfileUpdateResult {
496- if ( ! platformMembershipId ) {
497- metrics . increment ( 'update.validation.platformMembershipIdMissing.count' ) ;
498- return {
499- status : 'InvalidArgument' ,
500- message : 'Loadouts require platform membership ID to be set' ,
501- } ;
502- }
503-
504- const validationResult = validateLoadout ( 'update' , loadout , appId ) ;
505- if ( validationResult ) {
506- return validationResult ;
507- }
508-
509- return { status : 'Success' } ;
505+ function validateUpdateLoadout ( loadout : Loadout , appId : string ) : ProfileUpdateResult {
506+ return validateLoadout ( 'update' , loadout , appId ) ?? { status : 'Success' } ;
510507}
511508
512509export function validateLoadout ( metricPrefix : string , loadout : Loadout , appId : string ) {
@@ -610,17 +607,6 @@ async function deleteLoadout(
610607 metrics . timing ( 'update.deleteLoadout' , start ) ;
611608}
612609
613- function validateDeleteLoadout ( platformMembershipId : string | undefined ) : ProfileUpdateResult {
614- if ( ! platformMembershipId ) {
615- metrics . increment ( 'update.validation.platformMembershipIdMissing.count' ) ;
616- return {
617- status : 'InvalidArgument' ,
618- message : 'Loadouts require platform membership ID to be set' ,
619- } ;
620- }
621- return { status : 'Success' } ;
622- }
623-
624610async function updateItemAnnotation (
625611 client : ClientBase ,
626612 appId : string ,
@@ -641,27 +627,8 @@ async function updateItemAnnotation(
641627 metrics . timing ( 'update.tag' , start ) ;
642628}
643629
644- function validateUpdateItemAnnotation (
645- platformMembershipId : string | undefined ,
646- itemAnnotation : ItemAnnotation ,
647- appId : string ,
648- ) : ProfileUpdateResult {
649- if ( ! platformMembershipId ) {
650- metrics . increment ( 'update.validation.platformMembershipIdMissing.count' ) ;
651- return {
652- status : 'InvalidArgument' ,
653- message : 'Tags require platform membership ID to be set' ,
654- } ;
655- }
656-
630+ function validateUpdateItemAnnotation ( itemAnnotation : ItemAnnotation ) : ProfileUpdateResult {
657631 if ( ! isValidItemId ( itemAnnotation . id ) ) {
658- captureMessage ( 'item ID is not in the right format' , {
659- extra : {
660- itemAnnotation,
661- platformMembershipId,
662- appId,
663- } ,
664- } ) ;
665632 metrics . increment ( 'update.validation.badItemId.count' ) ;
666633 return {
667634 status : 'InvalidArgument' ,
@@ -690,27 +657,8 @@ function validateUpdateItemAnnotation(
690657 return { status : 'Success' } ;
691658}
692659
693- function validateUpdateItemHashTag (
694- platformMembershipId : string | undefined ,
695- itemAnnotation : ItemHashTag ,
696- appId : string ,
697- ) : ProfileUpdateResult {
698- if ( ! platformMembershipId ) {
699- metrics . increment ( 'update.validation.platformMembershipIdMissing.count' ) ;
700- return {
701- status : 'InvalidArgument' ,
702- message : 'Tags require platform membership ID to be set' ,
703- } ;
704- }
705-
660+ function validateUpdateItemHashTag ( itemAnnotation : ItemHashTag ) : ProfileUpdateResult {
706661 if ( ! Number . isInteger ( itemAnnotation . hash ) ) {
707- captureMessage ( 'item hash is not in the right format' , {
708- extra : {
709- itemAnnotation,
710- platformMembershipId,
711- appId,
712- } ,
713- } ) ;
714662 metrics . increment ( 'update.validation.badItemHash.count' ) ;
715663 return {
716664 status : 'InvalidArgument' ,
@@ -775,17 +723,6 @@ async function trackTriumph(
775723 metrics . timing ( 'update.trackTriumph' , start ) ;
776724}
777725
778- function validateTrackTriumph ( platformMembershipId : string | undefined ) : ProfileUpdateResult {
779- if ( ! platformMembershipId ) {
780- metrics . increment ( 'update.validation.platformMembershipIdMissing.count' ) ;
781- return {
782- status : 'InvalidArgument' ,
783- message : 'Tracked triumphs require platform membership ID to be set' ,
784- } ;
785- }
786- return { status : 'Success' } ;
787- }
788-
789726async function recordSearch (
790727 client : ClientBase ,
791728 appId : string ,
@@ -825,18 +762,7 @@ async function saveSearch(
825762 metrics . timing ( 'update.saveSearch' , start ) ;
826763}
827764
828- function validateSearch (
829- platformMembershipId : string | undefined ,
830- payload : UsedSearchUpdate [ 'payload' ] ,
831- ) : ProfileUpdateResult {
832- if ( ! platformMembershipId ) {
833- metrics . increment ( 'update.validation.platformMembershipIdMissing.count' ) ;
834- return {
835- status : 'InvalidArgument' ,
836- message : 'Searches require platform membership ID to be set' ,
837- } ;
838- }
839-
765+ function validateSearch ( payload : UsedSearchUpdate [ 'payload' ] ) : ProfileUpdateResult {
840766 if ( payload . query . length > 2048 ) {
841767 metrics . increment ( 'update.validation.searchTooLong.count' ) ;
842768 return {
@@ -870,17 +796,6 @@ async function deleteSearch(
870796 metrics . timing ( 'update.deleteSearch' , start ) ;
871797}
872798
873- function validateDeleteSearch ( platformMembershipId : string | undefined ) : ProfileUpdateResult {
874- if ( ! platformMembershipId ) {
875- metrics . increment ( 'update.validation.platformMembershipIdMissing.count' ) ;
876- return {
877- status : 'InvalidArgument' ,
878- message : 'Searches require platform membership ID to be set' ,
879- } ;
880- }
881- return { status : 'Success' } ;
882- }
883-
884799async function updateItemHashTag (
885800 client : ClientBase ,
886801 appId : string ,
0 commit comments