@skillswaveca/nova-shared-libraries 4.22.0 → 4.24.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.
@@ -1,31 +1,31 @@
1
1
  export const packageInfo = [
2
2
  {
3
3
  "name": "@skillswaveca/nova-utils",
4
- "version": "4.21.0",
4
+ "version": "4.23.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.21.0",
10
+ "version": "4.23.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.21.0",
16
+ "version": "4.23.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.21.0",
22
+ "version": "4.23.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.21.0",
28
+ "version": "4.23.0",
29
29
  "description": "Some helper drivers for AWS services",
30
30
  "docsPath": "./drivers/index.html"
31
31
  }
package/index.js CHANGED
@@ -9,6 +9,7 @@ export * from './packages/drivers/src/wave.js';
9
9
  export * from './packages/drivers/src/oauth.js';
10
10
  export * from './packages/drivers/src/nova-driver.js';
11
11
  export * from './packages/drivers/src/kraken-driver.js';
12
+ export * from './packages/drivers/src/hubspot.js';
12
13
  export * from './packages/drivers/src/http.js';
13
14
  export * from './packages/drivers/src/config.js';
14
15
  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.22.0",
6
+ "version": "4.24.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",
@@ -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.22.0",
6
+ "version": "4.24.0",
7
7
  "main": "index.js",
8
8
  "scripts": {
9
9
  "pre-release": "pnpm run create-index",
@@ -25,6 +25,7 @@ export class AwsBaseDriver extends NovaDriver {
25
25
  super(configOverrides);
26
26
  this.__client = new clientType(opts);
27
27
  this.isProd = this.config.prod;
28
+ this.region = opts?.region;
28
29
  if (this.config.xray.enabled) xray.captureAWSv3Client(this.__client);
29
30
  }
30
31
 
@@ -5,9 +5,9 @@ import { AwsBaseDriver } from './aws-base-driver.js';
5
5
  * SecretsManager class to simplify fetching secrets from AWS Secret Manager.
6
6
  */
7
7
  export class SecretsManager extends AwsBaseDriver {
8
- constructor() {
8
+ constructor({ region } = {}) {
9
9
  super(SecretsManagerClient, {
10
- region: process.env.AWS_REGION || 'us-east-1',
10
+ region: region || process.env.AWS_REGION || 'us-east-1',
11
11
  });
12
12
  }
13
13
 
@@ -34,8 +34,13 @@ export class SecretsManager extends AwsBaseDriver {
34
34
  return;
35
35
  }
36
36
 
37
+ const useArn = region && rootEnvironmentAwsAccountId;
38
+ if (useArn && region !== this.region) {
39
+ this.log.warn({ requestedRegion: region, configuredRegion: this.region }, 'Requested region does not match configured region');
40
+ }
41
+
37
42
  try {
38
- const secretId = region && rootEnvironmentAwsAccountId ?
43
+ const secretId = useArn ?
39
44
  `arn:aws:secretsmanager:${region}:${rootEnvironmentAwsAccountId}:secret:${secretName}` :
40
45
  secretName;
41
46
  const data = await this.client.send(new GetSecretValueCommand({ SecretId: secretId }));
@@ -0,0 +1,180 @@
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
+
7
+ const HUBSPOT_USER_ATTRIBUTE_MAPPINGS = {
8
+ firstName: 'firstname',
9
+ lastName: 'lastname',
10
+ email: 'email',
11
+ guid: 'external_id',
12
+ tenantId: 'tenant_id',
13
+ };
14
+
15
+ /**
16
+ * HubSpotDriver class provides methods to interact with HubSpot's CRM API using the HubSpot API client.
17
+ */
18
+ export class HubSpotDriver extends NovaDriver {
19
+
20
+ /**
21
+ * @param {Object} config
22
+ * @property {Object} hubSpot
23
+ * @property {string} hubSpot.secretName
24
+ * @property {string} hubSpot.secretKeyId
25
+ * @property {string} region
26
+ * @property {string} rootEnvironmentAwsAccountId
27
+ * @property {boolean} enabled
28
+ */
29
+ constructor(config) {
30
+ super(config);
31
+ this._client = null;
32
+ this.secretName = config.hubSpot.secretName;
33
+ this.secretKeyId = config.hubSpot.secretKeyId;
34
+ }
35
+
36
+ /**
37
+ * Maps a skillswave user to a hubSpot user object.
38
+ * @param {Object} user
39
+ * @returns {Object} The mapped HubSpot user object.
40
+ * @private
41
+ */
42
+ _mapToHubSpotUser(user) {
43
+ const hubSpotUser = {};
44
+ for (const [key, value] of Object.entries(user)) {
45
+ if (HUBSPOT_USER_ATTRIBUTE_MAPPINGS[key]) {
46
+ hubSpotUser[HUBSPOT_USER_ATTRIBUTE_MAPPINGS[key]] = value;
47
+ }
48
+ }
49
+
50
+ return hubSpotUser;
51
+ }
52
+
53
+ /**
54
+ * 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.
55
+ * @returns {Promise<Client>} The HubSpot client instance.
56
+ * @private
57
+ */
58
+ async _getClient() {
59
+ if (!this._client) {
60
+ const accessToken = await secretsManager.getSecret(this.secretName, this.secretKeyId, this.config.region, this.config.rootEnvironmentAwsAccountId);
61
+ const hubSpotClient = new Client({ accessToken });
62
+
63
+ this._client = hubSpotClient;
64
+ }
65
+
66
+ return this._client;
67
+ }
68
+
69
+ /**
70
+ * Calls a HubSpot API method based on the provided method path and arguments.
71
+ * @param {string|Array<string>} methodPath
72
+ * @param {...any} args
73
+ * @returns {Promise<any>} Returns the raw result from the HubSpot client method.
74
+ */
75
+ async _callHubSpot(methodPath, ...args) {
76
+ if (!this.enabled) {
77
+ log.warn('HubSpot driver is disabled');
78
+ return;
79
+ }
80
+
81
+ let client = await this._getClient();
82
+ if (!client) {
83
+ log.error('Failed to create HubSpot client');
84
+ return;
85
+ }
86
+
87
+ const path = Array.isArray(methodPath) ? methodPath : methodPath.split('.');
88
+ let parent = null;
89
+ for (const key of path) {
90
+ if (typeof client[key] === 'undefined') {
91
+ log.error(`Invalid HubSpot client method path: ${methodPath}`);
92
+ return;
93
+ }
94
+ parent = client;
95
+ client = client[key];
96
+ }
97
+
98
+ if (typeof client !== 'function') {
99
+ log.error(`Target at path ${methodPath} is not a function`);
100
+ return;
101
+ }
102
+
103
+ try {
104
+ return await client.apply(parent, args.length === 1 ? [args[0]] : args);
105
+ } catch (e) {
106
+ log.error({ error: e.message }, `Error calling HubSpot method ${methodPath}`);
107
+ throw e;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Updates a HubSpot deal with the given properties.
113
+ * @param {string} dealId
114
+ * @param {object} properties
115
+ * @returns The updated deal object.
116
+ */
117
+ async updateDeal(dealId, properties) {
118
+ return await this._callHubSpot('crm.deals.basicApi.update', dealId, { properties });
119
+ }
120
+
121
+ /**
122
+ * Looks up a HubSpot contact ID for a given user. May return multiple IDs.
123
+ * @param {Object} user
124
+ * @returns {Promise<string[]|null>} An array of HubSpot contact IDs or null if no contact is found.
125
+ */
126
+ async lookupUserContactId(user) {
127
+ if (!user.email) {
128
+ log.error('User email is required for lookup');
129
+ return;
130
+ }
131
+
132
+ try {
133
+ const response = await this._callHubSpot('crm.contacts.searchApi.doSearch', {
134
+ filterGroups: [{
135
+ filters: [{
136
+ propertyName: 'email',
137
+ operator: 'EQ',
138
+ value: user.email,
139
+ }]
140
+ }]
141
+ });
142
+
143
+ return response.results.length > 0 ? response.results.map(result => result.id) : [];
144
+ } catch (error) {
145
+ log.error({ error }, `Error looking up user ${user.email} in HubSpot`);
146
+ throw error;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * 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.
152
+ * @param {object} user
153
+ * @returns {object} The created/updated HubSpot user.
154
+ */
155
+ async upsertUser(user) {
156
+ const hubSpotUser = this._mapToHubSpotUser(user);
157
+ let response;
158
+ if (user.hubSpotContactId) {
159
+ response = await this._callHubSpot('crm.contacts.basicApi.update', user.hubSpotContactId, { properties: hubSpotUser });
160
+ } else {
161
+ response = await this._callHubSpot('crm.contacts.basicApi.create', { properties: hubSpotUser });
162
+ }
163
+
164
+ return { hubSpotContactId: response.id, ...user };
165
+ }
166
+
167
+ /**
168
+ * Deletes a HubSpot user.
169
+ * @param {object} user
170
+ * @returns {Promise<void>}
171
+ */
172
+ async deleteUser(user) {
173
+ if (!user.hubSpotContactId) {
174
+ log.error(`User ${user.email} does not have a HubSpot contact ID, skipping deletion.`);
175
+ return;
176
+ }
177
+
178
+ await this._callHubSpot('crm.contacts.basicApi.archive', user.hubSpotContactId);
179
+ }
180
+ }
@@ -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.22.0",
6
+ "version": "4.24.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.22.0",
6
+ "version": "4.24.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.22.0",
6
+ "version": "4.24.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-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.22.0",
6
+ "version": "4.24.0",
7
7
  "main": "index.js",
8
8
  "scripts": {
9
9
  "pre-release": "pnpm run create-index",