@dma-sdk/hubspot 1.0.1

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.
@@ -0,0 +1,23 @@
1
+ export declare const hsDataTypes: {
2
+ string: string;
3
+ number: string;
4
+ boolean: string;
5
+ date: string;
6
+ datetime: string;
7
+ single_select: string;
8
+ multiple_select: string;
9
+ checkbox: string;
10
+ calculation: string;
11
+ phone_number: string;
12
+ user: string;
13
+ file: string;
14
+ score: string;
15
+ currency: string;
16
+ rich_text: string;
17
+ owner: string;
18
+ location: string;
19
+ json: string;
20
+ percentage: string;
21
+ ip_address: string;
22
+ video: string;
23
+ };
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hsDataTypes = void 0;
4
+ exports.hsDataTypes = {
5
+ string: "string",
6
+ number: "number",
7
+ boolean: "bool",
8
+ date: "date",
9
+ datetime: "datetime",
10
+ single_select: "enumeration",
11
+ multiple_select: "enumeration",
12
+ checkbox: "bool",
13
+ calculation: "calculation_equation",
14
+ phone_number: "phone_number",
15
+ user: "hubspot_user",
16
+ file: "file",
17
+ score: "score",
18
+ currency: "currency",
19
+ rich_text: "rich_text",
20
+ owner: "owner",
21
+ location: "location",
22
+ json: "json",
23
+ percentage: "number",
24
+ ip_address: "ip_address",
25
+ video: "video"
26
+ };
@@ -0,0 +1,88 @@
1
+ import { HubspotUser } from './interfaces/user';
2
+ import { Configuration, Filter } from '@hubspot/api-client/lib/codegen/crm/objects';
3
+ import { SimplePublicObjectInputForCreate } from '@hubspot/api-client/lib/codegen/crm/objects/taxes/models/all';
4
+ import { SimplePublicObject } from '@hubspot/api-client/lib/codegen/crm/objects/tasks/models/all';
5
+ import { LabelsBetweenObjectPair } from '@hubspot/api-client/lib/codegen/crm/associations/v4';
6
+ import { TokenResponseIF } from '@hubspot/api-client/lib/codegen/oauth';
7
+ import { TokenResponse } from './interfaces/token';
8
+ import { CollectionResponsePropertyNoPaging, PropertyCreate, PropertyGroup, PropertyGroupCreate } from '@hubspot/api-client/lib/codegen/crm/properties';
9
+ import { HubspotSignatureData } from './types/auth/hs-signature-data';
10
+ export declare class HubspotService {
11
+ static HS_BASE_URL: string;
12
+ static readonly hubspotObjectType: {
13
+ contacts: string;
14
+ notes: string;
15
+ };
16
+ static readonly hubspotPropertyTypes: {
17
+ string: string;
18
+ number: string;
19
+ boolean: string;
20
+ date: string;
21
+ datetime: string;
22
+ single_select: string;
23
+ multiple_select: string;
24
+ checkbox: string;
25
+ calculation: string;
26
+ phone_number: string;
27
+ user: string;
28
+ file: string;
29
+ score: string;
30
+ currency: string;
31
+ rich_text: string;
32
+ owner: string;
33
+ location: string;
34
+ json: string;
35
+ percentage: string;
36
+ ip_address: string;
37
+ video: string;
38
+ };
39
+ private client;
40
+ private hsRedirectUriOauth;
41
+ private hsClientId;
42
+ private hsClientSecret;
43
+ private typeConvert;
44
+ private apiMethods;
45
+ constructor(hsRedirectUriOauth: string, hsClientId: string, hsClientSecret: string);
46
+ exchangeCodeForTokens(params: {
47
+ code: string;
48
+ }): Promise<TokenResponseIF>;
49
+ getUserInfoByToken(accessToken: string): Promise<HubspotUser>;
50
+ verifyFetchReqSignature(params: {
51
+ version: string;
52
+ data: HubspotSignatureData;
53
+ }): Promise<boolean>;
54
+ refreshAccessToken(refreshToken: string): Promise<TokenResponse>;
55
+ listObjectProperties(params: {
56
+ objectType: string;
57
+ types?: string[];
58
+ accessToken?: string;
59
+ }): Promise<CollectionResponsePropertyNoPaging>;
60
+ listObjects(objectType: string, filters: Filter[], accessToken: string): Promise<SimplePublicObject[] | null>;
61
+ createObject(objectType: string, properties: SimplePublicObjectInputForCreate, accessToken: string): Promise<SimplePublicObject | {
62
+ id: string;
63
+ }>;
64
+ getObject(objectType: string, objectId: string, accessToken: string): Promise<SimplePublicObject>;
65
+ getObjectProps(objectType: string, objectId: string, props: string[], // nome delle props da restituire
66
+ accessToken: string): Promise<SimplePublicObject>;
67
+ getObjectWithPropsType(objectType: string, objectId: string, accessToken: string, types?: string[]): Promise<SimplePublicObject & {
68
+ propertiesWithTypes: {
69
+ [key: string]: {
70
+ type: string;
71
+ label: string;
72
+ value: string | null;
73
+ };
74
+ };
75
+ }>;
76
+ updateObject(objectType: string, objectId: string, properties: {
77
+ [key: string]: string;
78
+ }, accessToken: string, options?: Configuration): Promise<SimplePublicObject>;
79
+ associateObjects(objectTypeFirst: string, objectTypeSecond: string, objectIdFirst: string, objectIdSecond: string, accessToken: string): Promise<LabelsBetweenObjectPair>;
80
+ createObjectProperties(objectType: string, properties: PropertyCreate[], accessToken: string): Promise<any>;
81
+ createPropertyGroup(objectType: string, group: PropertyGroupCreate, accessToken: string): Promise<PropertyGroup>;
82
+ convertPropertyValues(properties: {
83
+ [key: string]: any;
84
+ }, convertionMap: {
85
+ [propertyName: string]: PropertyCreate;
86
+ }): Record<string, any>;
87
+ private fetchApi;
88
+ }
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HubspotService = void 0;
4
+ const api_client_1 = require("@hubspot/api-client");
5
+ const type_converter_1 = require("./utils/type-converter");
6
+ const types_1 = require("./constants/types");
7
+ const api_client_2 = require("@hubspot/api-client");
8
+ const fetch_1 = require("@dma-sdk/utils/src/fetch");
9
+ // Per ogni metodo che viene creato, è fondamentale che
10
+ // il client venga inizializzato con il token di accesso
11
+ // altrimenti non funzionerà correttamente.
12
+ class HubspotService {
13
+ constructor(hsRedirectUriOauth, hsClientId, hsClientSecret) {
14
+ this.apiMethods = {
15
+ getUserInfoByToken: '/oauth/v1/access-tokens',
16
+ refreshToken: '/oauth/v1/token',
17
+ createObjectProperties: '/crm/v3/properties/{objectType}/batch/create',
18
+ };
19
+ ;
20
+ (this.hsRedirectUriOauth = hsRedirectUriOauth),
21
+ (this.hsClientId = hsClientId),
22
+ (this.hsClientSecret = hsClientSecret);
23
+ this.typeConvert = type_converter_1.typeConverter;
24
+ this.client = new api_client_1.Client();
25
+ }
26
+ async exchangeCodeForTokens(params) {
27
+ return await this.client.oauth.tokensApi.create('authorization_code', params.code, this.hsRedirectUriOauth, this.hsClientId, this.hsClientSecret);
28
+ }
29
+ async getUserInfoByToken(accessToken) {
30
+ const response = await this.client.apiRequest({
31
+ method: 'GET',
32
+ path: `${this.apiMethods.getUserInfoByToken}/${accessToken}`,
33
+ });
34
+ const jsonBody = await response.json();
35
+ const userAttributes = {
36
+ token: jsonBody.token,
37
+ user: jsonBody.user,
38
+ hubDomain: jsonBody.hub_domain,
39
+ scopes: jsonBody.scopes,
40
+ hubId: jsonBody.hub_id,
41
+ appId: jsonBody.app_id,
42
+ expiresIn: jsonBody.expires_in,
43
+ userId: jsonBody.user_id,
44
+ tokenType: jsonBody.token_type,
45
+ signedAccessToken: jsonBody.signed_access_token,
46
+ };
47
+ return userAttributes;
48
+ }
49
+ async verifyFetchReqSignature(params) {
50
+ const queryString = (params.data.qs && Object.keys(params.data.qs).length > 0) ? `?${new URLSearchParams(params.data.qs).toString()}` : '';
51
+ const url = decodeURIComponent(`https://${params.data.domainName}${params.data.path}${queryString}`);
52
+ return api_client_2.Signature.isValid({
53
+ method: params.data.httpMethod,
54
+ signatureVersion: params.version,
55
+ signature: params.data.signature,
56
+ requestBody: params.data.body,
57
+ timestamp: params.data.timestamp,
58
+ url,
59
+ clientSecret: this.hsClientSecret,
60
+ });
61
+ }
62
+ async refreshAccessToken(refreshToken) {
63
+ const token = await this.client.oauth.tokensApi.create('refresh_token', undefined, this.hsRedirectUriOauth, this.hsClientId, this.hsClientSecret, refreshToken);
64
+ return {
65
+ accessToken: token.accessToken,
66
+ expiresIn: token.expiresIn,
67
+ refreshToken: token.refreshToken,
68
+ tokenType: token.tokenType,
69
+ };
70
+ }
71
+ async listObjectProperties(params) {
72
+ const { objectType, types, accessToken } = params;
73
+ if (accessToken)
74
+ this.client.setAccessToken(accessToken);
75
+ const props = await this.client.crm.properties.coreApi.getAll(objectType);
76
+ if (!types)
77
+ return props;
78
+ return { results: props.results.filter((prop) => types.includes(prop.fieldType) || types.includes(prop.type)) };
79
+ }
80
+ async listObjects(objectType, filters, accessToken) {
81
+ this.client.setAccessToken(accessToken);
82
+ const response = await this.client.crm.objects.searchApi.doSearch(objectType, {
83
+ filterGroups: [
84
+ {
85
+ filters: filters,
86
+ },
87
+ ],
88
+ });
89
+ return response.results || null;
90
+ }
91
+ async createObject(objectType, properties, accessToken) {
92
+ this.client.setAccessToken(accessToken);
93
+ const response = await this.client.crm.objects.basicApi.create(objectType, properties);
94
+ return response;
95
+ }
96
+ async getObject(objectType, objectId, accessToken) {
97
+ this.client.setAccessToken(accessToken);
98
+ return await this.client.crm.objects.basicApi.getById(objectType, objectId);
99
+ }
100
+ async getObjectProps(objectType, objectId, props, // nome delle props da restituire
101
+ accessToken) {
102
+ this.client.setAccessToken(accessToken);
103
+ return await this.client.crm.objects.basicApi.getById(objectType, objectId, props);
104
+ }
105
+ async getObjectWithPropsType(objectType, objectId, accessToken, types) {
106
+ this.client.setAccessToken(accessToken);
107
+ const props = await this.listObjectProperties({ objectType, types });
108
+ const data = await this.getObjectProps(objectType, objectId, props.results.map((prop) => prop.name), accessToken);
109
+ let formattedProp = {};
110
+ props.results.forEach((property) => {
111
+ if (!data.properties[property.name]) {
112
+ return;
113
+ }
114
+ formattedProp[property.name] = {
115
+ type: property.fieldType,
116
+ label: property.label,
117
+ value: data.properties[property.name]
118
+ };
119
+ });
120
+ return {
121
+ ...data,
122
+ propertiesWithTypes: formattedProp
123
+ };
124
+ }
125
+ async updateObject(objectType, objectId, properties, accessToken, options) {
126
+ this.client.setAccessToken(accessToken);
127
+ const response = await this.client.crm.objects.basicApi.update(objectType, objectId, {
128
+ properties: {
129
+ ...properties
130
+ }
131
+ });
132
+ return response;
133
+ }
134
+ async associateObjects(objectTypeFirst, objectTypeSecond, objectIdFirst, objectIdSecond, accessToken) {
135
+ this.client.setAccessToken(accessToken);
136
+ const response = await this.client.crm.associations.v4.basicApi.create(objectTypeFirst, objectIdFirst, objectTypeSecond, objectIdSecond, []);
137
+ return response;
138
+ }
139
+ async createObjectProperties(objectType, properties, accessToken) {
140
+ const options = {
141
+ method: 'POST',
142
+ headers: {
143
+ Authorization: `Bearer ${accessToken}`,
144
+ 'Content-Type': 'application/json'
145
+ },
146
+ body: JSON.stringify({
147
+ inputs: properties
148
+ })
149
+ };
150
+ const response = await this.fetchApi(this.apiMethods.createObjectProperties.replace('{objectType}', objectType), options);
151
+ return await response.json();
152
+ }
153
+ async createPropertyGroup(objectType, group, accessToken) {
154
+ this.client.setAccessToken(accessToken);
155
+ return await this.client.crm.properties.groupsApi.create(objectType, group);
156
+ }
157
+ convertPropertyValues(properties, convertionMap) {
158
+ let convertedProps = {};
159
+ Object.entries(properties).forEach(([key, value]) => {
160
+ var _a;
161
+ if (value === undefined)
162
+ return;
163
+ const convertionFunc = this.typeConvert[(_a = convertionMap[key]) === null || _a === void 0 ? void 0 : _a.type];
164
+ if (convertionFunc === undefined) {
165
+ convertedProps[key] = value;
166
+ return;
167
+ }
168
+ convertedProps[key] = convertionFunc(value);
169
+ });
170
+ return convertedProps;
171
+ }
172
+ async fetchApi(endpoint, options, queryParams) {
173
+ return await (0, fetch_1.fetchApi)(HubspotService.HS_BASE_URL, endpoint, options, queryParams);
174
+ }
175
+ }
176
+ exports.HubspotService = HubspotService;
177
+ HubspotService.HS_BASE_URL = 'https://api.hubapi.com';
178
+ HubspotService.hubspotObjectType = {
179
+ contacts: 'contacts',
180
+ notes: 'notes',
181
+ };
182
+ HubspotService.hubspotPropertyTypes = types_1.hsDataTypes;
@@ -0,0 +1,3 @@
1
+ export interface HubspotTypeConverter {
2
+ [key: string]: (data: any) => any;
3
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ ;
@@ -0,0 +1,8 @@
1
+ export interface TokenResponse {
2
+ accessToken: string;
3
+ expiresIn: number;
4
+ hubId?: string;
5
+ refreshToken: string;
6
+ scopes?: string;
7
+ tokenType: string;
8
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,29 @@
1
+ export interface HubspotUser {
2
+ token: string;
3
+ user: string;
4
+ hubDomain: string;
5
+ scopes: string[];
6
+ hubId: string;
7
+ appId: number;
8
+ expiresIn: number;
9
+ userId: number;
10
+ tokenType: string;
11
+ signedAccessToken: string;
12
+ }
13
+ export interface HubspotFetchUser {
14
+ hubId: string;
15
+ email: string;
16
+ userId: number;
17
+ }
18
+ export interface ContextHubspotUser {
19
+ token: string;
20
+ user: string;
21
+ hubDomain: string;
22
+ scopes: string[];
23
+ hubId: string;
24
+ appId: number;
25
+ expiresIn: number;
26
+ userId: number;
27
+ tokenType: string;
28
+ signedAccessToken: string;
29
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+ export declare const hubspotSignatureDataSchema: z.ZodObject<{
3
+ signature: z.ZodString;
4
+ timestamp: z.ZodCoercedNumber<unknown>;
5
+ domainName: z.ZodString;
6
+ path: z.ZodString;
7
+ httpMethod: z.ZodString;
8
+ body: z.ZodString;
9
+ qs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
10
+ }, z.core.$strip>;
11
+ export type HubspotSignatureData = z.infer<typeof hubspotSignatureDataSchema>;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hubspotSignatureDataSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ exports.hubspotSignatureDataSchema = zod_1.z.object({
6
+ signature: zod_1.z.string(),
7
+ timestamp: zod_1.z.coerce.number(),
8
+ domainName: zod_1.z.string(),
9
+ path: zod_1.z.string(),
10
+ httpMethod: zod_1.z.string(),
11
+ body: zod_1.z.string(),
12
+ qs: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(),
13
+ });
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+ export declare const contactSchema: z.ZodObject<{
3
+ createdAt: z.ZodDate;
4
+ id: z.ZodString;
5
+ properties: z.ZodRecord<z.ZodString, z.ZodAny>;
6
+ updatedAt: z.ZodOptional<z.ZodDate>;
7
+ }, z.core.$strip>;
8
+ export declare const contactWithPropsTypeSchema: z.ZodObject<{
9
+ id: z.ZodString;
10
+ propertiesWithTypes: z.ZodRecord<z.ZodString, z.ZodObject<{
11
+ type: z.ZodString;
12
+ label: z.ZodString;
13
+ value: z.ZodNullable<z.ZodString>;
14
+ }, z.core.$strip>>;
15
+ createdAt: z.ZodDate;
16
+ updatedAt: z.ZodOptional<z.ZodDate>;
17
+ }, z.core.$strip>;
18
+ export type ContactGet = z.infer<typeof contactSchema>;
19
+ export type ContactWithPropsTypeGet = z.infer<typeof contactWithPropsTypeSchema>;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.contactWithPropsTypeSchema = exports.contactSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ exports.contactSchema = zod_1.z.object({
6
+ createdAt: zod_1.z.date(),
7
+ id: zod_1.z.string(),
8
+ properties: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
9
+ updatedAt: zod_1.z.date().optional(),
10
+ });
11
+ exports.contactWithPropsTypeSchema = exports.contactSchema
12
+ .extend({
13
+ propertiesWithTypes: zod_1.z.record(zod_1.z.string(), zod_1.z.object({
14
+ type: zod_1.z.string(),
15
+ label: zod_1.z.string(),
16
+ value: zod_1.z.string().nullable()
17
+ })),
18
+ })
19
+ .omit({
20
+ properties: true
21
+ });
@@ -0,0 +1,2 @@
1
+ import { HubspotTypeConverter } from "../interfaces/hubspot-type-converter";
2
+ export declare const typeConverter: HubspotTypeConverter;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.typeConverter = void 0;
4
+ const types_1 = require("../constants/types");
5
+ exports.typeConverter = {
6
+ [types_1.hsDataTypes.date]: (data) => {
7
+ return (new Date(data)).setUTCHours(0, 0, 0, 0);
8
+ },
9
+ [types_1.hsDataTypes.datetime]: (data) => {
10
+ return new Date(data).getTime();
11
+ },
12
+ [types_1.hsDataTypes.number]: (data) => {
13
+ return (typeof data === "number") ? data : parseFloat(data);
14
+ }
15
+ };
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@dma-sdk/hubspot",
3
+ "version": "1.0.1",
4
+ "description": "DMA SDK – Hubspot service",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepare": "npm run build"
10
+ },
11
+ "dependencies": {
12
+ "@dma-sdk/utils": "^1.0.0",
13
+ "@hubspot/api-client": "^13.4.0",
14
+ "zod": "^4.3.5"
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.2.0"
18
+ }
19
+ }
@@ -0,0 +1,23 @@
1
+ export const hsDataTypes = {
2
+ string: "string",
3
+ number: "number",
4
+ boolean: "bool",
5
+ date: "date",
6
+ datetime: "datetime",
7
+ single_select: "enumeration",
8
+ multiple_select: "enumeration",
9
+ checkbox: "bool",
10
+ calculation: "calculation_equation",
11
+ phone_number: "phone_number",
12
+ user: "hubspot_user",
13
+ file: "file",
14
+ score: "score",
15
+ currency: "currency",
16
+ rich_text: "rich_text",
17
+ owner: "owner",
18
+ location: "location",
19
+ json: "json",
20
+ percentage: "number",
21
+ ip_address: "ip_address",
22
+ video: "video"
23
+ };
@@ -0,0 +1,307 @@
1
+ import { Client as HubspotClient } from '@hubspot/api-client'
2
+ import { HubspotUser } from './interfaces/user'
3
+ import { Configuration, Filter } from '@hubspot/api-client/lib/codegen/crm/objects'
4
+ import { SimplePublicObjectInputForCreate } from '@hubspot/api-client/lib/codegen/crm/objects/taxes/models/all'
5
+ import { SimplePublicObject } from '@hubspot/api-client/lib/codegen/crm/objects/tasks/models/all'
6
+ import { LabelsBetweenObjectPair } from '@hubspot/api-client/lib/codegen/crm/associations/v4'
7
+ import { TokenResponseIF } from '@hubspot/api-client/lib/codegen/oauth'
8
+ import { typeConverter } from './utils/type-converter'
9
+ import { HubspotTypeConverter } from './interfaces/hubspot-type-converter'
10
+ import { hsDataTypes } from './constants/types'
11
+ import { Signature } from '@hubspot/api-client'
12
+ import { TokenResponse } from './interfaces/token'
13
+ import { CollectionResponsePropertyNoPaging, PropertyCreate, PropertyGroup, PropertyGroupCreate } from '@hubspot/api-client/lib/codegen/crm/properties'
14
+ import { HubspotSignatureData } from './types/auth/hs-signature-data'
15
+ import { fetchApi } from '@dma-sdk/utils/src/fetch'
16
+
17
+ // Per ogni metodo che viene creato, è fondamentale che
18
+ // il client venga inizializzato con il token di accesso
19
+ // altrimenti non funzionerà correttamente.
20
+
21
+ export class HubspotService {
22
+ public static HS_BASE_URL = 'https://api.hubapi.com'
23
+ public static readonly hubspotObjectType = {
24
+ contacts: 'contacts',
25
+ notes: 'notes',
26
+ }
27
+ public static readonly hubspotPropertyTypes = hsDataTypes
28
+
29
+ private client: HubspotClient
30
+ private hsRedirectUriOauth: string
31
+ private hsClientId: string
32
+ private hsClientSecret: string
33
+ private typeConvert: HubspotTypeConverter
34
+
35
+ private apiMethods = {
36
+ getUserInfoByToken: '/oauth/v1/access-tokens',
37
+ refreshToken: '/oauth/v1/token',
38
+ createObjectProperties: '/crm/v3/properties/{objectType}/batch/create',
39
+ }
40
+
41
+
42
+ constructor(hsRedirectUriOauth: string, hsClientId: string, hsClientSecret: string) {
43
+ ;(this.hsRedirectUriOauth = hsRedirectUriOauth),
44
+ (this.hsClientId = hsClientId),
45
+ (this.hsClientSecret = hsClientSecret)
46
+ this.typeConvert = typeConverter
47
+
48
+ this.client = new HubspotClient()
49
+ }
50
+
51
+ async exchangeCodeForTokens(params: { code: string }): Promise<TokenResponseIF> {
52
+ return await this.client.oauth.tokensApi.create(
53
+ 'authorization_code',
54
+ params.code,
55
+ this.hsRedirectUriOauth,
56
+ this.hsClientId,
57
+ this.hsClientSecret
58
+ )
59
+ }
60
+
61
+ async getUserInfoByToken(accessToken: string): Promise<HubspotUser> {
62
+ const response = await this.client.apiRequest({
63
+ method: 'GET',
64
+ path: `${this.apiMethods.getUserInfoByToken}/${accessToken}`,
65
+ })
66
+
67
+ const jsonBody = await response.json()
68
+ const userAttributes = {
69
+ token: jsonBody.token,
70
+ user: jsonBody.user,
71
+ hubDomain: jsonBody.hub_domain,
72
+ scopes: jsonBody.scopes,
73
+ hubId: jsonBody.hub_id,
74
+ appId: jsonBody.app_id,
75
+ expiresIn: jsonBody.expires_in,
76
+ userId: jsonBody.user_id,
77
+ tokenType: jsonBody.token_type,
78
+ signedAccessToken: jsonBody.signed_access_token,
79
+ }
80
+
81
+ return userAttributes
82
+ }
83
+
84
+ async verifyFetchReqSignature(params: { version: string, data: HubspotSignatureData }): Promise<boolean> {
85
+ const queryString: string = (params.data.qs && Object.keys(params.data.qs).length > 0) ? `?${new URLSearchParams(params.data.qs).toString()}` : ''
86
+ const url: string = decodeURIComponent(`https://${params.data.domainName}${params.data.path}${queryString}`)
87
+
88
+ return Signature.isValid({
89
+ method: params.data.httpMethod,
90
+ signatureVersion: params.version,
91
+ signature: params.data.signature,
92
+ requestBody: params.data.body,
93
+ timestamp: params.data.timestamp,
94
+ url,
95
+ clientSecret: this.hsClientSecret,
96
+ })
97
+ }
98
+
99
+ async refreshAccessToken(refreshToken: string): Promise<TokenResponse> {
100
+ const token = await this.client.oauth.tokensApi.create(
101
+ 'refresh_token',
102
+ undefined,
103
+ this.hsRedirectUriOauth,
104
+ this.hsClientId,
105
+ this.hsClientSecret,
106
+ refreshToken
107
+ )
108
+
109
+ return {
110
+ accessToken: token.accessToken,
111
+ expiresIn: token.expiresIn,
112
+ refreshToken: token.refreshToken,
113
+ tokenType: token.tokenType,
114
+ }
115
+ }
116
+
117
+
118
+ async listObjectProperties(params: {
119
+ objectType: string,
120
+ types?: string[], // tipi di props da restituire
121
+ accessToken?: string
122
+ }): Promise<CollectionResponsePropertyNoPaging> {
123
+ const { objectType, types, accessToken } = params
124
+ if(accessToken) this.client.setAccessToken(accessToken)
125
+ const props = await this.client.crm.properties.coreApi.getAll(objectType);
126
+
127
+ if(!types) return props
128
+
129
+ return { results: props.results.filter((prop) => types.includes(prop.fieldType) || types.includes(prop.type)) }
130
+ }
131
+
132
+ async listObjects(
133
+ objectType: string,
134
+ filters: Filter[],
135
+ accessToken: string
136
+ ): Promise<SimplePublicObject[] | null> {
137
+ this.client.setAccessToken(accessToken)
138
+ const response = await this.client.crm.objects.searchApi.doSearch(objectType, {
139
+ filterGroups: [
140
+ {
141
+ filters: filters,
142
+ },
143
+ ],
144
+ })
145
+
146
+ return response.results || null
147
+ }
148
+
149
+ async createObject(
150
+ objectType: string,
151
+ properties: SimplePublicObjectInputForCreate,
152
+ accessToken: string
153
+ ): Promise<SimplePublicObject | { id: string }> {
154
+ this.client.setAccessToken(accessToken)
155
+ const response = await this.client.crm.objects.basicApi.create(objectType, properties)
156
+
157
+ return response
158
+ }
159
+
160
+ async getObject(
161
+ objectType: string,
162
+ objectId: string,
163
+ accessToken: string
164
+ ): Promise<SimplePublicObject> {
165
+ this.client.setAccessToken(accessToken)
166
+ return await this.client.crm.objects.basicApi.getById(
167
+ objectType,
168
+ objectId
169
+ );
170
+ }
171
+
172
+ async getObjectProps(
173
+ objectType: string,
174
+ objectId: string,
175
+ props: string[], // nome delle props da restituire
176
+ accessToken: string
177
+ ): Promise<SimplePublicObject> {
178
+ this.client.setAccessToken(accessToken)
179
+ return await this.client.crm.objects.basicApi.getById(
180
+ objectType,
181
+ objectId,
182
+ props
183
+ );
184
+ }
185
+
186
+ async getObjectWithPropsType(
187
+ objectType: string,
188
+ objectId: string,
189
+ accessToken: string,
190
+ types?: string[]
191
+ ): Promise<SimplePublicObject & { propertiesWithTypes: { [key: string]: { type: string, label: string, value: string | null } }}> {
192
+ this.client.setAccessToken(accessToken)
193
+ const props = await this.listObjectProperties({ objectType, types })
194
+ const data = await this.getObjectProps(
195
+ objectType,
196
+ objectId,
197
+ props.results.map((prop) => prop.name),
198
+ accessToken
199
+ );
200
+
201
+
202
+ let formattedProp: { [key: string]: { type: string, label: string, value: string | null } } = {}
203
+ props.results.forEach((property) => {
204
+ if(!data.properties[property.name]){
205
+ return
206
+ }
207
+
208
+ formattedProp[property.name] = {
209
+ type: property.fieldType,
210
+ label: property.label,
211
+ value: data.properties[property.name]
212
+ }
213
+ })
214
+
215
+ return {
216
+ ...data,
217
+ propertiesWithTypes: formattedProp
218
+ }
219
+ }
220
+
221
+ async updateObject(
222
+ objectType: string,
223
+ objectId: string,
224
+ properties: {
225
+ [key: string]: string;
226
+ },
227
+ accessToken: string,
228
+ options?: Configuration
229
+ ): Promise<SimplePublicObject> {
230
+ this.client.setAccessToken(accessToken)
231
+ const response = await this.client.crm.objects.basicApi.update(objectType, objectId, {
232
+ properties: {
233
+ ...properties
234
+ }
235
+ })
236
+
237
+ return response
238
+ }
239
+
240
+ async associateObjects(
241
+ objectTypeFirst: string,
242
+ objectTypeSecond: string,
243
+ objectIdFirst: string,
244
+ objectIdSecond: string,
245
+ accessToken: string
246
+ ): Promise<LabelsBetweenObjectPair> {
247
+ this.client.setAccessToken(accessToken)
248
+ const response = await this.client.crm.associations.v4.basicApi.create(
249
+ objectTypeFirst,
250
+ objectIdFirst,
251
+ objectTypeSecond,
252
+ objectIdSecond,
253
+ []
254
+ )
255
+
256
+ return response
257
+ }
258
+
259
+ async createObjectProperties(
260
+ objectType: string,
261
+ properties: PropertyCreate[],
262
+ accessToken: string
263
+ ) {
264
+ const options = {
265
+ method: 'POST',
266
+ headers: {
267
+ Authorization: `Bearer ${accessToken}`,
268
+ 'Content-Type': 'application/json'
269
+ },
270
+ body: JSON.stringify({
271
+ inputs: properties
272
+ })
273
+ };
274
+
275
+ const response = await this.fetchApi(this.apiMethods.createObjectProperties.replace('{objectType}', objectType), options)
276
+ return await response.json()
277
+ }
278
+
279
+ async createPropertyGroup(
280
+ objectType: string,
281
+ group: PropertyGroupCreate,
282
+ accessToken: string
283
+ ): Promise<PropertyGroup> {
284
+ this.client.setAccessToken(accessToken)
285
+ return await this.client.crm.properties.groupsApi.create(objectType, group);
286
+ }
287
+
288
+ public convertPropertyValues(properties: { [key: string]: any }, convertionMap: { [propertyName: string]: PropertyCreate }) {
289
+ let convertedProps: Record<string, any> = {}
290
+ Object.entries(properties).forEach(([key, value]) => {
291
+ if(value === undefined) return
292
+ const convertionFunc = this.typeConvert[convertionMap[key]?.type]
293
+ if(convertionFunc === undefined) {
294
+ convertedProps[key] = value
295
+ return
296
+ }
297
+ convertedProps[key] = convertionFunc(value)
298
+ })
299
+
300
+
301
+ return convertedProps
302
+ }
303
+
304
+ private async fetchApi(endpoint: string, options: {}, queryParams?: {}): Promise<any> {
305
+ return await fetchApi(HubspotService.HS_BASE_URL, endpoint, options, queryParams)
306
+ }
307
+ }
@@ -0,0 +1,4 @@
1
+
2
+ export interface HubspotTypeConverter {
3
+ [key: string]: (data: any) => any
4
+ };
@@ -0,0 +1,8 @@
1
+ export interface TokenResponse {
2
+ accessToken: string,
3
+ expiresIn: number,
4
+ hubId?: string,
5
+ refreshToken: string,
6
+ scopes?: string,
7
+ tokenType: string
8
+ }
@@ -0,0 +1,34 @@
1
+ export interface HubspotUser {
2
+ token: string;
3
+ user: string;
4
+ hubDomain: string;
5
+ scopes: string[];
6
+ hubId: string;
7
+ appId: number;
8
+ expiresIn: number;
9
+ userId: number;
10
+ tokenType: string;
11
+ signedAccessToken: string;
12
+ }
13
+
14
+
15
+ export interface HubspotFetchUser {
16
+ hubId: string,
17
+ email: string,
18
+ userId: number
19
+ }
20
+
21
+
22
+ export interface ContextHubspotUser {
23
+ token: string;
24
+ user: string;
25
+ hubDomain: string;
26
+ scopes: string[];
27
+ hubId: string;
28
+ appId: number;
29
+ expiresIn: number;
30
+ userId: number;
31
+ tokenType: string;
32
+ signedAccessToken: string;
33
+ }
34
+
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod'
2
+
3
+ export const hubspotSignatureDataSchema = z.object({
4
+ signature: z.string(),
5
+ timestamp: z.coerce.number(),
6
+ domainName: z.string(),
7
+ path: z.string(),
8
+ httpMethod: z.string(),
9
+ body: z.string(),
10
+ qs: z.record(z.string(), z.string()).optional(),
11
+ })
12
+
13
+ export type HubspotSignatureData = z.infer<typeof hubspotSignatureDataSchema>
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod'
2
+
3
+ export const contactSchema = z.object({
4
+ createdAt: z.date(),
5
+ id: z.string(),
6
+ properties: z.record(z.string(), z.any()),
7
+ updatedAt: z.date().optional(),
8
+ });
9
+
10
+
11
+ export const contactWithPropsTypeSchema = contactSchema
12
+ .extend({
13
+ propertiesWithTypes: z.record(z.string(), z.object({
14
+ type: z.string(),
15
+ label: z.string(),
16
+ value: z.string().nullable()
17
+ })),
18
+ })
19
+ .omit({
20
+ properties: true
21
+ });
22
+
23
+
24
+ export type ContactGet = z.infer<typeof contactSchema>
25
+ export type ContactWithPropsTypeGet = z.infer<typeof contactWithPropsTypeSchema>
@@ -0,0 +1,18 @@
1
+ import { hsDataTypes } from "../constants/types";
2
+ import { HubspotTypeConverter } from "../interfaces/hubspot-type-converter";
3
+ import { string } from "zod";
4
+
5
+ export const typeConverter: HubspotTypeConverter = {
6
+ [hsDataTypes.date]:
7
+ (data: string) => {
8
+ return (new Date(data)).setUTCHours(0, 0, 0, 0);
9
+ },
10
+ [hsDataTypes.datetime]:
11
+ (data: string) => {
12
+ return new Date(data).getTime();
13
+ },
14
+ [hsDataTypes.number]:
15
+ (data: string | number) => {
16
+ return (typeof data === "number") ? data : parseFloat(data);
17
+ }
18
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2019",
4
+ "module": "CommonJS",
5
+ "declaration": true,
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true
10
+ },
11
+ "include": [ "src" ]
12
+ }