@skillswaveca/nova-shared-libraries 4.24.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 +1 -0
- package/package.json +3 -2
- package/packages/drivers/package.json +1 -1
- package/packages/drivers/src/hubspot.js +46 -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';
|
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": [],
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"pino": "^9.1.0",
|
|
26
26
|
"simple-oauth2": "^5.0.0",
|
|
27
27
|
"uuid": "^9.0.1",
|
|
28
|
-
"yn": "^5.0.0"
|
|
28
|
+
"yn": "^5.0.0",
|
|
29
|
+
"yup": "^1.4.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@alpha-lambda/eslint-config": "^2.0.0",
|
|
@@ -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",
|
|
@@ -3,6 +3,7 @@ import { Client } from '@hubspot/api-client';
|
|
|
3
3
|
import log from '../../nova-utils/src/logger.js';
|
|
4
4
|
import { NovaDriver } from './nova-driver.js';
|
|
5
5
|
import { secretsManager } from './aws/secrets-manager.js';
|
|
6
|
+
import { validateEvent, getEventDefinition, getEventMetadata } from '../../nova-utils/src/event-definitions.js';
|
|
6
7
|
|
|
7
8
|
const HUBSPOT_USER_ATTRIBUTE_MAPPINGS = {
|
|
8
9
|
firstName: 'firstname',
|
|
@@ -22,6 +23,7 @@ export class HubSpotDriver extends NovaDriver {
|
|
|
22
23
|
* @property {Object} hubSpot
|
|
23
24
|
* @property {string} hubSpot.secretName
|
|
24
25
|
* @property {string} hubSpot.secretKeyId
|
|
26
|
+
* @property {string} hubSpot.accountId
|
|
25
27
|
* @property {string} region
|
|
26
28
|
* @property {string} rootEnvironmentAwsAccountId
|
|
27
29
|
* @property {boolean} enabled
|
|
@@ -31,6 +33,7 @@ export class HubSpotDriver extends NovaDriver {
|
|
|
31
33
|
this._client = null;
|
|
32
34
|
this.secretName = config.hubSpot.secretName;
|
|
33
35
|
this.secretKeyId = config.hubSpot.secretKeyId;
|
|
36
|
+
this.accountId = config.hubSpot.accountId;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/**
|
|
@@ -177,4 +180,47 @@ export class HubSpotDriver extends NovaDriver {
|
|
|
177
180
|
|
|
178
181
|
await this._callHubSpot('crm.contacts.basicApi.archive', user.hubSpotContactId);
|
|
179
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
|
+
}
|
|
180
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
|
+
};
|