@memberjunction/connector-propfuel 1.0.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/dist/PropFuelConnector.d.ts +152 -0
- package/dist/PropFuelConnector.js +450 -0
- package/dist/PropFuelConnector.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { type UserInfo } from '@memberjunction/core';
|
|
2
|
+
import type { MJCompanyIntegrationEntity } from '@memberjunction/core-entities';
|
|
3
|
+
import { BaseRESTIntegrationConnector, type RESTAuthContext, type RESTResponse, type PaginationState, type PaginationType, type ConnectionTestResult, type ExternalObjectSchema, type ExternalFieldSchema, type FetchContext, type FetchBatchResult } from '@memberjunction/integration-engine';
|
|
4
|
+
/**
|
|
5
|
+
* PropFuel data-export file-feed connector.
|
|
6
|
+
*
|
|
7
|
+
* PropFuel exposes a per-tenant hourly data-export feed as a set of JSON files. There is NO
|
|
8
|
+
* public OpenAPI spec and NO describe/introspection endpoint, so this connector encodes the
|
|
9
|
+
* DISCOVERY MECHANISM, never a baked answer:
|
|
10
|
+
*
|
|
11
|
+
* - {@link DiscoverObjects} calls `GET /dataexport/{AccountID}/list` and derives each object
|
|
12
|
+
* from the `[microtime]-[datatype].json` filename suffix. The concrete data-type set is
|
|
13
|
+
* whatever the live listing actually contains — never a static `PROPFUEL_STREAMS` array.
|
|
14
|
+
* - {@link DiscoverFields} lists the files for a data type, downloads ONE sample file, and
|
|
15
|
+
* streams its records through the base {@link DiscoverFieldsViaStream} helper to derive the
|
|
16
|
+
* field set + data-informed soft-PK from real values — never a frozen `STREAM_FIELDS` catalog.
|
|
17
|
+
*
|
|
18
|
+
* Incremental sync uses a SYNTHETIC, FILE-LEVEL, CLIENT-SIDE cursor `__file_microtime`: the feed
|
|
19
|
+
* is append-only and chronologically sortable by the filename microtime prefix, so the connector
|
|
20
|
+
* resumes by tracking the max microtime seen across processed files (WatermarkService). The vendor
|
|
21
|
+
* `ack` endpoint is operational state and is NOT used as the read-only incremental cursor.
|
|
22
|
+
*
|
|
23
|
+
* READ-ONLY: the data-export feed creates/updates/deletes no source records, so all write
|
|
24
|
+
* capability getters stay false and no ack/POST/delete path is exercised.
|
|
25
|
+
*
|
|
26
|
+
* AccountID and the bearer Token both come from the credential (or Configuration JSON) — the
|
|
27
|
+
* AccountID is per-tenant config and is NEVER hardcoded.
|
|
28
|
+
*/
|
|
29
|
+
export declare class PropFuelConnector extends BaseRESTIntegrationConnector {
|
|
30
|
+
/** Verbatim three-way invariant name: ClassName / IntegrationName getter / MJ: Integrations.Name. */
|
|
31
|
+
get IntegrationName(): string;
|
|
32
|
+
/**
|
|
33
|
+
* KEYSET / no-watermark resume hint: every PropFuel object is a data-export file stream ordered
|
|
34
|
+
* by the filename microtime prefix, so 'microtime' is the stable, monotonic ordering key for all
|
|
35
|
+
* of them (the docs-provable feed object and every runtime-discovered data-type stream alike).
|
|
36
|
+
*/
|
|
37
|
+
StableOrderingKey(_objectName: string): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* The file-feed microtime IS a reliable monotonic high-water mark: PropFuel emits export files in
|
|
40
|
+
* ascending microtime order, and an updated record re-appears in a NEW (higher-microtime) file. So
|
|
41
|
+
* `NewWatermarkValue` (= max microtime seen) is the true global max — the engine can NARROW the next
|
|
42
|
+
* incremental to `microtime > watermark` instead of clearing the keyset position and re-scanning the
|
|
43
|
+
* whole stream every run. Without this, a clean sync's keyset-resume marker is cleared and every
|
|
44
|
+
* incremental re-walks the entire object (correct via content-hash, but wasteful at scale).
|
|
45
|
+
*/
|
|
46
|
+
get MonotonicWatermark(): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Resolves the bearer Token + per-tenant AccountID from the linked Credential entity, falling
|
|
49
|
+
* back to the CompanyIntegration.Configuration JSON. The credential bytes never leave this scope.
|
|
50
|
+
*/
|
|
51
|
+
protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<PropFuelAuthContext>;
|
|
52
|
+
/** Static Bearer header. No crypto/signing is required for a static token, so no auth-helper is used. */
|
|
53
|
+
protected BuildHeaders(auth: PropFuelAuthContext): Record<string, string>;
|
|
54
|
+
/** Base URL for the data-export feed, tenant-scoped by the resolved AccountID. */
|
|
55
|
+
protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: PropFuelAuthContext): string;
|
|
56
|
+
/**
|
|
57
|
+
* Executes an HTTP request via fetch. Parses JSON when the response is JSON, else returns the
|
|
58
|
+
* raw text body. The concrete connector owns the transport seam so tests can override it.
|
|
59
|
+
*/
|
|
60
|
+
protected MakeHTTPRequest(_auth: PropFuelAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
|
|
61
|
+
/**
|
|
62
|
+
* The download response for a data-export file is a JSON array of records (no envelope). When a
|
|
63
|
+
* vendor wraps records under a key, that key is honored; otherwise a root array / single object
|
|
64
|
+
* is normalized to an array.
|
|
65
|
+
*/
|
|
66
|
+
protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
|
|
67
|
+
/**
|
|
68
|
+
* The file feed has no in-file pagination — each downloaded file is read whole. File-level
|
|
69
|
+
* paging across the listing is handled by {@link FetchChanges} via the microtime cursor.
|
|
70
|
+
*/
|
|
71
|
+
protected ExtractPaginationInfo(_rawBody: unknown, _paginationType: PaginationType, _currentPage: number, _currentOffset: number, _pageSize: number): PaginationState;
|
|
72
|
+
/** Tests connectivity by listing the tenant's available data-export files. */
|
|
73
|
+
TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
|
|
74
|
+
/**
|
|
75
|
+
* Discovers objects by listing the live data-export files and deriving the distinct data-type
|
|
76
|
+
* suffix from each `[microtime]-[datatype].json` filename. The returned object set is exactly
|
|
77
|
+
* what the source currently exposes — the frozen contract's `propfuel_data_export_file` is the
|
|
78
|
+
* VERIFICATION FLOOR (discovery must surface at least the feed), never a ceiling.
|
|
79
|
+
*/
|
|
80
|
+
DiscoverObjects(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ExternalObjectSchema[]>;
|
|
81
|
+
/**
|
|
82
|
+
* Discovers fields by downloading ONE sample file for the data type and streaming its records
|
|
83
|
+
* through the base data-informed discovery helper. The field set + soft-PK come from the real
|
|
84
|
+
* values the source returns — never a static `STREAM_FIELDS` map. READ-ONLY: the sample file is
|
|
85
|
+
* fetched and parsed, never acked.
|
|
86
|
+
*/
|
|
87
|
+
DiscoverFields(companyIntegration: MJCompanyIntegrationEntity, objectName: string, contextUser: UserInfo): Promise<ExternalFieldSchema[]>;
|
|
88
|
+
/**
|
|
89
|
+
* Fetches records for a data type using the synthetic `__file_microtime` cursor.
|
|
90
|
+
*
|
|
91
|
+
* Mechanism: load the stored cursor (max microtime processed), list the live files, select the
|
|
92
|
+
* files of this data type whose microtime exceeds the cursor (ascending microtime order), and
|
|
93
|
+
* download each, emitting every record with full-record pass-through. The max microtime seen
|
|
94
|
+
* across the processed files is persisted as the new cursor — ONLY on full-batch success, so a
|
|
95
|
+
* partial failure leaves the cursor unchanged and the next run resumes from the same point.
|
|
96
|
+
*/
|
|
97
|
+
FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
|
|
98
|
+
/** Issues the list call. Path is relative; BuildHeaders + GetBaseURL supply auth + host. */
|
|
99
|
+
private ListFiles;
|
|
100
|
+
/** Downloads one file by name and returns its records (read-only; never acked). */
|
|
101
|
+
private DownloadFileRecords;
|
|
102
|
+
/**
|
|
103
|
+
* Parses the list endpoint body into an array of file names. The listing may be a bare array of
|
|
104
|
+
* strings, an array of objects with a name/file/filename key, or an object wrapping such an array.
|
|
105
|
+
*/
|
|
106
|
+
private ParseFileListing;
|
|
107
|
+
/** Finds the first array-valued property of an object (the file listing under a wrapper key). */
|
|
108
|
+
private FindArrayInObject;
|
|
109
|
+
/**
|
|
110
|
+
* Builds a stable record identity. The per-record schema is undocumented (no provable PK), so
|
|
111
|
+
* the connector prefers a common id key when one is present and otherwise falls back to a
|
|
112
|
+
* deterministic content hash scoped to the file's microtime — the base engine dedupes by the
|
|
113
|
+
* record-map + content hash regardless.
|
|
114
|
+
*/
|
|
115
|
+
private BuildRecordIdentity;
|
|
116
|
+
/** Throws a descriptive error on a non-2xx response (404 included, since a missing file is unexpected here). */
|
|
117
|
+
private AssertOK;
|
|
118
|
+
/**
|
|
119
|
+
* Resolves the bearer Token + per-tenant AccountID from the linked Credential entity, falling
|
|
120
|
+
* back to the CompanyIntegration.Configuration JSON. AccountID is per-tenant config — NEVER hardcoded.
|
|
121
|
+
*/
|
|
122
|
+
private LoadCredentials;
|
|
123
|
+
/** Loads Token/AccountID from a Credential entity's Values JSON. */
|
|
124
|
+
private LoadFromCredentialEntity;
|
|
125
|
+
/** Parses a JSON string to extract Token + AccountID (tolerant of casing/aliases). */
|
|
126
|
+
private ParseCredentialJson;
|
|
127
|
+
}
|
|
128
|
+
/** Auth context: resolved bearer token + per-tenant AccountID. */
|
|
129
|
+
interface PropFuelAuthContext extends RESTAuthContext {
|
|
130
|
+
Token: string;
|
|
131
|
+
AccountID: string;
|
|
132
|
+
/** Non-secret host override (e.g. a local mock for replay testing). Defaults to PROPFUEL_HOST. */
|
|
133
|
+
BaseHost?: string;
|
|
134
|
+
}
|
|
135
|
+
/** Parsed `[microtime]-[datatype].json` filename. */
|
|
136
|
+
interface ParsedFileName {
|
|
137
|
+
microtime: string;
|
|
138
|
+
dataType: string;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Parses a PropFuel data-export filename of the form `[microtime]-[datatype].json`.
|
|
142
|
+
* The microtime is the leading numeric segment (PHP microtime, e.g. `1717804800.123456`); the data
|
|
143
|
+
* type is everything between the first `-` and the `.json` suffix. Returns null when the name does
|
|
144
|
+
* not match the convention.
|
|
145
|
+
*/
|
|
146
|
+
export declare function parseFileName(fileName: string): ParsedFileName | null;
|
|
147
|
+
/**
|
|
148
|
+
* Compares two microtime strings chronologically. Microtimes are numeric (possibly fractional);
|
|
149
|
+
* numeric comparison avoids lexical pitfalls (e.g. different integer widths). Returns >0 when a>b.
|
|
150
|
+
*/
|
|
151
|
+
export declare function compareMicrotime(a: string, b: string): number;
|
|
152
|
+
export {};
|
|
@@ -0,0 +1,450 @@
|
|
|
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 { Metadata } from '@memberjunction/core';
|
|
9
|
+
import { BaseIntegrationConnector, BaseRESTIntegrationConnector, } from '@memberjunction/integration-engine';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
/**
|
|
12
|
+
* PropFuel data-export file-feed connector.
|
|
13
|
+
*
|
|
14
|
+
* PropFuel exposes a per-tenant hourly data-export feed as a set of JSON files. There is NO
|
|
15
|
+
* public OpenAPI spec and NO describe/introspection endpoint, so this connector encodes the
|
|
16
|
+
* DISCOVERY MECHANISM, never a baked answer:
|
|
17
|
+
*
|
|
18
|
+
* - {@link DiscoverObjects} calls `GET /dataexport/{AccountID}/list` and derives each object
|
|
19
|
+
* from the `[microtime]-[datatype].json` filename suffix. The concrete data-type set is
|
|
20
|
+
* whatever the live listing actually contains — never a static `PROPFUEL_STREAMS` array.
|
|
21
|
+
* - {@link DiscoverFields} lists the files for a data type, downloads ONE sample file, and
|
|
22
|
+
* streams its records through the base {@link DiscoverFieldsViaStream} helper to derive the
|
|
23
|
+
* field set + data-informed soft-PK from real values — never a frozen `STREAM_FIELDS` catalog.
|
|
24
|
+
*
|
|
25
|
+
* Incremental sync uses a SYNTHETIC, FILE-LEVEL, CLIENT-SIDE cursor `__file_microtime`: the feed
|
|
26
|
+
* is append-only and chronologically sortable by the filename microtime prefix, so the connector
|
|
27
|
+
* resumes by tracking the max microtime seen across processed files (WatermarkService). The vendor
|
|
28
|
+
* `ack` endpoint is operational state and is NOT used as the read-only incremental cursor.
|
|
29
|
+
*
|
|
30
|
+
* READ-ONLY: the data-export feed creates/updates/deletes no source records, so all write
|
|
31
|
+
* capability getters stay false and no ack/POST/delete path is exercised.
|
|
32
|
+
*
|
|
33
|
+
* AccountID and the bearer Token both come from the credential (or Configuration JSON) — the
|
|
34
|
+
* AccountID is per-tenant config and is NEVER hardcoded.
|
|
35
|
+
*/
|
|
36
|
+
let PropFuelConnector = class PropFuelConnector extends BaseRESTIntegrationConnector {
|
|
37
|
+
/** Verbatim three-way invariant name: ClassName / IntegrationName getter / MJ: Integrations.Name. */
|
|
38
|
+
get IntegrationName() {
|
|
39
|
+
return 'PropFuel';
|
|
40
|
+
}
|
|
41
|
+
// ─── Capability surface (read-only file feed) ────────────────────
|
|
42
|
+
// SupportsCreate/Update/Delete stay false (BaseIntegrationConnector defaults). The data-export
|
|
43
|
+
// feed is a pull-only source; the ack endpoint is operational and is not modelled as a write.
|
|
44
|
+
/**
|
|
45
|
+
* KEYSET / no-watermark resume hint: every PropFuel object is a data-export file stream ordered
|
|
46
|
+
* by the filename microtime prefix, so 'microtime' is the stable, monotonic ordering key for all
|
|
47
|
+
* of them (the docs-provable feed object and every runtime-discovered data-type stream alike).
|
|
48
|
+
*/
|
|
49
|
+
StableOrderingKey(_objectName) {
|
|
50
|
+
return MICROTIME_ORDERING_KEY;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The file-feed microtime IS a reliable monotonic high-water mark: PropFuel emits export files in
|
|
54
|
+
* ascending microtime order, and an updated record re-appears in a NEW (higher-microtime) file. So
|
|
55
|
+
* `NewWatermarkValue` (= max microtime seen) is the true global max — the engine can NARROW the next
|
|
56
|
+
* incremental to `microtime > watermark` instead of clearing the keyset position and re-scanning the
|
|
57
|
+
* whole stream every run. Without this, a clean sync's keyset-resume marker is cleared and every
|
|
58
|
+
* incremental re-walks the entire object (correct via content-hash, but wasteful at scale).
|
|
59
|
+
*/
|
|
60
|
+
get MonotonicWatermark() {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
// ─── Auth + transport (BaseRESTIntegrationConnector abstracts) ────
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the bearer Token + per-tenant AccountID from the linked Credential entity, falling
|
|
66
|
+
* back to the CompanyIntegration.Configuration JSON. The credential bytes never leave this scope.
|
|
67
|
+
*/
|
|
68
|
+
async Authenticate(companyIntegration, contextUser) {
|
|
69
|
+
const creds = await this.LoadCredentials(companyIntegration, contextUser);
|
|
70
|
+
return { Token: creds.Token, AccountID: creds.AccountID, BaseHost: creds.BaseHost };
|
|
71
|
+
}
|
|
72
|
+
/** Static Bearer header. No crypto/signing is required for a static token, so no auth-helper is used. */
|
|
73
|
+
BuildHeaders(auth) {
|
|
74
|
+
return {
|
|
75
|
+
'Authorization': `Bearer ${auth.Token}`,
|
|
76
|
+
'Accept': 'application/json',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Base URL for the data-export feed, tenant-scoped by the resolved AccountID. */
|
|
80
|
+
GetBaseURL(_companyIntegration, auth) {
|
|
81
|
+
return `${auth.BaseHost ?? PROPFUEL_HOST}/dataexport/${encodeURIComponent(auth.AccountID)}`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Executes an HTTP request via fetch. Parses JSON when the response is JSON, else returns the
|
|
85
|
+
* raw text body. The concrete connector owns the transport seam so tests can override it.
|
|
86
|
+
*/
|
|
87
|
+
async MakeHTTPRequest(_auth, url, method, headers, body) {
|
|
88
|
+
const init = { method, headers };
|
|
89
|
+
if (body !== undefined && method !== 'GET' && method !== 'HEAD') {
|
|
90
|
+
init.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
91
|
+
init.headers['Content-Type'] = 'application/json';
|
|
92
|
+
}
|
|
93
|
+
const response = await fetch(url, init);
|
|
94
|
+
const responseHeaders = {};
|
|
95
|
+
response.headers.forEach((value, key) => { responseHeaders[key.toLowerCase()] = value; });
|
|
96
|
+
const text = await response.text();
|
|
97
|
+
let parsed = text;
|
|
98
|
+
const contentType = responseHeaders['content-type'] ?? '';
|
|
99
|
+
if (contentType.includes('json') || (text.length > 0 && (text[0] === '{' || text[0] === '['))) {
|
|
100
|
+
try {
|
|
101
|
+
parsed = JSON.parse(text);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
parsed = text;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { Status: response.status, Body: parsed, Headers: responseHeaders };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The download response for a data-export file is a JSON array of records (no envelope). When a
|
|
111
|
+
* vendor wraps records under a key, that key is honored; otherwise a root array / single object
|
|
112
|
+
* is normalized to an array.
|
|
113
|
+
*/
|
|
114
|
+
NormalizeResponse(rawBody, responseDataKey) {
|
|
115
|
+
if (responseDataKey && rawBody && typeof rawBody === 'object' && !Array.isArray(rawBody)) {
|
|
116
|
+
const inner = rawBody[responseDataKey];
|
|
117
|
+
if (Array.isArray(inner))
|
|
118
|
+
return inner.filter(isRecord);
|
|
119
|
+
}
|
|
120
|
+
if (Array.isArray(rawBody))
|
|
121
|
+
return rawBody.filter(isRecord);
|
|
122
|
+
if (isRecord(rawBody))
|
|
123
|
+
return [rawBody];
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The file feed has no in-file pagination — each downloaded file is read whole. File-level
|
|
128
|
+
* paging across the listing is handled by {@link FetchChanges} via the microtime cursor.
|
|
129
|
+
*/
|
|
130
|
+
ExtractPaginationInfo(_rawBody, _paginationType, _currentPage, _currentOffset, _pageSize) {
|
|
131
|
+
return { HasMore: false };
|
|
132
|
+
}
|
|
133
|
+
/** Tests connectivity by listing the tenant's available data-export files. */
|
|
134
|
+
async TestConnection(companyIntegration, contextUser) {
|
|
135
|
+
try {
|
|
136
|
+
const auth = await this.Authenticate(companyIntegration, contextUser);
|
|
137
|
+
const response = await this.ListFiles(auth);
|
|
138
|
+
if (response.Status === 401 || response.Status === 403) {
|
|
139
|
+
return { Success: false, Message: `Authentication failed (HTTP ${response.Status}) listing PropFuel data-export files.` };
|
|
140
|
+
}
|
|
141
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
142
|
+
return { Success: false, Message: `PropFuel list endpoint returned HTTP ${response.Status}.` };
|
|
143
|
+
}
|
|
144
|
+
const files = this.ParseFileListing(response.Body);
|
|
145
|
+
return { Success: true, Message: `Connected to PropFuel account ${auth.AccountID}; ${files.length} data-export file(s) available.` };
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
149
|
+
return { Success: false, Message: `PropFuel connection error: ${message}` };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// ─── Runtime discovery MECHANISM (NOT a baked catalog) ───────────
|
|
153
|
+
/**
|
|
154
|
+
* Discovers objects by listing the live data-export files and deriving the distinct data-type
|
|
155
|
+
* suffix from each `[microtime]-[datatype].json` filename. The returned object set is exactly
|
|
156
|
+
* what the source currently exposes — the frozen contract's `propfuel_data_export_file` is the
|
|
157
|
+
* VERIFICATION FLOOR (discovery must surface at least the feed), never a ceiling.
|
|
158
|
+
*/
|
|
159
|
+
async DiscoverObjects(companyIntegration, contextUser) {
|
|
160
|
+
const auth = await this.Authenticate(companyIntegration, contextUser);
|
|
161
|
+
const response = await this.ListFiles(auth);
|
|
162
|
+
this.AssertOK(response, 'list data-export files');
|
|
163
|
+
const files = this.ParseFileListing(response.Body);
|
|
164
|
+
const dataTypes = new Set();
|
|
165
|
+
for (const f of files) {
|
|
166
|
+
const parsed = parseFileName(f);
|
|
167
|
+
if (parsed)
|
|
168
|
+
dataTypes.add(parsed.dataType);
|
|
169
|
+
}
|
|
170
|
+
return [...dataTypes].sort().map(dataType => ({
|
|
171
|
+
Name: dataType,
|
|
172
|
+
Label: dataType,
|
|
173
|
+
Description: `PropFuel data-export stream "${dataType}" (discovered from the data-export file listing).`,
|
|
174
|
+
SupportsIncrementalSync: true,
|
|
175
|
+
SupportsWrite: false,
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Discovers fields by downloading ONE sample file for the data type and streaming its records
|
|
180
|
+
* through the base data-informed discovery helper. The field set + soft-PK come from the real
|
|
181
|
+
* values the source returns — never a static `STREAM_FIELDS` map. READ-ONLY: the sample file is
|
|
182
|
+
* fetched and parsed, never acked.
|
|
183
|
+
*/
|
|
184
|
+
async DiscoverFields(companyIntegration, objectName, contextUser) {
|
|
185
|
+
const auth = await this.Authenticate(companyIntegration, contextUser);
|
|
186
|
+
const response = await this.ListFiles(auth);
|
|
187
|
+
this.AssertOK(response, 'list data-export files');
|
|
188
|
+
const files = this.ParseFileListing(response.Body);
|
|
189
|
+
// Pick the most-recent file (max microtime) of this data type as the discovery sample.
|
|
190
|
+
const sample = files
|
|
191
|
+
.map(f => ({ file: f, parsed: parseFileName(f) }))
|
|
192
|
+
.filter(x => x.parsed && x.parsed.dataType === objectName)
|
|
193
|
+
.sort((a, b) => compareMicrotime(b.parsed.microtime, a.parsed.microtime))[0];
|
|
194
|
+
if (!sample)
|
|
195
|
+
return [];
|
|
196
|
+
const records = await this.DownloadFileRecords(auth, sample.file);
|
|
197
|
+
// Data-informed discovery: full field corpus + statistics-first soft-PK over the real records.
|
|
198
|
+
// Default Pk options let the helper combine uniqueness/null statistics with the built-in
|
|
199
|
+
// naming-convention rank (defaultPkNameRank) to pick a best-available soft key.
|
|
200
|
+
return this.DiscoverFieldsViaStream(records, { ReadOnly: true });
|
|
201
|
+
}
|
|
202
|
+
// ─── Incremental fetch via the synthetic file-level microtime cursor ──
|
|
203
|
+
/**
|
|
204
|
+
* Fetches records for a data type using the synthetic `__file_microtime` cursor.
|
|
205
|
+
*
|
|
206
|
+
* Mechanism: load the stored cursor (max microtime processed), list the live files, select the
|
|
207
|
+
* files of this data type whose microtime exceeds the cursor (ascending microtime order), and
|
|
208
|
+
* download each, emitting every record with full-record pass-through. The max microtime seen
|
|
209
|
+
* across the processed files is persisted as the new cursor — ONLY on full-batch success, so a
|
|
210
|
+
* partial failure leaves the cursor unchanged and the next run resumes from the same point.
|
|
211
|
+
*/
|
|
212
|
+
async FetchChanges(ctx) {
|
|
213
|
+
const auth = await this.Authenticate(ctx.CompanyIntegration, ctx.ContextUser);
|
|
214
|
+
const listResponse = await this.ListFiles(auth);
|
|
215
|
+
this.AssertOK(listResponse, 'list data-export files');
|
|
216
|
+
const allFiles = this.ParseFileListing(listResponse.Body);
|
|
217
|
+
// The resume cursor: prefer the engine-provided keyset position, else the watermark value.
|
|
218
|
+
const cursor = ctx.AfterKeyValue ?? ctx.WatermarkValue ?? null;
|
|
219
|
+
const candidates = allFiles
|
|
220
|
+
.map(f => ({ file: f, parsed: parseFileName(f) }))
|
|
221
|
+
.filter((x) => x.parsed != null && x.parsed.dataType === ctx.ObjectName)
|
|
222
|
+
.filter(x => cursor == null || compareMicrotime(x.parsed.microtime, cursor) > 0)
|
|
223
|
+
.sort((a, b) => compareMicrotime(a.parsed.microtime, b.parsed.microtime));
|
|
224
|
+
const records = [];
|
|
225
|
+
let maxMicrotime = cursor ?? '';
|
|
226
|
+
const batchLimit = ctx.BatchSize && ctx.BatchSize > 0 ? ctx.BatchSize : Number.MAX_SAFE_INTEGER;
|
|
227
|
+
let filesProcessed = 0;
|
|
228
|
+
for (const candidate of candidates) {
|
|
229
|
+
if (records.length >= batchLimit)
|
|
230
|
+
break;
|
|
231
|
+
const fileRecords = await this.DownloadFileRecords(auth, candidate.file);
|
|
232
|
+
for (const raw of fileRecords) {
|
|
233
|
+
// Full-record pass-through: the COMPLETE source record reaches ExternalRecord.Fields
|
|
234
|
+
// so the framework's custom-column capture sees every key the source returned.
|
|
235
|
+
records.push({
|
|
236
|
+
ExternalID: this.BuildRecordIdentity(raw, candidate.parsed),
|
|
237
|
+
ObjectType: ctx.ObjectName,
|
|
238
|
+
Fields: raw,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
if (compareMicrotime(candidate.parsed.microtime, maxMicrotime) > 0) {
|
|
242
|
+
maxMicrotime = candidate.parsed.microtime;
|
|
243
|
+
}
|
|
244
|
+
filesProcessed++;
|
|
245
|
+
}
|
|
246
|
+
const moreFiles = filesProcessed < candidates.length;
|
|
247
|
+
const result = {
|
|
248
|
+
Records: records,
|
|
249
|
+
HasMore: moreFiles,
|
|
250
|
+
};
|
|
251
|
+
if (maxMicrotime && maxMicrotime !== (cursor ?? '')) {
|
|
252
|
+
// Surface the new cursor both as the watermark value and the keyset resume position.
|
|
253
|
+
result.NewWatermarkValue = maxMicrotime;
|
|
254
|
+
result.NextAfterKeyValue = maxMicrotime;
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
// ─── Helpers ──────────────────────────────────────────────────────
|
|
259
|
+
/** Issues the list call. Path is relative; BuildHeaders + GetBaseURL supply auth + host. */
|
|
260
|
+
async ListFiles(auth) {
|
|
261
|
+
const url = `${this.GetBaseURL({}, auth)}/list`;
|
|
262
|
+
return this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
263
|
+
}
|
|
264
|
+
/** Downloads one file by name and returns its records (read-only; never acked). */
|
|
265
|
+
async DownloadFileRecords(auth, file) {
|
|
266
|
+
const url = `${this.GetBaseURL({}, auth)}/download/${encodeURIComponent(file)}`;
|
|
267
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
268
|
+
this.AssertOK(response, `download file ${file}`);
|
|
269
|
+
return this.NormalizeResponse(response.Body, null);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Parses the list endpoint body into an array of file names. The listing may be a bare array of
|
|
273
|
+
* strings, an array of objects with a name/file/filename key, or an object wrapping such an array.
|
|
274
|
+
*/
|
|
275
|
+
ParseFileListing(body) {
|
|
276
|
+
const arr = Array.isArray(body)
|
|
277
|
+
? body
|
|
278
|
+
: (isRecord(body) ? this.FindArrayInObject(body) : []);
|
|
279
|
+
const files = [];
|
|
280
|
+
for (const item of arr) {
|
|
281
|
+
if (typeof item === 'string') {
|
|
282
|
+
files.push(item);
|
|
283
|
+
}
|
|
284
|
+
else if (isRecord(item)) {
|
|
285
|
+
const name = item['name'] ?? item['file'] ?? item['filename'] ?? item['fileName'] ?? item['key'];
|
|
286
|
+
if (typeof name === 'string')
|
|
287
|
+
files.push(name);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return files;
|
|
291
|
+
}
|
|
292
|
+
/** Finds the first array-valued property of an object (the file listing under a wrapper key). */
|
|
293
|
+
FindArrayInObject(obj) {
|
|
294
|
+
for (const v of Object.values(obj)) {
|
|
295
|
+
if (Array.isArray(v))
|
|
296
|
+
return v;
|
|
297
|
+
}
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Builds a stable record identity. The per-record schema is undocumented (no provable PK), so
|
|
302
|
+
* the connector prefers a common id key when one is present and otherwise falls back to a
|
|
303
|
+
* deterministic content hash scoped to the file's microtime — the base engine dedupes by the
|
|
304
|
+
* record-map + content hash regardless.
|
|
305
|
+
*/
|
|
306
|
+
BuildRecordIdentity(raw, parsed) {
|
|
307
|
+
for (const k of ['id', 'ID', 'Id', 'uuid', 'externalID', 'ExternalID']) {
|
|
308
|
+
const v = raw[k];
|
|
309
|
+
if (typeof v === 'string' && v.length > 0)
|
|
310
|
+
return v;
|
|
311
|
+
if (typeof v === 'number')
|
|
312
|
+
return String(v);
|
|
313
|
+
}
|
|
314
|
+
return `${parsed.microtime}:${stableHash(raw)}`;
|
|
315
|
+
}
|
|
316
|
+
/** Throws a descriptive error on a non-2xx response (404 included, since a missing file is unexpected here). */
|
|
317
|
+
AssertOK(response, action) {
|
|
318
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
319
|
+
throw new Error(`PropFuel failed to ${action}: HTTP ${response.Status}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Resolves the bearer Token + per-tenant AccountID from the linked Credential entity, falling
|
|
324
|
+
* back to the CompanyIntegration.Configuration JSON. AccountID is per-tenant config — NEVER hardcoded.
|
|
325
|
+
*/
|
|
326
|
+
async LoadCredentials(companyIntegration, contextUser) {
|
|
327
|
+
let token;
|
|
328
|
+
let accountID;
|
|
329
|
+
let baseHost;
|
|
330
|
+
const credentialID = companyIntegration.CredentialID;
|
|
331
|
+
if (credentialID) {
|
|
332
|
+
const fromCred = await this.LoadFromCredentialEntity(credentialID, contextUser);
|
|
333
|
+
if (fromCred) {
|
|
334
|
+
token = fromCred.Token ?? token;
|
|
335
|
+
accountID = fromCred.AccountID ?? accountID;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Configuration JSON supplies AccountID (and a token fallback) when not on the credential.
|
|
339
|
+
const configJson = companyIntegration.Configuration;
|
|
340
|
+
if (configJson) {
|
|
341
|
+
const fromConfig = this.ParseCredentialJson(configJson);
|
|
342
|
+
if (fromConfig) {
|
|
343
|
+
token = token ?? fromConfig.Token;
|
|
344
|
+
accountID = accountID ?? fromConfig.AccountID;
|
|
345
|
+
baseHost = baseHost ?? fromConfig.BaseHost;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (!token) {
|
|
349
|
+
throw new Error('No PropFuel credential or Configuration JSON found — a "Token" is required.');
|
|
350
|
+
}
|
|
351
|
+
if (!accountID) {
|
|
352
|
+
throw new Error('No PropFuel AccountID found — set "AccountID" on the credential or Configuration JSON (it is per-tenant, never hardcoded).');
|
|
353
|
+
}
|
|
354
|
+
return { Token: token, AccountID: accountID, BaseHost: baseHost };
|
|
355
|
+
}
|
|
356
|
+
/** Loads Token/AccountID from a Credential entity's Values JSON. */
|
|
357
|
+
async LoadFromCredentialEntity(credentialID, contextUser, provider) {
|
|
358
|
+
const md = provider ?? new Metadata();
|
|
359
|
+
const credential = await md.GetEntityObject('MJ: Credentials', contextUser);
|
|
360
|
+
const loaded = await credential.Load(credentialID);
|
|
361
|
+
if (!loaded || !credential.Values)
|
|
362
|
+
return null;
|
|
363
|
+
return this.ParseCredentialJson(credential.Values);
|
|
364
|
+
}
|
|
365
|
+
/** Parses a JSON string to extract Token + AccountID (tolerant of casing/aliases). */
|
|
366
|
+
ParseCredentialJson(json) {
|
|
367
|
+
try {
|
|
368
|
+
const result = PropFuelCredentialSchema.safeParse(JSON.parse(json));
|
|
369
|
+
if (!result.success)
|
|
370
|
+
return null;
|
|
371
|
+
const p = result.data;
|
|
372
|
+
const token = p.Token ?? p.token ?? p.apiKey ?? p.ApiKey;
|
|
373
|
+
const accountID = p.AccountID ?? p.accountId ?? p.accountID;
|
|
374
|
+
if (!token && accountID == null)
|
|
375
|
+
return null;
|
|
376
|
+
const baseHost = p.apiBaseUrl ?? p.BaseURL;
|
|
377
|
+
return { Token: token ?? '', AccountID: accountID != null ? String(accountID) : '', BaseHost: baseHost };
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
PropFuelConnector = __decorate([
|
|
385
|
+
RegisterClass(BaseIntegrationConnector, '@memberjunction/connector-propfuel')
|
|
386
|
+
], PropFuelConnector);
|
|
387
|
+
export { PropFuelConnector };
|
|
388
|
+
// ─── Module-level constants + helpers (mechanism, NOT a catalog) ──────
|
|
389
|
+
/** PropFuel host root. Tenant + endpoint are appended at runtime. */
|
|
390
|
+
const PROPFUEL_HOST = 'https://app.propfuel.com';
|
|
391
|
+
/** Stable ordering key name for keyset/no-watermark resume — the filename microtime prefix. */
|
|
392
|
+
const MICROTIME_ORDERING_KEY = 'microtime';
|
|
393
|
+
/** Zod schema for the credential/Configuration JSON shape (tolerant of casing aliases). */
|
|
394
|
+
const PropFuelCredentialSchema = z.object({
|
|
395
|
+
Token: z.string().optional(),
|
|
396
|
+
token: z.string().optional(),
|
|
397
|
+
apiKey: z.string().optional(),
|
|
398
|
+
ApiKey: z.string().optional(),
|
|
399
|
+
AccountID: z.union([z.string(), z.number()]).optional(),
|
|
400
|
+
accountId: z.union([z.string(), z.number()]).optional(),
|
|
401
|
+
accountID: z.union([z.string(), z.number()]).optional(),
|
|
402
|
+
apiBaseUrl: z.string().optional(),
|
|
403
|
+
BaseURL: z.string().optional(),
|
|
404
|
+
}).passthrough();
|
|
405
|
+
/** Narrows an unknown value to a plain record. */
|
|
406
|
+
function isRecord(v) {
|
|
407
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Parses a PropFuel data-export filename of the form `[microtime]-[datatype].json`.
|
|
411
|
+
* The microtime is the leading numeric segment (PHP microtime, e.g. `1717804800.123456`); the data
|
|
412
|
+
* type is everything between the first `-` and the `.json` suffix. Returns null when the name does
|
|
413
|
+
* not match the convention.
|
|
414
|
+
*/
|
|
415
|
+
export function parseFileName(fileName) {
|
|
416
|
+
// Strip any path prefix and the .json extension.
|
|
417
|
+
const base = fileName.split('/').pop() ?? fileName;
|
|
418
|
+
const noExt = base.replace(/\.json$/i, '');
|
|
419
|
+
const dashIndex = noExt.indexOf('-');
|
|
420
|
+
if (dashIndex <= 0)
|
|
421
|
+
return null;
|
|
422
|
+
const microtime = noExt.slice(0, dashIndex);
|
|
423
|
+
const dataType = noExt.slice(dashIndex + 1);
|
|
424
|
+
if (!/^[0-9]+(\.[0-9]+)?$/.test(microtime) || dataType.length === 0)
|
|
425
|
+
return null;
|
|
426
|
+
return { microtime, dataType };
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Compares two microtime strings chronologically. Microtimes are numeric (possibly fractional);
|
|
430
|
+
* numeric comparison avoids lexical pitfalls (e.g. different integer widths). Returns >0 when a>b.
|
|
431
|
+
*/
|
|
432
|
+
export function compareMicrotime(a, b) {
|
|
433
|
+
const na = Number(a);
|
|
434
|
+
const nb = Number(b);
|
|
435
|
+
if (Number.isFinite(na) && Number.isFinite(nb)) {
|
|
436
|
+
return na === nb ? 0 : (na > nb ? 1 : -1);
|
|
437
|
+
}
|
|
438
|
+
return a === b ? 0 : (a > b ? 1 : -1);
|
|
439
|
+
}
|
|
440
|
+
/** Small deterministic hash for record identity fallback (FNV-1a, hex). */
|
|
441
|
+
function stableHash(record) {
|
|
442
|
+
const json = JSON.stringify(record, Object.keys(record).sort());
|
|
443
|
+
let h = 0x811c9dc5;
|
|
444
|
+
for (let i = 0; i < json.length; i++) {
|
|
445
|
+
h ^= json.charCodeAt(i);
|
|
446
|
+
h = Math.imul(h, 0x01000193);
|
|
447
|
+
}
|
|
448
|
+
return (h >>> 0).toString(16);
|
|
449
|
+
}
|
|
450
|
+
//# sourceMappingURL=PropFuelConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PropFuelConnector.js","sourceRoot":"","sources":["../src/PropFuelConnector.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from './PropFuelConnector.js';
|
|
2
|
+
/** Open App bootstrap entry: importing this module ran the connector's @RegisterClass decorator;
|
|
3
|
+
* this no-op satisfies the loader's required startupExport and forces the import at MJAPI boot. */
|
|
4
|
+
export declare function registerConnector(): void;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './PropFuelConnector.js';
|
|
2
|
+
/** Open App bootstrap entry: importing this module ran the connector's @RegisterClass decorator;
|
|
3
|
+
* this no-op satisfies the loader's required startupExport and forces the import at MJAPI boot. */
|
|
4
|
+
export function registerConnector() { }
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AAEvC;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@memberjunction/connector-propfuel",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MemberJunction PropFuel connector.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"/dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc && tsc-alias -f",
|
|
13
|
+
"test": "vitest run --passWithNoTests"
|
|
14
|
+
},
|
|
15
|
+
"author": "MemberJunction.com",
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@memberjunction/core": ">=5.42.0 <6.0.0",
|
|
19
|
+
"@memberjunction/core-entities": ">=5.42.0 <6.0.0",
|
|
20
|
+
"@memberjunction/global": ">=5.42.0 <6.0.0",
|
|
21
|
+
"@memberjunction/integration-engine": ">=5.42.0 <6.0.0"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"zod": "~3.24.4"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "24.10.11",
|
|
28
|
+
"tsc-alias": "^1.8.16",
|
|
29
|
+
"typescript": "^5.9.3",
|
|
30
|
+
"vitest": "^4.0.18",
|
|
31
|
+
"@memberjunction/core": "^5.42.0",
|
|
32
|
+
"@memberjunction/core-entities": "^5.42.0",
|
|
33
|
+
"@memberjunction/global": "^5.42.0",
|
|
34
|
+
"@memberjunction/integration-engine": "^5.42.0"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/MemberJunction/Integrations"
|
|
39
|
+
}
|
|
40
|
+
}
|