Skip to content

Commit

Permalink
Fix after review
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoPonchia committed Dec 20, 2024
1 parent 87ba5fc commit 6aa3ce9
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand All @@ -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);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand All @@ -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',
Expand Down
19 changes: 12 additions & 7 deletions packages/active-campaign-client/src/handlers/resyncUserHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -29,31 +29,36 @@ 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

const { listsToUnsubscribe, newWebinarSlugs } =
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -49,7 +49,7 @@ export async function sqsQueueHandler(event: {
);
case 'DynamoREMOVE':
return manageError(
await removeContactToList(
await removeContactFromList(
queueEvent.detail.additionalEventData.sub,
queueEvent.webinarId || ''
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,35 @@ import { addContactToList } from './manageListSubscription';

export async function addArrayOfListToContact(event: {
readonly webinarSlugs: ReadonlyArray<string>;
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<void>, 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export async function deleteContact(
cognitoId: string
): Promise<APIGatewayProxyResult> {
try {
const contactId = await acClient.getContactByCognitoId(cognitoId);
const contactId = await acClient.getContactByCognitoUsername(cognitoId);

if (!contactId) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<APIGatewayProxyResult> {
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');
Expand Down Expand Up @@ -35,12 +37,14 @@ export async function addContactToList(
}
}

export async function removeContactToList(
cognitoId: string,
export async function removeContactFromList(
cognitoUsername: string,
listName: string
): Promise<APIGatewayProxyResult> {
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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>, 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export async function updateContact(
): Promise<APIGatewayProxyResult> {
try {
const acPayload = makeContactPayload(user);
const contactId = await acClient.getContactByCognitoId(user.username);
const contactId = await acClient.getContactByCognitoUsername(user.username);
if (!contactId) {
return {
statusCode: 404,
Expand Down

0 comments on commit 6aa3ce9

Please sign in to comment.