@@ -4,8 +4,10 @@ import * as O from 'fp-ts/Option';
44import * as A from 'fp-ts/Array' ;
55import * as t from 'io-ts' ;
66import * as tt from 'io-ts-types' ;
7+ import recurly from 'recurly' ;
78import { DomainEvent } from '../../types' ;
89import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3' ;
10+ import { EmailAddressCodec } from '../../types/email-address' ;
911
1012import { constructEvent , EventOfType } from '../../types/domain-event' ;
1113import {
@@ -366,94 +368,172 @@ export const pullTroubleTicketResponses = async (
366368
367369let lastTroubleTicketSync : O . Option < DateTime > = O . none ; // FIXME - Temporary for POC.
368370
369- export const asyncApplyExternalEventSources = (
371+ async function asyncApplyGoogleEvents (
370372 logger : Logger ,
371373 currentState : BetterSQLite3Database ,
372- googleHelpers : O . Option < GoogleHelpers > ,
374+ googleHelpers : GoogleHelpers ,
373375 updateState : ( event : DomainEvent ) => void ,
374376 googleRefreshIntervalMs : number ,
375377 troubleTicketSheetId : O . Option < string > ,
376378 cacheSheetData : Dependencies [ 'cacheSheetData' ] ,
377379 cacheTroubleTicketData : Dependencies [ 'cacheTroubleTicketData' ]
378- ) => {
379- return ( ) => async ( ) => {
380- logger . info ( 'Applying external event sources...' ) ;
381- if ( O . isNone ( googleHelpers ) ) {
382- logger . info ( 'Google external event source disabled' ) ;
383- return ;
380+ ) {
381+ if ( O . isSome ( troubleTicketSheetId ) ) {
382+ logger . info ( 'Pulling latest trouble ticket reports...' ) ;
383+ if (
384+ O . isNone ( lastTroubleTicketSync ) ||
385+ lastTroubleTicketSync . value . diffNow ( ) > TROUBLE_TICKET_SYNC_INTERVAL
386+ ) {
387+ await pullTroubleTicketResponses (
388+ logger ,
389+ googleHelpers ,
390+ troubleTicketSheetId . value ,
391+ updateState ,
392+ cacheTroubleTicketData
393+ ) ;
394+ lastTroubleTicketSync = O . some ( DateTime . now ( ) ) ;
395+ } else {
396+ logger . info (
397+ '%s since last trouble ticket sync - not resyncing yet' ,
398+ lastTroubleTicketSync . value . diffNow ( ) . toHuman ( )
399+ ) ;
384400 }
401+ logger . info ( '...done' ) ;
402+ }
385403
386- if ( O . isSome ( troubleTicketSheetId ) ) {
387- logger . info ( 'Pulling latest trouble ticket reports...' ) ;
388- if (
389- O . isNone ( lastTroubleTicketSync ) ||
390- lastTroubleTicketSync . value . diffNow ( ) > TROUBLE_TICKET_SYNC_INTERVAL
391- ) {
392- await pullTroubleTicketResponses (
393- logger ,
394- googleHelpers . value ,
395- troubleTicketSheetId . value ,
396- updateState ,
397- cacheTroubleTicketData
398- ) ;
399- lastTroubleTicketSync = O . some ( DateTime . now ( ) ) ;
400- } else {
401- logger . info (
402- '%s since last trouble ticket sync - not resyncing yet' ,
403- lastTroubleTicketSync . value . diffNow ( ) . toHuman ( )
404- ) ;
405- }
404+ logger . info ( 'Pulling google training sheet data...' ) ;
405+ for ( const equipment of getAllEquipmentMinimal ( currentState ) ) {
406+ const equipmentLogger = logger . child ( { equipment} ) ;
407+ if (
408+ O . isNone ( equipment . trainingSheetId ) ||
409+ ( O . isSome ( equipment . lastQuizSync ) &&
410+ Date . now ( ) - equipment . lastQuizSync . value < googleRefreshIntervalMs )
411+ ) {
412+ equipmentLogger . info ( 'No google training sheet refresh required' ) ;
413+ continue ;
406414 }
407415
408- logger . info (
409- 'Finished pulling trouble ticket reports, getting google training sheet data ...'
416+ equipmentLogger . info (
417+ 'Triggering event update from google training sheets ...'
410418 ) ;
411- for ( const equipment of getAllEquipmentMinimal ( currentState ) ) {
412- const equipmentLogger = logger . child ( { equipment} ) ;
413- if (
414- O . isNone ( equipment . trainingSheetId ) ||
415- ( O . isSome ( equipment . lastQuizSync ) &&
416- Date . now ( ) - equipment . lastQuizSync . value < googleRefreshIntervalMs )
417- ) {
418- equipmentLogger . info ( 'No google training sheet refresh required' ) ;
419- continue ;
420- }
421419
422- equipmentLogger . info (
423- 'Triggering event update from google training sheets...'
424- ) ;
425-
426- const events : (
420+ const events : (
421+ | EventOfType < 'EquipmentTrainingQuizSync' >
422+ | EventOfType < 'EquipmentTrainingQuizResult' >
423+ ) [ ] = [ ] ;
424+ const collectEvents = (
425+ event :
427426 | EventOfType < 'EquipmentTrainingQuizSync' >
428427 | EventOfType < 'EquipmentTrainingQuizResult' >
429- ) [ ] = [ ] ;
430- const collectEvents = (
431- event :
432- | EventOfType < 'EquipmentTrainingQuizSync' >
433- | EventOfType < 'EquipmentTrainingQuizResult' >
434- ) => {
435- events . push ( event ) ;
436- updateState ( event ) ;
437- } ;
428+ ) => {
429+ events . push ( event ) ;
430+ updateState ( event ) ;
431+ } ;
438432
439- await pullNewEquipmentQuizResults (
440- equipmentLogger ,
433+ await pullNewEquipmentQuizResults (
434+ equipmentLogger ,
435+ googleHelpers ,
436+ equipment . id ,
437+ equipment . trainingSheetId . value ,
438+ collectEvents
439+ ) ;
440+ equipmentLogger . info (
441+ 'Finished pulling %s events from google training sheet, caching...' ,
442+ events . length
443+ ) ;
444+ await cacheSheetData (
445+ new Date ( ) ,
446+ equipment . trainingSheetId . value ,
447+ equipmentLogger ,
448+ events
449+ ) ;
450+ }
451+ logger . info ( '...done' ) ;
452+ }
453+
454+ async function asyncApplyRecurlyEvents (
455+ logger : Logger ,
456+ currentState : BetterSQLite3Database ,
457+ updateState : ( event : DomainEvent ) => void ,
458+ recurlyToken : string
459+ ) {
460+ logger . info ( 'Fetching recurly events...' ) ;
461+ const client = new recurly . Client ( recurlyToken ) ;
462+
463+ const accounts = client . listAccounts ( ) ;
464+ for await ( const account of accounts . each ( ) ) {
465+ const {
466+ email,
467+ hasActiveSubscription,
468+ hasFutureSubscription,
469+ hasCanceledSubscription,
470+ hasPausedSubscription,
471+ hasPastDueInvoice,
472+ } = account ;
473+
474+ const maybeEmail = E . getOrElseW ( ( ) => undefined ) (
475+ EmailAddressCodec . decode ( email )
476+ ) ;
477+
478+ if ( maybeEmail === undefined ) {
479+ continue ;
480+ }
481+
482+ const event = constructEvent ( 'RecurlySubscriptionUpdated' ) ( {
483+ email : maybeEmail ,
484+ hasActiveSubscription : hasActiveSubscription ?? false ,
485+ hasFutureSubscription : hasFutureSubscription ?? false ,
486+ hasCanceledSubscription : hasCanceledSubscription ?? false ,
487+ hasPausedSubscription : hasPausedSubscription ?? false ,
488+ hasPastDueInvoice : hasPastDueInvoice ?? false ,
489+ } ) ;
490+
491+ updateState ( event ) ;
492+ }
493+
494+ logger . info ( '...done' ) ;
495+ }
496+
497+ export const asyncApplyExternalEventSources = (
498+ logger : Logger ,
499+ currentState : BetterSQLite3Database ,
500+ googleHelpers : O . Option < GoogleHelpers > ,
501+ updateState : ( event : DomainEvent ) => void ,
502+ googleRefreshIntervalMs : number ,
503+ troubleTicketSheetId : O . Option < string > ,
504+ cacheSheetData : Dependencies [ 'cacheSheetData' ] ,
505+ cacheTroubleTicketData : Dependencies [ 'cacheTroubleTicketData' ] ,
506+ recurlyToken : O . Option < string >
507+ ) => {
508+ return ( ) => async ( ) => {
509+ logger . info ( 'Applying external event sources...' ) ;
510+
511+ if ( O . isNone ( googleHelpers ) ) {
512+ logger . info ( 'Google external event source disabled' ) ;
513+ } else {
514+ await asyncApplyGoogleEvents (
515+ logger ,
516+ currentState ,
441517 googleHelpers . value ,
442- equipment . id ,
443- equipment . trainingSheetId . value ,
444- collectEvents
518+ updateState ,
519+ googleRefreshIntervalMs ,
520+ troubleTicketSheetId ,
521+ cacheSheetData ,
522+ cacheTroubleTicketData
445523 ) ;
446- equipmentLogger . info (
447- 'Finished pulling %s events from google training sheet, caching...' ,
448- events . length
449- ) ;
450- await cacheSheetData (
451- new Date ( ) ,
452- equipment . trainingSheetId . value ,
453- equipmentLogger ,
454- events
524+ }
525+
526+ if ( O . isNone ( recurlyToken ) ) {
527+ logger . info ( 'Recurly external event source disabled' ) ;
528+ } else {
529+ await asyncApplyRecurlyEvents (
530+ logger ,
531+ currentState ,
532+ updateState ,
533+ recurlyToken . value
455534 ) ;
456535 }
536+
457537 logger . info ( 'Finished applying external event sources' ) ;
458538 } ;
459539} ;
0 commit comments