@memberjunction/connector-zendesk 1.1.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/ZendeskConnector.d.ts +199 -0
- package/dist/ZendeskConnector.js +644 -0
- package/dist/ZendeskConnector.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 +43 -0
|
@@ -0,0 +1,199 @@
|
|
|
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 RateLimitPolicy, type CreateRecordContext, type CRUDResult, type SourceSchemaInfo } from '@memberjunction/integration-engine';
|
|
4
|
+
/**
|
|
5
|
+
* Zendesk connector — extends BaseRESTIntegrationConnector (REST/JSON over HTTP).
|
|
6
|
+
*
|
|
7
|
+
* Discovery, template-var read traversal (second-layer objects resolve their parent via
|
|
8
|
+
* Configuration.parentObjectName), and the paginated GET loop are inherited. This class supplies only the
|
|
9
|
+
* Zendesk-specific protocol surface: Basic auth, per-tenant base URL, cursor/offset pagination, the
|
|
10
|
+
* Incremental Export fetch, connection testing, generic per-operation CRUD with named-var path templating,
|
|
11
|
+
* and the §7/§10 sync-efficiency hooks the frozen contract evidences.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ZendeskConnector extends BaseRESTIntegrationConnector {
|
|
14
|
+
/** Cached auth for the lifetime of a single sync run (Zendesk API tokens don't expire). */
|
|
15
|
+
private cachedAuth;
|
|
16
|
+
/** Verbatim `MJ: Integrations.Name`. Load-bearing: the T1 three-way name check compares this === metadata Name. */
|
|
17
|
+
get IntegrationName(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Sample-union enrichment (MJ connector standard): the Declared metadata is docs/spec-derived and can
|
|
20
|
+
* miss vendor customs (custom ticket/user/organization fields, custom-object attributes). After the
|
|
21
|
+
* base cache-driven introspection, we sample each object's live read shape via `DiscoverFieldsViaFetch`
|
|
22
|
+
* and UNION it into the declared field set with `mergeDeclaredWithSampledFields` — never-shrink,
|
|
23
|
+
* declared-wins, capacities widened. Best-effort + parallel; a sample failure leaves the declared set
|
|
24
|
+
* untouched. NOTE: we override `IntrospectSchema` (NOT `DiscoverFields` — that would recurse into
|
|
25
|
+
* `DiscoverFieldsViaFetch`'s own fallback). Connector-agnostic: no Zendesk-specific field logic.
|
|
26
|
+
*/
|
|
27
|
+
IntrospectSchema(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<SourceSchemaInfo>;
|
|
28
|
+
get SupportsCreate(): boolean;
|
|
29
|
+
get SupportsUpdate(): boolean;
|
|
30
|
+
get SupportsDelete(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Discovery is NON-authoritative: DiscoverObjects / IntrospectSchema are cache-driven (they re-read
|
|
33
|
+
* persisted ACTIVE Declared metadata, NOT a live full-gamut enumeration). Zendesk exposes no complete
|
|
34
|
+
* describe endpoint; custom ticket/user/organization fields and custom objects flow through runtime
|
|
35
|
+
* discovery + the framework's custom-column capture. Absence in a refresh proves nothing → never
|
|
36
|
+
* deactivate. Matches Configuration.DiscoveryIsAuthoritative=false in the frozen contract.
|
|
37
|
+
*/
|
|
38
|
+
get DiscoveryIsAuthoritative(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* From Configuration.RateLimitPolicy: the lowest current Support-plan tier (Team) is 200 requests/min
|
|
41
|
+
* per account (≈3.3/s), BatchMaxRequestCount=200 / BatchRequestWaitTime=60. The engine's AIMD bucket
|
|
42
|
+
* reads the vendor's response rate-limit headers and adapts upward for higher plans; this is the safe,
|
|
43
|
+
* provable floor. The Incremental Export endpoints are separately limited (10/min), governed by the
|
|
44
|
+
* same bucket backing off on 429.
|
|
45
|
+
*/
|
|
46
|
+
get RateLimitPolicy(): RateLimitPolicy | null;
|
|
47
|
+
/**
|
|
48
|
+
* The Incremental Export cursor endpoints stream records in ascending watermark order, so the cursor
|
|
49
|
+
* we persist (`after_cursor`) is a reliable, monotonically-advancing resume position. Non-incremental
|
|
50
|
+
* objects never return a watermark, so this only affects the incremental-export streams — which ARE
|
|
51
|
+
* monotonic. Lets the engine narrow the next incremental instead of re-scanning to wall-clock now.
|
|
52
|
+
*/
|
|
53
|
+
get MonotonicWatermark(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Zendesk returns `Retry-After` (seconds) on every 429, alongside X-Rate-Limit / ratelimit-* headers.
|
|
56
|
+
* Parse it to ms so the engine's AIMD bucket backs off by the vendor's actual instruction.
|
|
57
|
+
*/
|
|
58
|
+
ExtractRetryAfterMs(error: unknown): number | undefined;
|
|
59
|
+
/** Conservative in-flight cap — Zendesk tolerates parallelism but the per-minute budget is the ceiling. */
|
|
60
|
+
get MaxConcurrencyHint(): number | null;
|
|
61
|
+
/**
|
|
62
|
+
* No-watermark objects resume by the object's StableOrderingKey — read from the IO metadata (the
|
|
63
|
+
* extractor emits `StableOrderingKey`, the universal `id`). Returns null when the object declares no
|
|
64
|
+
* stable key or the cache is unavailable (unit-test context).
|
|
65
|
+
*/
|
|
66
|
+
StableOrderingKey(objectName: string): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Resolves the Basic auth header + per-tenant subdomain from the linked Credential entity (preferred)
|
|
69
|
+
* or the CompanyIntegration Configuration JSON (fallback). Cached for the run. The Basic header is built
|
|
70
|
+
* via the auth-helper — no inline base64/crypto.
|
|
71
|
+
*/
|
|
72
|
+
protected Authenticate(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<RESTAuthContext>;
|
|
73
|
+
/** Zendesk Basic auth: the header value is built once in Authenticate; other headers are static. */
|
|
74
|
+
protected BuildHeaders(auth: RESTAuthContext): Record<string, string>;
|
|
75
|
+
/** HTTP transport (fetch). Owns the wire boundary; test subclasses override this to capture requests. */
|
|
76
|
+
protected MakeHTTPRequest(_auth: RESTAuthContext, url: string, method: string, headers: Record<string, string>, body?: unknown): Promise<RESTResponse>;
|
|
77
|
+
/**
|
|
78
|
+
* Strips the Zendesk list envelope to the per-object resource array (`{ tickets: [...] }` →
|
|
79
|
+
* ResponseDataKey='tickets'). Handles the get-one singular envelope (`{ ticket: {...} }`) by falling
|
|
80
|
+
* back to the singular of the resource key, and a bare-array/record body for non-standard shapes.
|
|
81
|
+
*/
|
|
82
|
+
protected NormalizeResponse(rawBody: unknown, responseDataKey: string | null): Record<string, unknown>[];
|
|
83
|
+
/**
|
|
84
|
+
* Zendesk pagination:
|
|
85
|
+
* - Cursor (JSON:API CBP): continuation via `meta.after_cursor` gated by `meta.has_more`.
|
|
86
|
+
* - Offset (legacy): continuation while `next_page` (a full URL) is present; the base loop advances
|
|
87
|
+
* the integer page number, which BuildPaginatedURL renders as `page`/`per_page`.
|
|
88
|
+
* currentPage/offset/pageSize are unused for cursor pagination.
|
|
89
|
+
*/
|
|
90
|
+
protected ExtractPaginationInfo(rawBody: unknown, paginationType: PaginationType, _currentPage: number, _currentOffset: number, _pageSize: number): PaginationState;
|
|
91
|
+
/**
|
|
92
|
+
* Per-tenant base host. The `/api/v2` version segment lives in each object's APIPath (metadata-driven).
|
|
93
|
+
* An explicit Configuration base-URL override (sandbox / test / mock) wins over the subdomain host so
|
|
94
|
+
* the connector can be redirected by data alone — production sets no override and uses the subdomain.
|
|
95
|
+
*/
|
|
96
|
+
protected GetBaseURL(_companyIntegration: MJCompanyIntegrationEntity, auth: RESTAuthContext): string;
|
|
97
|
+
/**
|
|
98
|
+
* Reads an explicit full base-URL override from the connection Configuration. Recognized keys:
|
|
99
|
+
* `BaseURL` / `BaseUrl` / `baseURL` / `APIBaseURL` / `ApiBaseURL`. Only an http(s) absolute URL is
|
|
100
|
+
* honored; anything else is ignored so a stray value can't misroute a production tenant. Returns null
|
|
101
|
+
* when no valid override is present (the normal production case → subdomain host is used).
|
|
102
|
+
*/
|
|
103
|
+
private resolveBaseURLOverride;
|
|
104
|
+
/** Subdomain resolution that returns null instead of throwing (used when a base-URL override is present). */
|
|
105
|
+
private tryResolveSubdomain;
|
|
106
|
+
/**
|
|
107
|
+
* Zendesk pagination params (NOT the base default `cursor=`/`offset=`):
|
|
108
|
+
* - Cursor: `page[size]=<n>` (first page) + `page[after]=<cursor>` (subsequent).
|
|
109
|
+
* - Offset: `page=<n>&per_page=<size>` (legacy integer pagination).
|
|
110
|
+
*/
|
|
111
|
+
protected BuildPaginatedURL(basePath: string, obj: MJIntegrationObjectEntity, page: number, _offset: number, cursor?: string, effectivePageSize?: number): string;
|
|
112
|
+
/**
|
|
113
|
+
* OVERRIDDEN only to route the 6 objects declaring an Incremental Export door through the cursor
|
|
114
|
+
* endpoint. Everything else (regular cursor/offset list objects, template-var second-layer objects)
|
|
115
|
+
* delegates to the inherited base GET path, which uses the BuildPaginatedURL / ExtractPaginationInfo
|
|
116
|
+
* overrides above.
|
|
117
|
+
*/
|
|
118
|
+
FetchChanges(ctx: FetchContext): Promise<FetchBatchResult>;
|
|
119
|
+
/** True when the IO declares an Incremental Export door (Configuration.incrementalEndpoint) + supports it. */
|
|
120
|
+
private usesIncrementalExport;
|
|
121
|
+
/**
|
|
122
|
+
* Incremental fetch via `GET <incrementalEndpoint>?start_time=<unix>` (first request) then
|
|
123
|
+
* `?cursor=<after_cursor>` (thereafter), paging until the engine's BatchSize or `end_of_stream`.
|
|
124
|
+
*
|
|
125
|
+
* Resume model: WITHIN a run, pagination continues via ctx.CurrentCursor. ACROSS runs, the persisted
|
|
126
|
+
* watermark IS the last `after_cursor` — so ctx.WatermarkValue (when present) is a cursor to resume
|
|
127
|
+
* from; only the very first ever run (no cursor) seeds from `start_time=0` (a complete backfill). The
|
|
128
|
+
* new watermark (the final `after_cursor`) is returned ONLY when the stream is fully drained
|
|
129
|
+
* (`end_of_stream`) — a partial batch never advances it, so a mid-sync failure resumes cleanly.
|
|
130
|
+
*/
|
|
131
|
+
private fetchViaIncrementalExport;
|
|
132
|
+
/**
|
|
133
|
+
* OVERRIDDEN for two Zendesk create idiosyncrasies the generic path can't express:
|
|
134
|
+
* 1. Nested-resource create paths carry PARENT template vars (e.g.
|
|
135
|
+
* `/api/v2/custom_objects/{custom_object_key}/records`, `/api/v2/help_center/articles/{article_id}/comments`)
|
|
136
|
+
* — filled here from the record's own Attributes.
|
|
137
|
+
* 2. The create RESPONSE wraps the new record in the singular resource key (`{ ticket: { id } }`),
|
|
138
|
+
* so the id is not at the body root — handled by the ExtractIDFromResponse override.
|
|
139
|
+
* The wrapped request body and the loud-on-empty-id BuildCreatedResult are preserved. Top-level creates
|
|
140
|
+
* (no parent vars) behave exactly as the generic path would.
|
|
141
|
+
*/
|
|
142
|
+
CreateRecord(ctx: CreateRecordContext): Promise<CRUDResult>;
|
|
143
|
+
/**
|
|
144
|
+
* OVERRIDDEN so the generic Update/Delete/Get path can template Zendesk's NAMED single-record path vars
|
|
145
|
+
* (`{ticket_id}`, `{user_id}`, `{organization_id}`, …) — the base only substitutes `{id}`/`{ExternalID}`.
|
|
146
|
+
* A single-var path takes the whole ExternalID; a multi-var (nested) path takes the composite
|
|
147
|
+
* `parent|child` ExternalID split in path order.
|
|
148
|
+
*/
|
|
149
|
+
protected SubstituteIDInPath(path: string, externalID: string, idLocation: string | null): string;
|
|
150
|
+
/**
|
|
151
|
+
* OVERRIDDEN to read the new record's id out of Zendesk's SINGULAR create envelope (`{ ticket: { id } }`),
|
|
152
|
+
* which the base (which only inspects the body root) would miss — silently failing every create via
|
|
153
|
+
* BuildCreatedResult. Checks a root-level id first, then the single wrapped resource object.
|
|
154
|
+
*/
|
|
155
|
+
protected ExtractIDFromResponse(response: RESTResponse, idLocation: string | null): string | undefined;
|
|
156
|
+
/**
|
|
157
|
+
* Tests the connection by hitting the current-user endpoint. A 2xx confirms the Basic credential +
|
|
158
|
+
* subdomain are valid; 401/403 → auth failure; anything else → error.
|
|
159
|
+
*/
|
|
160
|
+
TestConnection(companyIntegration: MJCompanyIntegrationEntity, contextUser: UserInfo): Promise<ConnectionTestResult>;
|
|
161
|
+
/** Reads the Zendesk credential from the linked Credential entity, or the Configuration JSON fallback. */
|
|
162
|
+
private loadCredentials;
|
|
163
|
+
/** Loads a credential row and parses its Values JSON. */
|
|
164
|
+
private loadFromCredentialEntity;
|
|
165
|
+
/** Extracts Zendesk credential + subdomain fields from a credential/config JSON string. */
|
|
166
|
+
private parseCredentialJson;
|
|
167
|
+
/**
|
|
168
|
+
* Builds the `Authorization: Basic …` header via the auth-helper (no inline base64). Zendesk's API-token
|
|
169
|
+
* pattern is userid=`{email}/token`, password=`{api_token}`. When a caller supplies a pre-composed
|
|
170
|
+
* Username/Password verbatim (e.g. Username already ends in `/token`), use them as-is.
|
|
171
|
+
*/
|
|
172
|
+
private buildAuthHeader;
|
|
173
|
+
/** Resolves the per-tenant subdomain from Configuration (SubdomainConfigKey='Subdomain') or the credential. */
|
|
174
|
+
private resolveSubdomain;
|
|
175
|
+
/** Reads the `Subdomain` key from a Configuration JSON string (tolerant of absent/invalid). */
|
|
176
|
+
private readSubdomainFromConfig;
|
|
177
|
+
/** Substitutes CreateAPIPath parent template vars from the record's own attributes (nested creates). */
|
|
178
|
+
private substitutePathVarsFromAttributes;
|
|
179
|
+
/** Joins a base URL and a path (mirrors the base's private BuildFullURL). */
|
|
180
|
+
private joinURL;
|
|
181
|
+
/** Reads an `id`-like value as a string (number or string); undefined when absent. */
|
|
182
|
+
private readId;
|
|
183
|
+
/** PK field names in Sequence order (falls back to ['id'] — Zendesk's universal PK — when unmarked). */
|
|
184
|
+
private pkFieldNames;
|
|
185
|
+
/**
|
|
186
|
+
* Builds an ExternalRecord with the FULL source record in Fields (full-record pass-through — the
|
|
187
|
+
* framework's custom-column capture diffs keys(Fields) against the field maps). ExternalID is the
|
|
188
|
+
* composite PK (when every component is present + non-empty) or the raw Zendesk `id`.
|
|
189
|
+
*/
|
|
190
|
+
private buildExternalRecord;
|
|
191
|
+
/** Gets an IO from the cache without throwing (used by StableOrderingKey, which may be called early). */
|
|
192
|
+
private tryGetCachedObject;
|
|
193
|
+
/** Reads a trimmed string value from an IntegrationObject's Configuration JSON (tolerant of absent/invalid). */
|
|
194
|
+
private readConfigString;
|
|
195
|
+
/** Returns the first present, non-empty string value among the given keys. */
|
|
196
|
+
private firstString;
|
|
197
|
+
/** Best-effort extraction of response headers from an error object (for ExtractRetryAfterMs). */
|
|
198
|
+
private extractHeadersFromError;
|
|
199
|
+
}
|
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
8
|
+
import { Metadata } from '@memberjunction/core';
|
|
9
|
+
import { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
|
|
10
|
+
import { BaseIntegrationConnector, BaseRESTIntegrationConnector, buildBasicAuthHeaderValue, } from '@memberjunction/integration-engine';
|
|
11
|
+
import { mergeDeclaredWithSampledFields } from '@memberjunction/connector-schema-merge';
|
|
12
|
+
// ─── Constants ────────────────────────────────────────────────────────
|
|
13
|
+
/** Zendesk cursor-incremental max page size (`per_page` caps at 1000 on the Incremental Export endpoints). */
|
|
14
|
+
const ZENDESK_INCREMENTAL_MAX_PAGE = 1000;
|
|
15
|
+
/** Zendesk list-endpoint cursor page size cap (`page[size]` caps at 100). */
|
|
16
|
+
const ZENDESK_LIST_MAX_PAGE = 100;
|
|
17
|
+
// ─── ZendeskConnector ──────────────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Zendesk connector — extends BaseRESTIntegrationConnector (REST/JSON over HTTP).
|
|
20
|
+
*
|
|
21
|
+
* Discovery, template-var read traversal (second-layer objects resolve their parent via
|
|
22
|
+
* Configuration.parentObjectName), and the paginated GET loop are inherited. This class supplies only the
|
|
23
|
+
* Zendesk-specific protocol surface: Basic auth, per-tenant base URL, cursor/offset pagination, the
|
|
24
|
+
* Incremental Export fetch, connection testing, generic per-operation CRUD with named-var path templating,
|
|
25
|
+
* and the §7/§10 sync-efficiency hooks the frozen contract evidences.
|
|
26
|
+
*/
|
|
27
|
+
let ZendeskConnector = class ZendeskConnector extends BaseRESTIntegrationConnector {
|
|
28
|
+
constructor() {
|
|
29
|
+
super(...arguments);
|
|
30
|
+
/** Cached auth for the lifetime of a single sync run (Zendesk API tokens don't expire). */
|
|
31
|
+
this.cachedAuth = null;
|
|
32
|
+
}
|
|
33
|
+
// ── Identity (T1 three-way invariant) ────────────────────────────
|
|
34
|
+
/** Verbatim `MJ: Integrations.Name`. Load-bearing: the T1 three-way name check compares this === metadata Name. */
|
|
35
|
+
get IntegrationName() {
|
|
36
|
+
return 'zendesk';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Sample-union enrichment (MJ connector standard): the Declared metadata is docs/spec-derived and can
|
|
40
|
+
* miss vendor customs (custom ticket/user/organization fields, custom-object attributes). After the
|
|
41
|
+
* base cache-driven introspection, we sample each object's live read shape via `DiscoverFieldsViaFetch`
|
|
42
|
+
* and UNION it into the declared field set with `mergeDeclaredWithSampledFields` — never-shrink,
|
|
43
|
+
* declared-wins, capacities widened. Best-effort + parallel; a sample failure leaves the declared set
|
|
44
|
+
* untouched. NOTE: we override `IntrospectSchema` (NOT `DiscoverFields` — that would recurse into
|
|
45
|
+
* `DiscoverFieldsViaFetch`'s own fallback). Connector-agnostic: no Zendesk-specific field logic.
|
|
46
|
+
*/
|
|
47
|
+
async IntrospectSchema(companyIntegration, contextUser) {
|
|
48
|
+
const info = await super.IntrospectSchema(companyIntegration, contextUser);
|
|
49
|
+
await Promise.all(info.Objects.map(async (obj) => {
|
|
50
|
+
try {
|
|
51
|
+
const sampled = await this.DiscoverFieldsViaFetch(companyIntegration, obj.ExternalName, contextUser);
|
|
52
|
+
obj.Fields = mergeDeclaredWithSampledFields(obj.Fields, sampled);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
/* best-effort — a sample failure leaves the declared fields as-is */
|
|
56
|
+
}
|
|
57
|
+
}));
|
|
58
|
+
return info;
|
|
59
|
+
}
|
|
60
|
+
// ── Capability getters (kept in lockstep with the per-op metadata columns) ──
|
|
61
|
+
get SupportsCreate() { return true; }
|
|
62
|
+
get SupportsUpdate() { return true; }
|
|
63
|
+
get SupportsDelete() { return true; }
|
|
64
|
+
/**
|
|
65
|
+
* Discovery is NON-authoritative: DiscoverObjects / IntrospectSchema are cache-driven (they re-read
|
|
66
|
+
* persisted ACTIVE Declared metadata, NOT a live full-gamut enumeration). Zendesk exposes no complete
|
|
67
|
+
* describe endpoint; custom ticket/user/organization fields and custom objects flow through runtime
|
|
68
|
+
* discovery + the framework's custom-column capture. Absence in a refresh proves nothing → never
|
|
69
|
+
* deactivate. Matches Configuration.DiscoveryIsAuthoritative=false in the frozen contract.
|
|
70
|
+
*/
|
|
71
|
+
get DiscoveryIsAuthoritative() {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// ── Sync-efficiency hooks (§7/§10 — populated from frozen-contract Configuration facts) ──
|
|
75
|
+
/**
|
|
76
|
+
* From Configuration.RateLimitPolicy: the lowest current Support-plan tier (Team) is 200 requests/min
|
|
77
|
+
* per account (≈3.3/s), BatchMaxRequestCount=200 / BatchRequestWaitTime=60. The engine's AIMD bucket
|
|
78
|
+
* reads the vendor's response rate-limit headers and adapts upward for higher plans; this is the safe,
|
|
79
|
+
* provable floor. The Incremental Export endpoints are separately limited (10/min), governed by the
|
|
80
|
+
* same bucket backing off on 429.
|
|
81
|
+
*/
|
|
82
|
+
get RateLimitPolicy() {
|
|
83
|
+
return { TokensPerSec: 3, Burst: 20 };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The Incremental Export cursor endpoints stream records in ascending watermark order, so the cursor
|
|
87
|
+
* we persist (`after_cursor`) is a reliable, monotonically-advancing resume position. Non-incremental
|
|
88
|
+
* objects never return a watermark, so this only affects the incremental-export streams — which ARE
|
|
89
|
+
* monotonic. Lets the engine narrow the next incremental instead of re-scanning to wall-clock now.
|
|
90
|
+
*/
|
|
91
|
+
get MonotonicWatermark() { return true; }
|
|
92
|
+
/**
|
|
93
|
+
* Zendesk returns `Retry-After` (seconds) on every 429, alongside X-Rate-Limit / ratelimit-* headers.
|
|
94
|
+
* Parse it to ms so the engine's AIMD bucket backs off by the vendor's actual instruction.
|
|
95
|
+
*/
|
|
96
|
+
ExtractRetryAfterMs(error) {
|
|
97
|
+
const headers = this.extractHeadersFromError(error);
|
|
98
|
+
if (!headers)
|
|
99
|
+
return undefined;
|
|
100
|
+
const retryAfter = headers['retry-after'] ?? headers['Retry-After'];
|
|
101
|
+
if (retryAfter != null) {
|
|
102
|
+
const secs = Number(retryAfter);
|
|
103
|
+
if (!isNaN(secs) && secs >= 0)
|
|
104
|
+
return Math.ceil(secs * 1000);
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
/** Conservative in-flight cap — Zendesk tolerates parallelism but the per-minute budget is the ceiling. */
|
|
109
|
+
get MaxConcurrencyHint() { return 4; }
|
|
110
|
+
/**
|
|
111
|
+
* No-watermark objects resume by the object's StableOrderingKey — read from the IO metadata (the
|
|
112
|
+
* extractor emits `StableOrderingKey`, the universal `id`). Returns null when the object declares no
|
|
113
|
+
* stable key or the cache is unavailable (unit-test context).
|
|
114
|
+
*/
|
|
115
|
+
StableOrderingKey(objectName) {
|
|
116
|
+
const obj = this.tryGetCachedObject(objectName);
|
|
117
|
+
if (!obj)
|
|
118
|
+
return null;
|
|
119
|
+
const declared = obj.StableOrderingKey;
|
|
120
|
+
if (declared && declared.trim().length > 0)
|
|
121
|
+
return declared.trim();
|
|
122
|
+
const pk = this.GetCachedFields(obj.ID).find(f => f.IsPrimaryKey);
|
|
123
|
+
return pk?.Name ?? null;
|
|
124
|
+
}
|
|
125
|
+
// ── Abstract REST hooks ──────────────────────────────────────────
|
|
126
|
+
/**
|
|
127
|
+
* Resolves the Basic auth header + per-tenant subdomain from the linked Credential entity (preferred)
|
|
128
|
+
* or the CompanyIntegration Configuration JSON (fallback). Cached for the run. The Basic header is built
|
|
129
|
+
* via the auth-helper — no inline base64/crypto.
|
|
130
|
+
*/
|
|
131
|
+
async Authenticate(companyIntegration, contextUser) {
|
|
132
|
+
if (this.cachedAuth)
|
|
133
|
+
return this.cachedAuth;
|
|
134
|
+
const creds = await this.loadCredentials(companyIntegration, contextUser);
|
|
135
|
+
const baseURLOverride = this.resolveBaseURLOverride(companyIntegration);
|
|
136
|
+
// When an explicit base-URL override is present, the subdomain is not needed to build URLs.
|
|
137
|
+
// Fall back to a benign placeholder so a sandbox/mock connection that supplies only BaseURL
|
|
138
|
+
// (and no Subdomain) still authenticates. Production supplies Subdomain and no override.
|
|
139
|
+
const subdomain = baseURLOverride
|
|
140
|
+
? (this.tryResolveSubdomain(companyIntegration, creds) ?? 'override')
|
|
141
|
+
: this.resolveSubdomain(companyIntegration, creds);
|
|
142
|
+
const authHeader = this.buildAuthHeader(creds);
|
|
143
|
+
this.cachedAuth = { AuthHeader: authHeader, Subdomain: subdomain, BaseURLOverride: baseURLOverride ?? undefined };
|
|
144
|
+
return this.cachedAuth;
|
|
145
|
+
}
|
|
146
|
+
/** Zendesk Basic auth: the header value is built once in Authenticate; other headers are static. */
|
|
147
|
+
BuildHeaders(auth) {
|
|
148
|
+
return {
|
|
149
|
+
'Authorization': auth.AuthHeader,
|
|
150
|
+
'Content-Type': 'application/json',
|
|
151
|
+
'Accept': 'application/json',
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/** HTTP transport (fetch). Owns the wire boundary; test subclasses override this to capture requests. */
|
|
155
|
+
async MakeHTTPRequest(_auth, url, method, headers, body) {
|
|
156
|
+
const response = await fetch(url, {
|
|
157
|
+
method,
|
|
158
|
+
headers,
|
|
159
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
160
|
+
});
|
|
161
|
+
const respHeaders = {};
|
|
162
|
+
response.headers.forEach((v, k) => { respHeaders[k.toLowerCase()] = v; });
|
|
163
|
+
const text = await response.text();
|
|
164
|
+
let parsed = null;
|
|
165
|
+
if (text.length > 0) {
|
|
166
|
+
try {
|
|
167
|
+
parsed = JSON.parse(text);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
parsed = text;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { Status: response.status, Body: parsed, Headers: respHeaders };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Strips the Zendesk list envelope to the per-object resource array (`{ tickets: [...] }` →
|
|
177
|
+
* ResponseDataKey='tickets'). Handles the get-one singular envelope (`{ ticket: {...} }`) by falling
|
|
178
|
+
* back to the singular of the resource key, and a bare-array/record body for non-standard shapes.
|
|
179
|
+
*/
|
|
180
|
+
NormalizeResponse(rawBody, responseDataKey) {
|
|
181
|
+
if (rawBody == null)
|
|
182
|
+
return [];
|
|
183
|
+
if (Array.isArray(rawBody))
|
|
184
|
+
return rawBody;
|
|
185
|
+
if (typeof rawBody !== 'object')
|
|
186
|
+
return [];
|
|
187
|
+
const body = rawBody;
|
|
188
|
+
if (responseDataKey) {
|
|
189
|
+
const arr = body[responseDataKey];
|
|
190
|
+
if (Array.isArray(arr))
|
|
191
|
+
return arr;
|
|
192
|
+
// get-one singular envelope: strip a trailing 's' from the (plural) list key.
|
|
193
|
+
const singular = responseDataKey.replace(/s$/, '');
|
|
194
|
+
const one = body[singular];
|
|
195
|
+
if (one && typeof one === 'object' && !Array.isArray(one))
|
|
196
|
+
return [one];
|
|
197
|
+
}
|
|
198
|
+
// Non-list / already-unwrapped record.
|
|
199
|
+
return [body];
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Zendesk pagination:
|
|
203
|
+
* - Cursor (JSON:API CBP): continuation via `meta.after_cursor` gated by `meta.has_more`.
|
|
204
|
+
* - Offset (legacy): continuation while `next_page` (a full URL) is present; the base loop advances
|
|
205
|
+
* the integer page number, which BuildPaginatedURL renders as `page`/`per_page`.
|
|
206
|
+
* currentPage/offset/pageSize are unused for cursor pagination.
|
|
207
|
+
*/
|
|
208
|
+
ExtractPaginationInfo(rawBody, paginationType, _currentPage, _currentOffset, _pageSize) {
|
|
209
|
+
if (!rawBody || typeof rawBody !== 'object')
|
|
210
|
+
return { HasMore: false };
|
|
211
|
+
const env = rawBody;
|
|
212
|
+
if (paginationType === 'Cursor') {
|
|
213
|
+
const after = env.meta?.after_cursor;
|
|
214
|
+
if (env.meta?.has_more === true && typeof after === 'string' && after.length > 0) {
|
|
215
|
+
return { HasMore: true, NextCursor: after, TotalRecords: env.count };
|
|
216
|
+
}
|
|
217
|
+
return { HasMore: false };
|
|
218
|
+
}
|
|
219
|
+
if (paginationType === 'Offset') {
|
|
220
|
+
const hasMore = typeof env.next_page === 'string' && env.next_page.length > 0;
|
|
221
|
+
return { HasMore: hasMore, TotalRecords: env.count };
|
|
222
|
+
}
|
|
223
|
+
return { HasMore: false };
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Per-tenant base host. The `/api/v2` version segment lives in each object's APIPath (metadata-driven).
|
|
227
|
+
* An explicit Configuration base-URL override (sandbox / test / mock) wins over the subdomain host so
|
|
228
|
+
* the connector can be redirected by data alone — production sets no override and uses the subdomain.
|
|
229
|
+
*/
|
|
230
|
+
GetBaseURL(_companyIntegration, auth) {
|
|
231
|
+
const ctx = auth;
|
|
232
|
+
if (ctx.BaseURLOverride)
|
|
233
|
+
return ctx.BaseURLOverride.replace(/\/+$/, '');
|
|
234
|
+
return `https://${ctx.Subdomain}.zendesk.com`;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Reads an explicit full base-URL override from the connection Configuration. Recognized keys:
|
|
238
|
+
* `BaseURL` / `BaseUrl` / `baseURL` / `APIBaseURL` / `ApiBaseURL`. Only an http(s) absolute URL is
|
|
239
|
+
* honored; anything else is ignored so a stray value can't misroute a production tenant. Returns null
|
|
240
|
+
* when no valid override is present (the normal production case → subdomain host is used).
|
|
241
|
+
*/
|
|
242
|
+
resolveBaseURLOverride(companyIntegration) {
|
|
243
|
+
if (!companyIntegration.Configuration)
|
|
244
|
+
return null;
|
|
245
|
+
let parsed;
|
|
246
|
+
try {
|
|
247
|
+
parsed = JSON.parse(companyIntegration.Configuration);
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const raw = this.firstString(parsed, ['BaseURL', 'BaseUrl', 'baseURL', 'baseUrl', 'APIBaseURL', 'ApiBaseURL', 'apiBaseUrl']);
|
|
253
|
+
if (raw && /^https?:\/\//i.test(raw.trim()))
|
|
254
|
+
return raw.trim();
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
/** Subdomain resolution that returns null instead of throwing (used when a base-URL override is present). */
|
|
258
|
+
tryResolveSubdomain(companyIntegration, creds) {
|
|
259
|
+
const fromConfig = companyIntegration.Configuration ? this.readSubdomainFromConfig(companyIntegration.Configuration) : null;
|
|
260
|
+
const subdomain = (fromConfig ?? creds.Subdomain ?? '').trim();
|
|
261
|
+
return subdomain || null;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Zendesk pagination params (NOT the base default `cursor=`/`offset=`):
|
|
265
|
+
* - Cursor: `page[size]=<n>` (first page) + `page[after]=<cursor>` (subsequent).
|
|
266
|
+
* - Offset: `page=<n>&per_page=<size>` (legacy integer pagination).
|
|
267
|
+
*/
|
|
268
|
+
BuildPaginatedURL(basePath, obj, page, _offset, cursor, effectivePageSize) {
|
|
269
|
+
const separator = basePath.includes('?') ? '&' : '?';
|
|
270
|
+
if (obj.PaginationType === 'Offset') {
|
|
271
|
+
const pageSize = effectivePageSize ?? obj.DefaultPageSize ?? ZENDESK_LIST_MAX_PAGE;
|
|
272
|
+
return `${basePath}${separator}page=${page}&per_page=${pageSize}`;
|
|
273
|
+
}
|
|
274
|
+
// Cursor (default for Zendesk list endpoints).
|
|
275
|
+
const pageSize = Math.min(effectivePageSize ?? obj.DefaultPageSize ?? ZENDESK_LIST_MAX_PAGE, ZENDESK_LIST_MAX_PAGE);
|
|
276
|
+
const parts = [`page[size]=${pageSize}`];
|
|
277
|
+
if (cursor)
|
|
278
|
+
parts.push(`page[after]=${encodeURIComponent(cursor)}`);
|
|
279
|
+
return `${basePath}${separator}${parts.join('&')}`;
|
|
280
|
+
}
|
|
281
|
+
// ── FetchChanges override (idiosyncratic: Incremental Export cursor endpoints) ──
|
|
282
|
+
/**
|
|
283
|
+
* OVERRIDDEN only to route the 6 objects declaring an Incremental Export door through the cursor
|
|
284
|
+
* endpoint. Everything else (regular cursor/offset list objects, template-var second-layer objects)
|
|
285
|
+
* delegates to the inherited base GET path, which uses the BuildPaginatedURL / ExtractPaginationInfo
|
|
286
|
+
* overrides above.
|
|
287
|
+
*/
|
|
288
|
+
async FetchChanges(ctx) {
|
|
289
|
+
const obj = this.GetCachedObject(ctx.CompanyIntegration.IntegrationID, ctx.ObjectName);
|
|
290
|
+
if (this.usesIncrementalExport(obj)) {
|
|
291
|
+
return this.fetchViaIncrementalExport(obj, ctx);
|
|
292
|
+
}
|
|
293
|
+
return super.FetchChanges(ctx);
|
|
294
|
+
}
|
|
295
|
+
/** True when the IO declares an Incremental Export door (Configuration.incrementalEndpoint) + supports it. */
|
|
296
|
+
usesIncrementalExport(obj) {
|
|
297
|
+
return obj.SupportsIncrementalSync && this.readConfigString(obj, 'incrementalEndpoint') != null;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Incremental fetch via `GET <incrementalEndpoint>?start_time=<unix>` (first request) then
|
|
301
|
+
* `?cursor=<after_cursor>` (thereafter), paging until the engine's BatchSize or `end_of_stream`.
|
|
302
|
+
*
|
|
303
|
+
* Resume model: WITHIN a run, pagination continues via ctx.CurrentCursor. ACROSS runs, the persisted
|
|
304
|
+
* watermark IS the last `after_cursor` — so ctx.WatermarkValue (when present) is a cursor to resume
|
|
305
|
+
* from; only the very first ever run (no cursor) seeds from `start_time=0` (a complete backfill). The
|
|
306
|
+
* new watermark (the final `after_cursor`) is returned ONLY when the stream is fully drained
|
|
307
|
+
* (`end_of_stream`) — a partial batch never advances it, so a mid-sync failure resumes cleanly.
|
|
308
|
+
*/
|
|
309
|
+
async fetchViaIncrementalExport(obj, ctx) {
|
|
310
|
+
const fields = this.GetCachedFields(obj.ID);
|
|
311
|
+
const pkFieldNames = this.pkFieldNames(fields);
|
|
312
|
+
const auth = await this.Authenticate(ctx.CompanyIntegration, ctx.ContextUser);
|
|
313
|
+
const baseURL = this.GetBaseURL(ctx.CompanyIntegration, auth);
|
|
314
|
+
const headers = this.BuildHeaders(auth);
|
|
315
|
+
const endpoint = this.readConfigString(obj, 'incrementalEndpoint');
|
|
316
|
+
const pageSize = Math.min(obj.DefaultPageSize ?? ZENDESK_INCREMENTAL_MAX_PAGE, ZENDESK_INCREMENTAL_MAX_PAGE);
|
|
317
|
+
const batchLimit = ctx.BatchSize && ctx.BatchSize > 0 ? ctx.BatchSize : Number.MAX_SAFE_INTEGER;
|
|
318
|
+
// Within-run cursor wins; else the cross-run saved cursor (the watermark); else a full backfill.
|
|
319
|
+
let cursor = ctx.CurrentCursor ?? ctx.WatermarkValue ?? undefined;
|
|
320
|
+
const records = [];
|
|
321
|
+
let endOfStream = false;
|
|
322
|
+
let lastCursor = cursor;
|
|
323
|
+
while (records.length < batchLimit) {
|
|
324
|
+
const perPage = Math.max(1, Math.min(pageSize, batchLimit - records.length));
|
|
325
|
+
const url = cursor
|
|
326
|
+
? `${baseURL}${endpoint}?cursor=${encodeURIComponent(cursor)}&per_page=${perPage}`
|
|
327
|
+
: `${baseURL}${endpoint}?start_time=0&per_page=${perPage}`;
|
|
328
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
|
|
329
|
+
if (response.Status < 200 || response.Status >= 300) {
|
|
330
|
+
throw new Error(`[zendesk] Incremental export failed for "${obj.Name}": HTTP ${response.Status}`);
|
|
331
|
+
}
|
|
332
|
+
const raw = this.NormalizeResponse(response.Body, obj.ResponseDataKey);
|
|
333
|
+
for (const r of raw) {
|
|
334
|
+
records.push(this.buildExternalRecord(this.applyTransformPreservingKeys(r, obj, fields), obj.Name, pkFieldNames));
|
|
335
|
+
}
|
|
336
|
+
const env = (response.Body ?? {});
|
|
337
|
+
endOfStream = env.end_of_stream === true;
|
|
338
|
+
const nextCursor = typeof env.after_cursor === 'string' && env.after_cursor.length > 0 ? env.after_cursor : undefined;
|
|
339
|
+
if (nextCursor)
|
|
340
|
+
lastCursor = nextCursor;
|
|
341
|
+
if (endOfStream || !nextCursor || raw.length === 0) {
|
|
342
|
+
endOfStream = endOfStream || !nextCursor;
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
cursor = nextCursor;
|
|
346
|
+
}
|
|
347
|
+
if (endOfStream) {
|
|
348
|
+
// Fully drained → safe to advance the watermark to the resume cursor for the next run.
|
|
349
|
+
return { Records: records, HasMore: false, NewWatermarkValue: lastCursor ?? ctx.WatermarkValue ?? undefined };
|
|
350
|
+
}
|
|
351
|
+
// Batch limit hit mid-stream → continue via cursor next call; do NOT advance the watermark.
|
|
352
|
+
return { Records: records, HasMore: true, NextCursor: lastCursor };
|
|
353
|
+
}
|
|
354
|
+
// ── CRUD ──────────────────────────────────────────────────────────
|
|
355
|
+
//
|
|
356
|
+
// Single-`{id}` top-level objects (tickets, users, organizations, groups, macros, views, triggers,
|
|
357
|
+
// ticket_fields, automations, …) use the INHERITED generic UpdateRecord / DeleteRecord / GetRecord,
|
|
358
|
+
// which read the per-operation IO columns and route through the SubstituteIDInPath override below (the
|
|
359
|
+
// base only templates `{id}`; Zendesk uses named vars like `{ticket_id}`). CreateRecord is overridden
|
|
360
|
+
// for the two Zendesk create idiosyncrasies — see below.
|
|
361
|
+
/**
|
|
362
|
+
* OVERRIDDEN for two Zendesk create idiosyncrasies the generic path can't express:
|
|
363
|
+
* 1. Nested-resource create paths carry PARENT template vars (e.g.
|
|
364
|
+
* `/api/v2/custom_objects/{custom_object_key}/records`, `/api/v2/help_center/articles/{article_id}/comments`)
|
|
365
|
+
* — filled here from the record's own Attributes.
|
|
366
|
+
* 2. The create RESPONSE wraps the new record in the singular resource key (`{ ticket: { id } }`),
|
|
367
|
+
* so the id is not at the body root — handled by the ExtractIDFromResponse override.
|
|
368
|
+
* The wrapped request body and the loud-on-empty-id BuildCreatedResult are preserved. Top-level creates
|
|
369
|
+
* (no parent vars) behave exactly as the generic path would.
|
|
370
|
+
*/
|
|
371
|
+
async CreateRecord(ctx) {
|
|
372
|
+
const ci = ctx.CompanyIntegration;
|
|
373
|
+
const contextUser = ctx.ContextUser;
|
|
374
|
+
const obj = this.GetCachedObject(ci.IntegrationID, ctx.ObjectName);
|
|
375
|
+
if (!obj.CreateAPIPath || !obj.CreateMethod) {
|
|
376
|
+
// Delegate to the base so the "not configured" error is raised consistently.
|
|
377
|
+
return super.CreateRecord(ctx);
|
|
378
|
+
}
|
|
379
|
+
const auth = await this.Authenticate(ci, contextUser);
|
|
380
|
+
const baseURL = this.GetBaseURL(ci, auth);
|
|
381
|
+
const headers = this.BuildHeaders(auth);
|
|
382
|
+
const path = this.substitutePathVarsFromAttributes(obj.CreateAPIPath, ctx.Attributes);
|
|
383
|
+
const url = this.joinURL(baseURL, path);
|
|
384
|
+
const body = this.BuildOperationBody(ctx.Attributes, obj.CreateBodyShape, obj.CreateBodyKey);
|
|
385
|
+
const response = await this.MakeHTTPRequest(auth, url, obj.CreateMethod, headers, body);
|
|
386
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
387
|
+
const externalID = this.ExtractIDFromResponse(response, obj.CreateIDLocation);
|
|
388
|
+
return this.BuildCreatedResult(externalID, response.Status, ctx.ObjectName);
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
Success: false,
|
|
392
|
+
StatusCode: response.Status,
|
|
393
|
+
ErrorMessage: this.ExtractErrorMessage(response) ?? `HTTP ${response.Status} on create`,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* OVERRIDDEN so the generic Update/Delete/Get path can template Zendesk's NAMED single-record path vars
|
|
398
|
+
* (`{ticket_id}`, `{user_id}`, `{organization_id}`, …) — the base only substitutes `{id}`/`{ExternalID}`.
|
|
399
|
+
* A single-var path takes the whole ExternalID; a multi-var (nested) path takes the composite
|
|
400
|
+
* `parent|child` ExternalID split in path order.
|
|
401
|
+
*/
|
|
402
|
+
SubstituteIDInPath(path, externalID, idLocation) {
|
|
403
|
+
if (idLocation && idLocation !== 'path')
|
|
404
|
+
return path;
|
|
405
|
+
const vars = path.match(/\{\w+\}/g);
|
|
406
|
+
if (!vars || vars.length === 0)
|
|
407
|
+
return path;
|
|
408
|
+
if (vars.length === 1)
|
|
409
|
+
return path.replace(vars[0], encodeURIComponent(externalID));
|
|
410
|
+
const parts = externalID.split('|');
|
|
411
|
+
let out = path;
|
|
412
|
+
vars.forEach((v, i) => {
|
|
413
|
+
const val = parts[i] ?? parts[parts.length - 1] ?? externalID;
|
|
414
|
+
out = out.replace(v, encodeURIComponent(val));
|
|
415
|
+
});
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* OVERRIDDEN to read the new record's id out of Zendesk's SINGULAR create envelope (`{ ticket: { id } }`),
|
|
420
|
+
* which the base (which only inspects the body root) would miss — silently failing every create via
|
|
421
|
+
* BuildCreatedResult. Checks a root-level id first, then the single wrapped resource object.
|
|
422
|
+
*/
|
|
423
|
+
ExtractIDFromResponse(response, idLocation) {
|
|
424
|
+
if (idLocation === 'header')
|
|
425
|
+
return super.ExtractIDFromResponse(response, idLocation);
|
|
426
|
+
const body = response.Body;
|
|
427
|
+
if (!body || typeof body !== 'object')
|
|
428
|
+
return undefined;
|
|
429
|
+
const b = body;
|
|
430
|
+
const rootId = this.readId(b);
|
|
431
|
+
if (rootId)
|
|
432
|
+
return rootId;
|
|
433
|
+
for (const v of Object.values(b)) {
|
|
434
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
435
|
+
const id = this.readId(v);
|
|
436
|
+
if (id)
|
|
437
|
+
return id;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
// ── Connection test ──────────────────────────────────────────────
|
|
443
|
+
/**
|
|
444
|
+
* Tests the connection by hitting the current-user endpoint. A 2xx confirms the Basic credential +
|
|
445
|
+
* subdomain are valid; 401/403 → auth failure; anything else → error.
|
|
446
|
+
*/
|
|
447
|
+
async TestConnection(companyIntegration, contextUser) {
|
|
448
|
+
try {
|
|
449
|
+
const auth = await this.Authenticate(companyIntegration, contextUser);
|
|
450
|
+
const baseURL = this.GetBaseURL(companyIntegration, auth);
|
|
451
|
+
const headers = this.BuildHeaders(auth);
|
|
452
|
+
const url = `${baseURL}/api/v2/users/me`;
|
|
453
|
+
const response = await this.MakeHTTPRequest(auth, url, 'GET', headers);
|
|
454
|
+
if (response.Status >= 200 && response.Status < 300) {
|
|
455
|
+
return { Success: true, Message: 'Zendesk connection successful.' };
|
|
456
|
+
}
|
|
457
|
+
if (response.Status === 401 || response.Status === 403) {
|
|
458
|
+
return { Success: false, Message: `Zendesk authentication failed (HTTP ${response.Status}). Check the email + API token and subdomain.` };
|
|
459
|
+
}
|
|
460
|
+
return { Success: false, Message: `Zendesk connection test returned HTTP ${response.Status}.` };
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
464
|
+
return { Success: false, Message: `Zendesk connection test error: ${msg}` };
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
// ── Credential + tenant loading ──────────────────────────────────
|
|
468
|
+
/** Reads the Zendesk credential from the linked Credential entity, or the Configuration JSON fallback. */
|
|
469
|
+
async loadCredentials(companyIntegration, contextUser) {
|
|
470
|
+
const credentialID = companyIntegration.CredentialID;
|
|
471
|
+
let creds = null;
|
|
472
|
+
if (credentialID) {
|
|
473
|
+
creds = await this.loadFromCredentialEntity(credentialID, contextUser);
|
|
474
|
+
}
|
|
475
|
+
// Merge Configuration JSON (carries the Subdomain, and can carry credential fields as a fallback).
|
|
476
|
+
const configCreds = companyIntegration.Configuration ? this.parseCredentialJson(companyIntegration.Configuration) : null;
|
|
477
|
+
const merged = { ...(configCreds ?? {}), ...(creds ?? {}) };
|
|
478
|
+
// Subdomain most often lives in Configuration, not the secret store — prefer it if the credential lacked one.
|
|
479
|
+
if (!merged.Subdomain && configCreds?.Subdomain)
|
|
480
|
+
merged.Subdomain = configCreds.Subdomain;
|
|
481
|
+
if (!creds && !configCreds) {
|
|
482
|
+
throw new Error('No Zendesk credential found. Attach a credential carrying the agent email + API token ' +
|
|
483
|
+
'(email/apiToken, or a pre-composed Username `{email}/token` + Password), or set Configuration JSON.');
|
|
484
|
+
}
|
|
485
|
+
return merged;
|
|
486
|
+
}
|
|
487
|
+
/** Loads a credential row and parses its Values JSON. */
|
|
488
|
+
async loadFromCredentialEntity(credentialID, contextUser, provider) {
|
|
489
|
+
const md = provider ?? new Metadata();
|
|
490
|
+
const credential = await md.GetEntityObject('MJ: Credentials', contextUser);
|
|
491
|
+
const loaded = await credential.Load(credentialID);
|
|
492
|
+
if (!loaded || !credential.Values)
|
|
493
|
+
return null;
|
|
494
|
+
return this.parseCredentialJson(credential.Values);
|
|
495
|
+
}
|
|
496
|
+
/** Extracts Zendesk credential + subdomain fields from a credential/config JSON string. */
|
|
497
|
+
parseCredentialJson(json) {
|
|
498
|
+
try {
|
|
499
|
+
const parsed = JSON.parse(json);
|
|
500
|
+
const out = {
|
|
501
|
+
Email: this.firstString(parsed, ['email', 'Email', 'emailAddress', 'EmailAddress']),
|
|
502
|
+
ApiToken: this.firstString(parsed, ['apiToken', 'ApiToken', 'apiKey', 'ApiKey', 'token', 'Token']),
|
|
503
|
+
Username: this.firstString(parsed, ['username', 'Username']),
|
|
504
|
+
Password: this.firstString(parsed, ['password', 'Password']),
|
|
505
|
+
Subdomain: this.firstString(parsed, ['subdomain', 'Subdomain']),
|
|
506
|
+
};
|
|
507
|
+
return out;
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Builds the `Authorization: Basic …` header via the auth-helper (no inline base64). Zendesk's API-token
|
|
515
|
+
* pattern is userid=`{email}/token`, password=`{api_token}`. When a caller supplies a pre-composed
|
|
516
|
+
* Username/Password verbatim (e.g. Username already ends in `/token`), use them as-is.
|
|
517
|
+
*/
|
|
518
|
+
buildAuthHeader(creds) {
|
|
519
|
+
if (creds.Email && creds.ApiToken) {
|
|
520
|
+
return buildBasicAuthHeaderValue({ Username: `${creds.Email}/token`, Password: creds.ApiToken });
|
|
521
|
+
}
|
|
522
|
+
if (creds.Username && creds.Password != null) {
|
|
523
|
+
return buildBasicAuthHeaderValue({ Username: creds.Username, Password: creds.Password });
|
|
524
|
+
}
|
|
525
|
+
throw new Error('Zendesk credential incomplete: provide email + apiToken (preferred), or a pre-composed ' +
|
|
526
|
+
'Username (`{email}/token`) + Password (the API token).');
|
|
527
|
+
}
|
|
528
|
+
/** Resolves the per-tenant subdomain from Configuration (SubdomainConfigKey='Subdomain') or the credential. */
|
|
529
|
+
resolveSubdomain(companyIntegration, creds) {
|
|
530
|
+
const fromConfig = companyIntegration.Configuration ? this.readSubdomainFromConfig(companyIntegration.Configuration) : null;
|
|
531
|
+
const subdomain = (fromConfig ?? creds.Subdomain ?? '').trim();
|
|
532
|
+
if (!subdomain) {
|
|
533
|
+
throw new Error('No Zendesk subdomain configured. Set the per-connection Configuration key "Subdomain" ' +
|
|
534
|
+
'(the `<subdomain>.zendesk.com` tenant identifier) — it is never baked into the connector.');
|
|
535
|
+
}
|
|
536
|
+
return subdomain;
|
|
537
|
+
}
|
|
538
|
+
/** Reads the `Subdomain` key from a Configuration JSON string (tolerant of absent/invalid). */
|
|
539
|
+
readSubdomainFromConfig(configJson) {
|
|
540
|
+
try {
|
|
541
|
+
const cfg = JSON.parse(configJson);
|
|
542
|
+
const v = cfg['Subdomain'] ?? cfg['subdomain'];
|
|
543
|
+
return typeof v === 'string' && v.trim().length > 0 ? v.trim() : null;
|
|
544
|
+
}
|
|
545
|
+
catch {
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
// ── Helpers ──────────────────────────────────────────────────────
|
|
550
|
+
/** Substitutes CreateAPIPath parent template vars from the record's own attributes (nested creates). */
|
|
551
|
+
substitutePathVarsFromAttributes(path, attributes) {
|
|
552
|
+
return path.replace(/\{(\w+)\}/g, (match, name) => {
|
|
553
|
+
const v = attributes[name];
|
|
554
|
+
return v != null && String(v).length > 0 ? encodeURIComponent(String(v)) : match;
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
/** Joins a base URL and a path (mirrors the base's private BuildFullURL). */
|
|
558
|
+
joinURL(baseURL, apiPath) {
|
|
559
|
+
const base = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL;
|
|
560
|
+
const path = apiPath.startsWith('/') ? apiPath : `/${apiPath}`;
|
|
561
|
+
return `${base}${path}`;
|
|
562
|
+
}
|
|
563
|
+
/** Reads an `id`-like value as a string (number or string); undefined when absent. */
|
|
564
|
+
readId(o) {
|
|
565
|
+
for (const k of ['id', 'ID', 'Id']) {
|
|
566
|
+
const v = o[k];
|
|
567
|
+
if (typeof v === 'string' && v.length > 0)
|
|
568
|
+
return v;
|
|
569
|
+
if (typeof v === 'number')
|
|
570
|
+
return String(v);
|
|
571
|
+
}
|
|
572
|
+
return undefined;
|
|
573
|
+
}
|
|
574
|
+
/** PK field names in Sequence order (falls back to ['id'] — Zendesk's universal PK — when unmarked). */
|
|
575
|
+
pkFieldNames(fields) {
|
|
576
|
+
const pk = fields.filter(f => f.IsPrimaryKey).sort((a, b) => a.Sequence - b.Sequence).map(f => f.Name);
|
|
577
|
+
return pk.length > 0 ? pk : ['id'];
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Builds an ExternalRecord with the FULL source record in Fields (full-record pass-through — the
|
|
581
|
+
* framework's custom-column capture diffs keys(Fields) against the field maps). ExternalID is the
|
|
582
|
+
* composite PK (when every component is present + non-empty) or the raw Zendesk `id`.
|
|
583
|
+
*/
|
|
584
|
+
buildExternalRecord(raw, objectType, pkFieldNames) {
|
|
585
|
+
const allPresent = pkFieldNames.length > 0 && pkFieldNames.every(n => raw[n] != null && String(raw[n]).length > 0);
|
|
586
|
+
const externalID = allPresent
|
|
587
|
+
? pkFieldNames.map(n => String(raw[n])).join('|')
|
|
588
|
+
: (raw.id != null ? String(raw.id) : '');
|
|
589
|
+
return { ExternalID: externalID, ObjectType: objectType, Fields: raw };
|
|
590
|
+
}
|
|
591
|
+
/** Gets an IO from the cache without throwing (used by StableOrderingKey, which may be called early). */
|
|
592
|
+
tryGetCachedObject(objectName) {
|
|
593
|
+
try {
|
|
594
|
+
const integ = IntegrationEngineBase.Instance.GetIntegrationByName(this.IntegrationName);
|
|
595
|
+
if (!integ)
|
|
596
|
+
return null;
|
|
597
|
+
return IntegrationEngineBase.Instance.GetIntegrationObject(integ.ID, objectName) ?? null;
|
|
598
|
+
}
|
|
599
|
+
catch {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/** Reads a trimmed string value from an IntegrationObject's Configuration JSON (tolerant of absent/invalid). */
|
|
604
|
+
readConfigString(obj, key) {
|
|
605
|
+
const raw = obj.Configuration;
|
|
606
|
+
if (!raw || typeof raw !== 'string')
|
|
607
|
+
return null;
|
|
608
|
+
try {
|
|
609
|
+
const cfg = JSON.parse(raw);
|
|
610
|
+
const v = cfg[key];
|
|
611
|
+
return typeof v === 'string' && v.trim().length > 0 ? v.trim() : null;
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
/** Returns the first present, non-empty string value among the given keys. */
|
|
618
|
+
firstString(obj, keys) {
|
|
619
|
+
for (const k of keys) {
|
|
620
|
+
const v = obj[k];
|
|
621
|
+
if (typeof v === 'string' && v.length > 0)
|
|
622
|
+
return v;
|
|
623
|
+
}
|
|
624
|
+
return undefined;
|
|
625
|
+
}
|
|
626
|
+
/** Best-effort extraction of response headers from an error object (for ExtractRetryAfterMs). */
|
|
627
|
+
extractHeadersFromError(error) {
|
|
628
|
+
if (!error || typeof error !== 'object')
|
|
629
|
+
return undefined;
|
|
630
|
+
const e = error;
|
|
631
|
+
const resp = e.response ?? e;
|
|
632
|
+
if (resp && typeof resp === 'object') {
|
|
633
|
+
const headers = resp.headers ?? resp.Headers;
|
|
634
|
+
if (headers && typeof headers === 'object')
|
|
635
|
+
return headers;
|
|
636
|
+
}
|
|
637
|
+
return undefined;
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
ZendeskConnector = __decorate([
|
|
641
|
+
RegisterClass(BaseIntegrationConnector, 'ZendeskConnector')
|
|
642
|
+
], ZendeskConnector);
|
|
643
|
+
export { ZendeskConnector };
|
|
644
|
+
//# sourceMappingURL=ZendeskConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ZendeskConnector.js","sourceRoot":"","sources":["../src/ZendeskConnector.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAyC,MAAM,sBAAsB,CAAC;AAOvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAChF,OAAO,EACH,wBAAwB,EACxB,4BAA4B,EAC5B,yBAAyB,GAa5B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,8BAA8B,EAAE,MAAM,wCAAwC,CAAC;AAyExF,yEAAyE;AAEzE,8GAA8G;AAC9G,MAAM,4BAA4B,GAAG,IAAI,CAAC;AAC1C,6EAA6E;AAC7E,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAElC,0EAA0E;AAE1E;;;;;;;;GAQG;AAEI,IAAM,gBAAgB,GAAtB,MAAM,gBAAiB,SAAQ,4BAA4B;IAA3D;;QAEH,2FAA2F;QACnF,eAAU,GAA8B,IAAI,CAAC;IA8oBzD,CAAC;IA5oBG,oEAAoE;IAEpE,mHAAmH;IACnH,IAAoB,eAAe;QAC/B,OAAO,SAAS,CAAC;IACrB,CAAC;IAED;;;;;;;;OAQG;IACa,KAAK,CAAC,gBAAgB,CAClC,kBAA8C,EAC9C,WAAqB;QAErB,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;QAC3E,MAAM,OAAO,CAAC,GAAG,CACb,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC3B,IAAI,CAAC;gBACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,kBAAkB,EAAE,GAAG,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;gBACrG,GAAG,CAAC,MAAM,GAAG,8BAA8B,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACrE,CAAC;YAAC,MAAM,CAAC;gBACL,qEAAqE;YACzE,CAAC;QACL,CAAC,CAAC,CACL,CAAC;QACF,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,+EAA+E;IAE/E,IAAoB,cAAc,KAAc,OAAO,IAAI,CAAC,CAAC,CAAC;IAC9D,IAAoB,cAAc,KAAc,OAAO,IAAI,CAAC,CAAC,CAAC;IAC9D,IAAoB,cAAc,KAAc,OAAO,IAAI,CAAC,CAAC,CAAC;IAE9D;;;;;;OAMG;IACH,IAAoB,wBAAwB;QACxC,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,4FAA4F;IAE5F;;;;;;OAMG;IACH,IAAoB,eAAe;QAC/B,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACH,IAAoB,kBAAkB,KAAc,OAAO,IAAI,CAAC,CAAC,CAAC;IAElE;;;OAGG;IACa,mBAAmB,CAAC,KAAc;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAC/B,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;QACpE,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,2GAA2G;IAC3G,IAAoB,kBAAkB,KAAoB,OAAO,CAAC,CAAC,CAAC,CAAC;IAErE;;;;OAIG;IACa,iBAAiB,CAAC,UAAkB;QAChD,MAAM,GAAG,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,QAAQ,GAAI,GAAwD,CAAC,iBAAiB,CAAC;QAC7F,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnE,MAAM,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QAClE,OAAO,EAAE,EAAE,IAAI,IAAI,IAAI,CAAC;IAC5B,CAAC;IAED,oEAAoE;IAEpE;;;;OAIG;IACgB,KAAK,CAAC,YAAY,CACjC,kBAA8C,EAC9C,WAAqB;QAErB,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC;QAC5C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;QAC1E,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,kBAAkB,CAAC,CAAC;QACxE,4FAA4F;QAC5F,4FAA4F;QAC5F,yFAAyF;QACzF,MAAM,SAAS,GAAG,eAAe;YAC7B,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,KAAK,CAAC,IAAI,UAAU,CAAC;YACrE,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,IAAI,SAAS,EAAE,CAAC;QAClH,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED,oGAAoG;IACjF,YAAY,CAAC,IAAqB;QACjD,OAAO;YACH,eAAe,EAAG,IAA2B,CAAC,UAAU;YACxD,cAAc,EAAE,kBAAkB;YAClC,QAAQ,EAAE,kBAAkB;SAC/B,CAAC;IACN,CAAC;IAED,yGAAyG;IACtF,KAAK,CAAC,eAAe,CACpC,KAAsB,EACtB,GAAW,EACX,MAAc,EACd,OAA+B,EAC/B,IAAc;QAEd,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC9B,MAAM;YACN,OAAO;YACP,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SAC9D,CAAC,CAAC;QACH,MAAM,WAAW,GAA2B,EAAE,CAAC;QAC/C,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,MAAM,GAAY,IAAI,CAAC;QAC3B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClB,IAAI,CAAC;gBAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,MAAM,GAAG,IAAI,CAAC;YAAC,CAAC;QAC/D,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAC3E,CAAC;IAED;;;;OAIG;IACgB,iBAAiB,CAAC,OAAgB,EAAE,eAA8B;QACjF,IAAI,OAAO,IAAI,IAAI;YAAE,OAAO,EAAE,CAAC;QAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,OAAO,OAAoC,CAAC;QACxE,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,OAAkC,CAAC;QAChD,IAAI,eAAe,EAAE,CAAC;YAClB,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;YAClC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBAAE,OAAO,GAAgC,CAAC;YAChE,8EAA8E;YAC9E,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACnD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,GAA8B,CAAC,CAAC;QACvG,CAAC;QACD,uCAAuC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IAED;;;;;;OAMG;IACgB,qBAAqB,CACpC,OAAgB,EAChB,cAA8B,EAC9B,YAAoB,EACpB,cAAsB,EACtB,SAAiB;QAEjB,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACvE,MAAM,GAAG,GAAG,OAAgC,CAAC;QAC7C,IAAI,cAAc,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC;YACrC,IAAI,GAAG,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;YACzE,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9B,CAAC;QACD,IAAI,cAAc,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;YAC9E,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;QACzD,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACgB,UAAU,CAAC,mBAA+C,EAAE,IAAqB;QAChG,MAAM,GAAG,GAAG,IAA0B,CAAC;QACvC,IAAI,GAAG,CAAC,eAAe;YAAE,OAAO,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACxE,OAAO,WAAW,GAAG,CAAC,SAAS,cAAc,CAAC;IAClD,CAAC;IAED;;;;;OAKG;IACK,sBAAsB,CAAC,kBAA8C;QACzE,IAAI,CAAC,kBAAkB,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QACnD,IAAI,MAA+B,CAAC;QACpC,IAAI,CAAC;YAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,aAAa,CAA4B,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QAChH,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;QAC7H,IAAI,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAAE,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;QAC/D,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,6GAA6G;IACrG,mBAAmB,CAAC,kBAA8C,EAAE,KAAyB;QACjG,MAAM,UAAU,GAAG,kBAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5H,MAAM,SAAS,GAAG,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/D,OAAO,SAAS,IAAI,IAAI,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACgB,iBAAiB,CAChC,QAAgB,EAChB,GAA8B,EAC9B,IAAY,EACZ,OAAe,EACf,MAAe,EACf,iBAA0B;QAE1B,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACrD,IAAI,GAAG,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,iBAAiB,IAAI,GAAG,CAAC,eAAe,IAAI,qBAAqB,CAAC;YACnF,OAAO,GAAG,QAAQ,GAAG,SAAS,QAAQ,IAAI,aAAa,QAAQ,EAAE,CAAC;QACtE,CAAC;QACD,+CAA+C;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,IAAI,GAAG,CAAC,eAAe,IAAI,qBAAqB,EAAE,qBAAqB,CAAC,CAAC;QACpH,MAAM,KAAK,GAAG,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAC;QACzC,IAAI,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,eAAe,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACpE,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IACvD,CAAC;IAED,mFAAmF;IAEnF;;;;;OAKG;IACa,KAAK,CAAC,YAAY,CAAC,GAAiB;QAChD,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,kBAAkB,CAAC,aAAa,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;QACvF,IAAI,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,yBAAyB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAED,8GAA8G;IACtG,qBAAqB,CAAC,GAA8B;QACxD,OAAO,GAAG,CAAC,uBAAuB,IAAI,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,qBAAqB,CAAC,IAAI,IAAI,CAAC;IACpG,CAAC;IAED;;;;;;;;;OASG;IACK,KAAK,CAAC,yBAAyB,CAAC,GAA8B,EAAE,GAAiB;QACrF,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,kBAAkB,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;QAC9E,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;QAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,qBAAqB,CAAE,CAAC;QACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,IAAI,4BAA4B,EAAE,4BAA4B,CAAC,CAAC;QAC7G,MAAM,UAAU,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAEhG,iGAAiG;QACjG,IAAI,MAAM,GAAuB,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,cAAc,IAAI,SAAS,CAAC;QACtF,MAAM,OAAO,GAAqB,EAAE,CAAC;QACrC,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,IAAI,UAAU,GAAuB,MAAM,CAAC;QAE5C,OAAO,OAAO,CAAC,MAAM,GAAG,UAAU,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7E,MAAM,GAAG,GAAG,MAAM;gBACd,CAAC,CAAC,GAAG,OAAO,GAAG,QAAQ,WAAW,kBAAkB,CAAC,MAAM,CAAC,aAAa,OAAO,EAAE;gBAClF,CAAC,CAAC,GAAG,OAAO,GAAG,QAAQ,0BAA0B,OAAO,EAAE,CAAC;YAC/D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YACvE,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;gBAClD,MAAM,IAAI,KAAK,CAAC,4CAA4C,GAAG,CAAC,IAAI,WAAW,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACtG,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC;YACvE,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;gBAClB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;YACtH,CAAC;YACD,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAA0B,CAAC;YAC3D,WAAW,GAAG,GAAG,CAAC,aAAa,KAAK,IAAI,CAAC;YACzC,MAAM,UAAU,GAAG,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;YACtH,IAAI,UAAU;gBAAE,UAAU,GAAG,UAAU,CAAC;YACxC,IAAI,WAAW,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjD,WAAW,GAAG,WAAW,IAAI,CAAC,UAAU,CAAC;gBACzC,MAAM;YACV,CAAC;YACD,MAAM,GAAG,UAAU,CAAC;QACxB,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YACd,uFAAuF;YACvF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,UAAU,IAAI,GAAG,CAAC,cAAc,IAAI,SAAS,EAAE,CAAC;QAClH,CAAC;QACD,4FAA4F;QAC5F,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;IACvE,CAAC;IAED,qEAAqE;IACrE,EAAE;IACF,mGAAmG;IACnG,oGAAoG;IACpG,uGAAuG;IACvG,sGAAsG;IACtG,yDAAyD;IAEzD;;;;;;;;;OASG;IACa,KAAK,CAAC,YAAY,CAAC,GAAwB;QACvD,MAAM,EAAE,GAAG,GAAG,CAAC,kBAAgD,CAAC;QAChE,MAAM,WAAW,GAAG,GAAG,CAAC,WAAuB,CAAC;QAChD,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,aAAa,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;QACnE,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YAC1C,6EAA6E;YAC7E,OAAO,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,gCAAgC,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;QACtF,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,eAAe,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;QAC7F,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACxF,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YAClD,MAAM,UAAU,GAAG,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;YAC9E,OAAO,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;QAChF,CAAC;QACD,OAAO;YACH,OAAO,EAAE,KAAK;YACd,UAAU,EAAE,QAAQ,CAAC,MAAM;YAC3B,YAAY,EAAE,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,IAAI,QAAQ,QAAQ,CAAC,MAAM,YAAY;SAC1F,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACgB,kBAAkB,CAAC,IAAY,EAAE,UAAkB,EAAE,UAAyB;QAC7F,IAAI,UAAU,IAAI,UAAU,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QACrD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC;QACpF,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,GAAG,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAClB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,UAAU,CAAC;YAC9D,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC;IACf,CAAC;IAED;;;;OAIG;IACgB,qBAAqB,CAAC,QAAsB,EAAE,UAAyB;QACtF,IAAI,UAAU,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC,qBAAqB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACtF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;QAC3B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACxD,MAAM,CAAC,GAAG,IAA+B,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClD,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAA4B,CAAC,CAAC;gBACrD,IAAI,EAAE;oBAAE,OAAO,EAAE,CAAC;YACtB,CAAC;QACL,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,oEAAoE;IAEpE;;;OAGG;IACa,KAAK,CAAC,cAAc,CAChC,kBAA8C,EAC9C,WAAqB;QAErB,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;YACtE,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;YAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,GAAG,GAAG,GAAG,OAAO,kBAAkB,CAAC;YACzC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YACvE,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,gCAAgC,EAAE,CAAC;YACxE,CAAC;YACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACrD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,uCAAuC,QAAQ,CAAC,MAAM,+CAA+C,EAAE,CAAC;YAC9I,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,yCAAyC,QAAQ,CAAC,MAAM,GAAG,EAAE,CAAC;QACpG,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,kCAAkC,GAAG,EAAE,EAAE,CAAC;QAChF,CAAC;IACL,CAAC;IAED,oEAAoE;IAEpE,0GAA0G;IAClG,KAAK,CAAC,eAAe,CACzB,kBAA8C,EAC9C,WAAqB;QAErB,MAAM,YAAY,GAAG,kBAAkB,CAAC,YAAY,CAAC;QACrD,IAAI,KAAK,GAA8B,IAAI,CAAC;QAC5C,IAAI,YAAY,EAAE,CAAC;YACf,KAAK,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QAC3E,CAAC;QACD,mGAAmG;QACnG,MAAM,WAAW,GAAG,kBAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACzH,MAAM,MAAM,GAAuB,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QAChF,8GAA8G;QAC9G,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,WAAW,EAAE,SAAS;YAAE,MAAM,CAAC,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC;QAC1F,IAAI,CAAC,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACX,wFAAwF;gBACxF,qGAAqG,CACxG,CAAC;QACN,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,yDAAyD;IACjD,KAAK,CAAC,wBAAwB,CAClC,YAAoB,EACpB,WAAqB,EACrB,QAA4B;QAE5B,MAAM,EAAE,GAAG,QAAQ,IAAI,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,eAAe,CAAqB,iBAAiB,EAAE,WAAW,CAAC,CAAC;QAChG,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACnD,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAC/C,OAAO,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACvD,CAAC;IAED,2FAA2F;IACnF,mBAAmB,CAAC,IAAY;QACpC,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;YAC3D,MAAM,GAAG,GAAuB;gBAC5B,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;gBACnF,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAClG,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBAC5D,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBAC5D,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;aAClE,CAAC;YACF,OAAO,GAAG,CAAC;QACf,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAED;;;;OAIG;IACK,eAAe,CAAC,KAAyB;QAC7C,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YAChC,OAAO,yBAAyB,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,KAAK,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QACrG,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;YAC3C,OAAO,yBAAyB,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,MAAM,IAAI,KAAK,CACX,yFAAyF;YACzF,wDAAwD,CAC3D,CAAC;IACN,CAAC;IAED,+GAA+G;IACvG,gBAAgB,CAAC,kBAA8C,EAAE,KAAyB;QAC9F,MAAM,UAAU,GAAG,kBAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5H,MAAM,SAAS,GAAG,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/D,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACX,wFAAwF;gBACxF,2FAA2F,CAC9F,CAAC;QACN,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,+FAA+F;IACvF,uBAAuB,CAAC,UAAkB;QAC9C,IAAI,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAA4B,CAAC;YAC9D,MAAM,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;YAC/C,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1E,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAED,oEAAoE;IAEpE,wGAAwG;IAChG,gCAAgC,CAAC,IAAY,EAAE,UAAmC;QACtF,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;YACtD,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACrF,CAAC,CAAC,CAAC;IACP,CAAC;IAED,6EAA6E;IACrE,OAAO,CAAC,OAAe,EAAE,OAAe;QAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACpE,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;QAC/D,OAAO,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED,sFAAsF;IAC9E,MAAM,CAAC,CAA0B;QACrC,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACf,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,CAAC,CAAC;YACpD,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,wGAAwG;IAChG,YAAY,CAAC,MAAwC;QACzD,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvG,OAAO,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAED;;;;OAIG;IACK,mBAAmB,CAAC,GAA4B,EAAE,UAAkB,EAAE,YAAsB;QAChG,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACnH,MAAM,UAAU,GAAG,UAAU;YACzB,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YACjD,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAC3E,CAAC;IAED,yGAAyG;IACjG,kBAAkB,CAAC,UAAkB;QACzC,IAAI,CAAC;YACD,MAAM,KAAK,GAAG,qBAAqB,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACxF,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YACxB,OAAO,qBAAqB,CAAC,QAAQ,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,IAAI,CAAC;QAC7F,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAED,gHAAgH;IACxG,gBAAgB,CAAC,GAA8B,EAAE,GAAW;QAChE,MAAM,GAAG,GAAI,GAAoD,CAAC,aAAa,CAAC;QAChF,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACjD,IAAI,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;YACvD,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACnB,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1E,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAED,8EAA8E;IACtE,WAAW,CAAC,GAA4B,EAAE,IAAc;QAC5D,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACnB,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;YACjB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,iGAAiG;IACzF,uBAAuB,CAAC,KAAc;QAC1C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAC1D,MAAM,CAAC,GAAG,KAAgC,CAAC;QAC3C,MAAM,IAAI,GAAI,CAAC,CAAC,QAAgD,IAAI,CAAC,CAAC;QACtE,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACnC,MAAM,OAAO,GAAI,IAAgC,CAAC,OAAO,IAAK,IAAgC,CAAC,OAAO,CAAC;YACvG,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;gBAAE,OAAO,OAAiC,CAAC;QACzF,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;CACJ,CAAA;AAjpBY,gBAAgB;IAD5B,aAAa,CAAC,wBAAwB,EAAE,kBAAkB,CAAC;GAC/C,gBAAgB,CAipB5B"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from './ZendeskConnector.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 './ZendeskConnector.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,uBAAuB,CAAC;AAEtC;oGACoG;AACpG,MAAM,UAAU,iBAAiB,KAAiD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@memberjunction/connector-zendesk",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MemberJunction Zendesk 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.45.0 <6.0.0",
|
|
19
|
+
"@memberjunction/core-entities": ">=5.45.0 <6.0.0",
|
|
20
|
+
"@memberjunction/global": ">=5.45.0 <6.0.0",
|
|
21
|
+
"@memberjunction/integration-engine": ">=5.45.0 <6.0.0",
|
|
22
|
+
"@memberjunction/integration-engine-base": ">=5.45.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/integration-engine-base": "^5.42.0",
|
|
37
|
+
"@memberjunction/connector-schema-merge": "^1.0.0"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/MemberJunction/Integrations"
|
|
42
|
+
}
|
|
43
|
+
}
|