@forestadmin/datasource-zendesk 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.
Files changed (52) hide show
  1. package/README.md +162 -0
  2. package/dist/client/index.d.ts +91 -0
  3. package/dist/client/index.js +243 -0
  4. package/dist/client-options.d.ts +16 -0
  5. package/dist/client-options.js +10 -0
  6. package/dist/collections/base-zendesk-collection.d.ts +52 -0
  7. package/dist/collections/base-zendesk-collection.js +203 -0
  8. package/dist/collections/organization-collection.d.ts +15 -0
  9. package/dist/collections/organization-collection.js +145 -0
  10. package/dist/collections/searchable-collection.d.ts +6 -0
  11. package/dist/collections/searchable-collection.js +20 -0
  12. package/dist/collections/ticket/comments-embedder.d.ts +5 -0
  13. package/dist/collections/ticket/comments-embedder.js +40 -0
  14. package/dist/collections/ticket/relation-embedder.d.ts +10 -0
  15. package/dist/collections/ticket/relation-embedder.js +62 -0
  16. package/dist/collections/ticket/schema-definition.d.ts +13 -0
  17. package/dist/collections/ticket/schema-definition.js +147 -0
  18. package/dist/collections/ticket/serializer.d.ts +15 -0
  19. package/dist/collections/ticket/serializer.js +96 -0
  20. package/dist/collections/ticket-collection.d.ts +18 -0
  21. package/dist/collections/ticket-collection.js +139 -0
  22. package/dist/collections/user-collection.d.ts +15 -0
  23. package/dist/collections/user-collection.js +162 -0
  24. package/dist/datasource.d.ts +16 -0
  25. package/dist/datasource.js +49 -0
  26. package/dist/enums.d.ts +10 -0
  27. package/dist/enums.js +12 -0
  28. package/dist/errors.d.ts +14 -0
  29. package/dist/errors.js +31 -0
  30. package/dist/factory.d.ts +5 -0
  31. package/dist/factory.js +15 -0
  32. package/dist/index.d.ts +15 -0
  33. package/dist/index.js +30 -0
  34. package/dist/plugins/close-ticket/errors.d.ts +7 -0
  35. package/dist/plugins/close-ticket/errors.js +44 -0
  36. package/dist/plugins/close-ticket/index.d.ts +13 -0
  37. package/dist/plugins/close-ticket/index.js +74 -0
  38. package/dist/plugins/close-ticket/messages.d.ts +12 -0
  39. package/dist/plugins/close-ticket/messages.js +33 -0
  40. package/dist/plugins/create-ticket-with-notification/form-builder.d.ts +30 -0
  41. package/dist/plugins/create-ticket-with-notification/form-builder.js +179 -0
  42. package/dist/plugins/create-ticket-with-notification/index.d.ts +19 -0
  43. package/dist/plugins/create-ticket-with-notification/index.js +81 -0
  44. package/dist/query/condition-tree-translator.d.ts +9 -0
  45. package/dist/query/condition-tree-translator.js +90 -0
  46. package/dist/query/operators.d.ts +15 -0
  47. package/dist/query/operators.js +34 -0
  48. package/dist/schema/custom-fields-introspector.d.ts +13 -0
  49. package/dist/schema/custom-fields-introspector.js +89 -0
  50. package/dist/types.d.ts +23 -0
  51. package/dist/types.js +3 -0
  52. package/package.json +33 -0
package/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # @forestadmin/datasource-zendesk
2
+
3
+ Forest Admin datasource for Zendesk Support, with optional smart-action plugins.
4
+
5
+ Exposes three collections wired to the Zendesk REST + Search APIs:
6
+
7
+ | Forest collection | Zendesk resource | CRUD | Aggregation |
8
+ | ----------------------- | ---------------- | ------------- | --------------- |
9
+ | `zendesk_ticket` | Ticket | List, create, update, delete | Count only |
10
+ | `zendesk_user` | User | List, create, update, delete | Count only |
11
+ | `zendesk_organization` | Organization | List, create, update, delete | Count only |
12
+
13
+ The datasource introspects Zendesk **custom fields** on startup (ticket, user, organization) and registers them as Forest columns.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ yarn add @forestadmin/datasource-zendesk
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ```ts
24
+ import { createAgent } from '@forestadmin/agent';
25
+ import {
26
+ createZendeskClient,
27
+ createZendeskDataSource,
28
+ closeTicketPlugin,
29
+ createTicketWithNotificationPlugin,
30
+ } from '@forestadmin/datasource-zendesk';
31
+
32
+ const zendeskClient = createZendeskClient({
33
+ subdomain: process.env.ZENDESK_SUBDOMAIN!,
34
+ email: process.env.ZENDESK_EMAIL!,
35
+ apiToken: process.env.ZENDESK_API_TOKEN!,
36
+ });
37
+
38
+ const agent = createAgent({ /* ... */ })
39
+ .addDataSource(createZendeskDataSource({ client: zendeskClient }))
40
+
41
+ // Smart action on Zendesk tickets: Mark as solved / closed.
42
+ .customizeCollection('zendesk_ticket', collection =>
43
+ collection.use(closeTicketPlugin, {
44
+ client: zendeskClient,
45
+ ticketIdField: 'id',
46
+ }),
47
+ )
48
+
49
+ // Smart action on ANY collection (here: customers): create a Zendesk ticket
50
+ // and notify the requester by email.
51
+ .customizeCollection('customers', collection =>
52
+ collection.use(createTicketWithNotificationPlugin, {
53
+ client: zendeskClient,
54
+ requesterEmailDefault: record => String(record.email ?? ''),
55
+ defaultSubject: 'Follow-up about your account',
56
+ ticketIdField: 'last_zendesk_ticket_id',
57
+ }),
58
+ );
59
+ ```
60
+
61
+ ## Datasource
62
+
63
+ ### `createZendeskDataSource(options)`
64
+
65
+ `options` accepts either an already-built `client` (recommended when also passing it to plugins) or raw credentials:
66
+
67
+ ```ts
68
+ createZendeskDataSource({ client: createZendeskClient({ subdomain, email, apiToken }) });
69
+
70
+ // or
71
+ createZendeskDataSource({ subdomain, email, apiToken });
72
+ ```
73
+
74
+ ### Filter operators
75
+
76
+ Zendesk Search supports a restricted set of operators. The datasource exposes:
77
+
78
+ - `Equal`, `In` for the primary key (`id`), resolved by id lookup outside of Search
79
+ - `Equal`, `NotEqual`, `In`, `NotIn`, `Present`, `Blank` for strings and enums
80
+ - `Equal`, `NotEqual` for booleans
81
+ - `Equal`, `NotEqual`, `In`, `NotIn`, `Present`, `Blank`, `GreaterThan`, `LessThan` for numbers
82
+ - `Equal`, `Before`, `After`, `Present`, `Blank` for dates
83
+
84
+ Zendesk Search has no `OR` operator. Multi-value membership (`In`/`NotIn`) on the primary key matches each value exactly (id lookup), but on other fields Zendesk ANDs the terms — so `status In ['open', 'pending']` returns nothing. Unsupported operators (`Contains`, `Or` aggregator, …) raise `UnsupportedOperatorError`. The Zendesk Search API caps result pagination at 1000 records — large skips raise the same error.
85
+
86
+ ### Custom fields
87
+
88
+ On boot, the datasource calls `GET /ticket_fields.json`, `/user_fields.json` and `/organization_fields.json`. Active fields are mapped to Forest columns:
89
+
90
+ | Zendesk type | Forest column |
91
+ | ------------ | ------------- |
92
+ | `text`, `textarea`, `regexp`, `partialcreditcard` | `String` |
93
+ | `integer`, `decimal`, `lookup` | `Number` |
94
+ | `date` | `Dateonly` |
95
+ | `checkbox` | `Boolean` |
96
+ | `dropdown`, `tagger` | `Enum` (or `String` if no options) |
97
+ | `multiselect` | `Json` |
98
+
99
+ Ticket custom fields are exposed as `custom_<id>`; user/organization ones use the Zendesk `key`.
100
+
101
+ ## Plugins
102
+
103
+ Both plugins accept the same client contract as the datasource: pass an
104
+ already-built `client`, **or** raw credentials (`subdomain`, `email`, `apiToken`)
105
+ and the plugin builds one for you. Reusing a single client across the datasource
106
+ and the plugins is recommended, but no longer required.
107
+
108
+ ```ts
109
+ // with a shared client
110
+ collection.use(closeTicketPlugin, { client, ticketIdField: 'id' });
111
+
112
+ // or with raw credentials (client is optional)
113
+ collection.use(closeTicketPlugin, {
114
+ subdomain: process.env.ZENDESK_SUBDOMAIN!,
115
+ email: process.env.ZENDESK_EMAIL!,
116
+ apiToken: process.env.ZENDESK_API_TOKEN!,
117
+ ticketIdField: 'id',
118
+ });
119
+ ```
120
+
121
+ ### `closeTicketPlugin`
122
+
123
+ Adds Single + Bulk actions to mark Zendesk tickets as `solved` or `closed`. Tickets that Zendesk reports as already closed are folded into the success message rather than counted as failures.
124
+
125
+ ```ts
126
+ collection.use(closeTicketPlugin, {
127
+ client,
128
+ ticketIdField: 'zendesk_id',
129
+ // optional:
130
+ statuses: ['solved'], // default: ['solved', 'closed']
131
+ scopes: ['Single'], // default: ['Single', 'Bulk']
132
+ });
133
+ ```
134
+
135
+ ### `createTicketWithNotificationPlugin`
136
+
137
+ Adds a Single action that creates a Zendesk ticket. Useful on a customer-facing collection so an agent can reach out without leaving Forest. With `emailTemplates`, the form becomes two-page (template picker → body).
138
+
139
+ ```ts
140
+ collection.use(createTicketWithNotificationPlugin, {
141
+ client,
142
+ actionName: 'Notify customer', // optional
143
+ emailTemplates: [
144
+ { title: 'Welcome', content: 'Hi {{ record.first_name }}, welcome aboard.' },
145
+ ],
146
+ requesterEmailDefault: record => String(record.email ?? ''),
147
+ defaultSubject: 'Follow-up',
148
+ priorityOverride: 'normal', // hides Priority from the form
149
+ ticketIdField: 'last_zendesk_ticket_id', // best-effort writeback after creation
150
+ showInternalNote: true, // adds a "Send as internal note" toggle
151
+ });
152
+ ```
153
+
154
+ Template interpolation supports `{{ record.field }}` (including dotted paths like `{{ record.org.name }}`).
155
+
156
+ ## Errors
157
+
158
+ All exceptions raised by the datasource are subclasses of Forest Admin's `BusinessError` / `ValidationError`:
159
+
160
+ - `ZendeskConfigurationError` — missing/invalid client options
161
+ - `ZendeskApiError` — Zendesk HTTP failure (carries the operation, status code and response body)
162
+ - `UnsupportedOperatorError` — filter / aggregation that Zendesk cannot express
@@ -0,0 +1,91 @@
1
+ import type { CustomFieldEntry, SearchParams, ZendeskClientOptions, ZendeskRecord, ZendeskResource } from '../types';
2
+ import type { Logger } from '@forestadmin/datasource-toolkit';
3
+ export declare const MAX_PER_PAGE = 100;
4
+ export declare const MAX_TOTAL_RESULTS = 1000;
5
+ export type RawCustomFieldDefinition = {
6
+ id: number;
7
+ key?: string;
8
+ title?: string;
9
+ raw_title?: string;
10
+ type: string;
11
+ active?: boolean;
12
+ removable?: boolean;
13
+ custom_field_options?: Array<{
14
+ value: string;
15
+ name?: string;
16
+ }>;
17
+ };
18
+ export type ZendeskClient = {
19
+ search(type: ZendeskResource, params: SearchParams): Promise<ZendeskRecord[]>;
20
+ count(type: ZendeskResource, query: string): Promise<number>;
21
+ findTicket(id: number | string): Promise<ZendeskRecord | null>;
22
+ findUser(id: number | string): Promise<ZendeskRecord | null>;
23
+ findOrganization(id: number | string): Promise<ZendeskRecord | null>;
24
+ fetchTicketsByIds(ids: Array<number | string>): Promise<Map<number, ZendeskRecord>>;
25
+ fetchUsersByIds(ids: Array<number | string>): Promise<Map<number, ZendeskRecord>>;
26
+ fetchOrganizationsByIds(ids: Array<number | string>): Promise<Map<number, ZendeskRecord>>;
27
+ fetchUserEmails(ids: Array<number | string>): Promise<Map<number, string>>;
28
+ fetchTicketComments(ticketId: number | string): Promise<ZendeskRecord[]>;
29
+ fetchTicketFields(): Promise<RawCustomFieldDefinition[]>;
30
+ fetchUserFields(): Promise<RawCustomFieldDefinition[]>;
31
+ fetchOrganizationFields(): Promise<RawCustomFieldDefinition[]>;
32
+ createTicket(attrs: ZendeskRecord): Promise<ZendeskRecord>;
33
+ updateTicket(id: number | string, attrs: ZendeskRecord): Promise<ZendeskRecord>;
34
+ deleteTicket(id: number | string): Promise<void>;
35
+ createUser(attrs: ZendeskRecord): Promise<ZendeskRecord>;
36
+ updateUser(id: number | string, attrs: ZendeskRecord): Promise<ZendeskRecord>;
37
+ deleteUser(id: number | string): Promise<void>;
38
+ createOrganization(attrs: ZendeskRecord): Promise<ZendeskRecord>;
39
+ updateOrganization(id: number | string, attrs: ZendeskRecord): Promise<ZendeskRecord>;
40
+ deleteOrganization(id: number | string): Promise<void>;
41
+ readonly baseUrl: string;
42
+ /**
43
+ * Best-effort wrapper that logs and returns the default if the action throws.
44
+ * Used internally and exposed so embedders/plugins can degrade gracefully.
45
+ */
46
+ bestEffort<T>(operation: string, defaultValue: T, fn: () => Promise<T>): Promise<T>;
47
+ };
48
+ export declare class ZendeskHttpClient implements ZendeskClient {
49
+ readonly baseUrl: string;
50
+ private readonly authHeader;
51
+ private readonly logger?;
52
+ constructor(options: ZendeskClientOptions, logger?: Logger);
53
+ search(type: ZendeskResource, params: SearchParams): Promise<ZendeskRecord[]>;
54
+ count(type: ZendeskResource, query: string): Promise<number>;
55
+ findTicket(id: number | string): Promise<ZendeskRecord | null>;
56
+ findUser(id: number | string): Promise<ZendeskRecord | null>;
57
+ findOrganization(id: number | string): Promise<ZendeskRecord | null>;
58
+ fetchTicketsByIds(ids: Array<number | string>): Promise<Map<number, ZendeskRecord>>;
59
+ fetchUsersByIds(ids: Array<number | string>): Promise<Map<number, ZendeskRecord>>;
60
+ fetchOrganizationsByIds(ids: Array<number | string>): Promise<Map<number, ZendeskRecord>>;
61
+ fetchUserEmails(ids: Array<number | string>): Promise<Map<number, string>>;
62
+ fetchTicketComments(ticketId: number | string): Promise<ZendeskRecord[]>;
63
+ fetchTicketFields(): Promise<RawCustomFieldDefinition[]>;
64
+ fetchUserFields(): Promise<RawCustomFieldDefinition[]>;
65
+ fetchOrganizationFields(): Promise<RawCustomFieldDefinition[]>;
66
+ createTicket(attrs: ZendeskRecord): Promise<ZendeskRecord>;
67
+ updateTicket(id: number | string, attrs: ZendeskRecord): Promise<ZendeskRecord>;
68
+ deleteTicket(id: number | string): Promise<void>;
69
+ createUser(attrs: ZendeskRecord): Promise<ZendeskRecord>;
70
+ updateUser(id: number | string, attrs: ZendeskRecord): Promise<ZendeskRecord>;
71
+ deleteUser(id: number | string): Promise<void>;
72
+ createOrganization(attrs: ZendeskRecord): Promise<ZendeskRecord>;
73
+ updateOrganization(id: number | string, attrs: ZendeskRecord): Promise<ZendeskRecord>;
74
+ deleteOrganization(id: number | string): Promise<void>;
75
+ bestEffort<T>(operation: string, defaultValue: T, fn: () => Promise<T>): Promise<T>;
76
+ private findOne;
77
+ private showMany;
78
+ private fetchFields;
79
+ private createOne;
80
+ private updateOne;
81
+ private deleteOne;
82
+ private mustSucceed;
83
+ private get;
84
+ private post;
85
+ private put;
86
+ private delete;
87
+ private send;
88
+ }
89
+ export declare function createZendeskClient(options: ZendeskClientOptions, logger?: Logger): ZendeskClient;
90
+ export type { CustomFieldEntry };
91
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ZendeskHttpClient = exports.MAX_TOTAL_RESULTS = exports.MAX_PER_PAGE = void 0;
7
+ exports.createZendeskClient = createZendeskClient;
8
+ const superagent_1 = __importDefault(require("superagent"));
9
+ const errors_1 = require("../errors");
10
+ exports.MAX_PER_PAGE = 100;
11
+ exports.MAX_TOTAL_RESULTS = 1000;
12
+ const RESOURCE_PLURAL = {
13
+ ticket: 'tickets',
14
+ user: 'users',
15
+ organization: 'organizations',
16
+ };
17
+ function composeQuery(type, query) {
18
+ const parts = [type ? `type:${type}` : null, (query ?? '').trim()].filter(Boolean);
19
+ return parts.join(' ').trim();
20
+ }
21
+ function extractResource(body, key, operation) {
22
+ const resource = body[key];
23
+ if (resource && typeof resource === 'object' && !Array.isArray(resource)) {
24
+ return resource;
25
+ }
26
+ throw new errors_1.ZendeskApiError(operation, undefined, body, new Error(`Zendesk API returned an unexpected body shape (missing '${key}')`));
27
+ }
28
+ function validate(options) {
29
+ const missing = [];
30
+ if (!options?.subdomain?.trim())
31
+ missing.push('subdomain');
32
+ if (!options?.email?.trim())
33
+ missing.push('email');
34
+ if (!options?.apiToken?.trim())
35
+ missing.push('apiToken');
36
+ if (missing.length > 0) {
37
+ throw new errors_1.ZendeskConfigurationError(`Zendesk client is missing required configuration: ${missing.join(', ')}`);
38
+ }
39
+ }
40
+ class ZendeskHttpClient {
41
+ constructor(options, logger) {
42
+ validate(options);
43
+ this.baseUrl = `https://${options.subdomain}.zendesk.com/api/v2`;
44
+ const credentials = Buffer.from(`${options.email}/token:${options.apiToken}`).toString('base64');
45
+ this.authHeader = `Basic ${credentials}`;
46
+ this.logger = logger;
47
+ }
48
+ async search(type, params) {
49
+ const query = composeQuery(type, params.query);
50
+ const perPage = Math.min(params.perPage ?? exports.MAX_PER_PAGE, exports.MAX_PER_PAGE);
51
+ return this.mustSucceed(`search(${type})`, async () => {
52
+ const queryParams = {
53
+ query,
54
+ per_page: perPage,
55
+ page: params.page ?? 1,
56
+ };
57
+ if (params.sortBy)
58
+ queryParams.sort_by = params.sortBy;
59
+ if (params.sortOrder)
60
+ queryParams.sort_order = params.sortOrder;
61
+ const body = await this.get('/search.json', queryParams);
62
+ return Array.isArray(body.results) ? body.results : [];
63
+ });
64
+ }
65
+ async count(type, query) {
66
+ return this.mustSucceed(`count(${type})`, async () => {
67
+ const body = await this.get('/search/count.json', { query: composeQuery(type, query) });
68
+ return typeof body.count === 'number' ? body.count : 0;
69
+ });
70
+ }
71
+ findTicket(id) {
72
+ return this.findOne('ticket', id);
73
+ }
74
+ findUser(id) {
75
+ return this.findOne('user', id);
76
+ }
77
+ findOrganization(id) {
78
+ return this.findOne('organization', id);
79
+ }
80
+ fetchTicketsByIds(ids) {
81
+ return this.showMany('ticket', ids, (r) => [Number(r.id), r]);
82
+ }
83
+ fetchUsersByIds(ids) {
84
+ return this.bestEffort('fetch_users_by_ids', new Map(), () => this.showMany('user', ids, r => [Number(r.id), r]));
85
+ }
86
+ fetchOrganizationsByIds(ids) {
87
+ return this.bestEffort('fetch_organizations_by_ids', new Map(), () => this.showMany('organization', ids, r => [Number(r.id), r]));
88
+ }
89
+ fetchUserEmails(ids) {
90
+ return this.bestEffort('fetch_user_emails', new Map(), () => this.showMany('user', ids, r => [Number(r.id), String(r.email ?? '')]));
91
+ }
92
+ async fetchTicketComments(ticketId) {
93
+ return this.mustSucceed(`fetch_ticket_comments(${ticketId})`, async () => {
94
+ const body = await this.get(`/tickets/${encodeURIComponent(String(ticketId))}/comments.json`);
95
+ return Array.isArray(body.comments) ? body.comments : [];
96
+ });
97
+ }
98
+ fetchTicketFields() {
99
+ return this.fetchFields('ticket_fields');
100
+ }
101
+ fetchUserFields() {
102
+ return this.fetchFields('user_fields');
103
+ }
104
+ fetchOrganizationFields() {
105
+ return this.fetchFields('organization_fields');
106
+ }
107
+ createTicket(attrs) {
108
+ return this.createOne('ticket', attrs);
109
+ }
110
+ updateTicket(id, attrs) {
111
+ return this.updateOne('ticket', id, attrs);
112
+ }
113
+ deleteTicket(id) {
114
+ return this.deleteOne('ticket', id);
115
+ }
116
+ createUser(attrs) {
117
+ return this.createOne('user', attrs);
118
+ }
119
+ updateUser(id, attrs) {
120
+ return this.updateOne('user', id, attrs);
121
+ }
122
+ deleteUser(id) {
123
+ return this.deleteOne('user', id);
124
+ }
125
+ createOrganization(attrs) {
126
+ return this.createOne('organization', attrs);
127
+ }
128
+ updateOrganization(id, attrs) {
129
+ return this.updateOne('organization', id, attrs);
130
+ }
131
+ deleteOrganization(id) {
132
+ return this.deleteOne('organization', id);
133
+ }
134
+ async bestEffort(operation, defaultValue, fn) {
135
+ try {
136
+ return await fn();
137
+ }
138
+ catch (err) {
139
+ this.logger?.('Warn', `[datasource-zendesk] ${operation} failed; degrading: ${err instanceof Error ? err.message : String(err)}`);
140
+ return defaultValue;
141
+ }
142
+ }
143
+ // ===== Internal helpers =====
144
+ async findOne(type, id) {
145
+ const plural = RESOURCE_PLURAL[type];
146
+ const path = `/${plural}/${encodeURIComponent(String(id))}.json`;
147
+ try {
148
+ const body = await this.mustSucceed(`find(${plural}/${id})`, () => this.get(path));
149
+ return body[type] ?? null;
150
+ }
151
+ catch (err) {
152
+ if (err instanceof errors_1.ZendeskApiError && err.status === 404)
153
+ return null;
154
+ throw err;
155
+ }
156
+ }
157
+ async showMany(type, ids, project) {
158
+ const plural = RESOURCE_PLURAL[type];
159
+ const uniqueIds = Array.from(new Set(ids.filter(id => id !== null && id !== undefined)));
160
+ const result = new Map();
161
+ if (uniqueIds.length === 0)
162
+ return result;
163
+ for (let i = 0; i < uniqueIds.length; i += exports.MAX_PER_PAGE) {
164
+ const chunk = uniqueIds.slice(i, i + exports.MAX_PER_PAGE);
165
+ // eslint-disable-next-line no-await-in-loop
166
+ const body = await this.mustSucceed(`fetch_${plural}_by_ids`, () => this.get(`/${plural}/show_many.json`, { ids: chunk.join(',') }));
167
+ const records = Array.isArray(body[plural]) ? body[plural] : [];
168
+ for (const record of records) {
169
+ const [key, value] = project(record);
170
+ result.set(key, value);
171
+ }
172
+ }
173
+ return result;
174
+ }
175
+ async fetchFields(path) {
176
+ return this.mustSucceed(`fetch_${path}`, async () => {
177
+ const body = await this.get(`/${path}.json`);
178
+ return Array.isArray(body[path]) ? body[path] : [];
179
+ });
180
+ }
181
+ async createOne(type, attrs) {
182
+ const plural = RESOURCE_PLURAL[type];
183
+ return this.mustSucceed(`create(${plural})`, async () => {
184
+ const body = await this.post(`/${plural}.json`, { [type]: attrs });
185
+ return extractResource(body, type, `create(${plural})`);
186
+ });
187
+ }
188
+ async updateOne(type, id, attrs) {
189
+ const plural = RESOURCE_PLURAL[type];
190
+ return this.mustSucceed(`update(${plural}/${id})`, async () => {
191
+ const body = await this.put(`/${plural}/${encodeURIComponent(String(id))}.json`, {
192
+ [type]: attrs,
193
+ });
194
+ return extractResource(body, type, `update(${plural}/${id})`);
195
+ });
196
+ }
197
+ async deleteOne(type, id) {
198
+ const plural = RESOURCE_PLURAL[type];
199
+ await this.mustSucceed(`delete(${plural}/${id})`, async () => {
200
+ await this.delete(`/${plural}/${encodeURIComponent(String(id))}.json`);
201
+ return null;
202
+ });
203
+ }
204
+ async mustSucceed(operation, fn) {
205
+ try {
206
+ return await fn();
207
+ }
208
+ catch (err) {
209
+ if (err instanceof errors_1.ZendeskApiError)
210
+ throw err;
211
+ const status = err?.status;
212
+ const body = err?.response?.body;
213
+ throw new errors_1.ZendeskApiError(operation, status, body, err);
214
+ }
215
+ }
216
+ get(path, query) {
217
+ const request = superagent_1.default.get(`${this.baseUrl}${path}`);
218
+ if (query)
219
+ request.query(query);
220
+ return this.send(request);
221
+ }
222
+ post(path, body) {
223
+ return this.send(superagent_1.default.post(`${this.baseUrl}${path}`).send(body));
224
+ }
225
+ put(path, body) {
226
+ return this.send(superagent_1.default.put(`${this.baseUrl}${path}`).send(body));
227
+ }
228
+ delete(path) {
229
+ return this.send(superagent_1.default.delete(`${this.baseUrl}${path}`));
230
+ }
231
+ async send(request) {
232
+ const response = await request
233
+ .set('Authorization', this.authHeader)
234
+ .set('Accept', 'application/json')
235
+ .set('Content-Type', 'application/json');
236
+ return (response.body ?? {});
237
+ }
238
+ }
239
+ exports.ZendeskHttpClient = ZendeskHttpClient;
240
+ function createZendeskClient(options, logger) {
241
+ return new ZendeskHttpClient(options, logger);
242
+ }
243
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQTRYQSxrREFFQztBQXBYRCw0REFBb0M7QUFFcEMsc0NBQXVFO0FBRTFELFFBQUEsWUFBWSxHQUFHLEdBQUcsQ0FBQztBQUNuQixRQUFBLGlCQUFpQixHQUFHLElBQUksQ0FBQztBQUV0QyxNQUFNLGVBQWUsR0FBb0M7SUFDdkQsTUFBTSxFQUFFLFNBQVM7SUFDakIsSUFBSSxFQUFFLE9BQU87SUFDYixZQUFZLEVBQUUsZUFBZTtDQUM5QixDQUFDO0FBNENGLFNBQVMsWUFBWSxDQUFDLElBQTRCLEVBQUUsS0FBYTtJQUMvRCxNQUFNLEtBQUssR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsUUFBUSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBYSxDQUFDO0lBRS9GLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBRUQsU0FBUyxlQUFlLENBQ3RCLElBQW1CLEVBQ25CLEdBQW9CLEVBQ3BCLFNBQWlCO0lBRWpCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUUzQixJQUFJLFFBQVEsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7UUFDekUsT0FBTyxRQUF5QixDQUFDO0lBQ25DLENBQUM7SUFFRCxNQUFNLElBQUksd0JBQWUsQ0FDdkIsU0FBUyxFQUNULFNBQVMsRUFDVCxJQUFJLEVBQ0osSUFBSSxLQUFLLENBQUMsMkRBQTJELEdBQUcsSUFBSSxDQUFDLENB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
@@ -0,0 +1,16 @@
1
+ import type { ZendeskClient } from './client';
2
+ import type { ZendeskClientOptions } from './types';
3
+ import type { Logger } from '@forestadmin/datasource-toolkit';
4
+ /**
5
+ * A Zendesk client can always be supplied directly, or built on-the-fly from raw
6
+ * credentials. The datasource factory and the smart-action plugins share this
7
+ * contract so users never have to instantiate a client by hand when they don't
8
+ * already have one.
9
+ */
10
+ export type ZendeskClientProvider = {
11
+ client: ZendeskClient;
12
+ } | ({
13
+ client?: never;
14
+ } & ZendeskClientOptions);
15
+ export declare function resolveZendeskClient(options: ZendeskClientProvider, logger?: Logger): ZendeskClient;
16
+ //# sourceMappingURL=client-options.d.ts.map
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveZendeskClient = resolveZendeskClient;
4
+ const client_1 = require("./client");
5
+ function resolveZendeskClient(options, logger) {
6
+ return 'client' in options && options.client
7
+ ? options.client
8
+ : (0, client_1.createZendeskClient)(options, logger);
9
+ }
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LW9wdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2xpZW50LW9wdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFnQkEsb0RBT0M7QUFuQkQscUNBQStDO0FBWS9DLFNBQWdCLG9CQUFvQixDQUNsQyxPQUE4QixFQUM5QixNQUFlO0lBRWYsT0FBTyxRQUFRLElBQUksT0FBTyxJQUFJLE9BQU8sQ0FBQyxNQUFNO1FBQzFDLENBQUMsQ0FBQyxPQUFPLENBQUMsTUFBTTtRQUNoQixDQUFDLENBQUMsSUFBQSw0QkFBbUIsRUFBQyxPQUErQixFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBQ25FLENBQUMifQ==
@@ -0,0 +1,52 @@
1
+ import type { ZendeskClient } from '../client';
2
+ import type { CustomFieldEntry, ZendeskRecord, ZendeskResource } from '../types';
3
+ import type { AggregateResult, Aggregation, Caller, ConditionTree, DataSource, Filter, Logger, PaginatedFilter, Projection, RecordData, Sort } from '@forestadmin/datasource-toolkit';
4
+ import { BaseCollection } from '@forestadmin/datasource-toolkit';
5
+ export { BOOLEAN_OPS, DATE_OPS, ID_OPS, NUMBER_OPS, STRING_OPS } from '../query/operators';
6
+ export default abstract class BaseZendeskCollection extends BaseCollection {
7
+ protected readonly client: ZendeskClient;
8
+ protected readonly resource: ZendeskResource;
9
+ protected readonly sortableFields: Record<string, string>;
10
+ protected readonly logger?: Logger;
11
+ protected readonly zendeskIdToColumnName: Map<number, string>;
12
+ constructor(name: string, datasource: DataSource, client: ZendeskClient, resource: ZendeskResource, sortableFields: Record<string, string>, logger?: Logger);
13
+ abstract create(caller: Caller, data: RecordData[]): Promise<RecordData[]>;
14
+ abstract list(caller: Caller, filter: PaginatedFilter, projection: Projection): Promise<RecordData[]>;
15
+ abstract update(caller: Caller, filter: Filter, patch: RecordData): Promise<void>;
16
+ abstract delete(caller: Caller, filter: Filter): Promise<void>;
17
+ aggregate(caller: Caller, filter: Filter, aggregation: Aggregation, limit?: number): Promise<AggregateResult[]>;
18
+ /** Fetch a single resource by primary key, or null when it does not exist (404). */
19
+ protected abstract findOne(id: number | string): Promise<ZendeskRecord | null>;
20
+ /** Convert a raw Zendesk payload into the Forest column layout this collection exposes. */
21
+ protected abstract serializeRecord(record: ZendeskRecord): RecordData;
22
+ /**
23
+ * Count the records matching the filter. An exact id-lookup is resolved by fetching the
24
+ * referenced records and re-applying the full condition tree in memory, so that non-existent
25
+ * ids and sibling conditions (scope/segment) are honored rather than blindly trusted.
26
+ */
27
+ protected aggregateCount(caller: Caller, filter: Filter): Promise<number>;
28
+ protected addCustomFields(customFields: CustomFieldEntry[]): void;
29
+ protected translateSort(sort: Sort | undefined): {
30
+ sortBy?: string;
31
+ sortOrder?: 'asc' | 'desc';
32
+ };
33
+ protected searchRecords(filter: PaginatedFilter | undefined): Promise<ZendeskRecord[]>;
34
+ protected fetchRecordsByIds(ids: number[]): Promise<ZendeskRecord[]>;
35
+ protected serializeForFilter(records: ZendeskRecord[]): Promise<RecordData[]>;
36
+ protected refine(records: RecordData[], filter: PaginatedFilter | undefined, timezone: string): RecordData[];
37
+ protected buildZendeskQuery(filter: Filter | PaginatedFilter | undefined): string;
38
+ /**
39
+ * Returns the list of primary-key values if the condition tree is an exact `id`
40
+ * lookup (Equal N or In [N1, N2, ...]), otherwise null. Lets `list/update/delete`
41
+ * bypass the Search API and hit the resource endpoint directly.
42
+ */
43
+ protected extractIdLookup(tree: ConditionTree | undefined): number[] | null;
44
+ /**
45
+ * Resolve the primary keys targeted by update/delete. A pure id-lookup is authoritative; an
46
+ * id-lookup combined with other conditions (scope/segment) is re-checked in memory so we never
47
+ * mutate a record outside the caller's perimeter. Otherwise every matching page is collected.
48
+ */
49
+ protected resolveIds(filter: Filter | undefined, timezone: string): Promise<number[]>;
50
+ private searchAllIds;
51
+ }
52
+ //# sourceMappingURL=base-zendesk-collection.d.ts.map