diff --git a/packages/active-campaign-client/src/__tests__/helpers/manageListSubscription.test.ts b/packages/active-campaign-client/src/__tests__/helpers/manageListSubscription.test.ts index 61e97cea0..b9b6ee49b 100644 --- a/packages/active-campaign-client/src/__tests__/helpers/manageListSubscription.test.ts +++ b/packages/active-campaign-client/src/__tests__/helpers/manageListSubscription.test.ts @@ -1,7 +1,7 @@ // remove .skip to run the test, be aware it does real API calls import { addContactToList, - removeContactToList, + removeContactFromList, } from '../../helpers/manageListSubscription'; describe.skip('manage list subscription', () => { @@ -15,7 +15,7 @@ describe.skip('manage list subscription', () => { }); it('should unsubscribe the contact from the list', async () => { - const result = await removeContactToList(cognitoUserId, listName); + const result = await removeContactFromList(cognitoUserId, listName); expect(result.statusCode).toBe(200); }); }); diff --git a/packages/active-campaign-client/src/clients/activeCampaignClient.ts b/packages/active-campaign-client/src/clients/activeCampaignClient.ts index 97f4e0653..a42a73518 100644 --- a/packages/active-campaign-client/src/clients/activeCampaignClient.ts +++ b/packages/active-campaign-client/src/clients/activeCampaignClient.ts @@ -10,6 +10,8 @@ import { } from '../types/contactResponse'; import { ActiveCampaignList } from '../types/activeCampaignList'; +const MAX_NUMBER_OF_LIST = '1000'; + async function getParameter( paramName: string, ssmClient: SSMClient, @@ -130,10 +132,12 @@ export class ActiveCampaignClient { return this.makeRequest('DELETE', `/api/3/contacts/${contactId}`); } - async getContactByCognitoId(cognitoId: string) { + async getContactByCognitoUsername(cognitoUsername: string) { const response = await this.makeRequest<{ readonly contacts: ReadonlyArray<{ readonly id: string }>; - }>('GET', '/api/3/contacts', undefined, { phone: `cognito:${cognitoId}` }); + }>('GET', '/api/3/contacts', undefined, { + phone: `cognito:${cognitoUsername}`, + }); return response?.contacts?.[0]?.id; } @@ -160,7 +164,7 @@ export class ActiveCampaignClient { } async getLists(ids?: readonly string[]) { - const limitParams = { limit: '1000' }; + const limitParams = { limit: MAX_NUMBER_OF_LIST }; return this.makeRequest<{ readonly lists: readonly ActiveCampaignList[] }>( 'GET', '/api/3/lists', diff --git a/packages/active-campaign-client/src/handlers/resyncUserHandler.ts b/packages/active-campaign-client/src/handlers/resyncUserHandler.ts index f8f849656..2d82df59c 100644 --- a/packages/active-campaign-client/src/handlers/resyncUserHandler.ts +++ b/packages/active-campaign-client/src/handlers/resyncUserHandler.ts @@ -20,7 +20,7 @@ export async function resyncUserHandler(event: { console.log('user:', user); // TODO: Remove after testing if (!user) { - const deletionResult = await deleteContact(cognitoUsername); // AC call * 2 + const deletionResult = await deleteContact(cognitoUsername); if ( deletionResult.statusCode !== 200 && deletionResult.statusCode !== 404 @@ -29,7 +29,7 @@ export async function resyncUserHandler(event: { throw new Error('Error deleting contact'); } } else { - const contactResponse = await addOrUpdateContact(user); // AC call * 3 + const contactResponse = await addOrUpdateContact(user); console.log('contactResponse:', contactResponse); // TODO: Remove after testing @@ -37,23 +37,28 @@ export async function resyncUserHandler(event: { await getNewWebinarsAndUnsubsriptionLists( contactResponse, cognitoUsername - ); // AC call * 1 + ); const resyncTimeoutMilliseconds: number = parseInt( process.env.AC_RESYNC_TIMEOUT_IN_MS || '1000' ); - await addArrayOfListToContact({ + const subscriptionsresult = await addArrayOfListToContact({ webinarSlugs: newWebinarSlugs, - cognitoId: cognitoUsername, + cognitoUsername: cognitoUsername, resyncTimeoutMilliseconds, }); - await removeArrayOfListFromContact({ + const unsubscriptionsResult = await removeArrayOfListFromContact({ listsToUnsubscribe, - cognitoUsername: contactResponse.contact.id, + contactId: contactResponse.contact.id, resyncTimeoutMilliseconds, }); + + if (!subscriptionsresult || !unsubscriptionsResult) { + // eslint-disable-next-line functional/no-throw-statements + throw new Error('Error managing list subscriptions'); + } } return { statusCode: 200, diff --git a/packages/active-campaign-client/src/handlers/sqsQueueHandler.ts b/packages/active-campaign-client/src/handlers/sqsQueueHandler.ts index 1a3034c63..dac54b235 100644 --- a/packages/active-campaign-client/src/handlers/sqsQueueHandler.ts +++ b/packages/active-campaign-client/src/handlers/sqsQueueHandler.ts @@ -6,7 +6,7 @@ import { deleteContact } from '../helpers/deleteContact'; import { queueEventParser } from '../helpers/queueEventParser'; import { addContactToList, - removeContactToList, + removeContactFromList, } from '../helpers/manageListSubscription'; function manageError(result: APIGatewayProxyResult) { @@ -49,7 +49,7 @@ export async function sqsQueueHandler(event: { ); case 'DynamoREMOVE': return manageError( - await removeContactToList( + await removeContactFromList( queueEvent.detail.additionalEventData.sub, queueEvent.webinarId || '' ) diff --git a/packages/active-campaign-client/src/helpers/addArrayOfListToContact.ts b/packages/active-campaign-client/src/helpers/addArrayOfListToContact.ts index e3ebf1b22..e009290fb 100644 --- a/packages/active-campaign-client/src/helpers/addArrayOfListToContact.ts +++ b/packages/active-campaign-client/src/helpers/addArrayOfListToContact.ts @@ -2,34 +2,35 @@ import { addContactToList } from './manageListSubscription'; export async function addArrayOfListToContact(event: { readonly webinarSlugs: ReadonlyArray; - readonly cognitoId: string; + readonly cognitoUsername: string; readonly resyncTimeoutMilliseconds: number; }) { - const { webinarSlugs, cognitoId, resyncTimeoutMilliseconds } = event; + const { webinarSlugs, cognitoUsername, resyncTimeoutMilliseconds } = event; // eslint-disable-next-line functional/prefer-readonly-type - const addWithErrors: string[] = []; - // add contact to list for each item in subscription lists - const responses = await webinarSlugs.reduce( + const subscriptionsWithErrors: string[] = []; + await webinarSlugs.reduce( async (prevPromise: Promise, webinarSlug: string) => { await prevPromise; try { - const result = await addContactToList(cognitoId, webinarSlug); // AC call 2 * N + const result = await addContactToList(cognitoUsername, webinarSlug); console.log('Add contact to list result:', result, webinarSlug); // TODO: Remove after testing await new Promise((resolve) => setTimeout(resolve, resyncTimeoutMilliseconds) - ); // wait 1 sec to avoid rate limiting + ); // wait to avoid rate limiting } catch (e) { - addWithErrors.push(webinarSlug); + subscriptionsWithErrors.push(webinarSlug); } }, Promise.resolve() ); - if (addWithErrors.length > 0) { - console.error('Error adding contact to list', addWithErrors.join(',')); // TODO: Remove after testing - // eslint-disable-next-line functional/no-throw-statements - throw new Error('Error adding contact to list'); + if (subscriptionsWithErrors.length > 0) { + console.error( + 'Error adding contact to list', + subscriptionsWithErrors.join(',') + ); + return false; } - return responses; + return true; } diff --git a/packages/active-campaign-client/src/helpers/addOrUpdateContact.ts b/packages/active-campaign-client/src/helpers/addOrUpdateContact.ts index 421048a69..2f25e23ff 100644 --- a/packages/active-campaign-client/src/helpers/addOrUpdateContact.ts +++ b/packages/active-campaign-client/src/helpers/addOrUpdateContact.ts @@ -3,7 +3,7 @@ import { makeContactPayload } from './makeContactPayload'; import { acClient } from '../clients/activeCampaignClient'; export async function addOrUpdateContact(user: User) { - const contactId = await acClient.getContactByCognitoId(user.username); + const contactId = await acClient.getContactByCognitoUsername(user.username); const acPayload = makeContactPayload(user); const { contact } = contactId diff --git a/packages/active-campaign-client/src/helpers/deleteContact.ts b/packages/active-campaign-client/src/helpers/deleteContact.ts index 085c6bc1a..1a984ce14 100644 --- a/packages/active-campaign-client/src/helpers/deleteContact.ts +++ b/packages/active-campaign-client/src/helpers/deleteContact.ts @@ -5,7 +5,7 @@ export async function deleteContact( cognitoId: string ): Promise { try { - const contactId = await acClient.getContactByCognitoId(cognitoId); + const contactId = await acClient.getContactByCognitoUsername(cognitoId); if (!contactId) { return { diff --git a/packages/active-campaign-client/src/helpers/getNewWebinarsAndUnsubsriptionLists.ts b/packages/active-campaign-client/src/helpers/getNewWebinarsAndUnsubsriptionLists.ts index a2278f277..35a5f3848 100644 --- a/packages/active-campaign-client/src/helpers/getNewWebinarsAndUnsubsriptionLists.ts +++ b/packages/active-campaign-client/src/helpers/getNewWebinarsAndUnsubsriptionLists.ts @@ -2,16 +2,21 @@ import { acClient } from '../clients/activeCampaignClient'; import { fetchSubscribedWebinarsFromDynamo } from './fetchSubscribedWebinarsFromDynamo'; import { ContactResponseWithLists } from '../types/contactResponse'; +const AC_STATUS_SUBSCRIBED = '1'; + export async function getNewWebinarsAndUnsubsriptionLists( contactResponse: ContactResponseWithLists, cognitoUsername: string ) { - const idsParams = contactResponse.contactLists - .filter(({ status }) => status === '1') + const listIds = contactResponse.contactLists + .filter(({ status }) => status === AC_STATUS_SUBSCRIBED) .map(({ list }) => list); - console.log('idsParams:', idsParams); // TODO: Remove after testing - const getListResponse = await acClient.getLists(idsParams); + console.log('idsParams:', listIds); // TODO: Remove after testing + const getListResponse = await acClient.getLists(listIds); + // eslint-disable-next-line functional/prefer-readonly-type + const contactCurrentlySubscribedLists: { name: string; id: string }[] = + getListResponse.lists.map(({ name, id }) => ({ name, id })); const userWebinarsSubscriptions = await fetchSubscribedWebinarsFromDynamo( cognitoUsername @@ -28,22 +33,20 @@ export async function getNewWebinarsAndUnsubsriptionLists( // eslint-disable-next-line functional/prefer-readonly-type const newWebinarSlugs: string[] = []; - // eslint-disable-next-line functional/prefer-readonly-type - const contactLists: { name: string; id: string }[] = - getListResponse.lists.map(({ name, id }) => ({ name, id })); webinarSlugs.forEach((webinarSlug) => { - const index = contactLists.findIndex(({ name }) => name === webinarSlug); + const index = contactCurrentlySubscribedLists.findIndex( + ({ name }) => name === webinarSlug + ); if (index >= 0) { - contactLists.splice(index, 1); + contactCurrentlySubscribedLists.splice(index, 1); } else { newWebinarSlugs.push(webinarSlug); } }); - const listsToUnsubscribe: readonly number[] = contactLists.map(({ id }) => - Number(id) - ); + const listsToUnsubscribe: readonly number[] = + contactCurrentlySubscribedLists.map(({ id }) => Number(id)); console.log('listsToUnsubscribe:', listsToUnsubscribe); // TODO: Remove after testing console.log('New webinar Slugs:', newWebinarSlugs); // TODO: Remove after testing diff --git a/packages/active-campaign-client/src/helpers/manageListSubscription.ts b/packages/active-campaign-client/src/helpers/manageListSubscription.ts index bc4791398..ddf6c237c 100644 --- a/packages/active-campaign-client/src/helpers/manageListSubscription.ts +++ b/packages/active-campaign-client/src/helpers/manageListSubscription.ts @@ -2,11 +2,13 @@ import { APIGatewayProxyResult } from 'aws-lambda'; import { acClient } from '../clients/activeCampaignClient'; export async function addContactToList( - cognitoId: string, + cognitoUsername: string, listName: string ): Promise { try { - const contactId = await acClient.getContactByCognitoId(cognitoId); + const contactId = await acClient.getContactByCognitoUsername( + cognitoUsername + ); if (!contactId) { // eslint-disable-next-line functional/no-throw-statements throw new Error('Contact not found'); @@ -35,12 +37,14 @@ export async function addContactToList( } } -export async function removeContactToList( - cognitoId: string, +export async function removeContactFromList( + cognitoUsername: string, listName: string ): Promise { try { - const contactId = await acClient.getContactByCognitoId(cognitoId); + const contactId = await acClient.getContactByCognitoUsername( + cognitoUsername + ); if (!contactId) { // eslint-disable-next-line functional/no-throw-statements throw new Error('Contact not found'); diff --git a/packages/active-campaign-client/src/helpers/removeArrayOfListFromContact.ts b/packages/active-campaign-client/src/helpers/removeArrayOfListFromContact.ts index 551ddf8e9..e53be7400 100644 --- a/packages/active-campaign-client/src/helpers/removeArrayOfListFromContact.ts +++ b/packages/active-campaign-client/src/helpers/removeArrayOfListFromContact.ts @@ -2,22 +2,35 @@ import { acClient } from '../clients/activeCampaignClient'; export async function removeArrayOfListFromContact(event: { readonly listsToUnsubscribe: readonly number[]; - readonly cognitoUsername: string; + readonly contactId: string; readonly resyncTimeoutMilliseconds: number; }) { - const { listsToUnsubscribe, cognitoUsername, resyncTimeoutMilliseconds } = - event; - // remove contact from list for each item in unsubscription lists + const { listsToUnsubscribe, contactId, resyncTimeoutMilliseconds } = event; + // eslint-disable-next-line functional/prefer-readonly-type + const unsubscriptionsWithErrors: string[] = []; await listsToUnsubscribe.reduce( async (prevPromise: Promise, id: number) => { await prevPromise; - // AC call * M - const result = await acClient.removeContactFromList(cognitoUsername, id); - console.log('Remove contact from list result:', result, id); // TODO: Remove after testing - await new Promise((resolve) => - setTimeout(resolve, resyncTimeoutMilliseconds) - ); // wait 1 sec to avoid rate limiting + try { + const result = await acClient.removeContactFromList(contactId, id); + console.log('Remove contact from list result:', result, id); // TODO: Remove after testing + await new Promise((resolve) => + setTimeout(resolve, resyncTimeoutMilliseconds) + ); // wait to avoid rate limiting + } catch (e) { + unsubscriptionsWithErrors.push(id.toString()); + } }, Promise.resolve() ); + + if (unsubscriptionsWithErrors.length > 0) { + console.error( + 'Error removing contact from list', + unsubscriptionsWithErrors.join(',') + ); + return false; + } + + return true; } diff --git a/packages/active-campaign-client/src/helpers/updateContact.ts b/packages/active-campaign-client/src/helpers/updateContact.ts index 275d2c101..9c9c71ada 100644 --- a/packages/active-campaign-client/src/helpers/updateContact.ts +++ b/packages/active-campaign-client/src/helpers/updateContact.ts @@ -8,7 +8,7 @@ export async function updateContact( ): Promise { try { const acPayload = makeContactPayload(user); - const contactId = await acClient.getContactByCognitoId(user.username); + const contactId = await acClient.getContactByCognitoUsername(user.username); if (!contactId) { return { statusCode: 404,