@memberjunction/connector-quickbooks 2.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/QuickBooksConnector.d.ts +189 -0
- package/dist/QuickBooksConnector.js +927 -0
- package/dist/QuickBooksConnector.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 +42 -0
|
@@ -0,0 +1,189 @@
|
|
|
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 FetchContext, type FetchBatchResult, type UpdateRecordContext, type DeleteRecordContext, type GetRecordContext, type CRUDResult, type ExternalRecord, type SourceSchemaInfo, type RateLimitPolicy } from '@memberjunction/integration-engine';
|
|
4
|
+
/** Auth context threaded through BuildHeaders / MakeHTTPRequest / GetBaseURL for a run. */
|
|
5
|
+
interface QuickBooksAuthContext extends RESTAuthContext {
|
|
6
|
+
/** Bearer access token. */
|
|
7
|
+
Token: string;
|
|
8
|
+
/** Tenant realm id. */
|
|
9
|
+
RealmId: string;
|
|
10
|
+
/** Fully-resolved company base URL, e.g. `https://quickbooks.api.intuit.com/v3/company/{realmId}`. */
|
|
11
|
+
CompanyBaseURL: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* QuickBooks Online Accounting API (v3) connector.
|
|
15
|
+
*
|
|
16
|
+
* Extends BaseRESTIntegrationConnector (REST/JSON over HTTP). Discovery (DiscoverObjects/DiscoverFields)
|
|
17
|
+
* and generic create are inherited; this class supplies the QBO-specific protocol surface: OAuth2 refresh
|
|
18
|
+
* with rotating-refresh-token persistence, the SQL-like /query read door with in-query pagination +
|
|
19
|
+
* watermark, CDC-based deletion detection, singleton reads, SyncToken-guarded full-object updates, and
|
|
20
|
+
* delete-vs-deactivate routing by entity class.
|
|
21
|
+
*
|
|
22
|
+
* DUAL registration: the TS class symbol `QuickBooksConnector` keeps the sandbox verification ladder green;
|
|
23
|
+
* the package-name key `@memberjunction/connector-quickbooks` is what the Integrations repo's
|
|
24
|
+
* validate-invariants requires as an @RegisterClass key exported by the package's own src.
|
|
25
|
+
*/
|
|
26
|
+
export declare class QuickBooksConnector extends BaseRESTIntegrationConnector {
|
|
27
|
+
/** One token manager per connector instance (crypto-free OAuth2 round-trip + in-memory access-token cache). */
|
|
28
|
+
private readonly tokenManager;
|
|
29
|
+
/** Cached auth for the lifetime of a single run, keyed by realm so a connection change re-authenticates. */
|
|
30
|
+
private cachedAuth;
|
|
31
|
+
private cachedAuthRealm;
|
|
32
|
+
/** Last-resolved vendor config, so the credential-free rate/concurrency getters can honor Configuration. */
|
|
33
|
+
private lastVendorConfig;
|
|
34
|
+
/** Verbatim `MJ: Integrations.Name`. The T1 three-way name check compares this === metadata display Name. */
|
|
35
|
+
get IntegrationName(): string;
|
|
36
|
+
get SupportsCreate(): boolean;
|
|
37
|
+
get SupportsUpdate(): boolean;
|
|
38
|
+
get SupportsDelete(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Discovery is NON-authoritative: QBO exposes no describe-all/complete-gamut endpoint. DiscoverObjects /
|
|
41
|
+
* IntrospectSchema are cache-driven (re-read persisted Declared metadata), and per-realm custom fields
|
|
42
|
+
* flow through the sample-union enrichment below + the framework's runtime custom-column capture. Absence
|
|
43
|
+
* on a refresh proves nothing → never deactivate.
|
|
44
|
+
*/
|
|
45
|
+
get DiscoveryIsAuthoritative(): boolean;
|
|
46
|
+
/** QBO: 500 requests/min/realm → ~8.3 tokens/sec sustained; burst capped by the 10-concurrent limit. */
|
|
47
|
+
get RateLimitPolicy(): RateLimitPolicy | null;
|
|
48
|
+
/** QBO documents a 10-concurrent-request ceiling per realm. */
|
|
49
|
+
get MaxConcurrencyHint(): number | null;
|
|
50
|
+
/** Parse QBO's Retry-After (seconds) off an exhausted-429 error into ms for the engine's precise backoff. */
|
|
51
|
+
ExtractRetryAfterMs(error: unknown): number | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Sample-union enrichment: Declared metadata is spec-derived and misses a realm's per-record CustomField
|
|
54
|
+
* columns. After the base cache-driven introspection (which sets `ExternalName = obj.Name`, i.e. the QBO
|
|
55
|
+
* entity name), we sample each object's live read shape via `DiscoverFieldsViaFetch` and UNION it into the
|
|
56
|
+
* declared set with `mergeDeclaredWithSampledFields` (never-shrink, declared-wins). Best-effort + parallel;
|
|
57
|
+
* a sample failure leaves the declared set untouched. We override IntrospectSchema (NOT DiscoverFields —
|
|
58
|
+
* that would recurse into DiscoverFieldsViaFetch's own fallback). Connector-agnostic.
|
|
59
|
+
*/
|
|
60
|
+
IntrospectSchema(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<SourceSchemaInfo>;
|
|
61
|
+
/** Reads the singleton CompanyInfo resource to verify connectivity + auth (read-only, non-mutating). */
|
|
62
|
+
TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
|
|
63
|
+
/**
|
|
64
|
+
* OVERRIDE (idiosyncratic): QBO reads ride a SQL-like /query door, not a flat REST collection, so the
|
|
65
|
+
* base's flat/offset URL machinery does not apply. This fetches ONE page (bounded by BatchSize, capped at
|
|
66
|
+
* QBO's 1000/query) and returns HasMore + NextOffset so the engine loops. On an incremental first page it
|
|
67
|
+
* also emits CDC deletion tombstones (the only documented QBO deletion source). Singleton read-only
|
|
68
|
+
* objects (CompanyInfo/Preferences) take the single-GET path.
|
|
69
|
+
*/
|
|
70
|
+
FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
|
|
71
|
+
/** OVERRIDE: single-record GET at `/{entity}/{id}?minorversion`; the record is nested under `<Entity>`. */
|
|
72
|
+
GetRecord(ctx: GetRecordContext): Promise<ExternalRecord | null>;
|
|
73
|
+
/**
|
|
74
|
+
* OVERRIDE (idiosyncratic): QBO update is a full-object POST to `/{entity}` (no id in path) carrying the
|
|
75
|
+
* record's `Id`, current `SyncToken`, and `sparse:true` (so only supplied fields change). SyncToken is
|
|
76
|
+
* fetch-or-carry: used from the caller's attributes when present, else read live via GetRecord. A stale
|
|
77
|
+
* SyncToken (optimistic-concurrency conflict) is CLASSIFIED and returned as a failure — never blind-retried.
|
|
78
|
+
*/
|
|
79
|
+
UpdateRecord(ctx: UpdateRecordContext): Promise<CRUDResult>;
|
|
80
|
+
/**
|
|
81
|
+
* OVERRIDE (idiosyncratic): QBO has NO uniform delete verb. Transaction entities with SupportsDelete
|
|
82
|
+
* hard-delete via `POST /{entity}?operation=delete` ({Id, SyncToken}); name-list entities have no hard
|
|
83
|
+
* delete and instead DEACTIVATE via a sparse update `Active=false` (SyncToken required). Anything else
|
|
84
|
+
* (read-only, or a transaction with delete intentionally unsupported) fails loudly.
|
|
85
|
+
*/
|
|
86
|
+
DeleteRecord(ctx: DeleteRecordContext): Promise<CRUDResult>;
|
|
87
|
+
/** Resolves credentials, mints/refreshes the access token via the shared manager, persists a rotated refresh token. */
|
|
88
|
+
protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<QuickBooksAuthContext>;
|
|
89
|
+
/** Bearer auth + explicit JSON Accept (QBO defaults to XML when Accept is absent). */
|
|
90
|
+
protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
|
|
91
|
+
/** HTTP transport (fetch) with bounded 429/503 backoff honoring Retry-After. Test subclasses override this. */
|
|
92
|
+
protected MakeHTTPRequest(_auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
|
|
93
|
+
/**
|
|
94
|
+
* Strips the QBO envelope to a record array. A /query response nests records under
|
|
95
|
+
* `QueryResponse.<Entity>`; a single-record response nests one object under `<Entity>`. `responseDataKey`
|
|
96
|
+
* carries the entity name. Falls back to the first array found under QueryResponse.
|
|
97
|
+
*/
|
|
98
|
+
protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
|
|
99
|
+
/** Offset pagination continuation from the QueryResponse totalCount, else a full-page heuristic. */
|
|
100
|
+
protected ExtractPaginationInfo(rawBody: unknown, paginationType: PaginationType, _currentPage: number, currentOffset: number, pageSize: number): PaginationState;
|
|
101
|
+
/** Company-scoped base URL, e.g. `https://quickbooks.api.intuit.com/v3/company/{realmId}`. */
|
|
102
|
+
protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
|
|
103
|
+
/** OVERRIDE: a created record's Id is nested under the entity name in the QBO create response. */
|
|
104
|
+
protected ExtractIDFromResponse(response: RESTResponse, _idLocation: string | null): string | undefined;
|
|
105
|
+
/** OVERRIDE: QBO Fault envelope (`{ fault: { error: [{ message, detail, code }] } }`, lowercased JSON keys). */
|
|
106
|
+
protected ExtractErrorMessage(response: RESTResponse): string | undefined;
|
|
107
|
+
/** Fetches one /query page with in-query STARTPOSITION/MAXRESULTS + optional watermark; emits CDC deletes. */
|
|
108
|
+
private fetchQueryPage;
|
|
109
|
+
/** Single-GET read for QBO singleton resources (CompanyInfo/Preferences) at `/{entity}/{realmId}`. */
|
|
110
|
+
private fetchSingleton;
|
|
111
|
+
/** Fetches the CDC door and returns deletion tombstones for the object. */
|
|
112
|
+
private fetchCdcDeletions;
|
|
113
|
+
/** Walks the CDCResponse envelope, extracting rows whose `status` is Deleted for the given entity. */
|
|
114
|
+
private parseCdcDeletions;
|
|
115
|
+
/**
|
|
116
|
+
* Builds an ExternalRecord with the FULL source record in Fields (custom-column pass-through contract).
|
|
117
|
+
* Mirrors the base ToExternalRecord identity/content-hash semantics: a fully-present PK → joined key;
|
|
118
|
+
* a keyless/partial-key record → a deterministic content hash stamped into the single PK field so it is
|
|
119
|
+
* still syncable + idempotent on re-sync. Never drops a source key.
|
|
120
|
+
*/
|
|
121
|
+
private buildExternalRecord;
|
|
122
|
+
/**
|
|
123
|
+
* Resolves the current SyncToken for an update/delete: from the caller's attributes when present, else a
|
|
124
|
+
* live GetRecord read. QBO's optimistic-concurrency requires the CURRENT token; a stale one is rejected
|
|
125
|
+
* by the server (classified as a conflict at write time), so we prefer a freshly-read token when absent.
|
|
126
|
+
*/
|
|
127
|
+
private resolveSyncToken;
|
|
128
|
+
/** Maps a QBO write response to a CRUDResult, classifying a stale-SyncToken conflict distinctly. */
|
|
129
|
+
private buildWriteResult;
|
|
130
|
+
/** True when the Fault carries QBO's stale-object-version code (5010) or an explicit stale-token message. */
|
|
131
|
+
private isStaleTokenConflict;
|
|
132
|
+
/** Merges credentials from the linked Credential entity (secrets win) and CompanyIntegration fields/Configuration. */
|
|
133
|
+
private loadCredentials;
|
|
134
|
+
/** Loads a Credential row and parses its Values JSON. */
|
|
135
|
+
private loadFromCredential;
|
|
136
|
+
/** Parses a QBO credential/config JSON blob, tolerant of key-casing variants. */
|
|
137
|
+
private parseCredentialJson;
|
|
138
|
+
/**
|
|
139
|
+
* Persists the (rotated) refresh token back to its source ONLY when it changed. Intuit invalidates the
|
|
140
|
+
* previous refresh token on every exchange, so a connector that fails to persist the new one authenticates
|
|
141
|
+
* once and then dies on the next process. Also caches the fresh access token + expiry on the connection.
|
|
142
|
+
*/
|
|
143
|
+
private persistRotatedRefreshToken;
|
|
144
|
+
/** Writes the rotated refresh token into the Credential.Values JSON (preserving the other keys). */
|
|
145
|
+
private saveRefreshTokenToCredential;
|
|
146
|
+
/** Writes the rotated refresh token to the CompanyIntegration.RefreshToken column. */
|
|
147
|
+
private saveRefreshTokenToConnection;
|
|
148
|
+
/** Caches the fresh access token + expiry on the connection (best-effort; a save failure is non-fatal). */
|
|
149
|
+
private saveAccessTokenOnConnection;
|
|
150
|
+
/** Reads Integration.Configuration for vendor-wide facts, with published QBO fallbacks (no tenant data). */
|
|
151
|
+
private resolveVendorConfig;
|
|
152
|
+
/** Reads the raw Integration.Configuration object from the engine cache (null-tolerant for unit tests). */
|
|
153
|
+
private readIntegrationConfig;
|
|
154
|
+
/** Parses the per-object IntegrationObject.Configuration into a typed shape (QueryEntity/entity class/etc). */
|
|
155
|
+
private parseObjectConfig;
|
|
156
|
+
/** Selects the host by environment and applies the company path template with the realm id. */
|
|
157
|
+
private buildCompanyBaseURL;
|
|
158
|
+
/** Builds a QBO SQL-like SELECT with the watermark WHERE + ORDERBY and STARTPOSITION/MAXRESULTS in-text. */
|
|
159
|
+
private buildQueryText;
|
|
160
|
+
/** Escapes a QBO SQL string literal (single quotes doubled). */
|
|
161
|
+
private escapeQueryLiteral;
|
|
162
|
+
private readQueryResponse;
|
|
163
|
+
private countQueryRecords;
|
|
164
|
+
/** Reads a single entity object nested under `<Entity>` in a singleton/get-one response. */
|
|
165
|
+
private readSingleEntity;
|
|
166
|
+
private readCompanyName;
|
|
167
|
+
private readFaultErrors;
|
|
168
|
+
/** Reads MetaData.LastUpdatedTime (the incremental watermark) off a raw record. */
|
|
169
|
+
private readLastUpdated;
|
|
170
|
+
/** Resolves a (possibly dotted, e.g. `MetaData.LastUpdatedTime`) watermark path to a string value. */
|
|
171
|
+
private readWatermarkString;
|
|
172
|
+
/** Highest watermark value across a batch (ISO-8601 compares lexically). */
|
|
173
|
+
private maxWatermark;
|
|
174
|
+
/** PK field names from the cached fields (universal QBO PK is 'Id'; empty when genuinely keyless). */
|
|
175
|
+
private primaryKeyFieldNames;
|
|
176
|
+
private parseRetryAfterMs;
|
|
177
|
+
private computeBackoffMs;
|
|
178
|
+
private sleep;
|
|
179
|
+
private normalizeEnvironment;
|
|
180
|
+
private parsePerMinuteLimit;
|
|
181
|
+
private firstString;
|
|
182
|
+
private asString;
|
|
183
|
+
private asNumber;
|
|
184
|
+
/** Removes undefined-valued keys so a spread merge doesn't clobber a lower-precedence real value. */
|
|
185
|
+
private stripUndefined;
|
|
186
|
+
}
|
|
187
|
+
/** Tree-shaking prevention: referenced from index to keep the @RegisterClass registration alive under bundling. */
|
|
188
|
+
export declare function LoadQuickBooksConnector(): void;
|
|
189
|
+
export {};
|
|
@@ -0,0 +1,927 @@
|
|
|
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 { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
|
|
10
|
+
import { BaseIntegrationConnector, BaseRESTIntegrationConnector, OAuth2TokenManager, ClassifyError, computeContentHash, serializeKeyValue, } from '@memberjunction/integration-engine';
|
|
11
|
+
import { mergeDeclaredWithSampledFields } from '@memberjunction/connector-schema-merge';
|
|
12
|
+
// ─── Constants (published QBO facts used ONLY as Configuration fallbacks; no tenant data) ──
|
|
13
|
+
const DEFAULT_HOST_PRODUCTION = 'https://quickbooks.api.intuit.com';
|
|
14
|
+
const DEFAULT_HOST_SANDBOX = 'https://sandbox-quickbooks.api.intuit.com';
|
|
15
|
+
const DEFAULT_COMPANY_PATH_TEMPLATE = 'v3/company/{realmId}';
|
|
16
|
+
/** Current QBO minor-version floor (v1–74 deprecated Aug 2025; the server treats anything lower as 75). */
|
|
17
|
+
const DEFAULT_MINOR_VERSION = 75;
|
|
18
|
+
const DEFAULT_TOKEN_ENDPOINT = 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer';
|
|
19
|
+
const DEFAULT_SCOPE = 'com.intuit.quickbooks.accounting';
|
|
20
|
+
const DEFAULT_RECORDS_PATH = 'QueryResponse';
|
|
21
|
+
const DEFAULT_CORRELATION_HEADER = 'intuit_tid';
|
|
22
|
+
const DEFAULT_PER_MINUTE_LIMIT = 500;
|
|
23
|
+
const DEFAULT_CONCURRENT_LIMIT = 10;
|
|
24
|
+
/** QBO caps a single /query at 1000 rows. */
|
|
25
|
+
const MAX_QUERY_RESULTS = 1000;
|
|
26
|
+
/** Fallback page size when the engine passes no BatchSize. */
|
|
27
|
+
const DEFAULT_PAGE_SIZE = 200;
|
|
28
|
+
/** Transient-status backoff retries inside the transport. */
|
|
29
|
+
const MAX_TRANSIENT_RETRIES = 5;
|
|
30
|
+
// ─── QuickBooksConnector ─────────────────────────────────────────────────
|
|
31
|
+
/**
|
|
32
|
+
* QuickBooks Online Accounting API (v3) connector.
|
|
33
|
+
*
|
|
34
|
+
* Extends BaseRESTIntegrationConnector (REST/JSON over HTTP). Discovery (DiscoverObjects/DiscoverFields)
|
|
35
|
+
* and generic create are inherited; this class supplies the QBO-specific protocol surface: OAuth2 refresh
|
|
36
|
+
* with rotating-refresh-token persistence, the SQL-like /query read door with in-query pagination +
|
|
37
|
+
* watermark, CDC-based deletion detection, singleton reads, SyncToken-guarded full-object updates, and
|
|
38
|
+
* delete-vs-deactivate routing by entity class.
|
|
39
|
+
*
|
|
40
|
+
* DUAL registration: the TS class symbol `QuickBooksConnector` keeps the sandbox verification ladder green;
|
|
41
|
+
* the package-name key `@memberjunction/connector-quickbooks` is what the Integrations repo's
|
|
42
|
+
* validate-invariants requires as an @RegisterClass key exported by the package's own src.
|
|
43
|
+
*/
|
|
44
|
+
let QuickBooksConnector = class QuickBooksConnector extends BaseRESTIntegrationConnector {
|
|
45
|
+
constructor() {
|
|
46
|
+
super(...arguments);
|
|
47
|
+
/** One token manager per connector instance (crypto-free OAuth2 round-trip + in-memory access-token cache). */
|
|
48
|
+
this.tokenManager = new OAuth2TokenManager();
|
|
49
|
+
/** Cached auth for the lifetime of a single run, keyed by realm so a connection change re-authenticates. */
|
|
50
|
+
this.cachedAuth = null;
|
|
51
|
+
this.cachedAuthRealm = null;
|
|
52
|
+
/** Last-resolved vendor config, so the credential-free rate/concurrency getters can honor Configuration. */
|
|
53
|
+
this.lastVendorConfig = null;
|
|
54
|
+
}
|
|
55
|
+
// ── Identity (T1 three-way invariant) ────────────────────────────
|
|
56
|
+
/** Verbatim `MJ: Integrations.Name`. The T1 three-way name check compares this === metadata display Name. */
|
|
57
|
+
get IntegrationName() {
|
|
58
|
+
return 'QuickBooks';
|
|
59
|
+
}
|
|
60
|
+
// ── Capability getters (agree with the per-op metadata columns) ──
|
|
61
|
+
// TRUE because SOME IOs carry the corresponding write columns; the generic/overridden per-op paths
|
|
62
|
+
// gate each call on the IO's OWN Create/Update/Delete columns + entity class, so an object whose
|
|
63
|
+
// metadata says "no" is never written blindly.
|
|
64
|
+
get SupportsCreate() { return true; }
|
|
65
|
+
get SupportsUpdate() { return true; }
|
|
66
|
+
get SupportsDelete() { return true; }
|
|
67
|
+
/**
|
|
68
|
+
* Discovery is NON-authoritative: QBO exposes no describe-all/complete-gamut endpoint. DiscoverObjects /
|
|
69
|
+
* IntrospectSchema are cache-driven (re-read persisted Declared metadata), and per-realm custom fields
|
|
70
|
+
* flow through the sample-union enrichment below + the framework's runtime custom-column capture. Absence
|
|
71
|
+
* on a refresh proves nothing → never deactivate.
|
|
72
|
+
*/
|
|
73
|
+
get DiscoveryIsAuthoritative() {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
// ── Sync-efficiency hooks (§7/§10 — populated from evidenced Configuration facts) ──
|
|
77
|
+
/** QBO: 500 requests/min/realm → ~8.3 tokens/sec sustained; burst capped by the 10-concurrent limit. */
|
|
78
|
+
get RateLimitPolicy() {
|
|
79
|
+
const perMin = this.lastVendorConfig?.perMinuteLimit ?? DEFAULT_PER_MINUTE_LIMIT;
|
|
80
|
+
const concurrent = this.lastVendorConfig?.concurrentLimit ?? DEFAULT_CONCURRENT_LIMIT;
|
|
81
|
+
return { TokensPerSec: perMin / 60, Burst: concurrent };
|
|
82
|
+
}
|
|
83
|
+
/** QBO documents a 10-concurrent-request ceiling per realm. */
|
|
84
|
+
get MaxConcurrencyHint() {
|
|
85
|
+
return this.lastVendorConfig?.concurrentLimit ?? DEFAULT_CONCURRENT_LIMIT;
|
|
86
|
+
}
|
|
87
|
+
/** Parse QBO's Retry-After (seconds) off an exhausted-429 error into ms for the engine's precise backoff. */
|
|
88
|
+
ExtractRetryAfterMs(error) {
|
|
89
|
+
if (error instanceof QuickBooksRateLimitError)
|
|
90
|
+
return error.RetryAfterMs;
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
// ── Sample-union field enrichment (MJ connector standard) ─────────
|
|
94
|
+
/**
|
|
95
|
+
* Sample-union enrichment: Declared metadata is spec-derived and misses a realm's per-record CustomField
|
|
96
|
+
* columns. After the base cache-driven introspection (which sets `ExternalName = obj.Name`, i.e. the QBO
|
|
97
|
+
* entity name), we sample each object's live read shape via `DiscoverFieldsViaFetch` and UNION it into the
|
|
98
|
+
* declared set with `mergeDeclaredWithSampledFields` (never-shrink, declared-wins). Best-effort + parallel;
|
|
99
|
+
* a sample failure leaves the declared set untouched. We override IntrospectSchema (NOT DiscoverFields —
|
|
100
|
+
* that would recurse into DiscoverFieldsViaFetch's own fallback). Connector-agnostic.
|
|
101
|
+
*/
|
|
102
|
+
async IntrospectSchema(companyIntegration, contextUser) {
|
|
103
|
+
const info = await super.IntrospectSchema(companyIntegration, contextUser);
|
|
104
|
+
await Promise.all(info.Objects.map(async (obj) => {
|
|
105
|
+
try {
|
|
106
|
+
const sampled = await this.DiscoverFieldsViaFetch(companyIntegration, obj.ExternalName, contextUser);
|
|
107
|
+
obj.Fields = mergeDeclaredWithSampledFields(obj.Fields, sampled);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
/* best-effort — a sample failure leaves the declared fields as-is */
|
|
111
|
+
}
|
|
112
|
+
}));
|
|
113
|
+
return info;
|
|
114
|
+
}
|
|
115
|
+
// ── TestConnection ────────────────────────────────────────────────
|
|
116
|
+
/** Reads the singleton CompanyInfo resource to verify connectivity + auth (read-only, non-mutating). */
|
|
117
|
+
async TestConnection(companyIntegration, contextUser) {
|
|
118
|
+
try {
|
|
119
|
+
const auth = await this.Authenticate(companyIntegration, contextUser);
|
|
120
|
+
const vendorCfg = this.resolveVendorConfig(companyIntegration.IntegrationID);
|
|
121
|
+
const url = `${auth.CompanyBaseURL}/companyinfo/${encodeURIComponent(auth.RealmId)}?minorversion=${vendorCfg.minorVersion}`;
|
|
122
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
123
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
124
|
+
const name = this.readCompanyName(response.Body);
|
|
125
|
+
return {
|
|
126
|
+
Success: true,
|
|
127
|
+
Message: `Connected to QuickBooks Online (realm ${auth.RealmId})${name ? `: ${name}` : ''}.`,
|
|
128
|
+
ServerVersion: `QuickBooks Online Accounting API v3 (minorversion ${vendorCfg.minorVersion})`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (response.Status === 401 || response.Status === 403) {
|
|
132
|
+
return { Success: false, Message: `QuickBooks authentication failed (HTTP ${response.Status}). ${this.ExtractErrorMessage(response) ?? ''}`.trim() };
|
|
133
|
+
}
|
|
134
|
+
return { Success: false, Message: `QuickBooks CompanyInfo probe returned HTTP ${response.Status}. ${this.ExtractErrorMessage(response) ?? ''}`.trim() };
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
return { Success: false, Message: `Connection failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// ── FetchChanges (OVERRIDE — QBO /query door + CDC deletions + singleton reads) ──
|
|
141
|
+
/**
|
|
142
|
+
* OVERRIDE (idiosyncratic): QBO reads ride a SQL-like /query door, not a flat REST collection, so the
|
|
143
|
+
* base's flat/offset URL machinery does not apply. This fetches ONE page (bounded by BatchSize, capped at
|
|
144
|
+
* QBO's 1000/query) and returns HasMore + NextOffset so the engine loops. On an incremental first page it
|
|
145
|
+
* also emits CDC deletion tombstones (the only documented QBO deletion source). Singleton read-only
|
|
146
|
+
* objects (CompanyInfo/Preferences) take the single-GET path.
|
|
147
|
+
*/
|
|
148
|
+
async FetchChanges(ctx) {
|
|
149
|
+
const obj = this.GetCachedObject(ctx.CompanyIntegration.IntegrationID, ctx.ObjectName);
|
|
150
|
+
const fields = this.GetCachedFields(obj.ID);
|
|
151
|
+
const objCfg = this.parseObjectConfig(obj);
|
|
152
|
+
const auth = await this.Authenticate(ctx.CompanyIntegration, ctx.ContextUser);
|
|
153
|
+
const vendorCfg = this.resolveVendorConfig(ctx.CompanyIntegration.IntegrationID);
|
|
154
|
+
if (objCfg.EntityClass === 'read-only') {
|
|
155
|
+
return this.fetchSingleton(auth, vendorCfg, obj, fields, objCfg);
|
|
156
|
+
}
|
|
157
|
+
return this.fetchQueryPage(auth, vendorCfg, obj, fields, objCfg, ctx);
|
|
158
|
+
}
|
|
159
|
+
// ── GetRecord (OVERRIDE — QBO `/{entity}/{id}` addressing + entity-name-nested unwrap) ──
|
|
160
|
+
/** OVERRIDE: single-record GET at `/{entity}/{id}?minorversion`; the record is nested under `<Entity>`. */
|
|
161
|
+
async GetRecord(ctx) {
|
|
162
|
+
const ci = ctx.CompanyIntegration;
|
|
163
|
+
const contextUser = ctx.ContextUser;
|
|
164
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
165
|
+
const fields = this.GetCachedFields(obj.ID);
|
|
166
|
+
const objCfg = this.parseObjectConfig(obj);
|
|
167
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
168
|
+
const vendorCfg = this.resolveVendorConfig(ci.IntegrationID);
|
|
169
|
+
const url = `${auth.CompanyBaseURL}/${objCfg.QueryEntity.toLowerCase()}/${encodeURIComponent(ctx.ExternalID)}?minorversion=${vendorCfg.minorVersion}`;
|
|
170
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
171
|
+
if (response.Status === 404)
|
|
172
|
+
return null;
|
|
173
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
174
|
+
throw new Error(`QuickBooks GetRecord "${ctx.ObjectName}"/${ctx.ExternalID} failed: HTTP ${response.Status} ${this.ExtractErrorMessage(response) ?? ''}`.trim());
|
|
175
|
+
}
|
|
176
|
+
const raw = this.readSingleEntity(response.Body, objCfg.QueryEntity);
|
|
177
|
+
if (!raw)
|
|
178
|
+
return null;
|
|
179
|
+
return this.buildExternalRecord(raw, obj, fields, objCfg);
|
|
180
|
+
}
|
|
181
|
+
// ── UpdateRecord (OVERRIDE — full-object POST + SyncToken + sparse) ──
|
|
182
|
+
/**
|
|
183
|
+
* OVERRIDE (idiosyncratic): QBO update is a full-object POST to `/{entity}` (no id in path) carrying the
|
|
184
|
+
* record's `Id`, current `SyncToken`, and `sparse:true` (so only supplied fields change). SyncToken is
|
|
185
|
+
* fetch-or-carry: used from the caller's attributes when present, else read live via GetRecord. A stale
|
|
186
|
+
* SyncToken (optimistic-concurrency conflict) is CLASSIFIED and returned as a failure — never blind-retried.
|
|
187
|
+
*/
|
|
188
|
+
async UpdateRecord(ctx) {
|
|
189
|
+
const ci = ctx.CompanyIntegration;
|
|
190
|
+
const contextUser = ctx.ContextUser;
|
|
191
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
192
|
+
if (!obj.SupportsUpdate || !obj.UpdateAPIPath) {
|
|
193
|
+
return { Success: false, StatusCode: 0, ErrorMessage: `UpdateRecord not supported for "${ctx.ObjectName}" (metadata declares no update path).` };
|
|
194
|
+
}
|
|
195
|
+
const objCfg = this.parseObjectConfig(obj);
|
|
196
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
197
|
+
const vendorCfg = this.resolveVendorConfig(ci.IntegrationID);
|
|
198
|
+
const syncToken = await this.resolveSyncToken(ctx.Attributes, obj, ci, contextUser, ctx.ExternalID);
|
|
199
|
+
if (syncToken == null) {
|
|
200
|
+
return { Success: false, StatusCode: 0, ErrorMessage: `QuickBooks update of "${ctx.ObjectName}"/${ctx.ExternalID}: could not resolve current SyncToken (required for optimistic concurrency).` };
|
|
201
|
+
}
|
|
202
|
+
const body = { ...ctx.Attributes, Id: ctx.ExternalID, SyncToken: syncToken, sparse: true };
|
|
203
|
+
const url = `${auth.CompanyBaseURL}/${objCfg.QueryEntity.toLowerCase()}?minorversion=${vendorCfg.minorVersion}`;
|
|
204
|
+
const response = await this.MakeHTTPRequest(auth, url, 'POST', this.BuildHeaders(auth), body);
|
|
205
|
+
return this.buildWriteResult(response, ctx.ExternalID, 'update', ctx.ObjectName);
|
|
206
|
+
}
|
|
207
|
+
// ── DeleteRecord (OVERRIDE — hard-delete transactions vs deactivate name-lists) ──
|
|
208
|
+
/**
|
|
209
|
+
* OVERRIDE (idiosyncratic): QBO has NO uniform delete verb. Transaction entities with SupportsDelete
|
|
210
|
+
* hard-delete via `POST /{entity}?operation=delete` ({Id, SyncToken}); name-list entities have no hard
|
|
211
|
+
* delete and instead DEACTIVATE via a sparse update `Active=false` (SyncToken required). Anything else
|
|
212
|
+
* (read-only, or a transaction with delete intentionally unsupported) fails loudly.
|
|
213
|
+
*/
|
|
214
|
+
async DeleteRecord(ctx) {
|
|
215
|
+
const ci = ctx.CompanyIntegration;
|
|
216
|
+
const contextUser = ctx.ContextUser;
|
|
217
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
218
|
+
const objCfg = this.parseObjectConfig(obj);
|
|
219
|
+
// Route FIRST — fail loudly for entity classes that support neither hard-delete nor deactivate,
|
|
220
|
+
// before spending a SyncToken read.
|
|
221
|
+
const hardDelete = objCfg.EntityClass === 'transaction' && obj.SupportsDelete;
|
|
222
|
+
const deactivate = objCfg.EntityClass === 'namelist';
|
|
223
|
+
if (!hardDelete && !deactivate) {
|
|
224
|
+
return { Success: false, StatusCode: 0, ErrorMessage: `QuickBooks "${ctx.ObjectName}" does not support delete or deactivate (entity class: ${objCfg.EntityClass}${objCfg.EntityClass === 'transaction' ? ', delete not enabled' : ''}).` };
|
|
225
|
+
}
|
|
226
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
227
|
+
const vendorCfg = this.resolveVendorConfig(ci.IntegrationID);
|
|
228
|
+
const syncToken = await this.resolveSyncToken({}, obj, ci, contextUser, ctx.ExternalID);
|
|
229
|
+
if (syncToken == null) {
|
|
230
|
+
return { Success: false, StatusCode: 0, ErrorMessage: `QuickBooks delete of "${ctx.ObjectName}"/${ctx.ExternalID}: could not resolve current SyncToken.` };
|
|
231
|
+
}
|
|
232
|
+
if (hardDelete) {
|
|
233
|
+
const url = `${auth.CompanyBaseURL}/${objCfg.QueryEntity.toLowerCase()}?operation=delete&minorversion=${vendorCfg.minorVersion}`;
|
|
234
|
+
const body = { Id: ctx.ExternalID, SyncToken: syncToken };
|
|
235
|
+
const response = await this.MakeHTTPRequest(auth, url, 'POST', this.BuildHeaders(auth), body);
|
|
236
|
+
return this.buildWriteResult(response, ctx.ExternalID, 'delete', ctx.ObjectName);
|
|
237
|
+
}
|
|
238
|
+
// Name-list "delete" is a soft deactivate: sparse update Active=false.
|
|
239
|
+
const url = `${auth.CompanyBaseURL}/${objCfg.QueryEntity.toLowerCase()}?minorversion=${vendorCfg.minorVersion}`;
|
|
240
|
+
const body = { Id: ctx.ExternalID, SyncToken: syncToken, sparse: true, Active: false };
|
|
241
|
+
const response = await this.MakeHTTPRequest(auth, url, 'POST', this.BuildHeaders(auth), body);
|
|
242
|
+
return this.buildWriteResult(response, ctx.ExternalID, 'deactivate', ctx.ObjectName);
|
|
243
|
+
}
|
|
244
|
+
// ── Abstract REST hooks ───────────────────────────────────────────
|
|
245
|
+
/** Resolves credentials, mints/refreshes the access token via the shared manager, persists a rotated refresh token. */
|
|
246
|
+
async Authenticate(companyIntegration, contextUser) {
|
|
247
|
+
const creds = await this.loadCredentials(companyIntegration, contextUser);
|
|
248
|
+
if (!creds.RealmId) {
|
|
249
|
+
throw new Error('QuickBooks connector: no realmId (QuickBooks Company ID) on the Credential or CompanyIntegration.');
|
|
250
|
+
}
|
|
251
|
+
if (this.cachedAuth && this.cachedAuthRealm === creds.RealmId)
|
|
252
|
+
return this.cachedAuth;
|
|
253
|
+
if (!creds.ClientId || !creds.ClientSecret) {
|
|
254
|
+
throw new Error('QuickBooks connector: OAuth2 clientId/clientSecret not found on the Credential or CompanyIntegration.');
|
|
255
|
+
}
|
|
256
|
+
if (!creds.RefreshToken) {
|
|
257
|
+
throw new Error('QuickBooks connector: no OAuth2 refresh token found — the authorization-code flow must be completed and its refresh token stored first.');
|
|
258
|
+
}
|
|
259
|
+
const vendorCfg = this.resolveVendorConfig(companyIntegration.IntegrationID);
|
|
260
|
+
const token = await this.tokenManager.GetAccessToken({
|
|
261
|
+
TokenURL: vendorCfg.tokenEndpoint,
|
|
262
|
+
ClientId: creds.ClientId,
|
|
263
|
+
ClientSecret: creds.ClientSecret,
|
|
264
|
+
RefreshToken: creds.RefreshToken,
|
|
265
|
+
Scopes: vendorCfg.scope,
|
|
266
|
+
UseBasicAuth: true,
|
|
267
|
+
}, 'refresh_token');
|
|
268
|
+
await this.persistRotatedRefreshToken(companyIntegration, contextUser, creds, token.RefreshToken, token.AccessToken, token.ExpiresAt);
|
|
269
|
+
this.cachedAuth = {
|
|
270
|
+
Token: token.AccessToken,
|
|
271
|
+
RealmId: creds.RealmId,
|
|
272
|
+
CompanyBaseURL: this.buildCompanyBaseURL(vendorCfg, creds),
|
|
273
|
+
ExpiresAt: new Date(token.ExpiresAt),
|
|
274
|
+
};
|
|
275
|
+
this.cachedAuthRealm = creds.RealmId;
|
|
276
|
+
return this.cachedAuth;
|
|
277
|
+
}
|
|
278
|
+
/** Bearer auth + explicit JSON Accept (QBO defaults to XML when Accept is absent). */
|
|
279
|
+
BuildHeaders(auth) {
|
|
280
|
+
return {
|
|
281
|
+
'Authorization': `Bearer ${auth.Token}`,
|
|
282
|
+
'Accept': 'application/json',
|
|
283
|
+
'Content-Type': 'application/json',
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
/** HTTP transport (fetch) with bounded 429/503 backoff honoring Retry-After. Test subclasses override this. */
|
|
287
|
+
async MakeHTTPRequest(_auth, url, method, headers, body) {
|
|
288
|
+
let lastTid;
|
|
289
|
+
for (let attempt = 0; attempt <= MAX_TRANSIENT_RETRIES; attempt++) {
|
|
290
|
+
const response = await fetch(url, {
|
|
291
|
+
method,
|
|
292
|
+
headers,
|
|
293
|
+
body: body !== undefined && body !== null ? JSON.stringify(body) : undefined,
|
|
294
|
+
});
|
|
295
|
+
const respHeaders = {};
|
|
296
|
+
response.headers.forEach((v, k) => { respHeaders[k.toLowerCase()] = v; });
|
|
297
|
+
lastTid = respHeaders[DEFAULT_CORRELATION_HEADER];
|
|
298
|
+
if ((response.status === 429 || response.status === 503) && attempt < MAX_TRANSIENT_RETRIES) {
|
|
299
|
+
await this.sleep(this.computeBackoffMs(respHeaders, attempt));
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (response.status === 429) {
|
|
303
|
+
throw new QuickBooksRateLimitError(`QuickBooks rate limit (HTTP 429) after ${MAX_TRANSIENT_RETRIES} retries: ${method} ${url}`, this.parseRetryAfterMs(respHeaders), lastTid);
|
|
304
|
+
}
|
|
305
|
+
const text = await response.text();
|
|
306
|
+
let parsed = null;
|
|
307
|
+
if (text.length > 0) {
|
|
308
|
+
try {
|
|
309
|
+
parsed = JSON.parse(text);
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
parsed = text;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { Status: response.status, Body: parsed, Headers: respHeaders };
|
|
316
|
+
}
|
|
317
|
+
throw new QuickBooksRateLimitError(`QuickBooks request exhausted retries: ${method} ${url}`, undefined, lastTid);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Strips the QBO envelope to a record array. A /query response nests records under
|
|
321
|
+
* `QueryResponse.<Entity>`; a single-record response nests one object under `<Entity>`. `responseDataKey`
|
|
322
|
+
* carries the entity name. Falls back to the first array found under QueryResponse.
|
|
323
|
+
*/
|
|
324
|
+
NormalizeResponse(rawBody, responseDataKey) {
|
|
325
|
+
if (rawBody == null || typeof rawBody !== 'object')
|
|
326
|
+
return [];
|
|
327
|
+
const body = rawBody;
|
|
328
|
+
const queryResponse = body[DEFAULT_RECORDS_PATH];
|
|
329
|
+
if (queryResponse && typeof queryResponse === 'object') {
|
|
330
|
+
const qr = queryResponse;
|
|
331
|
+
if (responseDataKey && Array.isArray(qr[responseDataKey]))
|
|
332
|
+
return qr[responseDataKey];
|
|
333
|
+
for (const v of Object.values(qr)) {
|
|
334
|
+
if (Array.isArray(v))
|
|
335
|
+
return v;
|
|
336
|
+
}
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
339
|
+
if (responseDataKey) {
|
|
340
|
+
const v = body[responseDataKey];
|
|
341
|
+
if (Array.isArray(v))
|
|
342
|
+
return v;
|
|
343
|
+
if (v && typeof v === 'object')
|
|
344
|
+
return [v];
|
|
345
|
+
}
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
/** Offset pagination continuation from the QueryResponse totalCount, else a full-page heuristic. */
|
|
349
|
+
ExtractPaginationInfo(rawBody, paginationType, _currentPage, currentOffset, pageSize) {
|
|
350
|
+
if (paginationType !== 'Offset')
|
|
351
|
+
return { HasMore: false };
|
|
352
|
+
const qr = this.readQueryResponse(rawBody);
|
|
353
|
+
const totalCount = typeof qr?.totalCount === 'number' ? qr.totalCount : undefined;
|
|
354
|
+
const fetched = this.countQueryRecords(qr);
|
|
355
|
+
const nextOffset = currentOffset + fetched;
|
|
356
|
+
const hasMore = totalCount != null ? nextOffset < totalCount : fetched >= pageSize;
|
|
357
|
+
return { HasMore: hasMore, NextOffset: hasMore ? nextOffset : undefined, TotalRecords: totalCount };
|
|
358
|
+
}
|
|
359
|
+
/** Company-scoped base URL, e.g. `https://quickbooks.api.intuit.com/v3/company/{realmId}`. */
|
|
360
|
+
GetBaseURL(_companyIntegration, auth) {
|
|
361
|
+
return auth.CompanyBaseURL;
|
|
362
|
+
}
|
|
363
|
+
/** OVERRIDE: a created record's Id is nested under the entity name in the QBO create response. */
|
|
364
|
+
ExtractIDFromResponse(response, _idLocation) {
|
|
365
|
+
if (!response.Body || typeof response.Body !== 'object')
|
|
366
|
+
return undefined;
|
|
367
|
+
const body = response.Body;
|
|
368
|
+
for (const v of Object.values(body)) {
|
|
369
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
370
|
+
const id = v.Id;
|
|
371
|
+
if (typeof id === 'string' || typeof id === 'number')
|
|
372
|
+
return String(id);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return undefined;
|
|
376
|
+
}
|
|
377
|
+
/** OVERRIDE: QBO Fault envelope (`{ fault: { error: [{ message, detail, code }] } }`, lowercased JSON keys). */
|
|
378
|
+
ExtractErrorMessage(response) {
|
|
379
|
+
const errs = this.readFaultErrors(response.Body);
|
|
380
|
+
if (errs.length === 0)
|
|
381
|
+
return undefined;
|
|
382
|
+
return errs.map(e => `[${e.code ?? '?'}] ${e.message ?? ''}${e.detail ? ` — ${e.detail}` : ''}`.trim()).join('; ');
|
|
383
|
+
}
|
|
384
|
+
// ── Read helpers ──────────────────────────────────────────────────
|
|
385
|
+
/** Fetches one /query page with in-query STARTPOSITION/MAXRESULTS + optional watermark; emits CDC deletes. */
|
|
386
|
+
async fetchQueryPage(auth, vendorCfg, obj, fields, objCfg, ctx) {
|
|
387
|
+
const offset = ctx.CurrentOffset ?? 0;
|
|
388
|
+
const startPosition = offset + objCfg.SkipBase;
|
|
389
|
+
const maxResults = Math.min(ctx.BatchSize && ctx.BatchSize > 0 ? ctx.BatchSize : DEFAULT_PAGE_SIZE, MAX_QUERY_RESULTS);
|
|
390
|
+
const incremental = obj.SupportsIncrementalSync && !!ctx.WatermarkValue && !!obj.IncrementalWatermarkField;
|
|
391
|
+
const queryText = this.buildQueryText(objCfg.QueryEntity, incremental ? ctx.WatermarkValue : null, obj.IncrementalWatermarkField, startPosition, maxResults);
|
|
392
|
+
const url = `${auth.CompanyBaseURL}/query?query=${encodeURIComponent(queryText)}&minorversion=${vendorCfg.minorVersion}`;
|
|
393
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
394
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
395
|
+
throw new Error(`QuickBooks query "${objCfg.QueryEntity}" failed: HTTP ${response.Status} ${this.ExtractErrorMessage(response) ?? ''}`.trim());
|
|
396
|
+
}
|
|
397
|
+
const raw = this.NormalizeResponse(response.Body, objCfg.QueryEntity);
|
|
398
|
+
const records = raw.map(r => this.buildExternalRecord(r, obj, fields, objCfg));
|
|
399
|
+
const pagination = this.ExtractPaginationInfo(response.Body, 'Offset', 1, offset, maxResults);
|
|
400
|
+
const warnings = [];
|
|
401
|
+
// CDC deletions (the only documented QBO deletion source) — once, on the incremental FIRST page.
|
|
402
|
+
if (incremental && objCfg.CdcEligible && offset === 0) {
|
|
403
|
+
try {
|
|
404
|
+
const tombstones = await this.fetchCdcDeletions(auth, vendorCfg, objCfg, ctx.WatermarkValue, obj);
|
|
405
|
+
for (const t of tombstones)
|
|
406
|
+
records.push(t);
|
|
407
|
+
}
|
|
408
|
+
catch (e) {
|
|
409
|
+
warnings.push({ Code: 'CDC_DELETIONS_UNAVAILABLE', Message: `CDC deletion probe for "${objCfg.QueryEntity}" failed: ${e instanceof Error ? e.message : String(e)}`, Data: { object: obj.Name } });
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const result = {
|
|
413
|
+
Records: records,
|
|
414
|
+
HasMore: pagination.HasMore,
|
|
415
|
+
NextOffset: pagination.NextOffset,
|
|
416
|
+
};
|
|
417
|
+
const newWatermark = this.maxWatermark(raw, obj.IncrementalWatermarkField);
|
|
418
|
+
if (newWatermark)
|
|
419
|
+
result.NewWatermarkValue = newWatermark;
|
|
420
|
+
if (warnings.length > 0)
|
|
421
|
+
result.Warnings = warnings;
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
424
|
+
/** Single-GET read for QBO singleton resources (CompanyInfo/Preferences) at `/{entity}/{realmId}`. */
|
|
425
|
+
async fetchSingleton(auth, vendorCfg, obj, fields, objCfg) {
|
|
426
|
+
const url = `${auth.CompanyBaseURL}/${objCfg.QueryEntity.toLowerCase()}/${encodeURIComponent(auth.RealmId)}?minorversion=${vendorCfg.minorVersion}`;
|
|
427
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
428
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
429
|
+
throw new Error(`QuickBooks read "${objCfg.QueryEntity}" failed: HTTP ${response.Status} ${this.ExtractErrorMessage(response) ?? ''}`.trim());
|
|
430
|
+
}
|
|
431
|
+
const raw = this.readSingleEntity(response.Body, objCfg.QueryEntity);
|
|
432
|
+
const records = raw ? [this.buildExternalRecord(raw, obj, fields, objCfg)] : [];
|
|
433
|
+
return { Records: records, HasMore: false };
|
|
434
|
+
}
|
|
435
|
+
/** Fetches the CDC door and returns deletion tombstones for the object. */
|
|
436
|
+
async fetchCdcDeletions(auth, vendorCfg, objCfg, changedSince, obj) {
|
|
437
|
+
const url = `${auth.CompanyBaseURL}/cdc?entities=${encodeURIComponent(objCfg.QueryEntity)}&changedSince=${encodeURIComponent(changedSince)}&minorversion=${vendorCfg.minorVersion}`;
|
|
438
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
439
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
440
|
+
throw new Error(`HTTP ${response.Status} ${this.ExtractErrorMessage(response) ?? ''}`.trim());
|
|
441
|
+
}
|
|
442
|
+
return this.parseCdcDeletions(response.Body, objCfg.QueryEntity, obj.Name);
|
|
443
|
+
}
|
|
444
|
+
/** Walks the CDCResponse envelope, extracting rows whose `status` is Deleted for the given entity. */
|
|
445
|
+
parseCdcDeletions(body, queryEntity, objectType) {
|
|
446
|
+
const out = [];
|
|
447
|
+
if (!body || typeof body !== 'object')
|
|
448
|
+
return out;
|
|
449
|
+
const cdc = body.CDCResponse;
|
|
450
|
+
if (!Array.isArray(cdc))
|
|
451
|
+
return out;
|
|
452
|
+
for (const block of cdc) {
|
|
453
|
+
if (!block || typeof block !== 'object')
|
|
454
|
+
continue;
|
|
455
|
+
const qrs = block.QueryResponse;
|
|
456
|
+
if (!Array.isArray(qrs))
|
|
457
|
+
continue;
|
|
458
|
+
for (const qr of qrs) {
|
|
459
|
+
if (!qr || typeof qr !== 'object')
|
|
460
|
+
continue;
|
|
461
|
+
const arr = qr[queryEntity];
|
|
462
|
+
if (!Array.isArray(arr))
|
|
463
|
+
continue;
|
|
464
|
+
for (const item of arr) {
|
|
465
|
+
if (!item || typeof item !== 'object')
|
|
466
|
+
continue;
|
|
467
|
+
const row = item;
|
|
468
|
+
const status = typeof row.status === 'string' ? row.status : (typeof row.Status === 'string' ? row.Status : '');
|
|
469
|
+
if (status.toLowerCase() !== 'deleted')
|
|
470
|
+
continue;
|
|
471
|
+
const id = row.Id;
|
|
472
|
+
if (typeof id !== 'string' && typeof id !== 'number')
|
|
473
|
+
continue;
|
|
474
|
+
out.push({ ExternalID: String(id), ObjectType: objectType, Fields: { ...row }, IsDeleted: true });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Builds an ExternalRecord with the FULL source record in Fields (custom-column pass-through contract).
|
|
482
|
+
* Mirrors the base ToExternalRecord identity/content-hash semantics: a fully-present PK → joined key;
|
|
483
|
+
* a keyless/partial-key record → a deterministic content hash stamped into the single PK field so it is
|
|
484
|
+
* still syncable + idempotent on re-sync. Never drops a source key.
|
|
485
|
+
*/
|
|
486
|
+
buildExternalRecord(raw, obj, fields, objCfg) {
|
|
487
|
+
// applyTransformPreservingKeys runs TransformRecord then re-adds any dropped key → Fields stays FULL.
|
|
488
|
+
const transformed = this.applyTransformPreservingKeys(raw, obj, fields);
|
|
489
|
+
const declaredPk = this.primaryKeyFieldNames(fields);
|
|
490
|
+
const pkNames = declaredPk.length > 0 ? declaredPk : ['Id']; // QBO universal PK
|
|
491
|
+
const allPkPresent = pkNames.every(n => transformed[n] != null && serializeKeyValue(transformed[n]).length > 0);
|
|
492
|
+
const joined = pkNames.map(n => serializeKeyValue(transformed[n])).join('|');
|
|
493
|
+
const resolvedID = allPkPresent ? joined : computeContentHash(transformed);
|
|
494
|
+
let fieldsOut = transformed;
|
|
495
|
+
if (!allPkPresent && pkNames.length === 1 && (transformed[pkNames[0]] == null || serializeKeyValue(transformed[pkNames[0]]).length === 0)) {
|
|
496
|
+
fieldsOut = { ...transformed, [pkNames[0]]: resolvedID };
|
|
497
|
+
}
|
|
498
|
+
const modifiedAt = this.readLastUpdated(raw, obj.IncrementalWatermarkField);
|
|
499
|
+
void objCfg;
|
|
500
|
+
return {
|
|
501
|
+
ExternalID: resolvedID,
|
|
502
|
+
ObjectType: obj.Name,
|
|
503
|
+
Fields: fieldsOut,
|
|
504
|
+
...(modifiedAt ? { ModifiedAt: modifiedAt } : {}),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
// ── Write helpers ─────────────────────────────────────────────────
|
|
508
|
+
/**
|
|
509
|
+
* Resolves the current SyncToken for an update/delete: from the caller's attributes when present, else a
|
|
510
|
+
* live GetRecord read. QBO's optimistic-concurrency requires the CURRENT token; a stale one is rejected
|
|
511
|
+
* by the server (classified as a conflict at write time), so we prefer a freshly-read token when absent.
|
|
512
|
+
*/
|
|
513
|
+
async resolveSyncToken(attributes, obj, ci, contextUser, externalID) {
|
|
514
|
+
const carried = attributes.SyncToken;
|
|
515
|
+
if (typeof carried === 'string' && carried.length > 0)
|
|
516
|
+
return carried;
|
|
517
|
+
if (typeof carried === 'number')
|
|
518
|
+
return String(carried);
|
|
519
|
+
const id = externalID ?? (typeof attributes.Id === 'string' ? attributes.Id : undefined);
|
|
520
|
+
if (!id)
|
|
521
|
+
return null;
|
|
522
|
+
const current = await this.GetRecord({ CompanyIntegration: ci, ObjectName: obj.Name, ContextUser: contextUser, ExternalID: id });
|
|
523
|
+
const token = current?.Fields?.SyncToken;
|
|
524
|
+
if (typeof token === 'string' && token.length > 0)
|
|
525
|
+
return token;
|
|
526
|
+
if (typeof token === 'number')
|
|
527
|
+
return String(token);
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
/** Maps a QBO write response to a CRUDResult, classifying a stale-SyncToken conflict distinctly. */
|
|
531
|
+
buildWriteResult(response, externalID, op, objectName) {
|
|
532
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
533
|
+
const returnedId = this.ExtractIDFromResponse(response, 'body') ?? externalID;
|
|
534
|
+
return { Success: true, StatusCode: response.Status, ExternalID: returnedId };
|
|
535
|
+
}
|
|
536
|
+
const message = this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on ${op}`;
|
|
537
|
+
if (this.isStaleTokenConflict(response)) {
|
|
538
|
+
const severity = ClassifyError(new Error(message)).Severity;
|
|
539
|
+
return {
|
|
540
|
+
Success: false,
|
|
541
|
+
StatusCode: response.Status,
|
|
542
|
+
ErrorMessage: `QuickBooks ${op} of "${objectName}"/${externalID} conflict [${severity}] — stale SyncToken (optimistic concurrency). Re-read the record and retry with its current SyncToken. ${message}`.trim(),
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
return { Success: false, StatusCode: response.Status, ErrorMessage: `QuickBooks ${op} of "${objectName}"/${externalID}: ${message}` };
|
|
546
|
+
}
|
|
547
|
+
/** True when the Fault carries QBO's stale-object-version code (5010) or an explicit stale-token message. */
|
|
548
|
+
isStaleTokenConflict(response) {
|
|
549
|
+
const errs = this.readFaultErrors(response.Body);
|
|
550
|
+
return errs.some(e => e.code === '5010' || (typeof e.message === 'string' && /stale|sync token|object version/i.test(e.message)));
|
|
551
|
+
}
|
|
552
|
+
// ── Credential resolution + rotating-refresh-token persistence ────
|
|
553
|
+
/** Merges credentials from the linked Credential entity (secrets win) and CompanyIntegration fields/Configuration. */
|
|
554
|
+
async loadCredentials(companyIntegration, contextUser) {
|
|
555
|
+
const fromCiConfig = companyIntegration.Configuration ? this.parseCredentialJson(companyIntegration.Configuration) : null;
|
|
556
|
+
const fromCiColumns = {
|
|
557
|
+
ClientId: companyIntegration.ClientID ?? undefined,
|
|
558
|
+
ClientSecret: companyIntegration.ClientSecret ?? undefined,
|
|
559
|
+
RefreshToken: companyIntegration.RefreshToken ?? undefined,
|
|
560
|
+
RealmId: companyIntegration.ExternalSystemID ?? undefined,
|
|
561
|
+
};
|
|
562
|
+
let fromCred = null;
|
|
563
|
+
if (companyIntegration.CredentialID) {
|
|
564
|
+
fromCred = await this.loadFromCredential(companyIntegration.CredentialID, contextUser);
|
|
565
|
+
}
|
|
566
|
+
const merged = {
|
|
567
|
+
Environment: 'production',
|
|
568
|
+
RefreshTokenSource: 'none',
|
|
569
|
+
...(fromCiConfig ?? {}),
|
|
570
|
+
...this.stripUndefined(fromCiColumns),
|
|
571
|
+
...this.stripUndefined(fromCred ?? {}),
|
|
572
|
+
};
|
|
573
|
+
// Determine where the refresh token actually came from (credential wins), so a rotation writes back there.
|
|
574
|
+
if (fromCred?.RefreshToken)
|
|
575
|
+
merged.RefreshTokenSource = 'credential';
|
|
576
|
+
else if (fromCiColumns.RefreshToken || fromCiConfig?.RefreshToken)
|
|
577
|
+
merged.RefreshTokenSource = 'companyIntegration';
|
|
578
|
+
merged.Environment = this.normalizeEnvironment(merged.Environment);
|
|
579
|
+
return merged;
|
|
580
|
+
}
|
|
581
|
+
/** Loads a Credential row and parses its Values JSON. */
|
|
582
|
+
async loadFromCredential(credentialID, contextUser, provider) {
|
|
583
|
+
const md = provider ?? new Metadata();
|
|
584
|
+
const credential = await md.GetEntityObject('MJ: Credentials', contextUser);
|
|
585
|
+
const loaded = await credential.Load(credentialID);
|
|
586
|
+
if (!loaded || !credential.Values)
|
|
587
|
+
return null;
|
|
588
|
+
return this.parseCredentialJson(credential.Values);
|
|
589
|
+
}
|
|
590
|
+
/** Parses a QBO credential/config JSON blob, tolerant of key-casing variants. */
|
|
591
|
+
parseCredentialJson(json) {
|
|
592
|
+
let parsed;
|
|
593
|
+
try {
|
|
594
|
+
parsed = JSON.parse(json);
|
|
595
|
+
}
|
|
596
|
+
catch {
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
const env = this.firstString(parsed, ['Environment', 'environment', 'env']);
|
|
600
|
+
return {
|
|
601
|
+
ClientId: this.firstString(parsed, ['ClientId', 'clientId', 'client_id', 'ClientID']),
|
|
602
|
+
ClientSecret: this.firstString(parsed, ['ClientSecret', 'clientSecret', 'client_secret']),
|
|
603
|
+
RefreshToken: this.firstString(parsed, ['RefreshToken', 'refreshToken', 'refresh_token']),
|
|
604
|
+
RealmId: this.firstString(parsed, ['RealmId', 'realmId', 'realm_id', 'RealmID', 'companyId', 'CompanyId']),
|
|
605
|
+
...(env ? { Environment: this.normalizeEnvironment(env) } : {}),
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Persists the (rotated) refresh token back to its source ONLY when it changed. Intuit invalidates the
|
|
610
|
+
* previous refresh token on every exchange, so a connector that fails to persist the new one authenticates
|
|
611
|
+
* once and then dies on the next process. Also caches the fresh access token + expiry on the connection.
|
|
612
|
+
*/
|
|
613
|
+
async persistRotatedRefreshToken(companyIntegration, contextUser, creds, newRefreshToken, accessToken, expiresAt) {
|
|
614
|
+
if (!newRefreshToken || newRefreshToken === creds.RefreshToken) {
|
|
615
|
+
// No rotation: still refresh the cached access token on the connection (best-effort).
|
|
616
|
+
await this.saveAccessTokenOnConnection(companyIntegration, accessToken, expiresAt);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
if (creds.RefreshTokenSource === 'credential' && companyIntegration.CredentialID) {
|
|
620
|
+
await this.saveRefreshTokenToCredential(companyIntegration.CredentialID, contextUser, newRefreshToken);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
await this.saveRefreshTokenToConnection(companyIntegration, newRefreshToken);
|
|
624
|
+
}
|
|
625
|
+
await this.saveAccessTokenOnConnection(companyIntegration, accessToken, expiresAt);
|
|
626
|
+
}
|
|
627
|
+
/** Writes the rotated refresh token into the Credential.Values JSON (preserving the other keys). */
|
|
628
|
+
async saveRefreshTokenToCredential(credentialID, contextUser, newRefreshToken) {
|
|
629
|
+
const md = new Metadata();
|
|
630
|
+
const credential = await md.GetEntityObject('MJ: Credentials', contextUser);
|
|
631
|
+
if (!(await credential.Load(credentialID)))
|
|
632
|
+
return;
|
|
633
|
+
let values = {};
|
|
634
|
+
try {
|
|
635
|
+
values = credential.Values ? JSON.parse(credential.Values) : {};
|
|
636
|
+
}
|
|
637
|
+
catch {
|
|
638
|
+
values = {};
|
|
639
|
+
}
|
|
640
|
+
// Update whichever refresh-token key the blob already used, else the canonical camelCase.
|
|
641
|
+
const key = ['refreshToken', 'RefreshToken', 'refresh_token'].find(k => k in values) ?? 'refreshToken';
|
|
642
|
+
values[key] = newRefreshToken;
|
|
643
|
+
credential.Values = JSON.stringify(values);
|
|
644
|
+
await credential.Save();
|
|
645
|
+
}
|
|
646
|
+
/** Writes the rotated refresh token to the CompanyIntegration.RefreshToken column. */
|
|
647
|
+
async saveRefreshTokenToConnection(companyIntegration, newRefreshToken) {
|
|
648
|
+
companyIntegration.RefreshToken = newRefreshToken;
|
|
649
|
+
await companyIntegration.Save();
|
|
650
|
+
}
|
|
651
|
+
/** Caches the fresh access token + expiry on the connection (best-effort; a save failure is non-fatal). */
|
|
652
|
+
async saveAccessTokenOnConnection(companyIntegration, accessToken, expiresAt) {
|
|
653
|
+
try {
|
|
654
|
+
companyIntegration.AccessToken = accessToken;
|
|
655
|
+
companyIntegration.TokenExpirationDate = new Date(expiresAt);
|
|
656
|
+
await companyIntegration.Save();
|
|
657
|
+
}
|
|
658
|
+
catch {
|
|
659
|
+
/* non-fatal: the in-memory token manager still holds the live access token for this run */
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
// ── Config resolution ─────────────────────────────────────────────
|
|
663
|
+
/** Reads Integration.Configuration for vendor-wide facts, with published QBO fallbacks (no tenant data). */
|
|
664
|
+
resolveVendorConfig(integrationID) {
|
|
665
|
+
const raw = this.readIntegrationConfig(integrationID);
|
|
666
|
+
const hosts = (raw?.hosts && typeof raw.hosts === 'object') ? raw.hosts : {};
|
|
667
|
+
const minor = (raw?.minorVersion && typeof raw.minorVersion === 'object') ? raw.minorVersion : {};
|
|
668
|
+
const auth = (raw?.authFlow && typeof raw.authFlow === 'object') ? raw.authFlow : {};
|
|
669
|
+
const rate = (raw?.rateLimitPolicy && typeof raw.rateLimitPolicy === 'object') ? raw.rateLimitPolicy : {};
|
|
670
|
+
const cfg = {
|
|
671
|
+
hosts: {
|
|
672
|
+
production: this.asString(hosts.production) ?? DEFAULT_HOST_PRODUCTION,
|
|
673
|
+
sandbox: this.asString(hosts.sandbox) ?? DEFAULT_HOST_SANDBOX,
|
|
674
|
+
},
|
|
675
|
+
companyPathTemplate: this.asString(raw?.companyPathTemplate) ?? DEFAULT_COMPANY_PATH_TEMPLATE,
|
|
676
|
+
minorVersion: this.asNumber(minor.thisBuildTargets) ?? DEFAULT_MINOR_VERSION,
|
|
677
|
+
tokenEndpoint: this.asString(auth.tokenEndpoint) ?? DEFAULT_TOKEN_ENDPOINT,
|
|
678
|
+
scope: this.asString(auth.scope) ?? DEFAULT_SCOPE,
|
|
679
|
+
recordsPath: this.asString(raw?.recordsPath) ?? DEFAULT_RECORDS_PATH,
|
|
680
|
+
correlationHeader: this.asString(raw?.correlationHeader) ?? DEFAULT_CORRELATION_HEADER,
|
|
681
|
+
perMinuteLimit: this.parsePerMinuteLimit(rate.perRealmLimit) ?? DEFAULT_PER_MINUTE_LIMIT,
|
|
682
|
+
concurrentLimit: this.asNumber(rate.concurrentRequestLimit) ?? DEFAULT_CONCURRENT_LIMIT,
|
|
683
|
+
};
|
|
684
|
+
this.lastVendorConfig = cfg;
|
|
685
|
+
return cfg;
|
|
686
|
+
}
|
|
687
|
+
/** Reads the raw Integration.Configuration object from the engine cache (null-tolerant for unit tests). */
|
|
688
|
+
readIntegrationConfig(integrationID) {
|
|
689
|
+
try {
|
|
690
|
+
const integration = IntegrationEngineBase.Instance.GetIntegrationByID(integrationID);
|
|
691
|
+
const cfg = integration?.Configuration;
|
|
692
|
+
if (!cfg || typeof cfg !== 'string')
|
|
693
|
+
return null;
|
|
694
|
+
return JSON.parse(cfg);
|
|
695
|
+
}
|
|
696
|
+
catch {
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
/** Parses the per-object IntegrationObject.Configuration into a typed shape (QueryEntity/entity class/etc). */
|
|
701
|
+
parseObjectConfig(obj) {
|
|
702
|
+
let cfg = {};
|
|
703
|
+
const rawCfg = obj.Configuration;
|
|
704
|
+
if (rawCfg && typeof rawCfg === 'string') {
|
|
705
|
+
try {
|
|
706
|
+
cfg = JSON.parse(rawCfg);
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
cfg = {};
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
const queryEntity = this.asString(cfg.QueryEntity) ?? obj.Name;
|
|
713
|
+
const entityClassRaw = this.asString(cfg.entityClass);
|
|
714
|
+
const entityClass = entityClassRaw === 'namelist' || entityClassRaw === 'read-only' ? entityClassRaw : 'transaction';
|
|
715
|
+
const pagination = (cfg.pagination && typeof cfg.pagination === 'object') ? cfg.pagination : {};
|
|
716
|
+
return {
|
|
717
|
+
QueryEntity: queryEntity,
|
|
718
|
+
EntityClass: entityClass,
|
|
719
|
+
CdcEligible: cfg.cdcEligible === true,
|
|
720
|
+
SkipBase: this.asNumber(pagination.skipParamBase) ?? 1,
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
/** Selects the host by environment and applies the company path template with the realm id. */
|
|
724
|
+
buildCompanyBaseURL(vendorCfg, creds) {
|
|
725
|
+
const host = (creds.Environment === 'sandbox' ? vendorCfg.hosts.sandbox : vendorCfg.hosts.production).replace(/\/+$/, '');
|
|
726
|
+
const path = vendorCfg.companyPathTemplate.replace('{realmId}', encodeURIComponent(creds.RealmId ?? '')).replace(/^\/+/, '');
|
|
727
|
+
return `${host}/${path}`;
|
|
728
|
+
}
|
|
729
|
+
// ── Query-text builder ────────────────────────────────────────────
|
|
730
|
+
/** Builds a QBO SQL-like SELECT with the watermark WHERE + ORDERBY and STARTPOSITION/MAXRESULTS in-text. */
|
|
731
|
+
buildQueryText(queryEntity, watermark, watermarkField, startPosition, maxResults) {
|
|
732
|
+
let q = `select * from ${queryEntity}`;
|
|
733
|
+
if (watermark && watermarkField) {
|
|
734
|
+
q += ` where ${watermarkField} > '${this.escapeQueryLiteral(watermark)}'`;
|
|
735
|
+
q += ` orderby ${watermarkField}`;
|
|
736
|
+
}
|
|
737
|
+
q += ` startposition ${startPosition} maxresults ${maxResults}`;
|
|
738
|
+
return q;
|
|
739
|
+
}
|
|
740
|
+
/** Escapes a QBO SQL string literal (single quotes doubled). */
|
|
741
|
+
escapeQueryLiteral(value) {
|
|
742
|
+
return value.replace(/'/g, "''");
|
|
743
|
+
}
|
|
744
|
+
// ── Small read helpers ────────────────────────────────────────────
|
|
745
|
+
readQueryResponse(body) {
|
|
746
|
+
if (!body || typeof body !== 'object')
|
|
747
|
+
return null;
|
|
748
|
+
const qr = body[DEFAULT_RECORDS_PATH];
|
|
749
|
+
return (qr && typeof qr === 'object') ? qr : null;
|
|
750
|
+
}
|
|
751
|
+
countQueryRecords(qr) {
|
|
752
|
+
if (!qr)
|
|
753
|
+
return 0;
|
|
754
|
+
for (const v of Object.values(qr)) {
|
|
755
|
+
if (Array.isArray(v))
|
|
756
|
+
return v.length;
|
|
757
|
+
}
|
|
758
|
+
return 0;
|
|
759
|
+
}
|
|
760
|
+
/** Reads a single entity object nested under `<Entity>` in a singleton/get-one response. */
|
|
761
|
+
readSingleEntity(body, queryEntity) {
|
|
762
|
+
if (!body || typeof body !== 'object')
|
|
763
|
+
return null;
|
|
764
|
+
const b = body;
|
|
765
|
+
const direct = b[queryEntity];
|
|
766
|
+
if (direct && typeof direct === 'object' && !Array.isArray(direct))
|
|
767
|
+
return direct;
|
|
768
|
+
// Fallback: the first object-valued property that isn't the `time` scalar.
|
|
769
|
+
for (const [k, v] of Object.entries(b)) {
|
|
770
|
+
if (k === 'time')
|
|
771
|
+
continue;
|
|
772
|
+
if (v && typeof v === 'object' && !Array.isArray(v))
|
|
773
|
+
return v;
|
|
774
|
+
}
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
readCompanyName(body) {
|
|
778
|
+
const ci = this.readSingleEntity(body, 'CompanyInfo');
|
|
779
|
+
const name = ci?.CompanyName;
|
|
780
|
+
return typeof name === 'string' ? name : undefined;
|
|
781
|
+
}
|
|
782
|
+
readFaultErrors(body) {
|
|
783
|
+
if (!body || typeof body !== 'object')
|
|
784
|
+
return [];
|
|
785
|
+
const fault = body.fault ?? body.Fault;
|
|
786
|
+
if (!fault || typeof fault !== 'object')
|
|
787
|
+
return [];
|
|
788
|
+
const arr = fault.error ?? fault.Error;
|
|
789
|
+
if (!Array.isArray(arr))
|
|
790
|
+
return [];
|
|
791
|
+
return arr.map(e => {
|
|
792
|
+
const o = (e && typeof e === 'object') ? e : {};
|
|
793
|
+
return {
|
|
794
|
+
message: this.asString(o.message ?? o.Message),
|
|
795
|
+
detail: this.asString(o.detail ?? o.Detail),
|
|
796
|
+
code: this.asString(o.code ?? o.Code),
|
|
797
|
+
element: this.asString(o.element ?? o.Element),
|
|
798
|
+
};
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
/** Reads MetaData.LastUpdatedTime (the incremental watermark) off a raw record. */
|
|
802
|
+
readLastUpdated(raw, watermarkField) {
|
|
803
|
+
const iso = this.readWatermarkString(raw, watermarkField);
|
|
804
|
+
if (!iso)
|
|
805
|
+
return undefined;
|
|
806
|
+
const d = new Date(iso);
|
|
807
|
+
return Number.isNaN(d.getTime()) ? undefined : d;
|
|
808
|
+
}
|
|
809
|
+
/** Resolves a (possibly dotted, e.g. `MetaData.LastUpdatedTime`) watermark path to a string value. */
|
|
810
|
+
readWatermarkString(raw, watermarkField) {
|
|
811
|
+
if (!watermarkField)
|
|
812
|
+
return undefined;
|
|
813
|
+
let cur = raw;
|
|
814
|
+
for (const seg of watermarkField.split('.')) {
|
|
815
|
+
if (!cur || typeof cur !== 'object')
|
|
816
|
+
return undefined;
|
|
817
|
+
cur = cur[seg];
|
|
818
|
+
}
|
|
819
|
+
return typeof cur === 'string' ? cur : undefined;
|
|
820
|
+
}
|
|
821
|
+
/** Highest watermark value across a batch (ISO-8601 compares lexically). */
|
|
822
|
+
maxWatermark(records, watermarkField) {
|
|
823
|
+
if (!watermarkField)
|
|
824
|
+
return undefined;
|
|
825
|
+
let max;
|
|
826
|
+
for (const r of records) {
|
|
827
|
+
const v = this.readWatermarkString(r, watermarkField);
|
|
828
|
+
if (v && (max === undefined || v > max))
|
|
829
|
+
max = v;
|
|
830
|
+
}
|
|
831
|
+
return max;
|
|
832
|
+
}
|
|
833
|
+
/** PK field names from the cached fields (universal QBO PK is 'Id'; empty when genuinely keyless). */
|
|
834
|
+
primaryKeyFieldNames(fields) {
|
|
835
|
+
return fields.filter(f => f.IsPrimaryKey).map(f => f.Name);
|
|
836
|
+
}
|
|
837
|
+
// ── Rate-limit / backoff helpers ──────────────────────────────────
|
|
838
|
+
parseRetryAfterMs(headers) {
|
|
839
|
+
const raw = headers['retry-after'];
|
|
840
|
+
if (!raw)
|
|
841
|
+
return undefined;
|
|
842
|
+
const seconds = Number(raw);
|
|
843
|
+
if (Number.isFinite(seconds))
|
|
844
|
+
return Math.max(0, seconds * 1000);
|
|
845
|
+
const when = new Date(raw).getTime();
|
|
846
|
+
return Number.isFinite(when) ? Math.max(0, when - Date.now()) : undefined;
|
|
847
|
+
}
|
|
848
|
+
computeBackoffMs(headers, attempt) {
|
|
849
|
+
const retryAfter = this.parseRetryAfterMs(headers);
|
|
850
|
+
if (retryAfter != null)
|
|
851
|
+
return retryAfter;
|
|
852
|
+
return Math.min(30_000, 500 * Math.pow(2, attempt));
|
|
853
|
+
}
|
|
854
|
+
sleep(ms) {
|
|
855
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
856
|
+
}
|
|
857
|
+
// ── Tiny typed coercion helpers ───────────────────────────────────
|
|
858
|
+
normalizeEnvironment(value) {
|
|
859
|
+
return (value ?? '').toLowerCase() === 'sandbox' ? 'sandbox' : 'production';
|
|
860
|
+
}
|
|
861
|
+
parsePerMinuteLimit(value) {
|
|
862
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
863
|
+
return value;
|
|
864
|
+
if (typeof value === 'string') {
|
|
865
|
+
const m = value.match(/(\d+)/);
|
|
866
|
+
if (m)
|
|
867
|
+
return Number(m[1]);
|
|
868
|
+
}
|
|
869
|
+
return undefined;
|
|
870
|
+
}
|
|
871
|
+
firstString(obj, keys) {
|
|
872
|
+
for (const k of keys) {
|
|
873
|
+
const v = obj[k];
|
|
874
|
+
if (typeof v === 'string' && v.trim().length > 0)
|
|
875
|
+
return v.trim();
|
|
876
|
+
if (typeof v === 'number')
|
|
877
|
+
return String(v);
|
|
878
|
+
}
|
|
879
|
+
return undefined;
|
|
880
|
+
}
|
|
881
|
+
asString(value) {
|
|
882
|
+
if (typeof value === 'string' && value.trim().length > 0)
|
|
883
|
+
return value.trim();
|
|
884
|
+
if (typeof value === 'number')
|
|
885
|
+
return String(value);
|
|
886
|
+
return undefined;
|
|
887
|
+
}
|
|
888
|
+
asNumber(value) {
|
|
889
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
890
|
+
return value;
|
|
891
|
+
if (typeof value === 'string') {
|
|
892
|
+
const n = Number(value);
|
|
893
|
+
if (Number.isFinite(n))
|
|
894
|
+
return n;
|
|
895
|
+
}
|
|
896
|
+
return undefined;
|
|
897
|
+
}
|
|
898
|
+
/** Removes undefined-valued keys so a spread merge doesn't clobber a lower-precedence real value. */
|
|
899
|
+
stripUndefined(obj) {
|
|
900
|
+
const out = {};
|
|
901
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
902
|
+
if (v !== undefined)
|
|
903
|
+
out[k] = v;
|
|
904
|
+
}
|
|
905
|
+
return out;
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
QuickBooksConnector = __decorate([
|
|
909
|
+
RegisterClass(BaseIntegrationConnector, 'QuickBooksConnector'),
|
|
910
|
+
RegisterClass(BaseIntegrationConnector, '@memberjunction/connector-quickbooks')
|
|
911
|
+
], QuickBooksConnector);
|
|
912
|
+
export { QuickBooksConnector };
|
|
913
|
+
// ─── Rate-limit / correlation-aware transport error ────────────────────────
|
|
914
|
+
/** Thrown by the transport on an exhausted 429; carries the parsed Retry-After for the engine hook. */
|
|
915
|
+
class QuickBooksRateLimitError extends Error {
|
|
916
|
+
constructor(message, retryAfterMs, intuitTid) {
|
|
917
|
+
super(message);
|
|
918
|
+
this.name = 'QuickBooksRateLimitError';
|
|
919
|
+
this.RetryAfterMs = retryAfterMs;
|
|
920
|
+
this.IntuitTid = intuitTid;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
/** Tree-shaking prevention: referenced from index to keep the @RegisterClass registration alive under bundling. */
|
|
924
|
+
export function LoadQuickBooksConnector() {
|
|
925
|
+
// no-op — the mere existence of this exported function prevents the class from being tree-shaken.
|
|
926
|
+
}
|
|
927
|
+
//# sourceMappingURL=QuickBooksConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuickBooksConnector.js","sourceRoot":"","sources":["../src/QuickBooksConnector.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from './QuickBooksConnector.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 './QuickBooksConnector.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,0BAA0B,CAAC;AAEzC;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@memberjunction/connector-quickbooks",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "MemberJunction QuickBooks connector.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"/dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc && tsc-alias -f",
|
|
14
|
+
"test": "vitest run --passWithNoTests"
|
|
15
|
+
},
|
|
16
|
+
"author": "MemberJunction.com",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@memberjunction/core": ">=5.42.0 <6.0.0",
|
|
20
|
+
"@memberjunction/core-entities": ">=5.42.0 <6.0.0",
|
|
21
|
+
"@memberjunction/global": ">=5.42.0 <6.0.0",
|
|
22
|
+
"@memberjunction/integration-engine": ">=5.42.0 <6.0.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@memberjunction/connector-schema-merge": "^1.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "24.10.11",
|
|
29
|
+
"tsc-alias": "^1.8.16",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"vitest": "^4.0.18",
|
|
32
|
+
"@memberjunction/core": "^5.42.0",
|
|
33
|
+
"@memberjunction/core-entities": "^5.42.0",
|
|
34
|
+
"@memberjunction/global": "^5.42.0",
|
|
35
|
+
"@memberjunction/integration-engine": "^5.42.0",
|
|
36
|
+
"@memberjunction/connector-schema-merge": "^1.0.0"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/MemberJunction/Integrations"
|
|
41
|
+
}
|
|
42
|
+
}
|