Skip to content

Commit

Permalink
[DEV-2047] Add Active Campaign integration to create and delete lists…
Browse files Browse the repository at this point in the history
… when creating and deleting webinars (#1260)

* Add Active Campaign integration to create and delete lists when creating and deleting webinars

* Fix naming and refactor error handling

* update lifecycle env var retrive

---------

Co-authored-by: Marco Ponchia <[email protected]>
Co-authored-by: Marco Ponchia <[email protected]>
  • Loading branch information
3 people authored Dec 5, 2024
1 parent 22aea81 commit 82b7d4b
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/weak-monkeys-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"strapi-cms": minor
---

Add Active Campaign integration to create and delete lists when creating and deleting webinars
6 changes: 6 additions & 0 deletions apps/strapi-cms/.env.default
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,9 @@ GOOGLE_OAUTH_CLIENT_ID=
GOOGLE_OAUTH_CLIENT_SECRET=
GOOGLE_OAUTH_REDIRECT_URI=
GOOGLE_GSUITE_HD=

# ACTIVE CAMPAIGN
ACTIVE_CAMPAIGN_INTEGRATION_IS_ENABLED=True
AC_BASE_URL=https://<your_username>.activehosted.com
AC_API_KEY=your_api_key
SENDER_URL=your_server_url
1 change: 1 addition & 0 deletions apps/strapi-cms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@strapi/plugin-users-permissions": "4.24.2",
"@strapi/provider-upload-aws-s3": "^4.24.2",
"@strapi/strapi": "4.24.2",
"axios": "^1.7.8",
"better-sqlite3": "8.6.0",
"pg": "^8.11.5",
"react": "^18.2.0",
Expand Down
163 changes: 161 additions & 2 deletions apps/strapi-cms/src/api/webinar/content-types/webinar/lifecycles.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
import { errors } from '@strapi/utils';
import { errors, env } from '@strapi/utils';
import axios from 'axios';

interface IActiveCampaignListPayload {
readonly list: {
readonly name: string;
readonly stringid: string;
readonly sender_url: string;
readonly sender_reminder: string;
readonly subscription_notify?: string;
readonly unsubscription_notify?: string;
};
}

const activeCampaignIntegrationIsEnabled =
env('ACTIVE_CAMPAIGN_INTEGRATION_IS_ENABLED', 'False') === 'True';

function getHeaders() {
return {
// eslint-disable-next-line @typescript-eslint/naming-convention
'Api-Token': env('AC_API_KEY', ''),
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/json',
};
}

interface IWebinar {
readonly id?: string;
readonly slug?: string;
readonly title?: string;
readonly locale?: string;
readonly startDatetime?: string;
readonly endDatetime?: string;
readonly publishedAt?: string;
}

interface IWebinarEvent {
Expand All @@ -14,6 +41,7 @@ interface IWebinarEvent {
readonly id?: string;
};
};
readonly result: IWebinar;
}

const validateDates = (event: IWebinarEvent): boolean => {
Expand All @@ -36,11 +64,142 @@ const validateDates = (event: IWebinarEvent): boolean => {
return true;
};

const validateSlug = async (event: IWebinarEvent): Promise<boolean> => {
const { id } = event.params.data;
if (!id) {
throw new errors.ApplicationError('Webinar id not found');
}
const previousWebinar = await strapi.db
.query('api::webinar.webinar')
.findOne({
select: ['slug'],
where: { id },
});
if (previousWebinar && previousWebinar.slug !== event.params.data.slug) {
throw new errors.ApplicationError(
'The slug of a webinar cannot be changed'
);
}
return true;
};

const activeCampaignError = (message: string) => {
throw new errors.ApplicationError(
`Something went wrong during Active Campaign ${message}`
);
};

const createActiveCampaignList = async (
event: IWebinarEvent
): Promise<boolean> => {
if (
!activeCampaignIntegrationIsEnabled ||
!event.result?.slug ||
!event.result?.title
) {
return true;
}

const { slug: name, title: stringid } = event.result;

const payload: IActiveCampaignListPayload = {
list: {
name,
sender_reminder: '',
sender_url: `${env(
'SENDER_URL',
'http://localhost:3000/'
)}/webinars/${name}`,
stringid,
subscription_notify: '',
unsubscription_notify: '',
},
};

const response = await axios
.post(`${env('AC_BASE_URL')}/api/3/lists`, payload, {
headers: getHeaders(),
})
.catch((_) => {
activeCampaignError('list creation');
});

if (response?.status !== 201) {
activeCampaignError('list creation');
}

return response?.status === 201;
};

const getListIdByName = async (name: string): Promise<number> => {
const response = await axios.get<{
readonly lists: ReadonlyArray<{ readonly id: number }>;
}>(
`${env('AC_BASE_URL')}/api/3/lists`,
// eslint-disable-next-line @typescript-eslint/naming-convention
{ headers: getHeaders(), params: { 'filters[name][eq]': name } }
);
return response?.data.lists[0]?.id;
};

const deleteActiveCampaignList = async (
event: IWebinarEvent
): Promise<boolean> => {
if (
!activeCampaignIntegrationIsEnabled ||
!event?.params.where ||
!event.params.where.id
) {
return true;
}
const webinar = await strapi.db
.query('api::webinar.webinar')
.findOne({ where: { id: event.params.where.id } });

if (!webinar?.slug) {
activeCampaignError('list deletion: webinar slug is missing');
}
// Get list ID using the slug (name)
const listId = await getListIdByName(webinar.slug);

if (!listId) {
return false;
}

const response = await axios
.delete(`${env('AC_BASE_URL')}/api/3/lists/${listId}`, {
headers: getHeaders(),
})
.catch((_) => {
activeCampaignError('list deletion');
});

if (response?.status !== 200) {
activeCampaignError('list deletion');
}

return response?.status === 200;
};

module.exports = {
async afterCreate(event: IWebinarEvent) {
await createActiveCampaignList(event);
},
beforeCreate(event: IWebinarEvent) {
validateDates(event);
},
beforeUpdate(event: IWebinarEvent) {
async beforeDelete(event: IWebinarEvent) {
await deleteActiveCampaignList(event);
},
beforeDeleteMany() {
if (activeCampaignIntegrationIsEnabled) {
throw new errors.ApplicationError(
'Bulk deletion is not allowed for webinars if Active Campaign integration is enabled'
);
}
},
async beforeUpdate(event: IWebinarEvent) {
validateDates(event);
await validateSlug(event);
},
};
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 82b7d4b

Please sign in to comment.