@skillswaveca/nova-shared-libraries 4.23.0 → 4.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs-base/package-info.js +5 -5
- package/index.js +2 -0
- package/package.json +4 -2
- package/packages/drivers/index.js +1 -0
- package/packages/drivers/package.json +1 -1
- package/packages/drivers/src/hubspot.js +226 -0
- package/packages/nova-middleware/package.json +1 -1
- package/packages/nova-model/package.json +1 -1
- package/packages/nova-router/package.json +1 -1
- package/packages/nova-utils/index.js +1 -0
- package/packages/nova-utils/package-lock.json +54 -0
- package/packages/nova-utils/package.json +4 -2
- package/packages/nova-utils/src/event-definitions.js +104 -0
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
export const packageInfo = [
|
|
2
2
|
{
|
|
3
3
|
"name": "@skillswaveca/nova-utils",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.24.0",
|
|
5
5
|
"description": "A collection of random utils used in nova repos",
|
|
6
6
|
"docsPath": "./nova-utils/index.html"
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
"name": "@skillswaveca/nova-router",
|
|
10
|
-
"version": "4.
|
|
10
|
+
"version": "4.24.0",
|
|
11
11
|
"description": "An extended Koa router that enables better validation",
|
|
12
12
|
"docsPath": "./nova-router/index.html"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"name": "@skillswaveca/nova-model",
|
|
16
|
-
"version": "4.
|
|
16
|
+
"version": "4.24.0",
|
|
17
17
|
"description": "Nova model stuff",
|
|
18
18
|
"docsPath": "./nova-model/index.html"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"name": "@skillswaveca/nova-middleware",
|
|
22
|
-
"version": "4.
|
|
22
|
+
"version": "4.24.0",
|
|
23
23
|
"description": "A collection of middleware used by nova projects",
|
|
24
24
|
"docsPath": "./nova-middleware/index.html"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"name": "@skillswaveca/nova-drivers",
|
|
28
|
-
"version": "4.
|
|
28
|
+
"version": "4.24.0",
|
|
29
29
|
"description": "Some helper drivers for AWS services",
|
|
30
30
|
"docsPath": "./drivers/index.html"
|
|
31
31
|
}
|
package/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './packages/nova-utils/src/tenant-utils.js';
|
|
2
2
|
export * from './packages/nova-utils/src/logger.js';
|
|
3
3
|
export * from './packages/nova-utils/src/general.js';
|
|
4
|
+
export * from './packages/nova-utils/src/event-definitions.js';
|
|
4
5
|
export * from './packages/nova-utils/src/config.js';
|
|
5
6
|
export * from './packages/nova-router/src/nova-router.js';
|
|
6
7
|
export * from './packages/nova-middleware/src/oauth-middleware.js';
|
|
@@ -9,6 +10,7 @@ export * from './packages/drivers/src/wave.js';
|
|
|
9
10
|
export * from './packages/drivers/src/oauth.js';
|
|
10
11
|
export * from './packages/drivers/src/nova-driver.js';
|
|
11
12
|
export * from './packages/drivers/src/kraken-driver.js';
|
|
13
|
+
export * from './packages/drivers/src/hubspot.js';
|
|
12
14
|
export * from './packages/drivers/src/http.js';
|
|
13
15
|
export * from './packages/drivers/src/config.js';
|
|
14
16
|
export * from './packages/nova-model/src/stream/nova-stream-task-router.js';
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "@skillswaveca/nova-shared-libraries",
|
|
4
4
|
"description": "A monorepo of shared libraries for Nova projects.",
|
|
5
5
|
"repository": "https://github.com/SkillsWave/nova-shared-libraries",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.25.0",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"keywords": [],
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"@aws-sdk/client-sqs": "^3.682.0",
|
|
15
15
|
"@aws-sdk/client-ssm": "^3.682.0",
|
|
16
16
|
"@aws-sdk/lib-dynamodb": "^3.682.0",
|
|
17
|
+
"@hubspot/api-client": "^13.0.0",
|
|
17
18
|
"@koa/router": "^13.1.0",
|
|
18
19
|
"aws-xray-sdk-core": "^3.6.0",
|
|
19
20
|
"jsonwebtoken": "^9.0.2",
|
|
@@ -24,7 +25,8 @@
|
|
|
24
25
|
"pino": "^9.1.0",
|
|
25
26
|
"simple-oauth2": "^5.0.0",
|
|
26
27
|
"uuid": "^9.0.1",
|
|
27
|
-
"yn": "^5.0.0"
|
|
28
|
+
"yn": "^5.0.0",
|
|
29
|
+
"yup": "^1.4.0"
|
|
28
30
|
},
|
|
29
31
|
"devDependencies": {
|
|
30
32
|
"@alpha-lambda/eslint-config": "^2.0.0",
|
|
@@ -2,6 +2,7 @@ export * from './src/wave.js';
|
|
|
2
2
|
export * from './src/oauth.js';
|
|
3
3
|
export * from './src/nova-driver.js';
|
|
4
4
|
export * from './src/kraken-driver.js';
|
|
5
|
+
export * from './src/hubspot.js';
|
|
5
6
|
export * from './src/http.js';
|
|
6
7
|
export * from './src/config.js';
|
|
7
8
|
export * from './src/aws/sqs.js';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "@skillswaveca/nova-drivers",
|
|
4
4
|
"description": "Some helper drivers for AWS services",
|
|
5
5
|
"repository": "https://github.com/SkillsWave/nova-shared-libraries",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.25.0",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"pre-release": "pnpm run create-index",
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { Client } from '@hubspot/api-client';
|
|
2
|
+
|
|
3
|
+
import log from '../../nova-utils/src/logger.js';
|
|
4
|
+
import { NovaDriver } from './nova-driver.js';
|
|
5
|
+
import { secretsManager } from './aws/secrets-manager.js';
|
|
6
|
+
import { validateEvent, getEventDefinition, getEventMetadata } from '../../nova-utils/src/event-definitions.js';
|
|
7
|
+
|
|
8
|
+
const HUBSPOT_USER_ATTRIBUTE_MAPPINGS = {
|
|
9
|
+
firstName: 'firstname',
|
|
10
|
+
lastName: 'lastname',
|
|
11
|
+
email: 'email',
|
|
12
|
+
guid: 'external_id',
|
|
13
|
+
tenantId: 'tenant_id',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* HubSpotDriver class provides methods to interact with HubSpot's CRM API using the HubSpot API client.
|
|
18
|
+
*/
|
|
19
|
+
export class HubSpotDriver extends NovaDriver {
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {Object} config
|
|
23
|
+
* @property {Object} hubSpot
|
|
24
|
+
* @property {string} hubSpot.secretName
|
|
25
|
+
* @property {string} hubSpot.secretKeyId
|
|
26
|
+
* @property {string} hubSpot.accountId
|
|
27
|
+
* @property {string} region
|
|
28
|
+
* @property {string} rootEnvironmentAwsAccountId
|
|
29
|
+
* @property {boolean} enabled
|
|
30
|
+
*/
|
|
31
|
+
constructor(config) {
|
|
32
|
+
super(config);
|
|
33
|
+
this._client = null;
|
|
34
|
+
this.secretName = config.hubSpot.secretName;
|
|
35
|
+
this.secretKeyId = config.hubSpot.secretKeyId;
|
|
36
|
+
this.accountId = config.hubSpot.accountId;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Maps a skillswave user to a hubSpot user object.
|
|
41
|
+
* @param {Object} user
|
|
42
|
+
* @returns {Object} The mapped HubSpot user object.
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
_mapToHubSpotUser(user) {
|
|
46
|
+
const hubSpotUser = {};
|
|
47
|
+
for (const [key, value] of Object.entries(user)) {
|
|
48
|
+
if (HUBSPOT_USER_ATTRIBUTE_MAPPINGS[key]) {
|
|
49
|
+
hubSpotUser[HUBSPOT_USER_ATTRIBUTE_MAPPINGS[key]] = value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return hubSpotUser;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* This method retrieves the access token from AWS Secrets Manager and creates a new HubSpot client instance. It will return the existing client if it has already been created.
|
|
58
|
+
* @returns {Promise<Client>} The HubSpot client instance.
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
async _getClient() {
|
|
62
|
+
if (!this._client) {
|
|
63
|
+
const accessToken = await secretsManager.getSecret(this.secretName, this.secretKeyId, this.config.region, this.config.rootEnvironmentAwsAccountId);
|
|
64
|
+
const hubSpotClient = new Client({ accessToken });
|
|
65
|
+
|
|
66
|
+
this._client = hubSpotClient;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return this._client;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Calls a HubSpot API method based on the provided method path and arguments.
|
|
74
|
+
* @param {string|Array<string>} methodPath
|
|
75
|
+
* @param {...any} args
|
|
76
|
+
* @returns {Promise<any>} Returns the raw result from the HubSpot client method.
|
|
77
|
+
*/
|
|
78
|
+
async _callHubSpot(methodPath, ...args) {
|
|
79
|
+
if (!this.enabled) {
|
|
80
|
+
log.warn('HubSpot driver is disabled');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let client = await this._getClient();
|
|
85
|
+
if (!client) {
|
|
86
|
+
log.error('Failed to create HubSpot client');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const path = Array.isArray(methodPath) ? methodPath : methodPath.split('.');
|
|
91
|
+
let parent = null;
|
|
92
|
+
for (const key of path) {
|
|
93
|
+
if (typeof client[key] === 'undefined') {
|
|
94
|
+
log.error(`Invalid HubSpot client method path: ${methodPath}`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
parent = client;
|
|
98
|
+
client = client[key];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (typeof client !== 'function') {
|
|
102
|
+
log.error(`Target at path ${methodPath} is not a function`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
return await client.apply(parent, args.length === 1 ? [args[0]] : args);
|
|
108
|
+
} catch (e) {
|
|
109
|
+
log.error({ error: e.message }, `Error calling HubSpot method ${methodPath}`);
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Updates a HubSpot deal with the given properties.
|
|
116
|
+
* @param {string} dealId
|
|
117
|
+
* @param {object} properties
|
|
118
|
+
* @returns The updated deal object.
|
|
119
|
+
*/
|
|
120
|
+
async updateDeal(dealId, properties) {
|
|
121
|
+
return await this._callHubSpot('crm.deals.basicApi.update', dealId, { properties });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Looks up a HubSpot contact ID for a given user. May return multiple IDs.
|
|
126
|
+
* @param {Object} user
|
|
127
|
+
* @returns {Promise<string[]|null>} An array of HubSpot contact IDs or null if no contact is found.
|
|
128
|
+
*/
|
|
129
|
+
async lookupUserContactId(user) {
|
|
130
|
+
if (!user.email) {
|
|
131
|
+
log.error('User email is required for lookup');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const response = await this._callHubSpot('crm.contacts.searchApi.doSearch', {
|
|
137
|
+
filterGroups: [{
|
|
138
|
+
filters: [{
|
|
139
|
+
propertyName: 'email',
|
|
140
|
+
operator: 'EQ',
|
|
141
|
+
value: user.email,
|
|
142
|
+
}]
|
|
143
|
+
}]
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return response.results.length > 0 ? response.results.map(result => result.id) : [];
|
|
147
|
+
} catch (error) {
|
|
148
|
+
log.error({ error }, `Error looking up user ${user.email} in HubSpot`);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Creates or updates a HubSpot contact. If the provided user has a `hubSpotContactId`, it will update the existing contact; otherwise, it will create a new one.
|
|
155
|
+
* @param {object} user
|
|
156
|
+
* @returns {object} The created/updated HubSpot user.
|
|
157
|
+
*/
|
|
158
|
+
async upsertUser(user) {
|
|
159
|
+
const hubSpotUser = this._mapToHubSpotUser(user);
|
|
160
|
+
let response;
|
|
161
|
+
if (user.hubSpotContactId) {
|
|
162
|
+
response = await this._callHubSpot('crm.contacts.basicApi.update', user.hubSpotContactId, { properties: hubSpotUser });
|
|
163
|
+
} else {
|
|
164
|
+
response = await this._callHubSpot('crm.contacts.basicApi.create', { properties: hubSpotUser });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return { hubSpotContactId: response.id, ...user };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Deletes a HubSpot user.
|
|
172
|
+
* @param {object} user
|
|
173
|
+
* @returns {Promise<void>}
|
|
174
|
+
*/
|
|
175
|
+
async deleteUser(user) {
|
|
176
|
+
if (!user.hubSpotContactId) {
|
|
177
|
+
log.error(`User ${user.email} does not have a HubSpot contact ID, skipping deletion.`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
await this._callHubSpot('crm.contacts.basicApi.archive', user.hubSpotContactId);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Sends a custom event to HubSpot using the event definitions.
|
|
186
|
+
* This method validates the event data against the predefined schema before sending.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} eventName - The name of the event to send
|
|
189
|
+
* @param {object} eventData - The event data to send
|
|
190
|
+
* @returns {Promise<object>} The response from HubSpot API
|
|
191
|
+
* @throws {Error} If validation fails or HubSpot API returns an error
|
|
192
|
+
*/
|
|
193
|
+
async sendEvent(eventName, eventData) {
|
|
194
|
+
try {
|
|
195
|
+
// Validate the event exists and get its definition
|
|
196
|
+
const eventDefinition = getEventDefinition(eventName);
|
|
197
|
+
if (!eventDefinition) {
|
|
198
|
+
log.error({ eventName }, 'Event definition not found');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const validatedData = await validateEvent(eventName, eventData);
|
|
203
|
+
|
|
204
|
+
const eventMetadata = await getEventMetadata(eventName, validatedData);
|
|
205
|
+
|
|
206
|
+
const response = await this._callHubSpot('events.send.basicApi.send', {
|
|
207
|
+
eventName: `pe${this.accountId}_${eventName}`,
|
|
208
|
+
properties: validatedData,
|
|
209
|
+
occurredAt: new Date(),
|
|
210
|
+
...eventMetadata,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
log.debug({ eventName, response }, 'Event sent to HubSpot successfully');
|
|
214
|
+
return response;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
log.error({
|
|
217
|
+
eventName,
|
|
218
|
+
eventData,
|
|
219
|
+
error: error.message,
|
|
220
|
+
stack: error.stack
|
|
221
|
+
}, 'Unexpected error while sending event');
|
|
222
|
+
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "@skillswaveca/nova-middleware",
|
|
4
4
|
"description": "A collection of middleware used by nova projects",
|
|
5
5
|
"repository": "https://github.com/SkillsWave/nova-shared-libraries",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.25.0",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"pre-release": "pnpm run create-index",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "@skillswaveca/nova-model",
|
|
4
4
|
"description": "Nova model stuff",
|
|
5
5
|
"repository": "https://github.com/SkillsWave/nova-shared-libraries",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.25.0",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"pre-release": "pnpm run create-index",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "@skillswaveca/nova-router",
|
|
4
4
|
"description": "An extended Koa router that enables better validation",
|
|
5
5
|
"repository": "https://github.com/SkillsWave/nova-shared-libraries",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.25.0",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"pre-release": "pnpm run create-index",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@skillswaveca/nova-utils",
|
|
3
|
+
"version": "4.24.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "@skillswaveca/nova-utils",
|
|
9
|
+
"version": "4.24.0",
|
|
10
|
+
"license": "UNLICENSED",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"yup": "^1.4.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {}
|
|
15
|
+
},
|
|
16
|
+
"node_modules/property-expr": {
|
|
17
|
+
"version": "2.0.6",
|
|
18
|
+
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
|
|
19
|
+
"integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA=="
|
|
20
|
+
},
|
|
21
|
+
"node_modules/tiny-case": {
|
|
22
|
+
"version": "1.0.3",
|
|
23
|
+
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
|
|
24
|
+
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="
|
|
25
|
+
},
|
|
26
|
+
"node_modules/toposort": {
|
|
27
|
+
"version": "2.0.2",
|
|
28
|
+
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
|
|
29
|
+
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
|
|
30
|
+
},
|
|
31
|
+
"node_modules/type-fest": {
|
|
32
|
+
"version": "2.19.0",
|
|
33
|
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
|
|
34
|
+
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=12.20"
|
|
37
|
+
},
|
|
38
|
+
"funding": {
|
|
39
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"node_modules/yup": {
|
|
43
|
+
"version": "1.6.1",
|
|
44
|
+
"resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz",
|
|
45
|
+
"integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==",
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"property-expr": "^2.0.5",
|
|
48
|
+
"tiny-case": "^1.0.3",
|
|
49
|
+
"toposort": "^2.0.2",
|
|
50
|
+
"type-fest": "^2.19.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "@skillswaveca/nova-utils",
|
|
4
4
|
"description": "A collection of random utils used in nova repos",
|
|
5
5
|
"repository": "https://github.com/SkillsWave/nova-shared-libraries",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.25.0",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"pre-release": "pnpm run create-index",
|
|
@@ -14,5 +14,7 @@
|
|
|
14
14
|
"author": "SkillsWave",
|
|
15
15
|
"license": "UNLICENSED",
|
|
16
16
|
"devDependencies": {},
|
|
17
|
-
"dependencies": {
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"yup": "^1.4.0"
|
|
19
|
+
}
|
|
18
20
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as yup from 'yup';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HubSpot Event Definitions
|
|
5
|
+
*
|
|
6
|
+
* This module contains the schema definitions for custom events that can be sent to HubSpot.
|
|
7
|
+
* Each event definition includes:
|
|
8
|
+
* - name: The event name used in HubSpot
|
|
9
|
+
* - description: Human-readable description of the event
|
|
10
|
+
* - properties: Schema definition for event properties
|
|
11
|
+
* - validator: Yup validation schema for the event data
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const testEventDefinition = {
|
|
15
|
+
name: 'hubspot_test_event',
|
|
16
|
+
description: 'Test event for HubSpot integration',
|
|
17
|
+
properties: {
|
|
18
|
+
email: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
required: true,
|
|
21
|
+
description: 'Email address of the user',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
validator: yup.object({
|
|
25
|
+
email: yup.string().email('Must be a valid email').required('Email is required'),
|
|
26
|
+
}),
|
|
27
|
+
getMetadata: eventData => {
|
|
28
|
+
return {
|
|
29
|
+
email: eventData.email,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Map of all available event definitions
|
|
36
|
+
* Key: event name, Value: event definition object
|
|
37
|
+
*/
|
|
38
|
+
export const eventDefinitions = {
|
|
39
|
+
[testEventDefinition.name]: testEventDefinition,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get event definition by name
|
|
44
|
+
* @param {string} eventName - The name of the event
|
|
45
|
+
* @returns {Object|null} The event definition or null if not found
|
|
46
|
+
*/
|
|
47
|
+
export const getEventDefinition = eventName => {
|
|
48
|
+
return eventDefinitions[eventName] || null;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get all available event names
|
|
53
|
+
* @returns {string[]} Array of event names
|
|
54
|
+
*/
|
|
55
|
+
export const getEventNames = () => {
|
|
56
|
+
return Object.keys(eventDefinitions);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Validate event data against its schema
|
|
61
|
+
* @param {string} eventName - The name of the event
|
|
62
|
+
* @param {Object} eventData - The event data to validate
|
|
63
|
+
* @returns {Promise<Object>} The validated and transformed event data
|
|
64
|
+
* @throws {Error} If validation fails
|
|
65
|
+
*/
|
|
66
|
+
export const validateEvent = async(eventName, eventData) => {
|
|
67
|
+
const eventDef = getEventDefinition(eventName);
|
|
68
|
+
|
|
69
|
+
if (!eventDef) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const validatedData = await eventDef.validator.validate(eventData, {
|
|
74
|
+
abortEarly: false,
|
|
75
|
+
stripUnknown: true,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return validatedData;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check if an event name is valid
|
|
83
|
+
* @param {string} eventName - The name of the event
|
|
84
|
+
* @returns {boolean} True if the event exists, false otherwise
|
|
85
|
+
*/
|
|
86
|
+
export const isValidEventName = eventName => {
|
|
87
|
+
return eventDefinitions.hasOwnProperty(eventName);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Gets any event metadata required to send the event to HubSpot.
|
|
92
|
+
* @param {string} eventName The name of the event
|
|
93
|
+
* @param {Object} eventData The custom data for the event
|
|
94
|
+
* @returns {Promise<Object>} The metadata for the event required to send it to HubSpot
|
|
95
|
+
*/
|
|
96
|
+
export const getEventMetadata = async(eventName, eventData) => {
|
|
97
|
+
const eventDef = getEventDefinition(eventName);
|
|
98
|
+
|
|
99
|
+
if (!eventDef || typeof eventDef.getMetadata !== 'function') {
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return await eventDef.getMetadata(eventData);
|
|
104
|
+
};
|