@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.
- package/README.md +162 -0
- package/dist/client/index.d.ts +91 -0
- package/dist/client/index.js +243 -0
- package/dist/client-options.d.ts +16 -0
- package/dist/client-options.js +10 -0
- package/dist/collections/base-zendesk-collection.d.ts +52 -0
- package/dist/collections/base-zendesk-collection.js +203 -0
- package/dist/collections/organization-collection.d.ts +15 -0
- package/dist/collections/organization-collection.js +145 -0
- package/dist/collections/searchable-collection.d.ts +6 -0
- package/dist/collections/searchable-collection.js +20 -0
- package/dist/collections/ticket/comments-embedder.d.ts +5 -0
- package/dist/collections/ticket/comments-embedder.js +40 -0
- package/dist/collections/ticket/relation-embedder.d.ts +10 -0
- package/dist/collections/ticket/relation-embedder.js +62 -0
- package/dist/collections/ticket/schema-definition.d.ts +13 -0
- package/dist/collections/ticket/schema-definition.js +147 -0
- package/dist/collections/ticket/serializer.d.ts +15 -0
- package/dist/collections/ticket/serializer.js +96 -0
- package/dist/collections/ticket-collection.d.ts +18 -0
- package/dist/collections/ticket-collection.js +139 -0
- package/dist/collections/user-collection.d.ts +15 -0
- package/dist/collections/user-collection.js +162 -0
- package/dist/datasource.d.ts +16 -0
- package/dist/datasource.js +49 -0
- package/dist/enums.d.ts +10 -0
- package/dist/enums.js +12 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +31 -0
- package/dist/factory.d.ts +5 -0
- package/dist/factory.js +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +30 -0
- package/dist/plugins/close-ticket/errors.d.ts +7 -0
- package/dist/plugins/close-ticket/errors.js +44 -0
- package/dist/plugins/close-ticket/index.d.ts +13 -0
- package/dist/plugins/close-ticket/index.js +74 -0
- package/dist/plugins/close-ticket/messages.d.ts +12 -0
- package/dist/plugins/close-ticket/messages.js +33 -0
- package/dist/plugins/create-ticket-with-notification/form-builder.d.ts +30 -0
- package/dist/plugins/create-ticket-with-notification/form-builder.js +179 -0
- package/dist/plugins/create-ticket-with-notification/index.d.ts +19 -0
- package/dist/plugins/create-ticket-with-notification/index.js +81 -0
- package/dist/query/condition-tree-translator.d.ts +9 -0
- package/dist/query/condition-tree-translator.js +90 -0
- package/dist/query/operators.d.ts +15 -0
- package/dist/query/operators.js +34 -0
- package/dist/schema/custom-fields-introspector.d.ts +13 -0
- package/dist/schema/custom-fields-introspector.js +89 -0
- package/dist/types.d.ts +23 -0
- package/dist/types.js +3 -0
- package/package.json +33 -0
|
@@ -0,0 +1,49 @@
|
|
|
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.COLLECTION_NAMES = void 0;
|
|
7
|
+
const datasource_toolkit_1 = require("@forestadmin/datasource-toolkit");
|
|
8
|
+
const organization_collection_1 = __importDefault(require("./collections/organization-collection"));
|
|
9
|
+
const ticket_collection_1 = __importDefault(require("./collections/ticket-collection"));
|
|
10
|
+
const user_collection_1 = __importDefault(require("./collections/user-collection"));
|
|
11
|
+
const custom_fields_introspector_1 = require("./schema/custom-fields-introspector");
|
|
12
|
+
exports.COLLECTION_NAMES = {
|
|
13
|
+
ticket: 'zendesk_ticket',
|
|
14
|
+
user: 'zendesk_user',
|
|
15
|
+
organization: 'zendesk_organization',
|
|
16
|
+
};
|
|
17
|
+
function buildCustomFieldMapping(ticketFields, userFields, organizationFields) {
|
|
18
|
+
const mapping = new Map();
|
|
19
|
+
for (const field of ticketFields) {
|
|
20
|
+
mapping.set(field.columnName, `custom_field_${field.zendeskId}`);
|
|
21
|
+
}
|
|
22
|
+
for (const field of [...userFields, ...organizationFields]) {
|
|
23
|
+
if (field.zendeskKey && !mapping.has(field.columnName)) {
|
|
24
|
+
mapping.set(field.columnName, field.zendeskKey);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return mapping;
|
|
28
|
+
}
|
|
29
|
+
class ZendeskDataSource extends datasource_toolkit_1.BaseDataSource {
|
|
30
|
+
static async create(client, logger) {
|
|
31
|
+
const introspector = new custom_fields_introspector_1.CustomFieldsIntrospector(client, logger);
|
|
32
|
+
const [ticketFields, userFields, organizationFields] = await Promise.all([
|
|
33
|
+
introspector.ticketCustomFields(),
|
|
34
|
+
introspector.userCustomFields(),
|
|
35
|
+
introspector.organizationCustomFields(),
|
|
36
|
+
]);
|
|
37
|
+
return new ZendeskDataSource(client, ticketFields, userFields, organizationFields, logger);
|
|
38
|
+
}
|
|
39
|
+
constructor(client, ticketFields, userFields, organizationFields, logger) {
|
|
40
|
+
super();
|
|
41
|
+
this.client = client;
|
|
42
|
+
this.customFieldMapping = buildCustomFieldMapping(ticketFields, userFields, organizationFields);
|
|
43
|
+
this.addCollection(new ticket_collection_1.default(this, client, ticketFields, logger));
|
|
44
|
+
this.addCollection(new user_collection_1.default(this, client, userFields, logger));
|
|
45
|
+
this.addCollection(new organization_collection_1.default(this, client, organizationFields, logger));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.default = ZendeskDataSource;
|
|
49
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YXNvdXJjZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9kYXRhc291cmNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUlBLHdFQUFpRTtBQUVqRSxvR0FBMkU7QUFDM0Usd0ZBQStEO0FBQy9ELG9GQUEyRDtBQUMzRCxvRkFBK0U7QUFFbEUsUUFBQSxnQkFBZ0IsR0FBRztJQUM5QixNQUFNLEVBQUUsZ0JBQWdCO0lBQ3hCLElBQUksRUFBRSxjQUFjO0lBQ3BCLFlBQVksRUFBRSxzQkFBc0I7Q0FDNUIsQ0FBQztBQUVYLFNBQVMsdUJBQXVCLENBQzlCLFlBQWdDLEVBQ2hDLFVBQThCLEVBQzlCLGtCQUFzQztJQUV0QyxNQUFNLE9BQU8sR0FBdUIsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUU5QyxLQUFLLE1BQU0sS0FBSyxJQUFJLFlBQVksRUFBRSxDQUFDO1FBQ2pDLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxnQkFBZ0IsS0FBSyxDQUFDLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDbkUsQ0FBQztJQUVELEtBQUssTUFBTSxLQUFLLElBQUksQ0FBQyxHQUFHLFVBQVUsRUFBRSxHQUFHLGtCQUFrQixDQUFDLEVBQUUsQ0FBQztRQUMzRCxJQUFJLEtBQUssQ0FBQyxVQUFVLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQ3ZELE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEQsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDO0FBRUQsTUFBcUIsaUJBQWtCLFNBQVEsbUNBQWM7SUFJM0QsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsTUFBcUIsRUFBRSxNQUFlO1FBQ3hELE1BQU0sWUFBWSxHQUFHLElBQUkscURBQXdCLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ2xFLE1BQU0sQ0FBQyxZQUFZLEVBQUUsVUFBVSxFQUFFLGtCQUFrQixDQUFDLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDO1lBQ3ZFLFlBQVksQ0FBQyxrQkFBa0IsRUFBRTtZQUNqQyxZQUFZLENBQUMsZ0JBQWdCLEVBQUU7WUFDL0IsWUFBWSxDQUFDLHdCQUF3QixFQUFFO1NBQ3hDLENBQUMsQ0FBQztRQUVILE9BQU8sSUFBSSxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUM3RixDQUFDO0lBRUQsWUFDRSxNQUFxQixFQUNyQixZQUFnQyxFQUNoQyxVQUE4QixFQUM5QixrQkFBc0MsRUFDdEMsTUFBZTtRQUVmLEtBQUssRUFBRSxDQUFDO1FBQ1IsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLGtCQUFrQixHQUFHLHVCQUF1QixDQUFDLFlBQVksRUFBRSxVQUFVLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUVoRyxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksMkJBQWdCLENBQUMsSUFBSSxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUM3RSxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUkseUJBQWMsQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ3pFLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxpQ0FBc0IsQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLGtCQUFrQixFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDM0YsQ0FBQztDQUNGO0FBOUJELG9DQThCQyJ9
|
package/dist/enums.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const TICKET_STATUSES: readonly ["new", "open", "pending", "hold", "solved", "closed"];
|
|
2
|
+
export declare const TICKET_PRIORITIES: readonly ["low", "normal", "high", "urgent"];
|
|
3
|
+
export declare const TICKET_TYPES: readonly ["problem", "incident", "question", "task"];
|
|
4
|
+
export declare const USER_ROLES: readonly ["end-user", "agent", "admin"];
|
|
5
|
+
export type TicketStatus = (typeof TICKET_STATUSES)[number];
|
|
6
|
+
export type TicketPriority = (typeof TICKET_PRIORITIES)[number];
|
|
7
|
+
export type TicketType = (typeof TICKET_TYPES)[number];
|
|
8
|
+
export type UserRole = (typeof USER_ROLES)[number];
|
|
9
|
+
export declare const CLOSEABLE_STATUSES: ReadonlyArray<Extract<TicketStatus, 'solved' | 'closed'>>;
|
|
10
|
+
//# sourceMappingURL=enums.d.ts.map
|
package/dist/enums.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CLOSEABLE_STATUSES = exports.USER_ROLES = exports.TICKET_TYPES = exports.TICKET_PRIORITIES = exports.TICKET_STATUSES = void 0;
|
|
4
|
+
exports.TICKET_STATUSES = ['new', 'open', 'pending', 'hold', 'solved', 'closed'];
|
|
5
|
+
exports.TICKET_PRIORITIES = ['low', 'normal', 'high', 'urgent'];
|
|
6
|
+
exports.TICKET_TYPES = ['problem', 'incident', 'question', 'task'];
|
|
7
|
+
exports.USER_ROLES = ['end-user', 'agent', 'admin'];
|
|
8
|
+
exports.CLOSEABLE_STATUSES = [
|
|
9
|
+
'solved',
|
|
10
|
+
'closed',
|
|
11
|
+
];
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW51bXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZW51bXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQWEsUUFBQSxlQUFlLEdBQUcsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLFFBQVEsQ0FBVSxDQUFDO0FBQ2xGLFFBQUEsaUJBQWlCLEdBQUcsQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxRQUFRLENBQVUsQ0FBQztBQUNqRSxRQUFBLFlBQVksR0FBRyxDQUFDLFNBQVMsRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLE1BQU0sQ0FBVSxDQUFDO0FBQ3BFLFFBQUEsVUFBVSxHQUFHLENBQUMsVUFBVSxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQVUsQ0FBQztBQU9yRCxRQUFBLGtCQUFrQixHQUE4RDtJQUMzRixRQUFRO0lBQ1IsUUFBUTtDQUNULENBQUMifQ==
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BusinessError, ValidationError } from '@forestadmin/datasource-toolkit';
|
|
2
|
+
export declare class ZendeskConfigurationError extends ValidationError {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class ZendeskApiError extends BusinessError {
|
|
6
|
+
readonly operation: string;
|
|
7
|
+
readonly status?: number;
|
|
8
|
+
readonly body?: unknown;
|
|
9
|
+
constructor(operation: string, status: number | undefined, body: unknown, cause?: Error);
|
|
10
|
+
}
|
|
11
|
+
export declare class UnsupportedOperatorError extends BusinessError {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnsupportedOperatorError = exports.ZendeskApiError = exports.ZendeskConfigurationError = void 0;
|
|
4
|
+
/* eslint-disable max-classes-per-file */
|
|
5
|
+
const datasource_toolkit_1 = require("@forestadmin/datasource-toolkit");
|
|
6
|
+
class ZendeskConfigurationError extends datasource_toolkit_1.ValidationError {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = 'ZendeskConfigurationError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.ZendeskConfigurationError = ZendeskConfigurationError;
|
|
13
|
+
class ZendeskApiError extends datasource_toolkit_1.BusinessError {
|
|
14
|
+
constructor(operation, status, body, cause) {
|
|
15
|
+
const reason = status ? `HTTP ${status}` : cause?.message ?? 'unknown error';
|
|
16
|
+
super(`Zendesk API call failed (${operation}): ${reason}`);
|
|
17
|
+
this.name = 'ZendeskApiError';
|
|
18
|
+
this.operation = operation;
|
|
19
|
+
this.status = status;
|
|
20
|
+
this.body = body;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.ZendeskApiError = ZendeskApiError;
|
|
24
|
+
class UnsupportedOperatorError extends datasource_toolkit_1.BusinessError {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'UnsupportedOperatorError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.UnsupportedOperatorError = UnsupportedOperatorError;
|
|
31
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx5Q0FBeUM7QUFDekMsd0VBQWlGO0FBRWpGLE1BQWEseUJBQTBCLFNBQVEsb0NBQWU7SUFDNUQsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNmLElBQUksQ0FBQyxJQUFJLEdBQUcsMkJBQTJCLENBQUM7SUFDMUMsQ0FBQztDQUNGO0FBTEQsOERBS0M7QUFFRCxNQUFhLGVBQWdCLFNBQVEsa0NBQWE7SUFLaEQsWUFBWSxTQUFpQixFQUFFLE1BQTBCLEVBQUUsSUFBYSxFQUFFLEtBQWE7UUFDckYsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxRQUFRLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxJQUFJLGVBQWUsQ0FBQztRQUM3RSxLQUFLLENBQUMsNEJBQTRCLFNBQVMsTUFBTSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQzNELElBQUksQ0FBQyxJQUFJLEdBQUcsaUJBQWlCLENBQUM7UUFDOUIsSUFBSSxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUM7UUFDM0IsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7SUFDbkIsQ0FBQztDQUNGO0FBYkQsMENBYUM7QUFFRCxNQUFhLHdCQUF5QixTQUFRLGtDQUFhO0lBQ3pELFlBQVksT0FBZTtRQUN6QixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDZixJQUFJLENBQUMsSUFBSSxHQUFHLDBCQUEwQixDQUFDO0lBQ3pDLENBQUM7Q0FDRjtBQUxELDREQUtDIn0=
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ZendeskClientProvider } from './client-options';
|
|
2
|
+
import type { DataSourceFactory } from '@forestadmin/datasource-toolkit';
|
|
3
|
+
export type ZendeskDataSourceOptions = ZendeskClientProvider;
|
|
4
|
+
export declare function createZendeskDataSource(options: ZendeskDataSourceOptions): DataSourceFactory;
|
|
5
|
+
//# sourceMappingURL=factory.d.ts.map
|
package/dist/factory.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
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.createZendeskDataSource = createZendeskDataSource;
|
|
7
|
+
const client_options_1 = require("./client-options");
|
|
8
|
+
const datasource_1 = __importDefault(require("./datasource"));
|
|
9
|
+
function createZendeskDataSource(options) {
|
|
10
|
+
return async (logger) => {
|
|
11
|
+
const client = (0, client_options_1.resolveZendeskClient)(options, logger);
|
|
12
|
+
return datasource_1.default.create(client, logger);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9mYWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBUUEsMERBTUM7QUFYRCxxREFBd0Q7QUFDeEQsOERBQTZDO0FBSTdDLFNBQWdCLHVCQUF1QixDQUFDLE9BQWlDO0lBQ3ZFLE9BQU8sS0FBSyxFQUFFLE1BQU0sRUFBdUIsRUFBRTtRQUMzQyxNQUFNLE1BQU0sR0FBRyxJQUFBLHFDQUFvQixFQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUVyRCxPQUFPLG9CQUFpQixDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDbEQsQ0FBQyxDQUFDO0FBQ0osQ0FBQyJ9
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { createZendeskDataSource } from './factory';
|
|
2
|
+
export type { ZendeskDataSourceOptions } from './factory';
|
|
3
|
+
export { default as ZendeskDataSource, COLLECTION_NAMES } from './datasource';
|
|
4
|
+
export { createZendeskClient, ZendeskHttpClient, MAX_PER_PAGE, MAX_TOTAL_RESULTS } from './client';
|
|
5
|
+
export type { ZendeskClient, RawCustomFieldDefinition } from './client';
|
|
6
|
+
export type { ZendeskClientProvider } from './client-options';
|
|
7
|
+
export { closeTicketPlugin } from './plugins/close-ticket';
|
|
8
|
+
export type { CloseTicketOptions } from './plugins/close-ticket';
|
|
9
|
+
export { createTicketWithNotificationPlugin } from './plugins/create-ticket-with-notification';
|
|
10
|
+
export type { CreateTicketWithNotificationOptions, EmailTemplate, } from './plugins/create-ticket-with-notification';
|
|
11
|
+
export { TICKET_PRIORITIES, TICKET_STATUSES, TICKET_TYPES, USER_ROLES } from './enums';
|
|
12
|
+
export type { TicketPriority, TicketStatus, TicketType, UserRole } from './enums';
|
|
13
|
+
export type { CustomFieldEntry, CustomFieldMapping, SearchParams, ZendeskClientOptions, ZendeskRecord, ZendeskResource, } from './types';
|
|
14
|
+
export { UnsupportedOperatorError, ZendeskApiError, ZendeskConfigurationError } from './errors';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
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.ZendeskConfigurationError = exports.ZendeskApiError = exports.UnsupportedOperatorError = exports.USER_ROLES = exports.TICKET_TYPES = exports.TICKET_STATUSES = exports.TICKET_PRIORITIES = exports.createTicketWithNotificationPlugin = exports.closeTicketPlugin = exports.MAX_TOTAL_RESULTS = exports.MAX_PER_PAGE = exports.ZendeskHttpClient = exports.createZendeskClient = exports.COLLECTION_NAMES = exports.ZendeskDataSource = exports.createZendeskDataSource = void 0;
|
|
7
|
+
var factory_1 = require("./factory");
|
|
8
|
+
Object.defineProperty(exports, "createZendeskDataSource", { enumerable: true, get: function () { return factory_1.createZendeskDataSource; } });
|
|
9
|
+
var datasource_1 = require("./datasource");
|
|
10
|
+
Object.defineProperty(exports, "ZendeskDataSource", { enumerable: true, get: function () { return __importDefault(datasource_1).default; } });
|
|
11
|
+
Object.defineProperty(exports, "COLLECTION_NAMES", { enumerable: true, get: function () { return datasource_1.COLLECTION_NAMES; } });
|
|
12
|
+
var client_1 = require("./client");
|
|
13
|
+
Object.defineProperty(exports, "createZendeskClient", { enumerable: true, get: function () { return client_1.createZendeskClient; } });
|
|
14
|
+
Object.defineProperty(exports, "ZendeskHttpClient", { enumerable: true, get: function () { return client_1.ZendeskHttpClient; } });
|
|
15
|
+
Object.defineProperty(exports, "MAX_PER_PAGE", { enumerable: true, get: function () { return client_1.MAX_PER_PAGE; } });
|
|
16
|
+
Object.defineProperty(exports, "MAX_TOTAL_RESULTS", { enumerable: true, get: function () { return client_1.MAX_TOTAL_RESULTS; } });
|
|
17
|
+
var close_ticket_1 = require("./plugins/close-ticket");
|
|
18
|
+
Object.defineProperty(exports, "closeTicketPlugin", { enumerable: true, get: function () { return close_ticket_1.closeTicketPlugin; } });
|
|
19
|
+
var create_ticket_with_notification_1 = require("./plugins/create-ticket-with-notification");
|
|
20
|
+
Object.defineProperty(exports, "createTicketWithNotificationPlugin", { enumerable: true, get: function () { return create_ticket_with_notification_1.createTicketWithNotificationPlugin; } });
|
|
21
|
+
var enums_1 = require("./enums");
|
|
22
|
+
Object.defineProperty(exports, "TICKET_PRIORITIES", { enumerable: true, get: function () { return enums_1.TICKET_PRIORITIES; } });
|
|
23
|
+
Object.defineProperty(exports, "TICKET_STATUSES", { enumerable: true, get: function () { return enums_1.TICKET_STATUSES; } });
|
|
24
|
+
Object.defineProperty(exports, "TICKET_TYPES", { enumerable: true, get: function () { return enums_1.TICKET_TYPES; } });
|
|
25
|
+
Object.defineProperty(exports, "USER_ROLES", { enumerable: true, get: function () { return enums_1.USER_ROLES; } });
|
|
26
|
+
var errors_1 = require("./errors");
|
|
27
|
+
Object.defineProperty(exports, "UnsupportedOperatorError", { enumerable: true, get: function () { return errors_1.UnsupportedOperatorError; } });
|
|
28
|
+
Object.defineProperty(exports, "ZendeskApiError", { enumerable: true, get: function () { return errors_1.ZendeskApiError; } });
|
|
29
|
+
Object.defineProperty(exports, "ZendeskConfigurationError", { enumerable: true, get: function () { return errors_1.ZendeskConfigurationError; } });
|
|
30
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEscUNBQW9EO0FBQTNDLGtIQUFBLHVCQUF1QixPQUFBO0FBRWhDLDJDQUE4RTtBQUFyRSxnSUFBQSxPQUFPLE9BQXFCO0FBQUUsOEdBQUEsZ0JBQWdCLE9BQUE7QUFDdkQsbUNBQW1HO0FBQTFGLDZHQUFBLG1CQUFtQixPQUFBO0FBQUUsMkdBQUEsaUJBQWlCLE9BQUE7QUFBRSxzR0FBQSxZQUFZLE9BQUE7QUFBRSwyR0FBQSxpQkFBaUIsT0FBQTtBQUdoRix1REFBMkQ7QUFBbEQsaUhBQUEsaUJBQWlCLE9BQUE7QUFFMUIsNkZBQStGO0FBQXRGLHFKQUFBLGtDQUFrQyxPQUFBO0FBSzNDLGlDQUF1RjtBQUE5RSwwR0FBQSxpQkFBaUIsT0FBQTtBQUFFLHdHQUFBLGVBQWUsT0FBQTtBQUFFLHFHQUFBLFlBQVksT0FBQTtBQUFFLG1HQUFBLFVBQVUsT0FBQTtBQVVyRSxtQ0FBZ0c7QUFBdkYsa0hBQUEsd0JBQXdCLE9BQUE7QUFBRSx5R0FBQSxlQUFlLE9BQUE7QUFBRSxtSEFBQSx5QkFBeUIsT0FBQSJ9
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zendesk surfaces "you cannot edit a closed ticket" as a 422 with a status-field
|
|
3
|
+
* details entry containing the phrase below. We need to peel back the API error
|
|
4
|
+
* envelope to detect it and report those tickets as already closed rather than failed.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isAlreadyClosedError(error: unknown): boolean;
|
|
7
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isAlreadyClosedError = isAlreadyClosedError;
|
|
4
|
+
const errors_1 = require("../../errors");
|
|
5
|
+
const ALREADY_CLOSED_PATTERN = /closed prevents ticket update/i;
|
|
6
|
+
function hasAlreadyClosedSignature(body) {
|
|
7
|
+
if (!body || typeof body !== 'object')
|
|
8
|
+
return false;
|
|
9
|
+
const { details } = body;
|
|
10
|
+
if (details && typeof details === 'object') {
|
|
11
|
+
const statusErrors = details.status;
|
|
12
|
+
if (Array.isArray(statusErrors)) {
|
|
13
|
+
for (const item of statusErrors) {
|
|
14
|
+
if (item && typeof item === 'object') {
|
|
15
|
+
const { description } = item;
|
|
16
|
+
if (typeof description === 'string' && ALREADY_CLOSED_PATTERN.test(description)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const { description } = body;
|
|
24
|
+
if (typeof description === 'string' && ALREADY_CLOSED_PATTERN.test(description))
|
|
25
|
+
return true;
|
|
26
|
+
const { error } = body;
|
|
27
|
+
if (typeof error === 'string' && ALREADY_CLOSED_PATTERN.test(error))
|
|
28
|
+
return true;
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Zendesk surfaces "you cannot edit a closed ticket" as a 422 with a status-field
|
|
33
|
+
* details entry containing the phrase below. We need to peel back the API error
|
|
34
|
+
* envelope to detect it and report those tickets as already closed rather than failed.
|
|
35
|
+
*/
|
|
36
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
37
|
+
function isAlreadyClosedError(error) {
|
|
38
|
+
if (!(error instanceof errors_1.ZendeskApiError))
|
|
39
|
+
return false;
|
|
40
|
+
if (error.status !== 422)
|
|
41
|
+
return false;
|
|
42
|
+
return hasAlreadyClosedSignature(error.body);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3BsdWdpbnMvY2xvc2UtdGlja2V0L2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQXdDQSxvREFLQztBQTdDRCx5Q0FBK0M7QUFFL0MsTUFBTSxzQkFBc0IsR0FBRyxnQ0FBZ0MsQ0FBQztBQUVoRSxTQUFTLHlCQUF5QixDQUFDLElBQWE7SUFDOUMsSUFBSSxDQUFDLElBQUksSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFFcEQsTUFBTSxFQUFFLE9BQU8sRUFBRSxHQUFHLElBQTZCLENBQUM7SUFFbEQsSUFBSSxPQUFPLElBQUksT0FBTyxPQUFPLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDM0MsTUFBTSxZQUFZLEdBQUksT0FBZ0MsQ0FBQyxNQUFNLENBQUM7UUFFOUQsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUM7WUFDaEMsS0FBSyxNQUFNLElBQUksSUFBSSxZQUFZLEVBQUUsQ0FBQztnQkFDaEMsSUFBSSxJQUFJLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxFQUFFLENBQUM7b0JBQ3JDLE1BQU0sRUFBRSxXQUFXLEVBQUUsR0FBRyxJQUFpQyxDQUFDO29CQUUxRCxJQUFJLE9BQU8sV0FBVyxLQUFLLFFBQVEsSUFBSSxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQzt3QkFDaEYsT0FBTyxJQUFJLENBQUM7b0JBQ2QsQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQsTUFBTSxFQUFFLFdBQVcsRUFBRSxHQUFHLElBQWlDLENBQUM7SUFDMUQsSUFBSSxPQUFPLFdBQVcsS0FBSyxRQUFRLElBQUksc0JBQXNCLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRTdGLE1BQU0sRUFBRSxLQUFLLEVBQUUsR0FBRyxJQUEyQixDQUFDO0lBQzlDLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLHNCQUFzQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUVqRixPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsd0RBQXdEO0FBQ3hELFNBQWdCLG9CQUFvQixDQUFDLEtBQWM7SUFDakQsSUFBSSxDQUFDLENBQUMsS0FBSyxZQUFZLHdCQUFlLENBQUM7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUN0RCxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssR0FBRztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBRXZDLE9BQU8seUJBQXlCLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQy9DLENBQUMifQ==
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CloseTicketOutcome, CloseTicketStatus } from './messages';
|
|
2
|
+
import type { ZendeskClient } from '../../client';
|
|
3
|
+
import type { ZendeskClientProvider } from '../../client-options';
|
|
4
|
+
import type { Plugin } from '@forestadmin/datasource-customizer';
|
|
5
|
+
import type { ActionScope } from '@forestadmin/datasource-toolkit';
|
|
6
|
+
export type CloseTicketOptions = {
|
|
7
|
+
ticketIdField: string;
|
|
8
|
+
statuses?: ReadonlyArray<CloseTicketStatus>;
|
|
9
|
+
scopes?: ReadonlyArray<Extract<ActionScope, 'Single' | 'Bulk'>>;
|
|
10
|
+
} & ZendeskClientProvider;
|
|
11
|
+
export declare function closeTickets(client: ZendeskClient, ids: Array<number | string>, status: CloseTicketStatus): Promise<CloseTicketOutcome>;
|
|
12
|
+
export declare const closeTicketPlugin: Plugin<CloseTicketOptions>;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.closeTicketPlugin = void 0;
|
|
4
|
+
exports.closeTickets = closeTickets;
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const messages_1 = require("./messages");
|
|
7
|
+
const client_options_1 = require("../../client-options");
|
|
8
|
+
const enums_1 = require("../../enums");
|
|
9
|
+
const DEFAULT_SCOPES = ['Single', 'Bulk'];
|
|
10
|
+
function actionLabel(scope, status) {
|
|
11
|
+
return scope === 'Single'
|
|
12
|
+
? `Mark Zendesk ticket as ${status}`
|
|
13
|
+
: `Mark selected Zendesk tickets as ${status}`;
|
|
14
|
+
}
|
|
15
|
+
async function closeTickets(client, ids, status) {
|
|
16
|
+
const outcome = {
|
|
17
|
+
succeeded: [],
|
|
18
|
+
alreadyClosed: [],
|
|
19
|
+
failed: [],
|
|
20
|
+
};
|
|
21
|
+
for (const id of ids) {
|
|
22
|
+
try {
|
|
23
|
+
// eslint-disable-next-line no-await-in-loop
|
|
24
|
+
await client.updateTicket(id, { status });
|
|
25
|
+
outcome.succeeded.push(id);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if ((0, errors_1.isAlreadyClosedError)(error)) {
|
|
29
|
+
outcome.alreadyClosed.push(id);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
outcome.failed.push({
|
|
33
|
+
id,
|
|
34
|
+
error: error instanceof Error ? error.message : String(error),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return outcome;
|
|
40
|
+
}
|
|
41
|
+
const closeTicketPlugin = async (_dataSource, collection, options, logger) => {
|
|
42
|
+
if (!collection) {
|
|
43
|
+
throw new Error('closeTicketPlugin can only be used on collections.');
|
|
44
|
+
}
|
|
45
|
+
if (!options?.ticketIdField) {
|
|
46
|
+
throw new Error('closeTicketPlugin requires a `ticketIdField` option.');
|
|
47
|
+
}
|
|
48
|
+
const client = (0, client_options_1.resolveZendeskClient)(options, logger);
|
|
49
|
+
const statuses = options.statuses ?? enums_1.CLOSEABLE_STATUSES;
|
|
50
|
+
const scopes = options.scopes ?? DEFAULT_SCOPES;
|
|
51
|
+
for (const status of statuses) {
|
|
52
|
+
for (const scope of scopes) {
|
|
53
|
+
collection.addAction(actionLabel(scope, status), {
|
|
54
|
+
scope,
|
|
55
|
+
execute: async (context, resultBuilder) => {
|
|
56
|
+
const records = (await context.getRecords([options.ticketIdField]));
|
|
57
|
+
const ids = records
|
|
58
|
+
.map(record => record[options.ticketIdField])
|
|
59
|
+
.filter((id) => typeof id === 'number' || (typeof id === 'string' && id.length > 0));
|
|
60
|
+
if (ids.length === 0) {
|
|
61
|
+
return resultBuilder.error('No ticket id available on the selected record(s).');
|
|
62
|
+
}
|
|
63
|
+
const outcome = await closeTickets(client, ids, status);
|
|
64
|
+
if (outcome.succeeded.length === 0 && outcome.alreadyClosed.length === 0) {
|
|
65
|
+
return resultBuilder.error((0, messages_1.buildErrorMessage)(outcome, status));
|
|
66
|
+
}
|
|
67
|
+
return resultBuilder.success((0, messages_1.buildSuccessMessage)(outcome, status));
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
exports.closeTicketPlugin = closeTicketPlugin;
|
|
74
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcGx1Z2lucy9jbG9zZS10aWNrZXQvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBNkJBLG9DQTZCQztBQWhERCxxQ0FBZ0Q7QUFDaEQseUNBQW9FO0FBQ3BFLHlEQUE0RDtBQUM1RCx1Q0FBaUQ7QUFRakQsTUFBTSxjQUFjLEdBQTJELENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBRWxHLFNBQVMsV0FBVyxDQUFDLEtBQWtCLEVBQUUsTUFBeUI7SUFDaEUsT0FBTyxLQUFLLEtBQUssUUFBUTtRQUN2QixDQUFDLENBQUMsMEJBQTBCLE1BQU0sRUFBRTtRQUNwQyxDQUFDLENBQUMsb0NBQW9DLE1BQU0sRUFBRSxDQUFDO0FBQ25ELENBQUM7QUFFTSxLQUFLLFVBQVUsWUFBWSxDQUNoQyxNQUFxQixFQUNyQixHQUEyQixFQUMzQixNQUF5QjtJQUV6QixNQUFNLE9BQU8sR0FBdUI7UUFDbEMsU0FBUyxFQUFFLEVBQUU7UUFDYixhQUFhLEVBQUUsRUFBRTtRQUNqQixNQUFNLEVBQUUsRUFBRTtLQUNYLENBQUM7SUFFRixLQUFLLE1BQU0sRUFBRSxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ3JCLElBQUksQ0FBQztZQUNILDRDQUE0QztZQUM1QyxNQUFNLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxFQUFFLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUMxQyxPQUFPLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUM3QixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksSUFBQSw2QkFBb0IsRUFBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUNoQyxPQUFPLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUNqQyxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sT0FBTyxDQUFD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
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type CloseTicketStatus = 'solved' | 'closed';
|
|
2
|
+
export type CloseTicketOutcome = {
|
|
3
|
+
succeeded: Array<number | string>;
|
|
4
|
+
alreadyClosed: Array<number | string>;
|
|
5
|
+
failed: Array<{
|
|
6
|
+
id: number | string;
|
|
7
|
+
error: string;
|
|
8
|
+
}>;
|
|
9
|
+
};
|
|
10
|
+
export declare function buildSuccessMessage(outcome: CloseTicketOutcome, status: CloseTicketStatus): string;
|
|
11
|
+
export declare function buildErrorMessage(outcome: CloseTicketOutcome, status: CloseTicketStatus): string;
|
|
12
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildSuccessMessage = buildSuccessMessage;
|
|
4
|
+
exports.buildErrorMessage = buildErrorMessage;
|
|
5
|
+
function buildSuccessMessage(outcome, status) {
|
|
6
|
+
const verb = status === 'solved' ? 'solved' : 'closed';
|
|
7
|
+
const parts = [];
|
|
8
|
+
if (outcome.succeeded.length === 1 && outcome.alreadyClosed.length === 0) {
|
|
9
|
+
parts.push(`Ticket #${outcome.succeeded[0]} marked as ${verb}.`);
|
|
10
|
+
}
|
|
11
|
+
else if (outcome.succeeded.length > 0) {
|
|
12
|
+
parts.push(`${outcome.succeeded.length} ticket(s) marked as ${verb}.`);
|
|
13
|
+
}
|
|
14
|
+
if (outcome.alreadyClosed.length > 0) {
|
|
15
|
+
parts.push(`${outcome.alreadyClosed.length} ticket(s) were already closed (Zendesk does not allow editing closed tickets).`);
|
|
16
|
+
}
|
|
17
|
+
if (outcome.failed.length > 0) {
|
|
18
|
+
parts.push(`${outcome.failed.length} ticket(s) failed to update.`);
|
|
19
|
+
}
|
|
20
|
+
return parts.join(' ');
|
|
21
|
+
}
|
|
22
|
+
function buildErrorMessage(outcome, status) {
|
|
23
|
+
const verb = status === 'solved' ? 'solve' : 'close';
|
|
24
|
+
if (outcome.failed.length === 1) {
|
|
25
|
+
const [{ id, error }] = outcome.failed;
|
|
26
|
+
return `Failed to ${verb} ticket #${id}: ${error}`;
|
|
27
|
+
}
|
|
28
|
+
if (outcome.failed.length > 1) {
|
|
29
|
+
return `Failed to ${verb} ${outcome.failed.length} ticket(s).`;
|
|
30
|
+
}
|
|
31
|
+
return `No tickets were ${verb === 'solve' ? 'solved' : 'closed'}.`;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVzc2FnZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcGx1Z2lucy9jbG9zZS10aWNrZXQvbWVzc2FnZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFRQSxrREF3QkM7QUFFRCw4Q0FjQztBQXhDRCxTQUFnQixtQkFBbUIsQ0FDakMsT0FBMkIsRUFDM0IsTUFBeUI7SUFFekIsTUFBTSxJQUFJLEdBQUcsTUFBTSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUM7SUFDdkQsTUFBTSxLQUFLLEdBQWEsRUFBRSxDQUFDO0lBRTNCLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLE9BQU8sQ0FBQyxhQUFhLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3pFLEtBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxjQUFjLElBQUksR0FBRyxDQUFDLENBQUM7SUFDbkUsQ0FBQztTQUFNLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDeEMsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsTUFBTSx3QkFBd0IsSUFBSSxHQUFHLENBQUMsQ0FBQztJQUN6RSxDQUFDO0lBRUQsSUFBSSxPQUFPLENBQUMsYUFBYSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNyQyxLQUFLLENBQUMsSUFBSSxDQUNSLEdBQUcsT0FBTyxDQUFDLGFBQWEsQ0FBQyxNQUFNLGlGQUFpRixDQUNqSCxDQUFDO0lBQ0osQ0FBQztJQUVELElBQUksT0FBTyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDOUIsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFFRCxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7QUFDekIsQ0FBQztBQUVELFNBQWdCLGlCQUFpQixDQUFDLE9BQTJCLEVBQUUsTUFBeUI7SUFDdEYsTUFBTSxJQUFJLEdBQUcsTUFBTSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7SUFFckQsSUFBSSxPQUFPLENBQUMsTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUNoQyxNQUFNLENBQUMsRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDO1FBRXZDLE9BQU8sYUFBYSxJQUFJLFlBQVksRUFBRSxLQUFLLEtBQUssRUFBRSxDQUFDO0lBQ3JELENBQUM7SUFFRCxJQUFJLE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQzlCLE9BQU8sYUFBYSxJQUFJLElBQUksT0FBTyxDQUFDLE1BQU0sQ0FBQyxNQUFNLGFBQWEsQ0FBQztJQUNqRSxDQUFDO0lBRUQsT0FBTyxtQkFBbUIsSUFBSSxLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxRQUFRLEdBQUcsQ0FBQztBQUN0RSxDQUFDIn0=
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ActionContextSingle, DynamicForm } from '@forestadmin/datasource-customizer';
|
|
2
|
+
import type { RecordData } from '@forestadmin/datasource-toolkit';
|
|
3
|
+
import { TICKET_PRIORITIES, TICKET_TYPES } from '../../enums';
|
|
4
|
+
export type EmailTemplate = {
|
|
5
|
+
title: string;
|
|
6
|
+
content: string;
|
|
7
|
+
};
|
|
8
|
+
export type FormBuilderOptions = {
|
|
9
|
+
emailTemplates?: EmailTemplate[];
|
|
10
|
+
requesterEmailDefault?: string | ((record: RecordData) => string);
|
|
11
|
+
defaultSubject?: string | ((record: RecordData) => string);
|
|
12
|
+
defaultMessage?: string | ((record: RecordData) => string);
|
|
13
|
+
priorityOverride?: (typeof TICKET_PRIORITIES)[number];
|
|
14
|
+
typeOverride?: (typeof TICKET_TYPES)[number];
|
|
15
|
+
showInternalNote?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare const FORM_FIELDS: {
|
|
18
|
+
readonly template: "Template";
|
|
19
|
+
readonly requesterEmail: "Requester email";
|
|
20
|
+
readonly subject: "Subject";
|
|
21
|
+
readonly message: "Message";
|
|
22
|
+
readonly priority: "Priority";
|
|
23
|
+
readonly type: "Type";
|
|
24
|
+
readonly internalNote: "Send as internal note";
|
|
25
|
+
};
|
|
26
|
+
type Ctx = ActionContextSingle;
|
|
27
|
+
export declare function interpolate(template: string, record: RecordData): string;
|
|
28
|
+
export declare function buildForm(options: FormBuilderOptions): DynamicForm<Ctx>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=form-builder.d.ts.map
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FORM_FIELDS = void 0;
|
|
4
|
+
exports.interpolate = interpolate;
|
|
5
|
+
exports.buildForm = buildForm;
|
|
6
|
+
const enums_1 = require("../../enums");
|
|
7
|
+
exports.FORM_FIELDS = {
|
|
8
|
+
template: 'Template',
|
|
9
|
+
requesterEmail: 'Requester email',
|
|
10
|
+
subject: 'Subject',
|
|
11
|
+
message: 'Message',
|
|
12
|
+
priority: 'Priority',
|
|
13
|
+
type: 'Type',
|
|
14
|
+
internalNote: 'Send as internal note',
|
|
15
|
+
};
|
|
16
|
+
const NO_TEMPLATE = 'No template';
|
|
17
|
+
const TOKEN_PATTERN = /{{\s*record\.([\w.]+)\s*}}/g;
|
|
18
|
+
function readPath(record, path) {
|
|
19
|
+
return path
|
|
20
|
+
.split('.')
|
|
21
|
+
.reduce((acc, key) => acc && typeof acc === 'object' ? acc[key] : undefined, record);
|
|
22
|
+
}
|
|
23
|
+
function interpolate(template, record) {
|
|
24
|
+
return template.replace(TOKEN_PATTERN, (_match, path) => {
|
|
25
|
+
const value = readPath(record, path);
|
|
26
|
+
return value === undefined || value === null ? '' : String(value);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// The column field names of the action's collection. `getRecord` only loads the fields it is
|
|
30
|
+
// asked for, so an empty projection would yield a record with no usable values to interpolate.
|
|
31
|
+
function recordColumns(ctx) {
|
|
32
|
+
const fields = ctx.collection?.schema?.fields;
|
|
33
|
+
if (!fields)
|
|
34
|
+
return [];
|
|
35
|
+
return Object.entries(fields)
|
|
36
|
+
.filter(([, schema]) => schema?.type === 'Column')
|
|
37
|
+
.map(([name]) => name);
|
|
38
|
+
}
|
|
39
|
+
async function getSingleRecord(ctx) {
|
|
40
|
+
if ('getRecord' in ctx && typeof ctx.getRecord === 'function') {
|
|
41
|
+
try {
|
|
42
|
+
return ((await ctx.getRecord(recordColumns(ctx))) ?? {});
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
function withSingleRecord(source) {
|
|
51
|
+
return async (ctx) => {
|
|
52
|
+
const record = await getSingleRecord(ctx);
|
|
53
|
+
if (typeof source === 'function')
|
|
54
|
+
return interpolate(source(record), record);
|
|
55
|
+
return interpolate(source, record);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function applyMessageValue(field, options, useTemplate) {
|
|
59
|
+
if (!useTemplate) {
|
|
60
|
+
if (typeof options.defaultMessage === 'string') {
|
|
61
|
+
field.defaultValue = options.defaultMessage;
|
|
62
|
+
}
|
|
63
|
+
else if (typeof options.defaultMessage === 'function') {
|
|
64
|
+
const fn = options.defaultMessage;
|
|
65
|
+
field.defaultValue = async (ctx) => {
|
|
66
|
+
const record = await getSingleRecord(ctx);
|
|
67
|
+
return interpolate(fn(record), record);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const templates = options.emailTemplates ?? [];
|
|
73
|
+
const fallback = options.defaultMessage;
|
|
74
|
+
field.value = async (ctx) => {
|
|
75
|
+
const selected = ctx.formValues[exports.FORM_FIELDS.template];
|
|
76
|
+
if (typeof selected === 'string' && selected !== NO_TEMPLATE) {
|
|
77
|
+
const template = templates.find(t => t.title === selected);
|
|
78
|
+
if (template) {
|
|
79
|
+
const record = await getSingleRecord(ctx);
|
|
80
|
+
return interpolate(template.content, record);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (fallback) {
|
|
84
|
+
const record = await getSingleRecord(ctx);
|
|
85
|
+
const resolved = typeof fallback === 'function' ? fallback(record) : fallback;
|
|
86
|
+
return interpolate(resolved, record);
|
|
87
|
+
}
|
|
88
|
+
return '';
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function buildBodyFields(options, { useTemplate }) {
|
|
92
|
+
const fields = [];
|
|
93
|
+
const emailField = {
|
|
94
|
+
label: exports.FORM_FIELDS.requesterEmail,
|
|
95
|
+
type: 'String',
|
|
96
|
+
isRequired: true,
|
|
97
|
+
description: 'Email of the Zendesk requester. Pre-filled from the selected record when available.',
|
|
98
|
+
};
|
|
99
|
+
if (options.requesterEmailDefault !== undefined) {
|
|
100
|
+
emailField.defaultValue = withSingleRecord(options.requesterEmailDefault);
|
|
101
|
+
}
|
|
102
|
+
fields.push(emailField);
|
|
103
|
+
const subjectField = {
|
|
104
|
+
label: exports.FORM_FIELDS.subject,
|
|
105
|
+
type: 'String',
|
|
106
|
+
isRequired: true,
|
|
107
|
+
};
|
|
108
|
+
if (options.defaultSubject !== undefined) {
|
|
109
|
+
subjectField.defaultValue = withSingleRecord(options.defaultSubject);
|
|
110
|
+
}
|
|
111
|
+
fields.push(subjectField);
|
|
112
|
+
const messageField = {
|
|
113
|
+
label: exports.FORM_FIELDS.message,
|
|
114
|
+
type: 'String',
|
|
115
|
+
widget: 'RichText',
|
|
116
|
+
isRequired: true,
|
|
117
|
+
description:
|
|
118
|
+
// eslint-disable-next-line max-len
|
|
119
|
+
"Sent as the ticket's first comment (HTML). Public comments trigger the default Zendesk notification email to the requester.",
|
|
120
|
+
};
|
|
121
|
+
applyMessageValue(messageField, options, useTemplate);
|
|
122
|
+
fields.push(messageField);
|
|
123
|
+
if (!options.priorityOverride) {
|
|
124
|
+
fields.push({
|
|
125
|
+
label: exports.FORM_FIELDS.priority,
|
|
126
|
+
type: 'Enum',
|
|
127
|
+
enumValues: [...enums_1.TICKET_PRIORITIES],
|
|
128
|
+
defaultValue: 'normal',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (!options.typeOverride) {
|
|
132
|
+
fields.push({
|
|
133
|
+
label: exports.FORM_FIELDS.type,
|
|
134
|
+
type: 'Enum',
|
|
135
|
+
enumValues: [...enums_1.TICKET_TYPES],
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (options.showInternalNote) {
|
|
139
|
+
fields.push({
|
|
140
|
+
label: exports.FORM_FIELDS.internalNote,
|
|
141
|
+
type: 'Boolean',
|
|
142
|
+
defaultValue: false,
|
|
143
|
+
description: 'When checked, the first comment is private and no email is sent to the requester.',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return fields;
|
|
147
|
+
}
|
|
148
|
+
function buildMultiPageForm(options) {
|
|
149
|
+
const templates = options.emailTemplates ?? [];
|
|
150
|
+
const titles = templates.map(t => t.title);
|
|
151
|
+
return [
|
|
152
|
+
{
|
|
153
|
+
type: 'Layout',
|
|
154
|
+
component: 'Page',
|
|
155
|
+
nextButtonLabel: 'Continue',
|
|
156
|
+
elements: [
|
|
157
|
+
{
|
|
158
|
+
label: exports.FORM_FIELDS.template,
|
|
159
|
+
type: 'Enum',
|
|
160
|
+
isRequired: true,
|
|
161
|
+
enumValues: [NO_TEMPLATE, ...titles],
|
|
162
|
+
defaultValue: NO_TEMPLATE,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'Layout',
|
|
168
|
+
component: 'Page',
|
|
169
|
+
previousButtonLabel: 'Back',
|
|
170
|
+
elements: buildBodyFields(options, { useTemplate: true }),
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
function buildForm(options) {
|
|
175
|
+
return options.emailTemplates?.length
|
|
176
|
+
? buildMultiPageForm(options)
|
|
177
|
+
: buildBodyFields(options, { useTemplate: false });
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9ybS1idWlsZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3BsdWdpbnMvY3JlYXRlLXRpY2tldC13aXRoLW5vdGlmaWNhdGlvbi9mb3JtLWJ1aWxkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBK0NBLGtDQU1DO0FBOExELDhCQUlDO0FBaFBELHVDQUE4RDtBQWNqRCxRQUFBLFdBQVcsR0FBRztJQUN6QixRQUFRLEVBQUUsVUFBVTtJQUNwQixjQUFjLEVBQUUsaUJBQWlCO0lBQ2pDLE9BQU8sRUFBRSxTQUFTO0lBQ2xCLE9BQU8sRUFBRSxTQUFTO0lBQ2xCLFFBQVEsRUFBRSxVQUFVO0lBQ3BCLElBQUksRUFBRSxNQUFNO0lBQ1osWUFBWSxFQUFFLHVCQUF1QjtDQUM3QixDQUFDO0FBRVgsTUFBTSxXQUFXLEdBQUcsYUFBYSxDQUFDO0FBSWxDLE1BQU0sYUFBYSxHQUFHLDZCQUE2QixDQUFDO0FBRXBELFNBQVMsUUFBUSxDQUFDLE1BQWtCLEVBQUUsSUFBWTtJQUNoRCxPQUFPLElBQUk7U0FDUixLQUFLLENBQUMsR0FBRyxDQUFDO1NBQ1YsTUFBTSxDQUNMLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQ1gsR0FBRyxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUUsR0FBK0IsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxFQUNwRixNQUFNLENBQ1AsQ0FBQztBQUNOLENBQUM7QUFFRCxTQUFnQixXQUFXLENBQUMsUUFBZ0IsRUFBRSxNQUFrQjtJQUM5RCxPQUFPLFFBQVEsQ0FBQyxPQUFPLENBQUMsYUFBYSxFQUFFLENBQUMsTUFBTSxFQUFFLElBQVksRUFBRSxFQUFFO1FBQzlELE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFFckMsT0FBTyxLQUFLLEtBQUssU0FBUyxJQUFJLEtBQUssS0FBSyxJQUFJ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
|