@hoover-institution/hubspot-lib
Version:
A toolkit for deep integration with HubSpot's Marketing Events API with a plugin-based architecture.
995 lines (901 loc) • 30.3 kB
JavaScript
// @ts-nocheck
import { run as runHooks } from "../core/dispatcher.js";
import { EVENTS } from "../core/events.js";
import axios from "axios";
import { ContactManager } from "./ContactManager.js";
import { resolveHooks } from "../core/resolveHooks.js";
import { loadPlugins } from "../core/pluginLoader.js";
import { fullPluginMap } from "../core/loadPlugins.js";
/**
* Enum of valid subscriber states ("REGISTERED", "ATTENDED", etc.)
*/
/**
* Enum for subscriber state values.
* @readonly
* @enum {number}
* @property {number} REGISTERED
* @property {number} ATTENDED
* @property {number} CANCELED
*/
export const SUBSCRIBER_STATE = {
REGISTERED: 2,
ATTENDED: 1,
CANCELED: 0,
};
/**
* Custom library for managing marketing events in HubSpot.
* Provides methods to create, retrieve, and delete marketing events via the HubSpot API.
* Follows REST conventions: PUT is idempotent upsert with status reporting.
* All methods return both HubSpot data and plugin execution results.
*
* Example usage:
* const me = new MarketingEvent(externalAccountId, { plugins: ["PLUGIN_NAME"] });
* const result = await me.createEvent({ ... });
* console.log(result.hubspot); // HubSpot response
* console.log(result.plugins); // Plugin execution results
*/
/**
* @typedef {'REGISTERED' | 'ATTENDED' | 'CANCELED'} SubscriberStateName
*/
/**
* @typedef {Object} PluginResult
* @property {number} pluginId
* @property {string} pluginName
* @property {boolean} success
* @property {any} [result]
* @property {string} [error]
*/
/**
* @typedef {Object} HubSpotEvent
* @property {string} eventName
* @property {string} eventType
* @property {string} startDateTime
* @property {string|null} endDateTime
* @property {string} eventOrganizer
* @property {string|null} eventDescription
* @property {string|null} eventUrl
* @property {boolean} eventCancelled
* @property {boolean} eventCompleted
* @property {Array} customProperties
* @property {string} objectId
* @property {string} id
* @property {string} createdAt
* @property {string} updatedAt
* @property {string|number} externalEventId
* @property {string} externalAccountId
* @property {string} status
*/
/**
* @typedef {Object} EventResult
* @property {HubSpotEvent} hubspot The HubSpot event data
* @property {PluginResult[]} plugins Array of plugin execution results
*/
/**
* @typedef {Object} EventsResult
* @property {HubSpotEvent[]} hubspot Array of HubSpot event data
* @property {PluginResult[]} plugins Array of plugin execution results
*/
/**
* @typedef {Object} DeleteResult
* @property {{ success: boolean }} hubspot Deletion confirmation
* @property {PluginResult[]} plugins Array of plugin execution results
*/
/**
* @typedef {Object} RegisterEmailResult
* @property {boolean} success Indicates if the registration or cancellation was successful.
* @property {string} email The email address of the contact.
* @property {number} subscriberState The subscriber state after the operation (e.g., REGISTERED, CANCELED, ATTENDED).
* @property {string} contactId The HubSpot contact ID associated with the email.
*/
/**
* @typedef {Object} RegisterEmailResultWithPlugins
* @property {RegisterEmailResult} hubspot The registration result data
* @property {PluginResult[]} plugins Array of plugin execution results
*/
/**
* @typedef {Object} ContactsResult
* @property {HubSpotContact[]} hubspot Array of HubSpot contact data
* @property {PluginResult[]} plugins Array of plugin execution results
*/
/**
* @typedef {Object} ContactListResult
* @property {HubSpotContactList} hubspot The HubSpot contact list data
* @property {PluginResult[]} plugins Array of plugin execution results
*/
/**
* @typedef {Object} ContactStateResult
* @property {SubscriberStateName|null} hubspot The contact's subscriber state
* @property {PluginResult[]} plugins Array of plugin execution results
*/
/**
* @typedef {Object} HubSpotContact
* @property {string} id
* @property {Object} properties
* @property {string} properties.createdate
* @property {string} properties.email
* @property {string} properties.firstname
* @property {string} properties.hs_object_id
* @property {string} properties.lastmodifieddate
* @property {string} properties.lastname
* @property {string} createdAt
* @property {string} updatedAt
* @property {boolean} archived
*/
/**
* @typedef {Object} HubSpotContactList
* @property {string} listId The unique identifier for the contact list.
* @property {number} listVersion The version number of the list.
* @property {string} createdAt ISO timestamp when the list was created.
* @property {string} updatedAt ISO timestamp when the list was last updated.
* @property {string} filtersUpdatedAt ISO timestamp when the list filters were last updated.
* @property {string} processingStatus The processing status of the list (e.g., "COMPLETE").
* @property {string} createdById The user ID who created the list.
* @property {string} updatedById The user ID who last updated the list.
* @property {string} processingType The processing type of the list (e.g., "MANUAL").
* @property {string} objectTypeId The object type ID (e.g., "0-1" for contacts).
* @property {string} name The name of the list.
* @property {Object} listPermissions Permissions for the list.
* @property {Array} listPermissions.teamsWithEditAccess Teams with edit access.
* @property {Array} listPermissions.usersWithEditAccess Users with edit access.
* @property {Object} membershipSettings Membership settings for the list.
* @property {string|null} membershipSettings.membershipTeamId Team ID for membership, if any.
* @property {boolean|null} membershipSettings.includeUnassigned Whether to include unassigned contacts.
*/
// /**
// * Enum for subscriber state values.
// * @readonly
// * @enum {number}
// * @property {number} REGISTERED
// * @property {number} ATTENDED
// * @property {number} CANCELED
// */
// export const SUBSCRIBER_STATE = {
// REGISTERED: 2,
// ATTENDED: 1,
// CANCELED: 0,
// };
export class MarketingEvent {
/** @private */
#externalEventId = null;
/** @private */
#externalAccountId = null;
/** @private */
#client;
/** @private */
#pluginInit;
/**
* Creates a new MarketingEvent instance.
* @param {string} externalAccountId The external account ID for HubSpot context.
* @param {Object} [options]
* @param {string[]} [options.plugins] Array of plugin names to load.
*/
constructor(externalAccountId, options = {}) {
if (!externalAccountId) {
throw new Error("Constructor requires 'externalAccountId'");
}
// Normalize and dedupe plugin names
let plugins = Array.isArray(options.plugins)
? [...new Set(options.plugins.filter(Boolean))]
: [];
if (plugins.includes("ALL")) {
plugins = Object.keys(fullPluginMap).filter((name) => name !== "ALL");
}
// Set up hook bitmask from plugin names
this.hookFlags = resolveHooks(plugins);
// Trigger plugin loading (native, external, or inline)
this.#pluginInit = loadPlugins(plugins);
// Store account and create internal API client
this.#externalAccountId = externalAccountId;
this.#client = axios.create({
baseURL: MarketingEvent.eventBaseUrl,
headers: {
Authorization: `Bearer ${process.env.HUBSPOT_KEY}`,
"Content-Type": "application/json",
},
timeout: 5000,
});
}
/**
* HubSpot Marketing Events API base URL.
* @returns {string}
*/
static get eventBaseUrl() {
return "https://api.hubapi.com/marketing/v3/marketing-events/events";
}
/**
* HubSpot Marketing Events API base URL for bulk operations.
* @returns {string}
*/
static get bulkBaseUrl() {
return "https://api.hubapi.com/marketing/v3/marketing-events/";
}
/**
* HubSpot Marketing Events API base URL for attendance operations.
* @returns {string}
*/
static get attendanceBaseUrl() {
return "https://api.hubapi.com/marketing/v3/marketing-events/attendance";
}
/**
* Hubspot External Marketing Events API base URL for participant breakdown operations.
* @returns {string}
*/
static get breakdownBaseUrl() {
return "https://api.hubapi.com/marketing/v3/marketing-events/participations/";
}
/**
* Creates or updates a marketing event in HubSpot by the externalEventId.
* If the event exists, it is updated; otherwise, it is created.
* @async
* @param {Object} payload The event data. Must include externalEventId, eventName, eventOrganizer.
* @param {string|number} payload.externalEventId
* @param {string} payload.eventName
* @param {string} payload.eventOrganizer
* @returns {Promise<EventResult>} Object containing HubSpot response data and plugin execution results.
* @throws {Error} If required fields are missing or API call fails.
*/
async createEvent(payload = {}) {
await this.#pluginInit;
// change some naming for my sanity
if (payload.name && !payload.eventName) payload.eventName = payload.name;
const { eventName, eventOrganizer, externalEventId } = payload;
if (!externalEventId)
throw new Error("Payload must include 'externalEventId'");
if (!eventName)
throw new Error("Payload must include 'eventName' or 'name'");
if (!eventOrganizer)
throw new Error("Payload must include 'eventOrganizer'");
const url = `${externalEventId}`;
const body = {
...payload,
externalAccountId: this.#externalAccountId,
};
try {
// for qol, check if the event exists before creating
let existed = false;
try {
const existingEvent = await this.getEvent(externalEventId);
existed = !!existingEvent?.hubspot;
} catch (err) {
existed = false;
}
// idempotent upsert
const response = await this.#client.put(url, body);
this.#externalEventId = externalEventId;
const payload = {
...response.data,
externalEventId: externalEventId,
externalAccountId: this.#externalAccountId,
status: existed ? "updated" : "created",
};
// run hooks and capture their return values
const plugins = await runHooks(
this.hookFlags,
EVENTS.CREATE_EVENT,
payload
);
// return both HubSpot data and plugin results
return {
hubspot: payload,
plugins,
};
} catch (error) {
// run error hook
await runHooks(this.hookFlags, EVENTS.MARKETING_EVENT_ERROR, {
action: "createEvent",
error,
});
const data = error.response?.data;
console.error("createEvent error:", data || error);
throw new Error(data?.message || error.message);
}
}
/**
* Retrieves a marketing event by the externalEventId.
* @async
* @param {string|number} [externalEventId=this.#externalEventId] The external event ID to lookup.
* @returns {Promise<EventResult|null>} Object containing HubSpot response data and plugin execution results, or null if not found.
* @throws {Error} If required IDs are missing or API call fails.
*/
async getEvent(externalEventId = this.#externalEventId) {
await this.#pluginInit;
if (!externalEventId)
throw new Error("Missing externalEventId for getEvent");
if (!this.#externalAccountId)
throw new Error("Missing externalAccountId for getEvent");
try {
const params = new URLSearchParams({
externalAccountId: this.#externalAccountId,
});
const url = `${externalEventId}?${params.toString()}`;
const response = await this.#client.get(url);
const result = response.data;
const plugins = await runHooks(this.hookFlags, EVENTS.GET_EVENT, {
externalEventId,
found: !!result,
});
return {
hubspot: result,
plugins,
};
} catch (error) {
if (error.response?.status === 404) {
const plugins = await runHooks(this.hookFlags, EVENTS.GET_EVENT, {
externalEventId,
found: false,
});
console.error("Event not found:", externalEventId);
return null;
}
await runHooks(this.hookFlags, EVENTS.MARKETING_EVENT_ERROR, {
action: "getEvent",
error,
});
console.error("getEvent error:", error.response?.data || error);
throw new Error(error.response?.data?.message || error.message);
}
}
/**
* Retrieves all marketing events for the account.
* @async
* @param {Object} [options] Optional params (e.g., limit, offset).
* @returns {Promise<EventsResult>} Object containing array of HubSpotEvent objects and plugin execution results.
* @throws {Error} If required IDs are missing or API call fails.
*/
async getEvents(options = {}) {
await this.#pluginInit;
if (!this.#externalAccountId)
throw new Error("Missing externalAccountId for getEvents");
try {
const params = new URLSearchParams({
externalAccountId: this.#externalAccountId,
...options,
});
const url = `${MarketingEvent.bulkBaseUrl}?${params.toString()}`;
const response = await this.#client.get(url);
const results = response.data.results || [];
const plugins = await runHooks(this.hookFlags, EVENTS.GET_EVENTS, {
count: results.length,
events: results,
options,
});
return {
hubspot: results,
plugins,
};
} catch (error) {
await runHooks(this.hookFlags, EVENTS.MARKETING_EVENT_ERROR, {
action: "getEvents",
error,
});
const data = error.response?.data;
if (error.response?.status === 404) {
console.error("No events found for account:", this.#externalAccountId);
const plugins = await runHooks(this.hookFlags, EVENTS.GET_EVENTS, {
count: 0,
});
return {
hubspot: [],
plugins,
};
}
console.error("getEvents error:", data || error);
throw new Error(data?.message || error.message);
}
}
/**
* Deletes a marketing event by the externalEventId.
* @async
* @param {string|number} [externalEventId=this.#externalEventId] The external event ID to delete.
* @returns {Promise<DeleteResult>} Object containing deletion confirmation and plugin execution results.
* @throws {Error} If required IDs are missing or API call fails.
*/
async deleteEvent(externalEventId = this.#externalEventId) {
await this.#pluginInit;
if (!externalEventId)
throw new Error("Missing externalEventId for deleteEvent");
if (!this.#externalAccountId)
throw new Error("Missing externalAccountId for deleteEvent");
try {
const params = new URLSearchParams({
externalAccountId: this.#externalAccountId,
});
const url = `${externalEventId}?${params.toString()}`;
await this.#client.delete(url);
// run hooks
const plugins = await runHooks(this.hookFlags, EVENTS.DELETE_EVENT, {
externalEventId,
success: true,
});
return {
hubspot: { success: true },
plugins,
};
} catch (error) {
await runHooks(this.hookFlags, EVENTS.DELETE_EVENT, {
externalEventId,
success: false,
});
await runHooks(this.hookFlags, EVENTS.MARKETING_EVENT_ERROR, {
action: "deleteEvent",
error,
});
console.error("deleteEvent error:", error.response?.data || error);
throw new Error(error.response?.data?.message || error.message);
}
}
/**
* Registers or cancels a contact for an event by email (list-based).
* If the contact does not exist, it will be created.
* @async
* @param {string} email The email address to register or cancel.
* @param {string|number} [externalEventId=this.#externalEventId] The external event ID.
* @param {number} [subscriberState=SUBSCRIBER_STATE.REGISTERED] The subscriber state (REGISTERED, CANCELED, ATTENDED).
* @param {string} [fullName=""] Optional full name for contact creation if not existing.
* @returns {Promise<RegisterEmailResultWithPlugins>} Object containing registration result and plugin execution results.
* @throws {Error} If required fields are missing or API call fails.
*/
async registerEmail(
email,
externalEventId = this.#externalEventId,
subscriberState = SUBSCRIBER_STATE.REGISTERED,
fullName = ""
) {
await this.#pluginInit;
if (!email) throw new Error("Missing email for registerEmail");
if (!externalEventId)
throw new Error("Missing externalEventId for registerEmail");
let contactId;
try {
const searchRes = await this.#client.post(
"https://api.hubapi.com/crm/v3/objects/contacts/search",
{
filterGroups: [
{
filters: [
{
propertyName: "email",
operator: "EQ",
value: email,
},
],
},
],
properties: ["email"],
}
);
const result = searchRes.data.results?.[0];
if (result?.id) {
contactId = result.id;
}
} catch (e) {
console.warn(
`⚠️ Error searching contact:`,
e.response?.data || e.message
);
}
if (!contactId) {
console.log(`⚠️ Contact not found. Creating new contact for: ${email}`);
const createRes = await this.#client.post(
"https://api.hubapi.com/crm/v3/objects/contacts",
{
properties: {
email,
firstname: fullName.split(" ")[0] || "",
lastname: fullName.split(" ")[1] || "",
},
}
);
contactId = createRes.data.id;
}
// list names
const registeredListName = MarketingEvent.formatListName(
externalEventId,
SUBSCRIBER_STATE.REGISTERED
);
const canceledListName = MarketingEvent.formatListName(
externalEventId,
SUBSCRIBER_STATE.CANCELED
);
const attendingListName = MarketingEvent.formatListName(
externalEventId,
SUBSCRIBER_STATE.ATTENDED
);
// just FIND lists by name
const registeredListId = (
await this.createOrFindContactList(registeredListName)
).hubspot.listId;
const canceledListId = (
await this.createOrFindContactList(canceledListName)
).hubspot.listId;
const attendingListId = (
await this.createOrFindContactList(attendingListName)
).hubspot.listId;
// prep batch operations for speed
const addOps = [];
const removeOps = [];
if (subscriberState === SUBSCRIBER_STATE.REGISTERED) {
addOps.push(this.addContactToList(registeredListId, contactId));
removeOps.push(
this.removeContactFromList(canceledListId, contactId),
this.removeContactFromList(attendingListId, contactId)
);
} else if (subscriberState === SUBSCRIBER_STATE.CANCELED) {
addOps.push(this.addContactToList(canceledListId, contactId));
removeOps.push(
this.removeContactFromList(registeredListId, contactId),
this.removeContactFromList(attendingListId, contactId)
);
} else if (subscriberState === SUBSCRIBER_STATE.ATTENDED) {
addOps.push(this.addContactToList(attendingListId, contactId));
removeOps.push(
this.removeContactFromList(registeredListId, contactId),
this.removeContactFromList(canceledListId, contactId)
);
}
// run ops in parallel for speed
await Promise.all([...addOps, ...removeOps]);
// run hooks
const payload = {
email,
externalEventId,
subscriberState,
contactId,
timestamp: Date.now(),
registeredListId,
canceledListId,
attendingListId,
registeredListName,
canceledListName,
attendingListName,
};
const plugins = await runHooks(
this.hookFlags,
EVENTS.REGISTER_EMAIL,
payload
);
console.log(
`✅ ${
subscriberState === SUBSCRIBER_STATE.REGISTERED
? "Registered"
: subscriberState === SUBSCRIBER_STATE.CANCELED
? "Canceled"
: "Attended"
} contact: ${email} (ID: ${contactId}) for event: ${externalEventId}`
);
return {
hubspot: { success: true, email, subscriberState, contactId },
plugins,
};
}
/**
* Returns all contacts of a certain type (subscriber state) for an event.
* @async
* @param {string|number} externalEventId The external event ID to lookup.
* @param {number} subscriberState The subscriber state to filter by (e.g., SUBSCRIBER_STATE.REGISTERED).
* @returns {Promise<ContactsResult>} Object containing array of HubSpotContact objects and plugin execution results.
* @throws {Error} If required fields are missing or API call fails.
*/
async getContactsByState(externalEventId, subscriberState) {
await this.#pluginInit;
if (!externalEventId)
throw new Error("Missing externalEventId for getContactsByState");
if (isNaN(subscriberState)) {
throw new Error(
"Missing or invalid subscriberState for getContactsByState"
);
}
const listName = MarketingEvent.formatListName(
externalEventId,
subscriberState
);
const list = await this.createOrFindContactList(listName);
const response = await this.#client.get(
`https://api.hubapi.com/crm/v3/lists/${list.hubspot.listId}/memberships`
);
const results = response.data.results || [];
const plugins = await runHooks(
this.hookFlags,
EVENTS.GET_CONTACTS_BY_STATE,
{
externalEventId,
subscriberState,
count: results.length,
}
);
return {
hubspot: results,
plugins,
};
}
/**
* Finds a contact list by name, or creates it if not found.
* @async
* @param {string} listName The name of the contact list to find or create.
* @returns {Promise<ContactListResult>} Object containing the HubSpot contact list data and plugin execution results.
* @throws {Error} If API call fails.
*/
async createOrFindContactList(listName) {
await this.#pluginInit;
try {
const searchResponse = await this.#client.post(
"https://api.hubapi.com/crm/v3/lists/search",
{
objectTypeId: "contact",
query: listName,
}
);
const lists = searchResponse.data.lists || [];
const found = lists.find((list) => list.name === listName);
if (found) {
const plugins = await runHooks(
this.hookFlags,
EVENTS.CREATE_OR_FIND_CONTACT_LIST,
{
listName,
listId: found.listId,
created: false,
}
);
return {
hubspot: found,
plugins,
};
}
} catch (e) {
await runHooks(this.hookFlags, EVENTS.MARKETING_EVENT_ERROR, {
action: "createOrFindContactList",
error: e,
});
console.error(
`Error searching for contact list "${listName}":`,
e.response?.data || e.message
);
}
const createResponse = await this.#client.post(
"https://api.hubapi.com/crm/v3/lists/",
{
objectTypeId: "contact",
processingType: "MANUAL",
name: listName,
}
);
const list = createResponse?.data?.list;
const plugins = await runHooks(
this.hookFlags,
EVENTS.CREATE_OR_FIND_CONTACT_LIST,
{
list,
created: true,
}
);
return {
hubspot: list,
plugins,
};
}
/**
* See a contact's subscriber state for an event by email.
* @async
* @param {string} email The email address of the contact to lookup.
* @param {string|number} externalEventId The external event ID to lookup.
* @returns {Promise<ContactStateResult>} Object containing the contact's subscriber state and plugin execution results.
* @throws {Error} If required fields are missing or API call fails.
*/
async getContactEventState(email, externalEventId) {
await this.#pluginInit;
if (!externalEventId)
throw new Error("Missing externalEventId for getContactEventState");
if (!email) throw new Error("Missing email for getContactEventState");
// use ContactManager to find the contact by email
const instance = new ContactManager();
let contact = await instance.getContactByEmail(email);
// its faster to search in the 3 lists because we already have the contact ID
const contactId = contact.id;
if (!contactId) {
console.warn(`Contact with email ${email} not found.`);
const plugins = await runHooks(
this.hookFlags,
EVENTS.GET_CONTACT_EVENT_STATE,
{
email,
externalEventId,
state: null,
}
);
return {
hubspot: null,
plugins,
};
}
// for each list type (3), get the memberships
const listTypes = [
SUBSCRIBER_STATE.REGISTERED,
SUBSCRIBER_STATE.CANCELED,
SUBSCRIBER_STATE.ATTENDED,
];
const memberships = {};
let mostRecentTimestamp = null;
let mostRecentType = null;
const membershipPromises = listTypes.map((type) =>
this.getContactsByState(externalEventId, type)
);
const membershipResults = await Promise.all(membershipPromises);
for (let i = 0; i < listTypes.length; i++) {
const type = listTypes[i];
memberships[type] = membershipResults[i].hubspot.find(
(m) => m.recordId === contactId
);
if (memberships[type] && memberships[type].membershipTimestamp) {
if (
mostRecentTimestamp === null ||
memberships[type].membershipTimestamp > mostRecentTimestamp
) {
mostRecentTimestamp = memberships[type].membershipTimestamp;
mostRecentType = type;
}
}
}
const plugins = await runHooks(
this.hookFlags,
EVENTS.GET_CONTACT_EVENT_STATE,
{
email,
externalEventId,
state: mostRecentType,
}
);
return {
hubspot: isNaN(mostRecentType) ? null : mostRecentType,
plugins,
};
}
/**
* Adds a contact to a list.
* @async
* @param {string} listId The list ID.
* @param {string} contactId The contact ID.
* @returns {Promise<void>}
* @throws {Error} If API call fails.
*/
async addContactToList(listId, contactId) {
await this.#pluginInit;
try {
await this.#client.put(
`https://api.hubapi.com/crm/v3/lists/${listId}/memberships/add`,
[contactId]
);
await runHooks(this.hookFlags, EVENTS.ADD_CONTACT_TO_LIST, {
listId,
contactId,
});
} catch (error) {
await runHooks(this.hookFlags, EVENTS.MARKETING_EVENT_ERROR, {
action: "addContactToList",
error,
});
throw error;
}
}
/**
* Removes a contact from a list.
* @async
* @param {string} listId The list ID.
* @param {string} contactId The contact ID.
* @returns {Promise<void>}
* @throws {Error} If API call fails.
*/
async removeContactFromList(listId, contactId) {
await this.#pluginInit;
try {
await this.#client.put(
`https://api.hubapi.com/crm/v3/lists/${listId}/memberships/remove`,
[contactId]
);
await runHooks(this.hookFlags, EVENTS.REMOVE_CONTACT_FROM_LIST, {
listId,
contactId,
});
} catch (error) {
await runHooks(this.hookFlags, EVENTS.MARKETING_EVENT_ERROR, {
action: "removeContactFromList",
error,
});
throw error;
}
}
/**
* Associates a list with a marketing event.
* @async
* @param {string|number} objectId The event object ID.
* @param {string} listId The list ID.
* @returns {Promise<{hubspot: {success: boolean}, plugins: PluginResult[]}>}
* @throws {Error} If API call fails.
*/
async associateListWithEvent(objectId, listId) {
await this.#pluginInit;
let success = true;
try {
await this.#client.put(
`https://api.hubapi.com/marketing/v3/marketing-events/associations/${objectId}/lists/${listId}`
);
} catch (error) {
success = false;
await runHooks(this.hookFlags, EVENTS.MARKETING_EVENT_ERROR, {
action: "associateListWithEvent",
error,
});
// run plugin hook with failure
const plugins = await runHooks(
this.hookFlags,
EVENTS.ASSOCIATE_LIST_WITH_EVENT,
{
objectId,
listId,
success: false,
error,
}
);
return {
hubspot: { success: false },
plugins,
};
}
// run plugin hook with success
const plugins = await runHooks(
this.hookFlags,
EVENTS.ASSOCIATE_LIST_WITH_EVENT,
{
objectId,
listId,
success: true,
}
);
return {
hubspot: { success: true },
plugins,
};
}
/**
* Converts a numeric subscriber state to a human-readable string.
* @param {number} state
* @returns {SubscriberStateName|"UNKNOWN"}
*/
getSubscriberStateName(state) {
switch (state) {
case SUBSCRIBER_STATE.REGISTERED:
return "REGISTERED";
case SUBSCRIBER_STATE.ATTENDED:
return "ATTENDED";
case SUBSCRIBER_STATE.CANCELED:
return "CANCELED";
default:
return "UNKNOWN";
}
}
/**
* Helper method to code the names for the contact lists.
* @param {string|number} externalEventId
* @param {number} type
* @returns {string} The formatted list name.
*/
static formatListName(externalEventId, type) {
// convert type to char
const typeCode = type == 1 ? "A" : type == 2 ? "R" : "C";
return `E-${externalEventId}-${typeCode}`;
}
/**
* Returns the stored external event ID.
* @returns {string|number|null}
*/
get externalEventId() {
return this.#externalEventId;
}
/**
* Returns the stored external account ID.
* @returns {string|null}
*/
get externalAccountId() {
return this.#externalAccountId;
}
}