@memberjunction/connector-pheedloop 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,118 @@
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 CreateRecordContext, type CRUDResult } from '@memberjunction/integration-engine';
4
+ /**
5
+ * PheedLoop event-management platform connector (REST API v3).
6
+ *
7
+ * PheedLoop publishes its object catalog in a credential-free Postman collection (v3.3.0), so the
8
+ * connector's object/field set lives in the **Declared** metadata file
9
+ * (`metadata/integrations/pheedloop/.pheedloop.integration.json`) and is surfaced at runtime by the
10
+ * base {@link BaseRESTIntegrationConnector} `DiscoverObjects`/`DiscoverFields`/`IntrospectSchema`
11
+ * (cache-driven, credential-free) — there is NO baked catalog in this file and discovery is NOT
12
+ * overridden. The connector is pure mechanism: tri-component auth, page/page_size pagination over the
13
+ * Django-REST-Framework `{count,next,previous,results}` envelope, generic per-operation CRUD, and the
14
+ * one genuinely-idiosyncratic write (EventAttendance check-in) overridden below.
15
+ *
16
+ * Tri-component auth (dual header + path tenant):
17
+ * - `X-API-KEY` — the API Key (static; NOT a Bearer token)
18
+ * - `X-API-SECRET` — the API Secret (static; NOT a Bearer token)
19
+ * - Organization Code — a NON-secret tenant identifier injected as a URL-path segment:
20
+ * `https://api.pheedloop.com/api/v3/organization/{ORGANIZATION-CODE}/...`
21
+ * Key + secret come from the linked Credential (type "PheedLoop API"); the org code is read from the
22
+ * CompanyIntegration.Configuration JSON (`OrganizationCode`). Neither the credential nor the org code
23
+ * is ever hardcoded. The two header values are static strings — no signing/encoding crypto is involved,
24
+ * so no auth-helper applies (a helper would only matter for HMAC/OAuth/Basic encoding); the headers are
25
+ * assembled directly, which is NOT inline crypto.
26
+ */
27
+ export declare class PheedLoopConnector extends BaseRESTIntegrationConnector {
28
+ /** Verbatim three-way invariant: ClassName / IntegrationName getter / MJ: Integrations.Name. */
29
+ get IntegrationName(): string;
30
+ get SupportsCreate(): boolean;
31
+ get SupportsUpdate(): boolean;
32
+ get SupportsDelete(): boolean;
33
+ /**
34
+ * Resolves the API key + secret from the linked Credential entity and the non-secret organization
35
+ * code from the CompanyIntegration.Configuration JSON. Credential bytes never leave this scope.
36
+ */
37
+ protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<PheedLoopAuthContext>;
38
+ /**
39
+ * Dual static-header auth: `X-API-KEY` + `X-API-SECRET` on every request. Neither is a Bearer
40
+ * token; both are opaque key/secret strings requiring no encoding, so no auth-helper is used.
41
+ */
42
+ protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
43
+ /**
44
+ * Base URL with the organization code injected as a path segment. The org code is a non-secret
45
+ * tenant identifier from Configuration — never hardcoded. Every collection request rides this prefix.
46
+ */
47
+ protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
48
+ /**
49
+ * Executes an HTTP request via fetch. PheedLoop requires a TRAILING SLASH on every resource path
50
+ * (before any query string) — the base BuildFullURL strips it, so it is re-added here. JSON bodies
51
+ * are sent for non-GET/DELETE verbs.
52
+ */
53
+ protected MakeHTTPRequest(_auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
54
+ /**
55
+ * Unwraps the Django-REST-Framework pagination envelope `{count, next, previous, results}` to the
56
+ * `results[]` record array. Honors an explicit ResponseDataKey when set, falls back to `results`,
57
+ * and tolerates a bare array or single object (e.g. a create echo, or the flat-shape SessionRegistration).
58
+ */
59
+ protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
60
+ /**
61
+ * Page-number pagination over the DRF envelope. `next` (a non-null URL) is the authoritative
62
+ * "more pages" signal; a short/empty `results` page is the fallback. PaginationType 'None' (e.g.
63
+ * EventAttendance) reports a single page.
64
+ */
65
+ protected ExtractPaginationInfo(rawBody: unknown, paginationType: PaginationType, currentPage: number, currentOffset: number, pageSize: number): PaginationState;
66
+ /**
67
+ * PheedLoop uses `page` (1-based) + `page_size` query params (NOT the base loop's page/pageSize).
68
+ * Override to emit the vendor's actual param names. The base FetchPaginatedLoop is ALSO 1-based
69
+ * (`let page = ctx.CurrentPage ?? 1`), and PheedLoop's `page` is 1-based, so the loop page maps
70
+ * DIRECTLY to the vendor page — do NOT add 1 (doing so skipped page 1 and requested page 2 first,
71
+ * which PheedLoop rejects with 404 "Invalid page" → 0 records synced).
72
+ */
73
+ protected BuildPaginatedURL(basePath: string, obj: MJIntegrationObjectEntity, page: number, offset: number, cursor?: string, effectivePageSize?: number): string;
74
+ /** Tests connectivity by listing one page of the org-scoped Events collection. */
75
+ TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
76
+ CreateRecord(ctx: CreateRecordContext): Promise<CRUDResult>;
77
+ /**
78
+ * Executes a PheedLoop check-in: POST .../checkin/ with the flat body the metadata configures
79
+ * (typically `{codes:[<attendee-code>...]}`), reads the checked-in attendee from the response's
80
+ * `attendees[]` array as the created-record identity, and routes the outcome through
81
+ * BuildCreatedResult so a no-usable-attendee result is a loud failure, not a silent success.
82
+ */
83
+ private CheckInAttendance;
84
+ /**
85
+ * Reads the first successfully checked-in attendee code from a `{attendees:[...], errored_attendees:[...]}`
86
+ * check-in response. Returns undefined when `attendees` is empty/absent (so the check-in is treated
87
+ * as a failure). Entries may be bare strings (codes) or objects carrying a code/id field.
88
+ */
89
+ private ExtractCheckedInAttendee;
90
+ /**
91
+ * Extracts the new record's external ID from a create response. PheedLoop's universal PK is the
92
+ * STRING `code` field on most resources (numeric `id` on Tags / REST Hooks), so `code` is checked
93
+ * FIRST — ahead of the base class's id/externalID names — before falling back to the base for the
94
+ * numeric-id and Location-header cases. (EventAttendance's `n/a` create is handled in CreateRecord.)
95
+ */
96
+ protected ExtractIDFromResponse(response: RESTResponse, idLocation: string | null): string | undefined;
97
+ /**
98
+ * Resolves the API key + secret (from the linked Credential) and the organization code (from the
99
+ * CompanyIntegration.Configuration JSON). Throws a descriptive error when any required component is
100
+ * missing. The org code is per-tenant config — NEVER hardcoded.
101
+ */
102
+ private LoadAuth;
103
+ /** Loads ApiKey/ApiSecret/OrganizationCode from a Credential entity's Values JSON. */
104
+ private LoadFromCredentialEntity;
105
+ /** Parses a JSON string into PheedLoop credential components (tolerant of casing/aliases). */
106
+ private ParseCredentialJson;
107
+ /** Ensures the resource path carries a trailing slash before any query string (PheedLoop requirement). */
108
+ private EnsureTrailingSlash;
109
+ }
110
+ /** Resolved PheedLoop auth context (key + secret headers, org-code path segment, optional host override). */
111
+ interface PheedLoopAuthContext extends RESTAuthContext {
112
+ ApiKey: string;
113
+ ApiSecret: string;
114
+ OrganizationCode: string;
115
+ /** Non-secret host override (e.g. a local mock for replay testing). Defaults to PHEEDLOOP_HOST. */
116
+ BaseHost?: string;
117
+ }
118
+ export {};
@@ -0,0 +1,429 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { RegisterClass } from '@memberjunction/global';
8
+ import { Metadata } from '@memberjunction/core';
9
+ import { BaseIntegrationConnector, BaseRESTIntegrationConnector, } from '@memberjunction/integration-engine';
10
+ import { z } from 'zod';
11
+ /**
12
+ * PheedLoop event-management platform connector (REST API v3).
13
+ *
14
+ * PheedLoop publishes its object catalog in a credential-free Postman collection (v3.3.0), so the
15
+ * connector's object/field set lives in the **Declared** metadata file
16
+ * (`metadata/integrations/pheedloop/.pheedloop.integration.json`) and is surfaced at runtime by the
17
+ * base {@link BaseRESTIntegrationConnector} `DiscoverObjects`/`DiscoverFields`/`IntrospectSchema`
18
+ * (cache-driven, credential-free) — there is NO baked catalog in this file and discovery is NOT
19
+ * overridden. The connector is pure mechanism: tri-component auth, page/page_size pagination over the
20
+ * Django-REST-Framework `{count,next,previous,results}` envelope, generic per-operation CRUD, and the
21
+ * one genuinely-idiosyncratic write (EventAttendance check-in) overridden below.
22
+ *
23
+ * Tri-component auth (dual header + path tenant):
24
+ * - `X-API-KEY` — the API Key (static; NOT a Bearer token)
25
+ * - `X-API-SECRET` — the API Secret (static; NOT a Bearer token)
26
+ * - Organization Code — a NON-secret tenant identifier injected as a URL-path segment:
27
+ * `https://api.pheedloop.com/api/v3/organization/{ORGANIZATION-CODE}/...`
28
+ * Key + secret come from the linked Credential (type "PheedLoop API"); the org code is read from the
29
+ * CompanyIntegration.Configuration JSON (`OrganizationCode`). Neither the credential nor the org code
30
+ * is ever hardcoded. The two header values are static strings — no signing/encoding crypto is involved,
31
+ * so no auth-helper applies (a helper would only matter for HMAC/OAuth/Basic encoding); the headers are
32
+ * assembled directly, which is NOT inline crypto.
33
+ */
34
+ let PheedLoopConnector = class PheedLoopConnector extends BaseRESTIntegrationConnector {
35
+ /** Verbatim three-way invariant: ClassName / IntegrationName getter / MJ: Integrations.Name. */
36
+ get IntegrationName() {
37
+ return 'PheedLoop';
38
+ }
39
+ // ── Capability surface ───────────────────────────────────────────
40
+ // Per-IO write columns (Create/Update/Delete* on each IntegrationObject) drive the generic CRUD
41
+ // path. These getters report that the connector CAN write so the engine attempts the per-IO verbs
42
+ // the metadata configures (the metadata gates which IOs actually support which verb).
43
+ get SupportsCreate() { return true; }
44
+ get SupportsUpdate() { return true; }
45
+ get SupportsDelete() { return true; }
46
+ // ── Auth + transport (BaseRESTIntegrationConnector abstracts) ─────
47
+ /**
48
+ * Resolves the API key + secret from the linked Credential entity and the non-secret organization
49
+ * code from the CompanyIntegration.Configuration JSON. Credential bytes never leave this scope.
50
+ */
51
+ async Authenticate(companyIntegration, contextUser) {
52
+ return this.LoadAuth(companyIntegration, contextUser);
53
+ }
54
+ /**
55
+ * Dual static-header auth: `X-API-KEY` + `X-API-SECRET` on every request. Neither is a Bearer
56
+ * token; both are opaque key/secret strings requiring no encoding, so no auth-helper is used.
57
+ */
58
+ BuildHeaders(auth) {
59
+ const a = auth;
60
+ return {
61
+ 'X-API-KEY': a.ApiKey,
62
+ 'X-API-SECRET': a.ApiSecret,
63
+ 'Accept': 'application/json',
64
+ 'User-Agent': 'MemberJunction-Integration/1.0',
65
+ };
66
+ }
67
+ /**
68
+ * Base URL with the organization code injected as a path segment. The org code is a non-secret
69
+ * tenant identifier from Configuration — never hardcoded. Every collection request rides this prefix.
70
+ */
71
+ GetBaseURL(_companyIntegration, auth) {
72
+ const a = auth;
73
+ const host = (a.BaseHost ?? PHEEDLOOP_HOST).replace(/\/+$/, '');
74
+ return `${host}/api/v3/organization/${encodeURIComponent(a.OrganizationCode)}`;
75
+ }
76
+ /**
77
+ * Executes an HTTP request via fetch. PheedLoop requires a TRAILING SLASH on every resource path
78
+ * (before any query string) — the base BuildFullURL strips it, so it is re-added here. JSON bodies
79
+ * are sent for non-GET/DELETE verbs.
80
+ */
81
+ async MakeHTTPRequest(_auth, url, method, headers, body) {
82
+ const finalUrl = this.EnsureTrailingSlash(url);
83
+ const init = { method, headers: { ...headers } };
84
+ if (body !== undefined && method !== 'GET' && method !== 'HEAD' && method !== 'DELETE') {
85
+ init.body = typeof body === 'string' ? body : JSON.stringify(body);
86
+ init.headers['Content-Type'] = 'application/json';
87
+ }
88
+ const response = await fetch(finalUrl, init);
89
+ const responseHeaders = {};
90
+ response.headers.forEach((value, key) => { responseHeaders[key.toLowerCase()] = value; });
91
+ const text = await response.text();
92
+ let parsed = text;
93
+ const contentType = responseHeaders['content-type'] ?? '';
94
+ if (contentType.includes('json') || (text.length > 0 && (text[0] === '{' || text[0] === '['))) {
95
+ try {
96
+ parsed = JSON.parse(text);
97
+ }
98
+ catch {
99
+ parsed = text;
100
+ }
101
+ }
102
+ // PheedLoop signals "past the last page" with HTTP 404 {"detail":"Invalid page."} rather than
103
+ // an empty page (a DRF paginator behaviour). On a GET, treat that SPECIFIC 404 as a graceful
104
+ // empty final page so the paginated fetch terminates with the records already collected, instead
105
+ // of failing the whole sync. A genuine 404 (no "Invalid page" detail — e.g. a wrong path) is
106
+ // left as-is so real errors still surface.
107
+ if (response.status === 404 &&
108
+ method === 'GET' &&
109
+ isRecord(parsed) &&
110
+ /invalid page/i.test(String(parsed['detail'] ?? ''))) {
111
+ return { Status: 200, Body: { results: [], next: null }, Headers: responseHeaders };
112
+ }
113
+ return { Status: response.status, Body: parsed, Headers: responseHeaders };
114
+ }
115
+ /**
116
+ * Unwraps the Django-REST-Framework pagination envelope `{count, next, previous, results}` to the
117
+ * `results[]` record array. Honors an explicit ResponseDataKey when set, falls back to `results`,
118
+ * and tolerates a bare array or single object (e.g. a create echo, or the flat-shape SessionRegistration).
119
+ */
120
+ NormalizeResponse(rawBody, responseDataKey) {
121
+ if (rawBody == null)
122
+ return [];
123
+ if (Array.isArray(rawBody))
124
+ return rawBody.filter(isRecord);
125
+ if (!isRecord(rawBody))
126
+ return [];
127
+ const body = rawBody;
128
+ if (responseDataKey && Array.isArray(body[responseDataKey])) {
129
+ return body[responseDataKey].filter(isRecord);
130
+ }
131
+ if (Array.isArray(body['results'])) {
132
+ return body['results'].filter(isRecord);
133
+ }
134
+ // A single-object response (create echo / flat object endpoint) becomes a one-element array.
135
+ if (Object.keys(body).length > 0)
136
+ return [body];
137
+ return [];
138
+ }
139
+ /**
140
+ * Page-number pagination over the DRF envelope. `next` (a non-null URL) is the authoritative
141
+ * "more pages" signal; a short/empty `results` page is the fallback. PaginationType 'None' (e.g.
142
+ * EventAttendance) reports a single page.
143
+ */
144
+ ExtractPaginationInfo(rawBody, paginationType, currentPage, currentOffset, pageSize) {
145
+ if (paginationType === 'None')
146
+ return { HasMore: false };
147
+ const records = this.NormalizeResponse(rawBody, null);
148
+ const envelope = isRecord(rawBody) ? rawBody : null;
149
+ const next = envelope?.['next'];
150
+ const total = typeof envelope?.['count'] === 'number' ? envelope['count'] : undefined;
151
+ switch (paginationType) {
152
+ case 'PageNumber': {
153
+ const hasNextUrl = typeof next === 'string' && next.length > 0;
154
+ const effectivePageSize = pageSize > 0 ? pageSize : DEFAULT_PAGE_SIZE;
155
+ // Prefer the DRF `next` link; otherwise a full page implies another may follow.
156
+ const hasMore = hasNextUrl || (records.length >= effectivePageSize && records.length > 0);
157
+ return { HasMore: hasMore, NextPage: currentPage + 1, TotalRecords: total };
158
+ }
159
+ case 'Offset':
160
+ return { HasMore: records.length >= pageSize, NextOffset: currentOffset + records.length };
161
+ case 'Cursor': {
162
+ const cursor = typeof next === 'string' ? next : undefined;
163
+ return { HasMore: typeof cursor === 'string' && cursor.length > 0, NextCursor: cursor };
164
+ }
165
+ default:
166
+ return { HasMore: records.length >= pageSize };
167
+ }
168
+ }
169
+ /**
170
+ * PheedLoop uses `page` (1-based) + `page_size` query params (NOT the base loop's page/pageSize).
171
+ * Override to emit the vendor's actual param names. The base FetchPaginatedLoop is ALSO 1-based
172
+ * (`let page = ctx.CurrentPage ?? 1`), and PheedLoop's `page` is 1-based, so the loop page maps
173
+ * DIRECTLY to the vendor page — do NOT add 1 (doing so skipped page 1 and requested page 2 first,
174
+ * which PheedLoop rejects with 404 "Invalid page" → 0 records synced).
175
+ */
176
+ BuildPaginatedURL(basePath, obj, page, offset, cursor, effectivePageSize) {
177
+ const pageSize = effectivePageSize ?? obj.DefaultPageSize ?? DEFAULT_PAGE_SIZE;
178
+ const separator = basePath.includes('?') ? '&' : '?';
179
+ switch (obj.PaginationType) {
180
+ case 'PageNumber':
181
+ // Base loop page is 1-based (FetchPaginatedLoop: `page = ctx.CurrentPage ?? 1`) and so
182
+ // is PheedLoop's `page` — emit it directly. (Adding 1 skipped page 1 → 404 "Invalid page".)
183
+ return `${basePath}${separator}page=${page}&page_size=${pageSize}`;
184
+ case 'Offset':
185
+ return `${basePath}${separator}offset=${offset}&page_size=${pageSize}`;
186
+ case 'Cursor':
187
+ return cursor
188
+ ? `${basePath}${separator}cursor=${encodeURIComponent(cursor)}&page_size=${pageSize}`
189
+ : `${basePath}${separator}page_size=${pageSize}`;
190
+ default:
191
+ return basePath;
192
+ }
193
+ }
194
+ // ── Connection test ──────────────────────────────────────────────
195
+ /** Tests connectivity by listing one page of the org-scoped Events collection. */
196
+ async TestConnection(companyIntegration, contextUser) {
197
+ try {
198
+ const auth = await this.Authenticate(companyIntegration, contextUser);
199
+ const url = `${this.GetBaseURL(companyIntegration, auth)}/events/?page=1&page_size=1`;
200
+ const response = await this.MakeHTTPRequest(auth, url, 'GET', this.BuildHeaders(auth));
201
+ if (response.Status === 401 || response.Status === 403) {
202
+ return {
203
+ Success: false,
204
+ Message: `PheedLoop rejected the credentials (HTTP ${response.Status}). Verify the API Key / API Secret and the Organization Code.`,
205
+ };
206
+ }
207
+ if (response.Status === 404) {
208
+ return {
209
+ Success: false,
210
+ Message: `PheedLoop returned HTTP 404 — the Organization Code "${auth.OrganizationCode}" may be incorrect.`,
211
+ };
212
+ }
213
+ if (response.Status >= 200 && response.Status < 300) {
214
+ return { Success: true, Message: `Connected to PheedLoop organization "${auth.OrganizationCode}".` };
215
+ }
216
+ return { Success: false, Message: `PheedLoop responded HTTP ${response.Status}.` };
217
+ }
218
+ catch (err) {
219
+ const message = err instanceof Error ? err.message : String(err);
220
+ return { Success: false, Message: `PheedLoop connection error: ${message}` };
221
+ }
222
+ }
223
+ // NOTE: DiscoverObjects / DiscoverFields / IntrospectSchema / FetchChanges are intentionally NOT
224
+ // overridden. PheedLoop's catalog is credential-free (Postman collection v3.3.0) and lives as
225
+ // Declared metadata; the base implementations read it from the IntegrationEngineBase cache. The
226
+ // base FetchChanges walks {eventCode}/{sessionCode} template-var paths via ResolveParentChain.
227
+ // ── EventAttendance check-in override (REQUIRED — idiosyncratic write) ──
228
+ //
229
+ // POST .../checkin/ is a STATE MUTATION returning {attendees:[...], errored_attendees:[...]} — arrays
230
+ // of attendee codes — with NO created-record id (CreateIDLocation='n/a'). The generic CreateRecord →
231
+ // ExtractIDFromResponse would find no id and BuildCreatedResult would (correctly) fail. So for
232
+ // EventAttendance ONLY, the created-record identity is the checked-in attendee (attendees[0]); every
233
+ // other IO delegates to the generic per-operation-column create on the base class. The result STILL
234
+ // routes through BuildCreatedResult so an empty/failed check-in fails loudly (the empty-ID invariant).
235
+ async CreateRecord(ctx) {
236
+ if (ctx.ObjectName !== EVENT_ATTENDANCE_OBJECT) {
237
+ return super.CreateRecord(ctx);
238
+ }
239
+ return this.CheckInAttendance(ctx);
240
+ }
241
+ /**
242
+ * Executes a PheedLoop check-in: POST .../checkin/ with the flat body the metadata configures
243
+ * (typically `{codes:[<attendee-code>...]}`), reads the checked-in attendee from the response's
244
+ * `attendees[]` array as the created-record identity, and routes the outcome through
245
+ * BuildCreatedResult so a no-usable-attendee result is a loud failure, not a silent success.
246
+ */
247
+ async CheckInAttendance(ctx) {
248
+ const ci = ctx.CompanyIntegration;
249
+ const contextUser = ctx.ContextUser;
250
+ const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
251
+ if (!obj.CreateAPIPath || !obj.CreateMethod) {
252
+ throw new Error(`CreateRecord not supported for "${ctx.ObjectName}": CreateAPIPath / CreateMethod not configured.`);
253
+ }
254
+ // Use the overridable Authenticate seam (NOT the private LoadAuth) so tests can mock auth.
255
+ const auth = await this.Authenticate(ci, contextUser);
256
+ const baseURL = this.GetBaseURL(ci, auth);
257
+ const headers = this.BuildHeaders(auth);
258
+ const url = `${baseURL}${obj.CreateAPIPath.startsWith('/') ? '' : '/'}${obj.CreateAPIPath}`;
259
+ const body = this.BuildOperationBody(ctx.Attributes, obj.CreateBodyShape, obj.CreateBodyKey);
260
+ const response = await this.MakeHTTPRequest(auth, url, obj.CreateMethod, headers, body);
261
+ if (response.Status < 200 || response.Status >= 300) {
262
+ return {
263
+ Success: false,
264
+ StatusCode: response.Status,
265
+ ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on EventAttendance check-in`,
266
+ };
267
+ }
268
+ const checkedIn = this.ExtractCheckedInAttendee(response.Body);
269
+ // Empty/undefined identity → BuildCreatedResult turns the 2xx into a loud failure so a check-in
270
+ // that produced no usable attendee id cannot be silently lost.
271
+ return this.BuildCreatedResult(checkedIn, response.Status, ctx.ObjectName);
272
+ }
273
+ /**
274
+ * Reads the first successfully checked-in attendee code from a `{attendees:[...], errored_attendees:[...]}`
275
+ * check-in response. Returns undefined when `attendees` is empty/absent (so the check-in is treated
276
+ * as a failure). Entries may be bare strings (codes) or objects carrying a code/id field.
277
+ */
278
+ ExtractCheckedInAttendee(rawBody) {
279
+ if (!isRecord(rawBody))
280
+ return undefined;
281
+ const attendees = rawBody['attendees'];
282
+ if (!Array.isArray(attendees) || attendees.length === 0)
283
+ return undefined;
284
+ const first = attendees[0];
285
+ if (typeof first === 'string' && first.length > 0)
286
+ return first;
287
+ if (typeof first === 'number')
288
+ return String(first);
289
+ if (isRecord(first)) {
290
+ for (const k of ['code', 'Code', 'id', 'ID', 'Id', 'attendee_code']) {
291
+ const v = first[k];
292
+ if (typeof v === 'string' && v.length > 0)
293
+ return v;
294
+ if (typeof v === 'number')
295
+ return String(v);
296
+ }
297
+ }
298
+ return undefined;
299
+ }
300
+ /**
301
+ * Extracts the new record's external ID from a create response. PheedLoop's universal PK is the
302
+ * STRING `code` field on most resources (numeric `id` on Tags / REST Hooks), so `code` is checked
303
+ * FIRST — ahead of the base class's id/externalID names — before falling back to the base for the
304
+ * numeric-id and Location-header cases. (EventAttendance's `n/a` create is handled in CreateRecord.)
305
+ */
306
+ ExtractIDFromResponse(response, idLocation) {
307
+ if ((!idLocation || idLocation === 'body') && isRecord(response.Body)) {
308
+ const code = response.Body['code'];
309
+ if (typeof code === 'string' && code.length > 0)
310
+ return code;
311
+ if (typeof code === 'number')
312
+ return String(code);
313
+ }
314
+ return super.ExtractIDFromResponse(response, idLocation);
315
+ }
316
+ // ── Credential / configuration resolution ─────────────────────────
317
+ /**
318
+ * Resolves the API key + secret (from the linked Credential) and the organization code (from the
319
+ * CompanyIntegration.Configuration JSON). Throws a descriptive error when any required component is
320
+ * missing. The org code is per-tenant config — NEVER hardcoded.
321
+ */
322
+ async LoadAuth(companyIntegration, contextUser) {
323
+ let apiKey;
324
+ let apiSecret;
325
+ let organizationCode;
326
+ let baseHost;
327
+ const credentialID = companyIntegration.CredentialID;
328
+ if (credentialID) {
329
+ const fromCred = await this.LoadFromCredentialEntity(credentialID, contextUser);
330
+ if (fromCred) {
331
+ apiKey = fromCred.ApiKey ?? apiKey;
332
+ apiSecret = fromCred.ApiSecret ?? apiSecret;
333
+ organizationCode = fromCred.OrganizationCode ?? organizationCode;
334
+ }
335
+ }
336
+ // Configuration JSON supplies the non-secret OrganizationCode (and key/secret fallbacks).
337
+ const configJson = companyIntegration.Configuration;
338
+ if (configJson) {
339
+ const fromConfig = this.ParseCredentialJson(configJson);
340
+ if (fromConfig) {
341
+ apiKey = apiKey ?? fromConfig.ApiKey;
342
+ apiSecret = apiSecret ?? fromConfig.ApiSecret;
343
+ organizationCode = organizationCode ?? fromConfig.OrganizationCode;
344
+ baseHost = baseHost ?? fromConfig.BaseHost;
345
+ }
346
+ }
347
+ if (!apiKey || !apiSecret) {
348
+ throw new Error('No PheedLoop credential found — an "ApiKey" and "ApiSecret" are required (PheedLoop API credential type).');
349
+ }
350
+ if (!organizationCode) {
351
+ throw new Error('No PheedLoop OrganizationCode found — set "OrganizationCode" in the CompanyIntegration Configuration JSON (it is a non-secret tenant identifier, never hardcoded).');
352
+ }
353
+ return { ApiKey: apiKey, ApiSecret: apiSecret, OrganizationCode: organizationCode, BaseHost: baseHost };
354
+ }
355
+ /** Loads ApiKey/ApiSecret/OrganizationCode from a Credential entity's Values JSON. */
356
+ async LoadFromCredentialEntity(credentialID, contextUser, provider) {
357
+ const md = provider ?? new Metadata();
358
+ const credential = await md.GetEntityObject('MJ: Credentials', contextUser);
359
+ const loaded = await credential.Load(credentialID);
360
+ if (!loaded || !credential.Values)
361
+ return null;
362
+ return this.ParseCredentialJson(credential.Values);
363
+ }
364
+ /** Parses a JSON string into PheedLoop credential components (tolerant of casing/aliases). */
365
+ ParseCredentialJson(json) {
366
+ try {
367
+ const result = PheedLoopCredentialSchema.safeParse(JSON.parse(json));
368
+ if (!result.success)
369
+ return null;
370
+ const p = result.data;
371
+ const apiKey = p.ApiKey ?? p.apiKey ?? p['X-API-KEY'] ?? p.key;
372
+ const apiSecret = p.ApiSecret ?? p.apiSecret ?? p['X-API-SECRET'] ?? p.secret;
373
+ const organizationCode = p.OrganizationCode ?? p.organizationCode ?? p.orgCode ?? p.OrgCode;
374
+ const baseHost = p.apiBaseUrl ?? p.BaseURL ?? p.BaseHost;
375
+ if (!apiKey && !apiSecret && !organizationCode)
376
+ return null;
377
+ return { ApiKey: apiKey, ApiSecret: apiSecret, OrganizationCode: organizationCode, BaseHost: baseHost };
378
+ }
379
+ catch {
380
+ return null;
381
+ }
382
+ }
383
+ // ── Small helpers ─────────────────────────────────────────────────
384
+ /** Ensures the resource path carries a trailing slash before any query string (PheedLoop requirement). */
385
+ EnsureTrailingSlash(url) {
386
+ const qIndex = url.indexOf('?');
387
+ if (qIndex < 0) {
388
+ return url.endsWith('/') ? url : `${url}/`;
389
+ }
390
+ const path = url.slice(0, qIndex);
391
+ const query = url.slice(qIndex);
392
+ const pathWithSlash = path.endsWith('/') ? path : `${path}/`;
393
+ return `${pathWithSlash}${query}`;
394
+ }
395
+ };
396
+ PheedLoopConnector = __decorate([
397
+ RegisterClass(BaseIntegrationConnector, '@memberjunction/connector-pheedloop')
398
+ ], PheedLoopConnector);
399
+ export { PheedLoopConnector };
400
+ // ─── Module-level constants + helpers (mechanism, NOT a catalog) ──────
401
+ /** PheedLoop API host root. The /api/v3/organization/{org}/ prefix is appended in GetBaseURL. */
402
+ const PHEEDLOOP_HOST = 'https://api.pheedloop.com';
403
+ /** DRF default page size for PheedLoop data endpoints (Reports cap to 100 via metadata DefaultPageSize). */
404
+ const DEFAULT_PAGE_SIZE = 500;
405
+ /** The IO whose create is an idiosyncratic check-in state mutation (see CreateRecord override). */
406
+ const EVENT_ATTENDANCE_OBJECT = 'EventAttendance';
407
+ /** Zod schema for the credential / Configuration JSON shape (tolerant of casing aliases). */
408
+ const PheedLoopCredentialSchema = z.object({
409
+ ApiKey: z.string().optional(),
410
+ apiKey: z.string().optional(),
411
+ 'X-API-KEY': z.string().optional(),
412
+ key: z.string().optional(),
413
+ ApiSecret: z.string().optional(),
414
+ apiSecret: z.string().optional(),
415
+ 'X-API-SECRET': z.string().optional(),
416
+ secret: z.string().optional(),
417
+ OrganizationCode: z.string().optional(),
418
+ organizationCode: z.string().optional(),
419
+ orgCode: z.string().optional(),
420
+ OrgCode: z.string().optional(),
421
+ apiBaseUrl: z.string().optional(),
422
+ BaseURL: z.string().optional(),
423
+ BaseHost: z.string().optional(),
424
+ }).passthrough();
425
+ /** Narrows an unknown value to a plain record. */
426
+ function isRecord(v) {
427
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
428
+ }
429
+ //# sourceMappingURL=PheedLoopConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PheedLoopConnector.js","sourceRoot":"","sources":["../src/PheedLoopConnector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './PheedLoopConnector.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 './PheedLoopConnector.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,40 @@
1
+ {
2
+ "name": "@memberjunction/connector-pheedloop",
3
+ "version": "1.0.0",
4
+ "description": "MemberJunction PheedLoop connector.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "/dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc && tsc-alias -f",
13
+ "test": "vitest run --passWithNoTests"
14
+ },
15
+ "author": "MemberJunction.com",
16
+ "license": "ISC",
17
+ "peerDependencies": {
18
+ "@memberjunction/core": ">=5.42.0 <6.0.0",
19
+ "@memberjunction/core-entities": ">=5.42.0 <6.0.0",
20
+ "@memberjunction/global": ">=5.42.0 <6.0.0",
21
+ "@memberjunction/integration-engine": ">=5.42.0 <6.0.0"
22
+ },
23
+ "dependencies": {
24
+ "zod": "~3.24.4"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "24.10.11",
28
+ "tsc-alias": "^1.8.16",
29
+ "typescript": "^5.9.3",
30
+ "vitest": "^4.0.18",
31
+ "@memberjunction/core": "^5.42.0",
32
+ "@memberjunction/core-entities": "^5.42.0",
33
+ "@memberjunction/global": "^5.42.0",
34
+ "@memberjunction/integration-engine": "^5.42.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/MemberJunction/Integrations"
39
+ }
40
+ }