@memberjunction/connector-mailchimp 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/MailchimpConnector.d.ts +182 -0
- package/dist/MailchimpConnector.js +549 -0
- package/dist/MailchimpConnector.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,182 @@
|
|
|
1
|
+
import { type UserInfo } from '@memberjunction/core';
|
|
2
|
+
import type { MJCompanyIntegrationEntity, MJIntegrationObjectEntity } from '@memberjunction/core-entities';
|
|
3
|
+
import { BaseRESTIntegrationConnector, type RESTAuthContext, type RESTResponse, type PaginationState, type PaginationType, type ConnectionTestResult, type FetchContext, type FetchBatchResult, type CreateRecordContext, type UpdateRecordContext, type DeleteRecordContext, type CRUDResult, type SourceSchemaInfo } from '@memberjunction/integration-engine';
|
|
4
|
+
/** Extended auth context carrying the resolved Basic/Bearer header + the per-account data center. */
|
|
5
|
+
interface MailchimpAuthContext extends RESTAuthContext {
|
|
6
|
+
/** Full `Authorization` header value (Basic or Bearer). */
|
|
7
|
+
AuthHeader: string;
|
|
8
|
+
/** Resolved data-center prefix used to build every request host (e.g. `us15`). */
|
|
9
|
+
DataCenter: string;
|
|
10
|
+
/** Optional explicit full base-URL override (sandbox/test/mock); wins over the dc host in GetBaseURL. */
|
|
11
|
+
BaseURLOverride?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Mailchimp Marketing API (v3.0) connector — extends BaseRESTIntegrationConnector (REST/JSON over HTTP).
|
|
15
|
+
*
|
|
16
|
+
* Discovery, template-var read traversal (second-layer objects resolve their parent via
|
|
17
|
+
* Configuration.parentObjectName — e.g. `lists/{list_id}/members` iterates over synced `lists`), and the
|
|
18
|
+
* paginated GET loop are inherited. This class supplies only the Mailchimp-specific protocol surface:
|
|
19
|
+
* Basic auth, per-account base URL, offset pagination, per-endpoint incremental date filtering, connection
|
|
20
|
+
* testing, generic per-operation CRUD with named parent-var path templating, and the §7/§10
|
|
21
|
+
* sync-efficiency hooks the frozen contract evidences.
|
|
22
|
+
*/
|
|
23
|
+
export declare class MailchimpConnector extends BaseRESTIntegrationConnector {
|
|
24
|
+
/** Cached auth for the lifetime of a single sync run (Mailchimp API keys don't expire). */
|
|
25
|
+
private cachedAuth;
|
|
26
|
+
/** Active watermark for the current FetchChanges call — exposed to AppendDefaultQueryParams. */
|
|
27
|
+
private currentWatermark;
|
|
28
|
+
/** Verbatim `MJ: Integrations.Name`. Load-bearing: the T1 three-way name check compares this === metadata Name. */
|
|
29
|
+
get IntegrationName(): string;
|
|
30
|
+
get SupportsCreate(): boolean;
|
|
31
|
+
get SupportsUpdate(): boolean;
|
|
32
|
+
get SupportsDelete(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Discovery is NON-authoritative: DiscoverObjects / IntrospectSchema are cache-driven (they re-read
|
|
35
|
+
* persisted ACTIVE Declared metadata, NOT a live full-gamut enumeration). Mailchimp exposes no complete
|
|
36
|
+
* describe endpoint; per-list custom merge fields flow through the sample-union enrichment below plus the
|
|
37
|
+
* framework's custom-column capture. Absence in a refresh proves nothing → never deactivate. Matches the
|
|
38
|
+
* frozen contract (no complete-gamut describe endpoint).
|
|
39
|
+
*/
|
|
40
|
+
get DiscoveryIsAuthoritative(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Mailchimp's documented limit is a CONCURRENCY cap ("a limit of 10 simultaneous connections"), NOT a
|
|
43
|
+
* documented requests-per-window token rate — and it emits no `X-RateLimit-*` / `Retry-After` headers.
|
|
44
|
+
* So the evidenced hook is the concurrency ceiling (below); RateLimitPolicy stays null (no token rate to
|
|
45
|
+
* fabricate — the engine derives a conservative rate) and ExtractRetryAfterMs stays default (nothing to
|
|
46
|
+
* parse). The internal transport still honors an opportunistic Retry-After on a 429 as defensive backoff.
|
|
47
|
+
*/
|
|
48
|
+
get MaxConcurrencyHint(): number | null;
|
|
49
|
+
/**
|
|
50
|
+
* Sample-union enrichment: Declared metadata is spec-derived and misses a tenant's per-list custom merge
|
|
51
|
+
* fields. After the base cache-driven introspection, we sample each object's live read shape via
|
|
52
|
+
* `DiscoverFieldsViaFetch` and UNION it into the declared field set with `mergeDeclaredWithSampledFields`
|
|
53
|
+
* (never-shrink, declared-wins, capacities widened). Best-effort + parallel; a sample failure leaves the
|
|
54
|
+
* declared set untouched. We override `IntrospectSchema` (NOT `DiscoverFields` — that would recurse into
|
|
55
|
+
* `DiscoverFieldsViaFetch`'s own fallback). Connector-agnostic: no Mailchimp-specific field logic.
|
|
56
|
+
*/
|
|
57
|
+
IntrospectSchema(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<SourceSchemaInfo>;
|
|
58
|
+
/** Pings the Mailchimp API root (`/ping`) to verify connectivity + authentication (read-only). */
|
|
59
|
+
TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
|
|
60
|
+
/**
|
|
61
|
+
* Overridden ONLY to expose the active watermark to AppendDefaultQueryParams (which appends the
|
|
62
|
+
* per-endpoint `since_<field>` incremental filter). All read traversal — flat, offset-paginated, and
|
|
63
|
+
* per-parent template-var — is delegated to the inherited base implementation.
|
|
64
|
+
*/
|
|
65
|
+
FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
|
|
66
|
+
/** Resolves credentials + data center once per run and pre-builds the Authorization header via the helper. */
|
|
67
|
+
protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<MailchimpAuthContext>;
|
|
68
|
+
/** Static request headers; the Authorization value was built once in Authenticate. */
|
|
69
|
+
protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
|
|
70
|
+
/**
|
|
71
|
+
* HTTP transport (fetch), with bounded 429/503 backoff. Owns the wire boundary; test subclasses override
|
|
72
|
+
* this to capture the outbound request and return canned responses.
|
|
73
|
+
*/
|
|
74
|
+
protected MakeHTTPRequest(auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
|
|
75
|
+
/**
|
|
76
|
+
* Strips the Mailchimp collection envelope to the per-object record array
|
|
77
|
+
* (`{ members: [...], total_items }` → ResponseDataKey='members'). A single-record GET (no data key, or
|
|
78
|
+
* the key absent) is returned as a one-element array. A bare array body passes through unchanged.
|
|
79
|
+
*/
|
|
80
|
+
protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
|
|
81
|
+
/**
|
|
82
|
+
* Offset pagination: continue while `offset + fetched < total_items` (when the envelope reports a total),
|
|
83
|
+
* else while a full page came back. The base loop advances `offset` by the fetched count.
|
|
84
|
+
*
|
|
85
|
+
* Mailchimp's collection pagination is UNIFORMLY count/offset with a `total_items` count across every
|
|
86
|
+
* list endpoint — including the single IO the frozen contract classifies `Cursor`
|
|
87
|
+
* (`audiences/{audience_id}/contacts`), which returns the identical `{ <key>: [...], total_items }`
|
|
88
|
+
* envelope over the same `count`/`offset` params. So Offset AND Cursor advance by offset+fetched here;
|
|
89
|
+
* only `None` (and unknown types) are single-page. Treating the lone Cursor IO via the same mechanism
|
|
90
|
+
* prevents a silent one-page truncation (the endpoint could otherwise drop everything past page 1).
|
|
91
|
+
* Flagged RequiresLiveVerification in CODE_REPORT — the reality probe could not fill `{audience_id}`.
|
|
92
|
+
*/
|
|
93
|
+
protected ExtractPaginationInfo(rawBody: unknown, paginationType: PaginationType, _currentPage: number, currentOffset: number, pageSize: number): PaginationState;
|
|
94
|
+
/** Mailchimp offset pagination params: `count=<n>` (≤1000) + `offset=<n>` (NOT the base default `limit`/`offset`). */
|
|
95
|
+
protected BuildPaginatedURL(basePath: string, obj: MJIntegrationObjectEntity, _page: number, offset: number, _cursor?: string, effectivePageSize?: number): string;
|
|
96
|
+
/**
|
|
97
|
+
* Appends the per-endpoint incremental filter on a watermarked fetch: `since_<IncrementalWatermarkField>`
|
|
98
|
+
* (Mailchimp's documented date-filter convention — e.g. `since_last_changed` for list members). Only
|
|
99
|
+
* fires for objects the metadata marks `SupportsIncrementalSync` with a watermark field AND when a
|
|
100
|
+
* watermark value is present. Then delegates to the base for any metadata-declared DefaultQueryParams.
|
|
101
|
+
*/
|
|
102
|
+
protected AppendDefaultQueryParams(url: string, obj: MJIntegrationObjectEntity): string;
|
|
103
|
+
/** Per-account base host. The `/3.0` version segment is included; each object's APIPath is appended by the base. */
|
|
104
|
+
protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
|
|
105
|
+
/**
|
|
106
|
+
* OVERRIDE (idiosyncrasy): Mailchimp single-record paths use NAMED vars (`/campaigns/{campaign_id}`), not
|
|
107
|
+
* the base's `{ID}` placeholder. Fill each named `{var}` from the ExternalID — left-to-right across the
|
|
108
|
+
* `|`-split components of a composite ExternalID (mirrors ToExternalRecord's composite-PK join), so a
|
|
109
|
+
* single-var top-level path takes the whole ExternalID. This makes the INHERITED generic
|
|
110
|
+
* UpdateRecord / DeleteRecord / GetRecord work for all top-level writable IOs. (Nested paths that carry a
|
|
111
|
+
* PARENT var not present in the single-key ExternalID are handled by the Create/Update overrides below.)
|
|
112
|
+
*/
|
|
113
|
+
protected SubstituteIDInPath(path: string, externalID: string, idLocation: string | null): string;
|
|
114
|
+
/** Create: nested paths fill parent vars from the record attributes; top-level delegates to the generic path. */
|
|
115
|
+
CreateRecord(ctx: CreateRecordContext): Promise<CRUDResult>;
|
|
116
|
+
/** Update: nested paths fill parent vars from attributes + the innermost var from the ExternalID; top-level delegates. */
|
|
117
|
+
UpdateRecord(ctx: UpdateRecordContext): Promise<CRUDResult>;
|
|
118
|
+
/**
|
|
119
|
+
* Delete: nested paths resolve every `{var}` from the `|`-split composite ExternalID (right-aligned:
|
|
120
|
+
* `{list_id}`,`{subscriber_hash}` ← `list1|hashaaa`); top-level delegates. A nested delete whose parent
|
|
121
|
+
* var still can't be resolved (e.g. a single-component ExternalID that carries only the child key) fails
|
|
122
|
+
* loudly rather than issuing a request with an unresolved `{parent}` — a live-verification concern flagged
|
|
123
|
+
* in CODE_REPORT.
|
|
124
|
+
*/
|
|
125
|
+
DeleteRecord(ctx: DeleteRecordContext): Promise<CRUDResult>;
|
|
126
|
+
/**
|
|
127
|
+
* Counts the records in a Mailchimp collection envelope by finding the per-object data array (the first
|
|
128
|
+
* array-valued property that isn't the `_links` HATEOAS list). Used for pagination advancement — the
|
|
129
|
+
* data key isn't passed to ExtractPaginationInfo, so we locate the array structurally. Returns 0 for a
|
|
130
|
+
* single-record body (no array), which correctly reports "no more pages".
|
|
131
|
+
*/
|
|
132
|
+
private countEnvelopeRecords;
|
|
133
|
+
/** Extracts `{var}` names from a path template, in left-to-right order. */
|
|
134
|
+
private detectPathVars;
|
|
135
|
+
/**
|
|
136
|
+
* Fills each `{var}` in a nested write path by resolving, per var, in this order:
|
|
137
|
+
* 1. a same-named attribute on the record (e.g. the base tags fetched children with their parent FK
|
|
138
|
+
* field, so a synced member carries `list_id`);
|
|
139
|
+
* 2. the RIGHT-ALIGNED component of the `|`-split ExternalID — mirroring ToExternalRecord's composite-PK
|
|
140
|
+
* join order (`parent|…|child`): the innermost (last) var is the record's OWN key, preceding vars are
|
|
141
|
+
* the parent id(s). So a member's ExternalID `list1|hashaaa` fills `{list_id}`→`list1`,
|
|
142
|
+
* `{subscriber_hash}`→`hashaaa`, which is exactly what a nested update/delete needs even with no
|
|
143
|
+
* attributes present.
|
|
144
|
+
* Create paths pass externalID=null and thus fill purely from attributes. Any var that resolves to nothing
|
|
145
|
+
* is left in place so {@link assertPathResolved} / the delete guard can surface it (fail loudly, never
|
|
146
|
+
* issue a request with an unresolved `{parent}`).
|
|
147
|
+
*/
|
|
148
|
+
private fillPathFromAttributes;
|
|
149
|
+
/** Throws a clear error if a nested write path still has unresolved `{var}`s (missing parent id). */
|
|
150
|
+
private assertPathResolved;
|
|
151
|
+
/** True when a URL already carries a query param with `name` (case-insensitive). */
|
|
152
|
+
private urlHasParam;
|
|
153
|
+
/** Loads credentials from the linked Credential entity (preferred) or the CompanyIntegration Configuration JSON. */
|
|
154
|
+
private loadCredentials;
|
|
155
|
+
/** Loads a credential row and parses its Values JSON. */
|
|
156
|
+
private loadFromCredential;
|
|
157
|
+
/** Parses a Mailchimp credential/config JSON blob, tolerant of key casing variants. */
|
|
158
|
+
private parseCredentialJson;
|
|
159
|
+
/**
|
|
160
|
+
* Resolves the data-center prefix (e.g. `us15`):
|
|
161
|
+
* 1. explicit DataCenter / ServerPrefix from config/credential;
|
|
162
|
+
* 2. the `-<dc>` suffix parsed off the API key;
|
|
163
|
+
* 3. (runtime fallback for an OAuth bearer token with no dc) the OAuth2 metadata endpoint.
|
|
164
|
+
* A BaseURLOverride short-circuits the need for a real dc (used for sandbox/test/mock).
|
|
165
|
+
*/
|
|
166
|
+
private resolveDataCenter;
|
|
167
|
+
/** Extracts `<dc>` from a Mailchimp API key of the form `<secret>-<dc>` (e.g. `abc123-us15` → `us15`). */
|
|
168
|
+
private parseDataCenterFromKey;
|
|
169
|
+
/** Runtime fallback: GET /oauth2/metadata to discover the data center for an OAuth access token. */
|
|
170
|
+
private fetchDataCenterFromOAuthMetadata;
|
|
171
|
+
/** Returns the first non-empty string value among the candidate keys, else undefined. */
|
|
172
|
+
private firstString;
|
|
173
|
+
/** Returns the first candidate key whose value is a valid absolute http(s) URL, else undefined. */
|
|
174
|
+
private firstBaseURL;
|
|
175
|
+
/** Computes a 429/503 backoff delay, honoring an opportunistic Retry-After header when present. */
|
|
176
|
+
private computeBackoffMs;
|
|
177
|
+
/** Sleep helper. */
|
|
178
|
+
private sleep;
|
|
179
|
+
}
|
|
180
|
+
/** Tree-shaking prevention — call from the package entry point. */
|
|
181
|
+
export declare function LoadMailchimpConnector(): void;
|
|
182
|
+
export {};
|
|
@@ -0,0 +1,549 @@
|
|
|
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, buildBasicAuthHeaderValue, } from '@memberjunction/integration-engine';
|
|
10
|
+
import { mergeDeclaredWithSampledFields } from '@memberjunction/connector-schema-merge';
|
|
11
|
+
// ─── Constants ────────────────────────────────────────────────────────────
|
|
12
|
+
/** OAuth2 metadata endpoint — resolves the data center for an OAuth access token when not otherwise known. */
|
|
13
|
+
const MAILCHIMP_OAUTH_METADATA_URL = 'https://login.mailchimp.com/oauth2/metadata';
|
|
14
|
+
/** Default page size (metadata DefaultPageSize normally supplies this; safe fallback well below the ceiling). */
|
|
15
|
+
const DEFAULT_PAGE_SIZE = 200;
|
|
16
|
+
/** Absolute per-page ceiling enforced by the Mailchimp API (`count` maxes at 1000). */
|
|
17
|
+
const MAX_PAGE_SIZE = 1000;
|
|
18
|
+
/** Basic-auth userid — Mailchimp ignores the userid, so any non-empty string works (`anystring` per docs). */
|
|
19
|
+
const BASIC_AUTH_USERID = 'anystring';
|
|
20
|
+
/** Max in-flight backoff retries for a 429 / 503 transient response. */
|
|
21
|
+
const DEFAULT_MAX_RETRIES = 5;
|
|
22
|
+
// ─── MailchimpConnector ─────────────────────────────────────────────────
|
|
23
|
+
/**
|
|
24
|
+
* Mailchimp Marketing API (v3.0) connector — extends BaseRESTIntegrationConnector (REST/JSON over HTTP).
|
|
25
|
+
*
|
|
26
|
+
* Discovery, template-var read traversal (second-layer objects resolve their parent via
|
|
27
|
+
* Configuration.parentObjectName — e.g. `lists/{list_id}/members` iterates over synced `lists`), and the
|
|
28
|
+
* paginated GET loop are inherited. This class supplies only the Mailchimp-specific protocol surface:
|
|
29
|
+
* Basic auth, per-account base URL, offset pagination, per-endpoint incremental date filtering, connection
|
|
30
|
+
* testing, generic per-operation CRUD with named parent-var path templating, and the §7/§10
|
|
31
|
+
* sync-efficiency hooks the frozen contract evidences.
|
|
32
|
+
*/
|
|
33
|
+
let MailchimpConnector = class MailchimpConnector extends BaseRESTIntegrationConnector {
|
|
34
|
+
constructor() {
|
|
35
|
+
super(...arguments);
|
|
36
|
+
/** Cached auth for the lifetime of a single sync run (Mailchimp API keys don't expire). */
|
|
37
|
+
this.cachedAuth = null;
|
|
38
|
+
/** Active watermark for the current FetchChanges call — exposed to AppendDefaultQueryParams. */
|
|
39
|
+
this.currentWatermark = null;
|
|
40
|
+
}
|
|
41
|
+
// ── Identity (T1 three-way invariant) ────────────────────────────
|
|
42
|
+
/** Verbatim `MJ: Integrations.Name`. Load-bearing: the T1 three-way name check compares this === metadata Name. */
|
|
43
|
+
get IntegrationName() {
|
|
44
|
+
return 'mailchimp';
|
|
45
|
+
}
|
|
46
|
+
// ── Capability getters (kept in lockstep with the per-op metadata columns) ──
|
|
47
|
+
get SupportsCreate() { return true; }
|
|
48
|
+
get SupportsUpdate() { return true; }
|
|
49
|
+
get SupportsDelete() { return true; }
|
|
50
|
+
/**
|
|
51
|
+
* Discovery is NON-authoritative: DiscoverObjects / IntrospectSchema are cache-driven (they re-read
|
|
52
|
+
* persisted ACTIVE Declared metadata, NOT a live full-gamut enumeration). Mailchimp exposes no complete
|
|
53
|
+
* describe endpoint; per-list custom merge fields flow through the sample-union enrichment below plus the
|
|
54
|
+
* framework's custom-column capture. Absence in a refresh proves nothing → never deactivate. Matches the
|
|
55
|
+
* frozen contract (no complete-gamut describe endpoint).
|
|
56
|
+
*/
|
|
57
|
+
get DiscoveryIsAuthoritative() {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
// ── Sync-efficiency hooks (§7/§10 — populated ONLY from evidenced frozen-contract facts) ──
|
|
61
|
+
/**
|
|
62
|
+
* Mailchimp's documented limit is a CONCURRENCY cap ("a limit of 10 simultaneous connections"), NOT a
|
|
63
|
+
* documented requests-per-window token rate — and it emits no `X-RateLimit-*` / `Retry-After` headers.
|
|
64
|
+
* So the evidenced hook is the concurrency ceiling (below); RateLimitPolicy stays null (no token rate to
|
|
65
|
+
* fabricate — the engine derives a conservative rate) and ExtractRetryAfterMs stays default (nothing to
|
|
66
|
+
* parse). The internal transport still honors an opportunistic Retry-After on a 429 as defensive backoff.
|
|
67
|
+
*/
|
|
68
|
+
get MaxConcurrencyHint() { return 10; }
|
|
69
|
+
// ── Sample-union field enrichment (MJ connector standard) ─────────
|
|
70
|
+
/**
|
|
71
|
+
* Sample-union enrichment: Declared metadata is spec-derived and misses a tenant's per-list custom merge
|
|
72
|
+
* fields. After the base cache-driven introspection, we sample each object's live read shape via
|
|
73
|
+
* `DiscoverFieldsViaFetch` and UNION it into the declared field set with `mergeDeclaredWithSampledFields`
|
|
74
|
+
* (never-shrink, declared-wins, capacities widened). Best-effort + parallel; a sample failure leaves the
|
|
75
|
+
* declared set untouched. We override `IntrospectSchema` (NOT `DiscoverFields` — that would recurse into
|
|
76
|
+
* `DiscoverFieldsViaFetch`'s own fallback). Connector-agnostic: no Mailchimp-specific field logic.
|
|
77
|
+
*/
|
|
78
|
+
async IntrospectSchema(companyIntegration, contextUser) {
|
|
79
|
+
const info = await super.IntrospectSchema(companyIntegration, contextUser);
|
|
80
|
+
await Promise.all(info.Objects.map(async (obj) => {
|
|
81
|
+
try {
|
|
82
|
+
const sampled = await this.DiscoverFieldsViaFetch(companyIntegration, obj.ExternalName, contextUser);
|
|
83
|
+
obj.Fields = mergeDeclaredWithSampledFields(obj.Fields, sampled);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
/* best-effort — a sample failure leaves the declared fields as-is */
|
|
87
|
+
}
|
|
88
|
+
}));
|
|
89
|
+
return info;
|
|
90
|
+
}
|
|
91
|
+
// ── TestConnection ────────────────────────────────────────────────
|
|
92
|
+
/** Pings the Mailchimp API root (`/ping`) to verify connectivity + authentication (read-only). */
|
|
93
|
+
async TestConnection(companyIntegration, contextUser) {
|
|
94
|
+
try {
|
|
95
|
+
const auth = await this.Authenticate(companyIntegration, contextUser);
|
|
96
|
+
const url = `${this.GetBaseURL(companyIntegration, auth)}/ping`;
|
|
97
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
|
|
98
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
99
|
+
const body = response.Body;
|
|
100
|
+
return {
|
|
101
|
+
Success: true,
|
|
102
|
+
Message: `Connected to Mailchimp (${auth.DataCenter}): ${body?.health_status ?? 'OK'}`,
|
|
103
|
+
ServerVersion: 'Mailchimp Marketing API v3.0',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (response.Status === 401 || response.Status === 403) {
|
|
107
|
+
return { Success: false, Message: `Mailchimp authentication failed (HTTP ${response.Status}).` };
|
|
108
|
+
}
|
|
109
|
+
return { Success: false, Message: `Mailchimp /ping returned HTTP ${response.Status}.` };
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
return { Success: false, Message: `Connection failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// ── FetchChanges override (watermark exposure only) ───────────────
|
|
116
|
+
/**
|
|
117
|
+
* Overridden ONLY to expose the active watermark to AppendDefaultQueryParams (which appends the
|
|
118
|
+
* per-endpoint `since_<field>` incremental filter). All read traversal — flat, offset-paginated, and
|
|
119
|
+
* per-parent template-var — is delegated to the inherited base implementation.
|
|
120
|
+
*/
|
|
121
|
+
async FetchChanges(ctx) {
|
|
122
|
+
this.currentWatermark = ctx.WatermarkValue ?? null;
|
|
123
|
+
try {
|
|
124
|
+
return await super.FetchChanges(ctx);
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
this.currentWatermark = null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ── Abstract REST hooks ───────────────────────────────────────────
|
|
131
|
+
/** Resolves credentials + data center once per run and pre-builds the Authorization header via the helper. */
|
|
132
|
+
async Authenticate(companyIntegration, contextUser) {
|
|
133
|
+
if (this.cachedAuth)
|
|
134
|
+
return this.cachedAuth;
|
|
135
|
+
const creds = await this.loadCredentials(companyIntegration, contextUser);
|
|
136
|
+
if (!creds.ApiKey) {
|
|
137
|
+
throw new Error('Mailchimp connector: no API key found on the Credential or CompanyIntegration Configuration.');
|
|
138
|
+
}
|
|
139
|
+
const dataCenter = await this.resolveDataCenter(creds);
|
|
140
|
+
const authHeader = creds.Scheme === 'bearer'
|
|
141
|
+
? `Bearer ${creds.ApiKey}`
|
|
142
|
+
: buildBasicAuthHeaderValue({ Username: BASIC_AUTH_USERID, Password: creds.ApiKey });
|
|
143
|
+
this.cachedAuth = {
|
|
144
|
+
AuthHeader: authHeader,
|
|
145
|
+
DataCenter: dataCenter,
|
|
146
|
+
BaseURLOverride: creds.BaseURLOverride,
|
|
147
|
+
};
|
|
148
|
+
return this.cachedAuth;
|
|
149
|
+
}
|
|
150
|
+
/** Static request headers; the Authorization value was built once in Authenticate. */
|
|
151
|
+
BuildHeaders(auth) {
|
|
152
|
+
return {
|
|
153
|
+
'Authorization': auth.AuthHeader,
|
|
154
|
+
'Content-Type': 'application/json',
|
|
155
|
+
'Accept': 'application/json',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* HTTP transport (fetch), with bounded 429/503 backoff. Owns the wire boundary; test subclasses override
|
|
160
|
+
* this to capture the outbound request and return canned responses.
|
|
161
|
+
*/
|
|
162
|
+
async MakeHTTPRequest(auth, url, method, headers, body) {
|
|
163
|
+
for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
|
|
164
|
+
const response = await fetch(url, {
|
|
165
|
+
method,
|
|
166
|
+
headers,
|
|
167
|
+
body: body !== undefined && body !== null ? JSON.stringify(body) : undefined,
|
|
168
|
+
});
|
|
169
|
+
if ((response.status === 429 || response.status === 503) && attempt < DEFAULT_MAX_RETRIES) {
|
|
170
|
+
await this.sleep(this.computeBackoffMs(response, attempt));
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const respHeaders = {};
|
|
174
|
+
response.headers.forEach((v, k) => { respHeaders[k.toLowerCase()] = v; });
|
|
175
|
+
const text = await response.text();
|
|
176
|
+
let parsed = null;
|
|
177
|
+
if (text.length > 0) {
|
|
178
|
+
try {
|
|
179
|
+
parsed = JSON.parse(text);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
parsed = text;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return { Status: response.status, Body: parsed, Headers: respHeaders };
|
|
186
|
+
}
|
|
187
|
+
throw new Error(`Mailchimp request failed after ${DEFAULT_MAX_RETRIES} retries: ${method} ${url}`);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Strips the Mailchimp collection envelope to the per-object record array
|
|
191
|
+
* (`{ members: [...], total_items }` → ResponseDataKey='members'). A single-record GET (no data key, or
|
|
192
|
+
* the key absent) is returned as a one-element array. A bare array body passes through unchanged.
|
|
193
|
+
*/
|
|
194
|
+
NormalizeResponse(rawBody, responseDataKey) {
|
|
195
|
+
if (rawBody == null)
|
|
196
|
+
return [];
|
|
197
|
+
if (Array.isArray(rawBody))
|
|
198
|
+
return rawBody;
|
|
199
|
+
if (typeof rawBody !== 'object')
|
|
200
|
+
return [];
|
|
201
|
+
const body = rawBody;
|
|
202
|
+
if (responseDataKey) {
|
|
203
|
+
const arr = body[responseDataKey];
|
|
204
|
+
if (Array.isArray(arr))
|
|
205
|
+
return arr;
|
|
206
|
+
}
|
|
207
|
+
// Single-record / already-unwrapped object (or an empty/mismatched data key).
|
|
208
|
+
return [body];
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Offset pagination: continue while `offset + fetched < total_items` (when the envelope reports a total),
|
|
212
|
+
* else while a full page came back. The base loop advances `offset` by the fetched count.
|
|
213
|
+
*
|
|
214
|
+
* Mailchimp's collection pagination is UNIFORMLY count/offset with a `total_items` count across every
|
|
215
|
+
* list endpoint — including the single IO the frozen contract classifies `Cursor`
|
|
216
|
+
* (`audiences/{audience_id}/contacts`), which returns the identical `{ <key>: [...], total_items }`
|
|
217
|
+
* envelope over the same `count`/`offset` params. So Offset AND Cursor advance by offset+fetched here;
|
|
218
|
+
* only `None` (and unknown types) are single-page. Treating the lone Cursor IO via the same mechanism
|
|
219
|
+
* prevents a silent one-page truncation (the endpoint could otherwise drop everything past page 1).
|
|
220
|
+
* Flagged RequiresLiveVerification in CODE_REPORT — the reality probe could not fill `{audience_id}`.
|
|
221
|
+
*/
|
|
222
|
+
ExtractPaginationInfo(rawBody, paginationType, _currentPage, currentOffset, pageSize) {
|
|
223
|
+
if (paginationType !== 'Offset' && paginationType !== 'Cursor')
|
|
224
|
+
return { HasMore: false };
|
|
225
|
+
const env = (rawBody && typeof rawBody === 'object' ? rawBody : {});
|
|
226
|
+
const total = typeof env.total_items === 'number' ? env.total_items : undefined;
|
|
227
|
+
const fetched = this.countEnvelopeRecords(env);
|
|
228
|
+
const nextOffset = currentOffset + fetched;
|
|
229
|
+
const hasMore = total != null ? nextOffset < total : fetched >= pageSize;
|
|
230
|
+
return { HasMore: hasMore, NextOffset: hasMore ? nextOffset : undefined, TotalRecords: total };
|
|
231
|
+
}
|
|
232
|
+
/** Mailchimp offset pagination params: `count=<n>` (≤1000) + `offset=<n>` (NOT the base default `limit`/`offset`). */
|
|
233
|
+
BuildPaginatedURL(basePath, obj, _page, offset, _cursor, effectivePageSize) {
|
|
234
|
+
const pageSize = Math.min(effectivePageSize ?? obj.DefaultPageSize ?? DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
|
|
235
|
+
const separator = basePath.includes('?') ? '&' : '?';
|
|
236
|
+
return `${basePath}${separator}count=${pageSize}&offset=${offset}`;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Appends the per-endpoint incremental filter on a watermarked fetch: `since_<IncrementalWatermarkField>`
|
|
240
|
+
* (Mailchimp's documented date-filter convention — e.g. `since_last_changed` for list members). Only
|
|
241
|
+
* fires for objects the metadata marks `SupportsIncrementalSync` with a watermark field AND when a
|
|
242
|
+
* watermark value is present. Then delegates to the base for any metadata-declared DefaultQueryParams.
|
|
243
|
+
*/
|
|
244
|
+
AppendDefaultQueryParams(url, obj) {
|
|
245
|
+
let out = super.AppendDefaultQueryParams(url, obj);
|
|
246
|
+
if (this.currentWatermark && obj.SupportsIncrementalSync && obj.IncrementalWatermarkField) {
|
|
247
|
+
const param = `since_${obj.IncrementalWatermarkField}`;
|
|
248
|
+
if (!this.urlHasParam(out, param)) {
|
|
249
|
+
out += (out.includes('?') ? '&' : '?') + `${param}=${encodeURIComponent(this.currentWatermark)}`;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return out;
|
|
253
|
+
}
|
|
254
|
+
/** Per-account base host. The `/3.0` version segment is included; each object's APIPath is appended by the base. */
|
|
255
|
+
GetBaseURL(_companyIntegration, auth) {
|
|
256
|
+
const ctx = auth;
|
|
257
|
+
if (ctx.BaseURLOverride)
|
|
258
|
+
return ctx.BaseURLOverride.replace(/\/+$/, '');
|
|
259
|
+
return `https://${ctx.DataCenter}.api.mailchimp.com/3.0`;
|
|
260
|
+
}
|
|
261
|
+
// ── Path templating ───────────────────────────────────────────────
|
|
262
|
+
/**
|
|
263
|
+
* OVERRIDE (idiosyncrasy): Mailchimp single-record paths use NAMED vars (`/campaigns/{campaign_id}`), not
|
|
264
|
+
* the base's `{ID}` placeholder. Fill each named `{var}` from the ExternalID — left-to-right across the
|
|
265
|
+
* `|`-split components of a composite ExternalID (mirrors ToExternalRecord's composite-PK join), so a
|
|
266
|
+
* single-var top-level path takes the whole ExternalID. This makes the INHERITED generic
|
|
267
|
+
* UpdateRecord / DeleteRecord / GetRecord work for all top-level writable IOs. (Nested paths that carry a
|
|
268
|
+
* PARENT var not present in the single-key ExternalID are handled by the Create/Update overrides below.)
|
|
269
|
+
*/
|
|
270
|
+
SubstituteIDInPath(path, externalID, idLocation) {
|
|
271
|
+
if (idLocation && idLocation !== 'path')
|
|
272
|
+
return path;
|
|
273
|
+
const vars = this.detectPathVars(path);
|
|
274
|
+
if (vars.length === 0)
|
|
275
|
+
return path;
|
|
276
|
+
const parts = String(externalID).split('|');
|
|
277
|
+
let out = path;
|
|
278
|
+
vars.forEach((v, i) => {
|
|
279
|
+
const val = parts[i] ?? parts[parts.length - 1] ?? externalID;
|
|
280
|
+
out = out.replace(`{${v}}`, encodeURIComponent(val));
|
|
281
|
+
});
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
// ── CRUD ──────────────────────────────────────────────────────────
|
|
285
|
+
//
|
|
286
|
+
// Top-level writable IOs (campaigns, templates, lists, stores, …) use the INHERITED generic
|
|
287
|
+
// CreateRecord / UpdateRecord / DeleteRecord / GetRecord — all creates are flat-body with the new id at
|
|
288
|
+
// the response root, and single-var single-record paths resolve via the SubstituteIDInPath override
|
|
289
|
+
// above. The Create/Update/Delete methods here override ONLY the genuinely idiosyncratic case: NESTED
|
|
290
|
+
// resources whose write path carries a named PARENT var (`{list_id}`, `{store_id}`, …) that is not part
|
|
291
|
+
// of the child's single-key ExternalID and must be sourced from the record's own attributes.
|
|
292
|
+
/** Create: nested paths fill parent vars from the record attributes; top-level delegates to the generic path. */
|
|
293
|
+
async CreateRecord(ctx) {
|
|
294
|
+
const ci = ctx.CompanyIntegration;
|
|
295
|
+
const contextUser = ctx.ContextUser;
|
|
296
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
297
|
+
if (!obj.CreateAPIPath || !obj.CreateMethod)
|
|
298
|
+
return super.CreateRecord(ctx);
|
|
299
|
+
if (this.detectPathVars(obj.CreateAPIPath).length === 0)
|
|
300
|
+
return super.CreateRecord(ctx);
|
|
301
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
302
|
+
const path = this.fillPathFromAttributes(obj.CreateAPIPath, ctx.Attributes, null);
|
|
303
|
+
this.assertPathResolved(path, ctx.ObjectName, 'create');
|
|
304
|
+
const url = `${this.GetBaseURL(ci, auth)}${path.startsWith('/') ? path : `/${path}`}`;
|
|
305
|
+
const body = this.BuildOperationBody(ctx.Attributes, obj.CreateBodyShape, obj.CreateBodyKey);
|
|
306
|
+
const response = await this.MakeHTTPRequest(auth, url, obj.CreateMethod, this.BuildHeaders(auth), body);
|
|
307
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
308
|
+
const externalID = this.ExtractIDFromResponse(response, obj.CreateIDLocation);
|
|
309
|
+
return this.BuildCreatedResult(externalID, response.Status, ctx.ObjectName);
|
|
310
|
+
}
|
|
311
|
+
return { Success: false, StatusCode: response.Status, ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on create` };
|
|
312
|
+
}
|
|
313
|
+
/** Update: nested paths fill parent vars from attributes + the innermost var from the ExternalID; top-level delegates. */
|
|
314
|
+
async UpdateRecord(ctx) {
|
|
315
|
+
const ci = ctx.CompanyIntegration;
|
|
316
|
+
const contextUser = ctx.ContextUser;
|
|
317
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
318
|
+
if (!obj.UpdateAPIPath || !obj.UpdateMethod)
|
|
319
|
+
return super.UpdateRecord(ctx);
|
|
320
|
+
if (this.detectPathVars(obj.UpdateAPIPath).length <= 1)
|
|
321
|
+
return super.UpdateRecord(ctx);
|
|
322
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
323
|
+
const path = this.fillPathFromAttributes(obj.UpdateAPIPath, ctx.Attributes, ctx.ExternalID);
|
|
324
|
+
this.assertPathResolved(path, ctx.ObjectName, 'update');
|
|
325
|
+
const url = `${this.GetBaseURL(ci, auth)}${path.startsWith('/') ? path : `/${path}`}`;
|
|
326
|
+
const body = this.BuildOperationBody(ctx.Attributes, obj.UpdateBodyShape, obj.UpdateBodyKey);
|
|
327
|
+
const response = await this.MakeHTTPRequest(auth, url, obj.UpdateMethod, this.BuildHeaders(auth), body);
|
|
328
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
329
|
+
return { Success: true, StatusCode: response.Status, ExternalID: ctx.ExternalID };
|
|
330
|
+
}
|
|
331
|
+
return { Success: false, StatusCode: response.Status, ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on update` };
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Delete: nested paths resolve every `{var}` from the `|`-split composite ExternalID (right-aligned:
|
|
335
|
+
* `{list_id}`,`{subscriber_hash}` ← `list1|hashaaa`); top-level delegates. A nested delete whose parent
|
|
336
|
+
* var still can't be resolved (e.g. a single-component ExternalID that carries only the child key) fails
|
|
337
|
+
* loudly rather than issuing a request with an unresolved `{parent}` — a live-verification concern flagged
|
|
338
|
+
* in CODE_REPORT.
|
|
339
|
+
*/
|
|
340
|
+
async DeleteRecord(ctx) {
|
|
341
|
+
const ci = ctx.CompanyIntegration;
|
|
342
|
+
const contextUser = ctx.ContextUser;
|
|
343
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
344
|
+
if (!obj.DeleteAPIPath || !obj.DeleteMethod)
|
|
345
|
+
return super.DeleteRecord(ctx);
|
|
346
|
+
if (this.detectPathVars(obj.DeleteAPIPath).length <= 1)
|
|
347
|
+
return super.DeleteRecord(ctx);
|
|
348
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
349
|
+
const path = this.fillPathFromAttributes(obj.DeleteAPIPath, {}, ctx.ExternalID);
|
|
350
|
+
const unresolved = this.detectPathVars(path);
|
|
351
|
+
if (unresolved.length > 0) {
|
|
352
|
+
return {
|
|
353
|
+
Success: false,
|
|
354
|
+
StatusCode: 0,
|
|
355
|
+
ErrorMessage: `Delete of nested "${ctx.ObjectName}" could not resolve parent path var(s) {${unresolved.join(', ')}} — a nested delete needs the parent id, which the delete context does not carry (requires live verification / composite key).`,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
const url = `${this.GetBaseURL(ci, auth)}${path.startsWith('/') ? path : `/${path}`}`;
|
|
359
|
+
const response = await this.MakeHTTPRequest(auth, url, obj.DeleteMethod, this.BuildHeaders(auth));
|
|
360
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
361
|
+
return { Success: true, StatusCode: response.Status, ExternalID: ctx.ExternalID };
|
|
362
|
+
}
|
|
363
|
+
return { Success: false, StatusCode: response.Status, ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on delete` };
|
|
364
|
+
}
|
|
365
|
+
// ── Helpers ───────────────────────────────────────────────────────
|
|
366
|
+
/**
|
|
367
|
+
* Counts the records in a Mailchimp collection envelope by finding the per-object data array (the first
|
|
368
|
+
* array-valued property that isn't the `_links` HATEOAS list). Used for pagination advancement — the
|
|
369
|
+
* data key isn't passed to ExtractPaginationInfo, so we locate the array structurally. Returns 0 for a
|
|
370
|
+
* single-record body (no array), which correctly reports "no more pages".
|
|
371
|
+
*/
|
|
372
|
+
countEnvelopeRecords(env) {
|
|
373
|
+
for (const [key, value] of Object.entries(env)) {
|
|
374
|
+
if (key === '_links')
|
|
375
|
+
continue;
|
|
376
|
+
if (Array.isArray(value))
|
|
377
|
+
return value.length;
|
|
378
|
+
}
|
|
379
|
+
return 0;
|
|
380
|
+
}
|
|
381
|
+
/** Extracts `{var}` names from a path template, in left-to-right order. */
|
|
382
|
+
detectPathVars(path) {
|
|
383
|
+
const matches = path.match(/\{(\w+)\}/g);
|
|
384
|
+
return matches ? matches.map(m => m.slice(1, -1)) : [];
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Fills each `{var}` in a nested write path by resolving, per var, in this order:
|
|
388
|
+
* 1. a same-named attribute on the record (e.g. the base tags fetched children with their parent FK
|
|
389
|
+
* field, so a synced member carries `list_id`);
|
|
390
|
+
* 2. the RIGHT-ALIGNED component of the `|`-split ExternalID — mirroring ToExternalRecord's composite-PK
|
|
391
|
+
* join order (`parent|…|child`): the innermost (last) var is the record's OWN key, preceding vars are
|
|
392
|
+
* the parent id(s). So a member's ExternalID `list1|hashaaa` fills `{list_id}`→`list1`,
|
|
393
|
+
* `{subscriber_hash}`→`hashaaa`, which is exactly what a nested update/delete needs even with no
|
|
394
|
+
* attributes present.
|
|
395
|
+
* Create paths pass externalID=null and thus fill purely from attributes. Any var that resolves to nothing
|
|
396
|
+
* is left in place so {@link assertPathResolved} / the delete guard can surface it (fail loudly, never
|
|
397
|
+
* issue a request with an unresolved `{parent}`).
|
|
398
|
+
*/
|
|
399
|
+
fillPathFromAttributes(template, attributes, externalID) {
|
|
400
|
+
const vars = this.detectPathVars(template);
|
|
401
|
+
const components = externalID != null ? String(externalID).split('|') : [];
|
|
402
|
+
const offset = vars.length - components.length; // right-align components to the tail of the var list
|
|
403
|
+
let out = template;
|
|
404
|
+
vars.forEach((v, i) => {
|
|
405
|
+
let val = attributes[v];
|
|
406
|
+
if (val == null || val === '') {
|
|
407
|
+
const compIdx = i - offset;
|
|
408
|
+
if (compIdx >= 0 && compIdx < components.length)
|
|
409
|
+
val = components[compIdx];
|
|
410
|
+
}
|
|
411
|
+
if (val != null && val !== '')
|
|
412
|
+
out = out.replace(`{${v}}`, encodeURIComponent(String(val)));
|
|
413
|
+
});
|
|
414
|
+
return out;
|
|
415
|
+
}
|
|
416
|
+
/** Throws a clear error if a nested write path still has unresolved `{var}`s (missing parent id). */
|
|
417
|
+
assertPathResolved(path, objectName, op) {
|
|
418
|
+
const unresolved = this.detectPathVars(path);
|
|
419
|
+
if (unresolved.length > 0) {
|
|
420
|
+
throw new Error(`Mailchimp ${op} of "${objectName}": unresolved path var(s) {${unresolved.join(', ')}} — ` +
|
|
421
|
+
`the record must carry the parent id field(s) (e.g. list_id / store_id) in its attributes.`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/** True when a URL already carries a query param with `name` (case-insensitive). */
|
|
425
|
+
urlHasParam(url, name) {
|
|
426
|
+
const q = url.indexOf('?');
|
|
427
|
+
if (q < 0)
|
|
428
|
+
return false;
|
|
429
|
+
const lname = name.toLowerCase();
|
|
430
|
+
return url.substring(q + 1).split('&').some(p => {
|
|
431
|
+
const eq = p.indexOf('=');
|
|
432
|
+
const k = (eq >= 0 ? p.substring(0, eq) : p).toLowerCase();
|
|
433
|
+
return k === lname;
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
/** Loads credentials from the linked Credential entity (preferred) or the CompanyIntegration Configuration JSON. */
|
|
437
|
+
async loadCredentials(companyIntegration, contextUser) {
|
|
438
|
+
const fromConfig = companyIntegration.Configuration ? this.parseCredentialJson(companyIntegration.Configuration) : null;
|
|
439
|
+
let fromCred = null;
|
|
440
|
+
if (companyIntegration.CredentialID) {
|
|
441
|
+
fromCred = await this.loadFromCredential(companyIntegration.CredentialID, contextUser);
|
|
442
|
+
}
|
|
443
|
+
// Credential secret (ApiKey) wins; Configuration supplies non-secret dc / base-URL overrides.
|
|
444
|
+
return { ...(fromConfig ?? {}), ...(fromCred ?? {}) };
|
|
445
|
+
}
|
|
446
|
+
/** Loads a credential row and parses its Values JSON. */
|
|
447
|
+
async loadFromCredential(credentialID, contextUser, provider) {
|
|
448
|
+
const md = provider ?? new Metadata();
|
|
449
|
+
const credential = await md.GetEntityObject('MJ: Credentials', contextUser);
|
|
450
|
+
const loaded = await credential.Load(credentialID);
|
|
451
|
+
if (!loaded || !credential.Values)
|
|
452
|
+
return null;
|
|
453
|
+
return this.parseCredentialJson(credential.Values);
|
|
454
|
+
}
|
|
455
|
+
/** Parses a Mailchimp credential/config JSON blob, tolerant of key casing variants. */
|
|
456
|
+
parseCredentialJson(json) {
|
|
457
|
+
let parsed;
|
|
458
|
+
try {
|
|
459
|
+
parsed = JSON.parse(json);
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
const scheme = this.firstString(parsed, ['scheme', 'Scheme', 'authScheme', 'AuthScheme']);
|
|
465
|
+
return {
|
|
466
|
+
ApiKey: this.firstString(parsed, ['ApiKey', 'apiKey', 'API_Key', 'apikey', 'apiToken', 'ApiToken', 'token', 'Token', 'accessToken', 'AccessToken']),
|
|
467
|
+
DataCenter: this.firstString(parsed, ['DataCenter', 'dataCenter', 'ServerPrefix', 'serverPrefix', 'server', 'dc', 'prefix']),
|
|
468
|
+
BaseURLOverride: this.firstBaseURL(parsed, ['BaseURL', 'BaseUrl', 'baseURL', 'baseUrl', 'APIBaseURL', 'ApiBaseURL', 'apiBaseUrl']),
|
|
469
|
+
Scheme: scheme === 'bearer' ? 'bearer' : 'basic',
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Resolves the data-center prefix (e.g. `us15`):
|
|
474
|
+
* 1. explicit DataCenter / ServerPrefix from config/credential;
|
|
475
|
+
* 2. the `-<dc>` suffix parsed off the API key;
|
|
476
|
+
* 3. (runtime fallback for an OAuth bearer token with no dc) the OAuth2 metadata endpoint.
|
|
477
|
+
* A BaseURLOverride short-circuits the need for a real dc (used for sandbox/test/mock).
|
|
478
|
+
*/
|
|
479
|
+
async resolveDataCenter(creds) {
|
|
480
|
+
if (creds.DataCenter && creds.DataCenter.trim().length > 0)
|
|
481
|
+
return creds.DataCenter.trim().toLowerCase();
|
|
482
|
+
const fromKey = this.parseDataCenterFromKey(creds.ApiKey ?? '');
|
|
483
|
+
if (fromKey)
|
|
484
|
+
return fromKey;
|
|
485
|
+
if (creds.BaseURLOverride)
|
|
486
|
+
return 'override'; // dc unused when an explicit base URL is present
|
|
487
|
+
return this.fetchDataCenterFromOAuthMetadata(creds.ApiKey ?? '', creds.Scheme);
|
|
488
|
+
}
|
|
489
|
+
/** Extracts `<dc>` from a Mailchimp API key of the form `<secret>-<dc>` (e.g. `abc123-us15` → `us15`). */
|
|
490
|
+
parseDataCenterFromKey(apiKey) {
|
|
491
|
+
const idx = apiKey.lastIndexOf('-');
|
|
492
|
+
if (idx < 0 || idx >= apiKey.length - 1)
|
|
493
|
+
return null;
|
|
494
|
+
const suffix = apiKey.slice(idx + 1).toLowerCase();
|
|
495
|
+
return /^[a-z]{2}\d{1,3}$/.test(suffix) ? suffix : null;
|
|
496
|
+
}
|
|
497
|
+
/** Runtime fallback: GET /oauth2/metadata to discover the data center for an OAuth access token. */
|
|
498
|
+
async fetchDataCenterFromOAuthMetadata(token, scheme) {
|
|
499
|
+
const authValue = scheme === 'bearer' ? `Bearer ${token}` : `OAuth ${token}`;
|
|
500
|
+
const response = await fetch(MAILCHIMP_OAUTH_METADATA_URL, {
|
|
501
|
+
method: 'GET',
|
|
502
|
+
headers: { 'Authorization': authValue, 'Accept': 'application/json' },
|
|
503
|
+
});
|
|
504
|
+
if (!response.ok) {
|
|
505
|
+
throw new Error(`Mailchimp /oauth2/metadata returned ${response.status}; cannot resolve the data center. Set DataCenter (or ServerPrefix) explicitly.`);
|
|
506
|
+
}
|
|
507
|
+
const body = await response.json();
|
|
508
|
+
if (!body.dc)
|
|
509
|
+
throw new Error('Mailchimp /oauth2/metadata response did not include a dc field.');
|
|
510
|
+
return body.dc.toLowerCase();
|
|
511
|
+
}
|
|
512
|
+
/** Returns the first non-empty string value among the candidate keys, else undefined. */
|
|
513
|
+
firstString(obj, keys) {
|
|
514
|
+
for (const k of keys) {
|
|
515
|
+
const v = obj[k];
|
|
516
|
+
if (typeof v === 'string' && v.trim().length > 0)
|
|
517
|
+
return v.trim();
|
|
518
|
+
if (typeof v === 'number')
|
|
519
|
+
return String(v);
|
|
520
|
+
}
|
|
521
|
+
return undefined;
|
|
522
|
+
}
|
|
523
|
+
/** Returns the first candidate key whose value is a valid absolute http(s) URL, else undefined. */
|
|
524
|
+
firstBaseURL(obj, keys) {
|
|
525
|
+
const raw = this.firstString(obj, keys);
|
|
526
|
+
return raw && /^https?:\/\//i.test(raw) ? raw : undefined;
|
|
527
|
+
}
|
|
528
|
+
/** Computes a 429/503 backoff delay, honoring an opportunistic Retry-After header when present. */
|
|
529
|
+
computeBackoffMs(response, attempt) {
|
|
530
|
+
const retryAfter = response.headers.get('retry-after');
|
|
531
|
+
if (retryAfter) {
|
|
532
|
+
const secs = Number(retryAfter);
|
|
533
|
+
if (Number.isFinite(secs) && secs > 0)
|
|
534
|
+
return Math.min(secs * 1000, 60_000);
|
|
535
|
+
}
|
|
536
|
+
return Math.min(500 * Math.pow(2, attempt) + Math.floor(Math.random() * 250), 30_000);
|
|
537
|
+
}
|
|
538
|
+
/** Sleep helper. */
|
|
539
|
+
sleep(ms) {
|
|
540
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
MailchimpConnector = __decorate([
|
|
544
|
+
RegisterClass(BaseIntegrationConnector, 'MailchimpConnector')
|
|
545
|
+
], MailchimpConnector);
|
|
546
|
+
export { MailchimpConnector };
|
|
547
|
+
/** Tree-shaking prevention — call from the package entry point. */
|
|
548
|
+
export function LoadMailchimpConnector() { }
|
|
549
|
+
//# sourceMappingURL=MailchimpConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MailchimpConnector.js","sourceRoot":"","sources":["../src/MailchimpConnector.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from './MailchimpConnector.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 './MailchimpConnector.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,yBAAyB,CAAC;AAExC;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@memberjunction/connector-mailchimp",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "MemberJunction Mailchimp 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
|
+
}
|