@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.
- package/dist/PheedLoopConnector.d.ts +118 -0
- package/dist/PheedLoopConnector.js +429 -0
- package/dist/PheedLoopConnector.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|