@memberjunction/integration-connectors 0.0.1 → 5.9.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/README.md +107 -28
- package/dist/FileFeedConnector.d.ts +64 -0
- package/dist/FileFeedConnector.d.ts.map +1 -0
- package/dist/FileFeedConnector.js +190 -0
- package/dist/FileFeedConnector.js.map +1 -0
- package/dist/HubSpotConnector.d.ts +96 -0
- package/dist/HubSpotConnector.d.ts.map +1 -0
- package/dist/HubSpotConnector.js +446 -0
- package/dist/HubSpotConnector.js.map +1 -0
- package/dist/RelationalDBConnector.d.ts +101 -0
- package/dist/RelationalDBConnector.d.ts.map +1 -0
- package/dist/RelationalDBConnector.js +221 -0
- package/dist/RelationalDBConnector.js.map +1 -0
- package/dist/SalesforceConnector.d.ts +28 -0
- package/dist/SalesforceConnector.d.ts.map +1 -0
- package/dist/SalesforceConnector.js +73 -0
- package/dist/SalesforceConnector.js.map +1 -0
- package/dist/YourMembershipConnector.d.ts +143 -0
- package/dist/YourMembershipConnector.d.ts.map +1 -0
- package/dist/YourMembershipConnector.js +668 -0
- package/dist/YourMembershipConnector.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -7
package/README.md
CHANGED
|
@@ -1,45 +1,124 @@
|
|
|
1
1
|
# @memberjunction/integration-connectors
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Concrete integration connectors for the MemberJunction Integration Engine. This package provides four connectors that implement `BaseIntegrationConnector` from `@memberjunction/integration-engine`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Connectors
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### RelationalDBConnector (Base Class)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Abstract base class for connectors that read from SQL Server databases via the `mssql` package. Provides shared logic for connection management, object/field discovery, and batched record fetching with watermark support.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
**Configuration JSON:**
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"server": "your-sql-server",
|
|
15
|
+
"database": "YourDatabase",
|
|
16
|
+
"user": "username",
|
|
17
|
+
"password": "password"
|
|
18
|
+
}
|
|
19
|
+
```
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
**Shared capabilities:**
|
|
22
|
+
- `TestConnection` — runs `SELECT @@VERSION` to verify connectivity
|
|
23
|
+
- `DiscoverObjects` — queries `INFORMATION_SCHEMA.TABLES` for all base tables
|
|
24
|
+
- `DiscoverFields` — queries `INFORMATION_SCHEMA.COLUMNS` for a given table
|
|
25
|
+
- `FetchChangesFromTable` — parameterized incremental fetch with watermark filtering and batch limiting
|
|
26
|
+
- Connection pool caching per server+database pair
|
|
17
27
|
|
|
18
|
-
|
|
28
|
+
### HubSpotConnector
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
Reads from MockHubSpot database tables: `hs_Contacts`, `hs_Companies`, `hs_Deals`, `hs_Owners`.
|
|
21
31
|
|
|
22
|
-
|
|
32
|
+
**Registration:** `@RegisterClass(BaseIntegrationConnector, 'HubSpotConnector')`
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
**Default field mappings:**
|
|
35
|
+
| Source Object | Target Entity | Mappings |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `hs_Contacts` | Contacts | email→Email (key), firstname→FirstName, lastname→LastName, phone→Phone, company→CompanyName, lifecyclestage→Status |
|
|
38
|
+
| `hs_Companies` | Companies | name→Name (key), domain→Website, industry→Industry, city→City, state→State |
|
|
39
|
+
| `hs_Deals` | — | No default mappings |
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
**To swap for real HubSpot API:** Replace `RelationalDBConnector` with direct HTTP calls to the HubSpot V3 API. The `FetchChanges` method would use the HubSpot Search API with `lastmodifieddate` filters, and `Configuration` would store an API key or OAuth tokens instead of database credentials.
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
43
|
+
### SalesforceConnector
|
|
36
44
|
|
|
37
|
-
|
|
45
|
+
Reads from MockSalesforce database tables: `sf_Contact`, `sf_Account`, `sf_Opportunity`, `sf_User`.
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
47
|
+
**Registration:** `@RegisterClass(BaseIntegrationConnector, 'SalesforceConnector')`
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
**Default field mappings:**
|
|
50
|
+
| Source Object | Target Entity | Mappings |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `sf_Contact` | Contacts | Email→Email (key), FirstName→FirstName, LastName→LastName, Phone→Phone, Title→Title, Department→Department |
|
|
53
|
+
| `sf_Account` | Companies | Name→Name (key), Industry→Industry, BillingCity→City, BillingState→State, Phone→Phone |
|
|
44
54
|
|
|
45
|
-
**
|
|
55
|
+
**To swap for real Salesforce API:** Replace `RelationalDBConnector` with `jsforce` or direct REST calls to the Salesforce SOQL API. Use `SystemModstamp` for watermarks and the Salesforce Bulk API for large datasets.
|
|
56
|
+
|
|
57
|
+
### YourMembershipConnector
|
|
58
|
+
|
|
59
|
+
Reads from MockYourMembership database tables: `ym_Members`, `ym_Events`, `ym_EventRegistrations`, `ym_Chapters`, `ym_MembershipLevels`.
|
|
60
|
+
|
|
61
|
+
**Registration:** `@RegisterClass(BaseIntegrationConnector, 'YourMembershipConnector')`
|
|
62
|
+
|
|
63
|
+
**Default field mappings:**
|
|
64
|
+
| Source Object | Target Entity | Mappings |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `ym_Members` | Contacts | email→Email (key), first_name→FirstName, last_name→LastName, phone→Phone, status→Status |
|
|
67
|
+
|
|
68
|
+
### FileFeedConnector
|
|
69
|
+
|
|
70
|
+
Reads data from local CSV files. Always performs a full load (no incremental sync support).
|
|
71
|
+
|
|
72
|
+
**Registration:** `@RegisterClass(BaseIntegrationConnector, 'FileFeedConnector')`
|
|
73
|
+
|
|
74
|
+
**Configuration JSON:**
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"storagePath": "/absolute/path/to/file.csv",
|
|
78
|
+
"fileType": "csv"
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Capabilities:**
|
|
83
|
+
- `TestConnection` — checks that the file exists on disk
|
|
84
|
+
- `DiscoverObjects` — returns the file name as the single available object
|
|
85
|
+
- `DiscoverFields` — parses the CSV header row
|
|
86
|
+
- `FetchChanges` — parses all data rows into `ExternalRecord[]` (watermark is ignored)
|
|
87
|
+
|
|
88
|
+
**CSV parsing rules:**
|
|
89
|
+
- First row is treated as the header
|
|
90
|
+
- Supports quoted fields with commas inside
|
|
91
|
+
- Supports escaped quotes (`""`) inside quoted fields
|
|
92
|
+
- Empty values are mapped to `null`
|
|
93
|
+
|
|
94
|
+
## Building
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
cd packages/Integration/connectors
|
|
98
|
+
npm run build
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Testing
|
|
102
|
+
|
|
103
|
+
Tests are integration tests that connect to the mock SQL Server databases.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
cd packages/Integration/connectors
|
|
107
|
+
npm run test # single run
|
|
108
|
+
npm run test:watch # watch mode
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Architecture
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
src/
|
|
115
|
+
index.ts # Package exports
|
|
116
|
+
RelationalDBConnector.ts # Base class for SQL-based connectors
|
|
117
|
+
HubSpotConnector.ts # HubSpot mock connector
|
|
118
|
+
SalesforceConnector.ts # Salesforce mock connector
|
|
119
|
+
YourMembershipConnector.ts # YourMembership mock connector
|
|
120
|
+
FileFeedConnector.ts # CSV file connector
|
|
121
|
+
__tests__/ # Integration tests
|
|
122
|
+
test-fixtures/
|
|
123
|
+
sample-contacts.csv # 100-row sample for FileFeed testing
|
|
124
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { UserInfo } from '@memberjunction/core';
|
|
2
|
+
import type { MJCompanyIntegrationEntity } from '@memberjunction/core-entities';
|
|
3
|
+
import { BaseIntegrationConnector, type ConnectionTestResult, type ExternalObjectSchema, type ExternalFieldSchema, type FetchContext, type FetchBatchResult } from '@memberjunction/integration-engine';
|
|
4
|
+
/**
|
|
5
|
+
* Connector for file-based data feeds (CSV).
|
|
6
|
+
* Reads data from local CSV files. Always performs a full load (no incremental sync).
|
|
7
|
+
* Supports quoted fields with commas inside and empty values mapped to null.
|
|
8
|
+
*/
|
|
9
|
+
export declare class FileFeedConnector extends BaseIntegrationConnector {
|
|
10
|
+
/**
|
|
11
|
+
* Tests connectivity by checking that the configured file exists on disk.
|
|
12
|
+
* @param companyIntegration - The entity with Configuration JSON containing storagePath
|
|
13
|
+
* @param _contextUser - User context for authorization
|
|
14
|
+
* @returns Success if the file exists, failure otherwise
|
|
15
|
+
*/
|
|
16
|
+
TestConnection(companyIntegration: MJCompanyIntegrationEntity, _contextUser: UserInfo): Promise<ConnectionTestResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Discovers available objects by returning the file name as the single object.
|
|
19
|
+
* @param companyIntegration - The entity with Configuration JSON
|
|
20
|
+
* @param _contextUser - User context for authorization
|
|
21
|
+
* @returns Array with one entry for the configured file
|
|
22
|
+
*/
|
|
23
|
+
DiscoverObjects(companyIntegration: MJCompanyIntegrationEntity, _contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Discovers fields by parsing the CSV header row.
|
|
26
|
+
* @param companyIntegration - The entity with Configuration JSON
|
|
27
|
+
* @param _objectName - Object name (file name), not used since we only have one file
|
|
28
|
+
* @param _contextUser - User context for authorization
|
|
29
|
+
* @returns Array of field schemas from the CSV header
|
|
30
|
+
*/
|
|
31
|
+
DiscoverFields(companyIntegration: MJCompanyIntegrationEntity, _objectName: string, _contextUser: UserInfo): Promise<ExternalFieldSchema[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Fetches all records from the CSV file as a full load.
|
|
34
|
+
* CSV files do not support incremental sync — watermark is ignored.
|
|
35
|
+
* @param ctx - Fetch context (watermark is always ignored for file feeds)
|
|
36
|
+
* @returns All records from the CSV file in a single batch
|
|
37
|
+
*/
|
|
38
|
+
FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Parses the Configuration JSON to extract file feed settings.
|
|
41
|
+
* @param companyIntegration - The entity containing Configuration JSON
|
|
42
|
+
* @returns Parsed file feed configuration
|
|
43
|
+
*/
|
|
44
|
+
private parseConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Parses an array of CSV data lines into ExternalRecord objects.
|
|
47
|
+
* @param lines - CSV data lines (excluding header)
|
|
48
|
+
* @param headers - Column header names
|
|
49
|
+
* @param objectName - Object type name for the records
|
|
50
|
+
* @returns Array of ExternalRecord objects
|
|
51
|
+
*/
|
|
52
|
+
private parseDataLines;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Parses a single CSV line handling quoted fields with commas.
|
|
56
|
+
* Supports:
|
|
57
|
+
* - Unquoted fields separated by commas
|
|
58
|
+
* - Quoted fields that may contain commas
|
|
59
|
+
* - Escaped quotes ("") inside quoted fields
|
|
60
|
+
* @param line - A single CSV line to parse
|
|
61
|
+
* @returns Array of field values
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseCsvLine(line: string): string[];
|
|
64
|
+
//# sourceMappingURL=FileFeedConnector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileFeedConnector.d.ts","sourceRoot":"","sources":["../src/FileFeedConnector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EACH,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EAExB,MAAM,oCAAoC,CAAC;AAY5C;;;;GAIG;AACH,qBACa,iBAAkB,SAAQ,wBAAwB;IAC3D;;;;;OAKG;IACU,cAAc,CACvB,kBAAkB,EAAE,0BAA0B,EAC9C,YAAY,EAAE,QAAQ,GACvB,OAAO,CAAC,oBAAoB,CAAC;IAahC;;;;;OAKG;IACU,eAAe,CACxB,kBAAkB,EAAE,0BAA0B,EAC9C,YAAY,EAAE,QAAQ,GACvB,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAalC;;;;;;OAMG;IACU,cAAc,CACvB,kBAAkB,EAAE,0BAA0B,EAC9C,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,QAAQ,GACvB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAmBjC;;;;;OAKG;IACU,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAcvE;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAiBnB;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;CAsBzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAqCnD"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
8
|
+
import { BaseIntegrationConnector, } from '@memberjunction/integration-engine';
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
/**
|
|
12
|
+
* Connector for file-based data feeds (CSV).
|
|
13
|
+
* Reads data from local CSV files. Always performs a full load (no incremental sync).
|
|
14
|
+
* Supports quoted fields with commas inside and empty values mapped to null.
|
|
15
|
+
*/
|
|
16
|
+
let FileFeedConnector = class FileFeedConnector extends BaseIntegrationConnector {
|
|
17
|
+
/**
|
|
18
|
+
* Tests connectivity by checking that the configured file exists on disk.
|
|
19
|
+
* @param companyIntegration - The entity with Configuration JSON containing storagePath
|
|
20
|
+
* @param _contextUser - User context for authorization
|
|
21
|
+
* @returns Success if the file exists, failure otherwise
|
|
22
|
+
*/
|
|
23
|
+
async TestConnection(companyIntegration, _contextUser) {
|
|
24
|
+
try {
|
|
25
|
+
const config = this.parseConfig(companyIntegration);
|
|
26
|
+
if (!fs.existsSync(config.StoragePath)) {
|
|
27
|
+
return { Success: false, Message: `File not found: ${config.StoragePath}` };
|
|
28
|
+
}
|
|
29
|
+
return { Success: true, Message: `File exists: ${config.StoragePath}` };
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
33
|
+
return { Success: false, Message: `Configuration error: ${message}` };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Discovers available objects by returning the file name as the single object.
|
|
38
|
+
* @param companyIntegration - The entity with Configuration JSON
|
|
39
|
+
* @param _contextUser - User context for authorization
|
|
40
|
+
* @returns Array with one entry for the configured file
|
|
41
|
+
*/
|
|
42
|
+
async DiscoverObjects(companyIntegration, _contextUser) {
|
|
43
|
+
const config = this.parseConfig(companyIntegration);
|
|
44
|
+
const fileName = path.basename(config.StoragePath);
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
Name: fileName,
|
|
48
|
+
Label: fileName,
|
|
49
|
+
SupportsIncrementalSync: false,
|
|
50
|
+
SupportsWrite: false,
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Discovers fields by parsing the CSV header row.
|
|
56
|
+
* @param companyIntegration - The entity with Configuration JSON
|
|
57
|
+
* @param _objectName - Object name (file name), not used since we only have one file
|
|
58
|
+
* @param _contextUser - User context for authorization
|
|
59
|
+
* @returns Array of field schemas from the CSV header
|
|
60
|
+
*/
|
|
61
|
+
async DiscoverFields(companyIntegration, _objectName, _contextUser) {
|
|
62
|
+
const config = this.parseConfig(companyIntegration);
|
|
63
|
+
const content = fs.readFileSync(config.StoragePath, 'utf-8');
|
|
64
|
+
const firstLine = content.split('\n')[0]?.trim();
|
|
65
|
+
if (!firstLine) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
const headers = parseCsvLine(firstLine);
|
|
69
|
+
return headers.map((header) => ({
|
|
70
|
+
Name: header,
|
|
71
|
+
Label: header,
|
|
72
|
+
DataType: 'string',
|
|
73
|
+
IsRequired: false,
|
|
74
|
+
IsUniqueKey: false,
|
|
75
|
+
IsReadOnly: true,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Fetches all records from the CSV file as a full load.
|
|
80
|
+
* CSV files do not support incremental sync — watermark is ignored.
|
|
81
|
+
* @param ctx - Fetch context (watermark is always ignored for file feeds)
|
|
82
|
+
* @returns All records from the CSV file in a single batch
|
|
83
|
+
*/
|
|
84
|
+
async FetchChanges(ctx) {
|
|
85
|
+
const config = this.parseConfig(ctx.CompanyIntegration);
|
|
86
|
+
const content = fs.readFileSync(config.StoragePath, 'utf-8');
|
|
87
|
+
const lines = content.split('\n').filter((line) => line.trim().length > 0);
|
|
88
|
+
if (lines.length < 2) {
|
|
89
|
+
return { Records: [], HasMore: false };
|
|
90
|
+
}
|
|
91
|
+
const headers = parseCsvLine(lines[0]);
|
|
92
|
+
const records = this.parseDataLines(lines.slice(1), headers, ctx.ObjectName);
|
|
93
|
+
return { Records: records, HasMore: false };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Parses the Configuration JSON to extract file feed settings.
|
|
97
|
+
* @param companyIntegration - The entity containing Configuration JSON
|
|
98
|
+
* @returns Parsed file feed configuration
|
|
99
|
+
*/
|
|
100
|
+
parseConfig(companyIntegration) {
|
|
101
|
+
const configJson = companyIntegration.Get('Configuration');
|
|
102
|
+
if (!configJson) {
|
|
103
|
+
throw new Error('CompanyIntegration.Configuration is null or empty');
|
|
104
|
+
}
|
|
105
|
+
const parsed = JSON.parse(configJson);
|
|
106
|
+
const storagePath = parsed['storagePath'];
|
|
107
|
+
const fileType = parsed['fileType'] ?? 'csv';
|
|
108
|
+
if (!storagePath) {
|
|
109
|
+
throw new Error('Configuration JSON must contain a storagePath field');
|
|
110
|
+
}
|
|
111
|
+
return { StoragePath: storagePath, FileType: fileType };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Parses an array of CSV data lines into ExternalRecord objects.
|
|
115
|
+
* @param lines - CSV data lines (excluding header)
|
|
116
|
+
* @param headers - Column header names
|
|
117
|
+
* @param objectName - Object type name for the records
|
|
118
|
+
* @returns Array of ExternalRecord objects
|
|
119
|
+
*/
|
|
120
|
+
parseDataLines(lines, headers, objectName) {
|
|
121
|
+
return lines.map((line, index) => {
|
|
122
|
+
const values = parseCsvLine(line);
|
|
123
|
+
const fields = {};
|
|
124
|
+
for (let i = 0; i < headers.length; i++) {
|
|
125
|
+
const rawValue = i < values.length ? values[i] : '';
|
|
126
|
+
fields[headers[i]] = rawValue === '' ? null : rawValue;
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
ExternalID: String(index + 1),
|
|
130
|
+
ObjectType: objectName,
|
|
131
|
+
Fields: fields,
|
|
132
|
+
IsDeleted: false,
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
FileFeedConnector = __decorate([
|
|
138
|
+
RegisterClass(BaseIntegrationConnector, 'FileFeedConnector')
|
|
139
|
+
], FileFeedConnector);
|
|
140
|
+
export { FileFeedConnector };
|
|
141
|
+
/**
|
|
142
|
+
* Parses a single CSV line handling quoted fields with commas.
|
|
143
|
+
* Supports:
|
|
144
|
+
* - Unquoted fields separated by commas
|
|
145
|
+
* - Quoted fields that may contain commas
|
|
146
|
+
* - Escaped quotes ("") inside quoted fields
|
|
147
|
+
* @param line - A single CSV line to parse
|
|
148
|
+
* @returns Array of field values
|
|
149
|
+
*/
|
|
150
|
+
export function parseCsvLine(line) {
|
|
151
|
+
const fields = [];
|
|
152
|
+
let current = '';
|
|
153
|
+
let inQuotes = false;
|
|
154
|
+
let i = 0;
|
|
155
|
+
while (i < line.length) {
|
|
156
|
+
const char = line[i];
|
|
157
|
+
if (inQuotes) {
|
|
158
|
+
if (char === '"' && i + 1 < line.length && line[i + 1] === '"') {
|
|
159
|
+
current += '"';
|
|
160
|
+
i += 2;
|
|
161
|
+
}
|
|
162
|
+
else if (char === '"') {
|
|
163
|
+
inQuotes = false;
|
|
164
|
+
i++;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
current += char;
|
|
168
|
+
i++;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
if (char === '"') {
|
|
173
|
+
inQuotes = true;
|
|
174
|
+
i++;
|
|
175
|
+
}
|
|
176
|
+
else if (char === ',') {
|
|
177
|
+
fields.push(current.trim());
|
|
178
|
+
current = '';
|
|
179
|
+
i++;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
current += char;
|
|
183
|
+
i++;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
fields.push(current.trim());
|
|
188
|
+
return fields;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=FileFeedConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileFeedConnector.js","sourceRoot":"","sources":["../src/FileFeedConnector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type UserInfo } from '@memberjunction/core';
|
|
2
|
+
import type { MJCompanyIntegrationEntity, MJIntegrationObjectEntity } from '@memberjunction/core-entities';
|
|
3
|
+
import { BaseRESTIntegrationConnector, type RESTAuthContext, type RESTResponse, type PaginationState, type PaginationType, type ConnectionTestResult, type DefaultFieldMapping, type DefaultIntegrationConfig } from '@memberjunction/integration-engine';
|
|
4
|
+
/** HubSpot property definition (from /crm/v3/properties/{objectType}) */
|
|
5
|
+
interface HubSpotPropertyDef {
|
|
6
|
+
name: string;
|
|
7
|
+
label: string;
|
|
8
|
+
type: string;
|
|
9
|
+
fieldType: string;
|
|
10
|
+
groupName: string;
|
|
11
|
+
description: string;
|
|
12
|
+
hasUniqueValue: boolean;
|
|
13
|
+
calculated: boolean;
|
|
14
|
+
externalOptions: boolean;
|
|
15
|
+
modificationMetadata?: {
|
|
16
|
+
readOnlyValue: boolean;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Connector for HubSpot CRM via the HubSpot REST API v3.
|
|
21
|
+
*
|
|
22
|
+
* Extends BaseRESTIntegrationConnector to leverage metadata-driven object/field
|
|
23
|
+
* discovery from IntegrationEngineBase cache and generic pagination handling.
|
|
24
|
+
*
|
|
25
|
+
* Uses Bearer token authentication (private app key or OAuth access token).
|
|
26
|
+
* Supports cursor-based pagination and automatic response flattening.
|
|
27
|
+
*
|
|
28
|
+
* DATA SAFETY: This connector is READ-ONLY. It never writes data back to HubSpot.
|
|
29
|
+
*/
|
|
30
|
+
export declare class HubSpotConnector extends BaseRESTIntegrationConnector {
|
|
31
|
+
/** Timestamp of the last API request, used for throttling */
|
|
32
|
+
private lastRequestTime;
|
|
33
|
+
protected Authenticate(companyIntegration: MJCompanyIntegrationEntity): Promise<RESTAuthContext>;
|
|
34
|
+
protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
|
|
35
|
+
protected MakeHTTPRequest(_auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, _body?: unknown): Promise<RESTResponse>;
|
|
36
|
+
protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
|
|
37
|
+
protected ExtractPaginationInfo(rawBody: unknown, _paginationType: PaginationType, _currentPage: number, _currentOffset: number, _pageSize: number): PaginationState;
|
|
38
|
+
protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity): string;
|
|
39
|
+
/**
|
|
40
|
+
* Overrides base pagination URL building to use HubSpot's parameter names.
|
|
41
|
+
* HubSpot uses `after` for cursor pagination (not `cursor`), and needs
|
|
42
|
+
* `limit` instead of `pageSize`. Also appends `properties` query param.
|
|
43
|
+
*/
|
|
44
|
+
protected BuildPaginatedURL(basePath: string, obj: MJIntegrationObjectEntity, _page: number, _offset: number, cursor?: string): string;
|
|
45
|
+
/** Tests connectivity by authenticating and fetching 1 contact. */
|
|
46
|
+
TestConnection(companyIntegration: MJCompanyIntegrationEntity, _contextUser: UserInfo): Promise<ConnectionTestResult>;
|
|
47
|
+
GetDefaultFieldMappings(objectName: string, _entityName: string): DefaultFieldMapping[];
|
|
48
|
+
GetDefaultConfiguration(): DefaultIntegrationConfig;
|
|
49
|
+
/** Converts a HubSpot property definition to ExternalFieldSchema format */
|
|
50
|
+
MapPropertyToField(prop: HubSpotPropertyDef): {
|
|
51
|
+
Name: string;
|
|
52
|
+
Label: string;
|
|
53
|
+
DataType: string;
|
|
54
|
+
IsRequired: boolean;
|
|
55
|
+
IsUniqueKey: boolean;
|
|
56
|
+
IsReadOnly: boolean;
|
|
57
|
+
};
|
|
58
|
+
/** Maps HubSpot type + fieldType to a simplified data type string */
|
|
59
|
+
MapHubSpotType(type: string, fieldType: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Reads credentials from CompanyIntegration.CredentialID -> Credential.Values JSON,
|
|
62
|
+
* or falls back to CompanyIntegration Configuration JSON for backwards compat.
|
|
63
|
+
*/
|
|
64
|
+
private LoadCredentials;
|
|
65
|
+
/** Loads credentials from a Credential entity by ID. */
|
|
66
|
+
private LoadFromCredentialEntity;
|
|
67
|
+
/** Parses a JSON string to extract HubSpot credentials. Returns null if no token found. */
|
|
68
|
+
private ParseCredentialJson;
|
|
69
|
+
/**
|
|
70
|
+
* Flattens a HubSpot CRM record from the nested format:
|
|
71
|
+
* { id, properties: { field1, field2 }, createdAt, updatedAt, archived }
|
|
72
|
+
* into a flat record with all properties at the top level,
|
|
73
|
+
* plus system fields (hs_object_id, createdAt, updatedAt, archived).
|
|
74
|
+
*/
|
|
75
|
+
private FlattenHubSpotRecord;
|
|
76
|
+
/**
|
|
77
|
+
* Extracts the HubSpot object name from an API path.
|
|
78
|
+
* E.g., "/crm/v3/objects/contacts" -> "contacts"
|
|
79
|
+
*/
|
|
80
|
+
private ExtractObjectNameFromPath;
|
|
81
|
+
/**
|
|
82
|
+
* Builds the `properties` query parameter for a HubSpot object type.
|
|
83
|
+
* Returns empty string if no properties are configured for the object.
|
|
84
|
+
*/
|
|
85
|
+
private BuildPropertiesParam;
|
|
86
|
+
/** Executes an HTTP request with a timeout. */
|
|
87
|
+
private FetchWithTimeout;
|
|
88
|
+
/** Calculates retry delay from Retry-After header or exponential backoff. */
|
|
89
|
+
private CalculateRetryDelay;
|
|
90
|
+
/** Converts a fetch Response + parsed body into a RESTResponse. */
|
|
91
|
+
private BuildRESTResponse;
|
|
92
|
+
/** Returns a promise that resolves after the specified number of milliseconds. */
|
|
93
|
+
private Sleep;
|
|
94
|
+
}
|
|
95
|
+
export {};
|
|
96
|
+
//# sourceMappingURL=HubSpotConnector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HubSpotConnector.d.ts","sourceRoot":"","sources":["../src/HubSpotConnector.ts"],"names":[],"mappings":"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"}
|