feat(zendesk): add create ticket action

This commit is contained in:
Rıdvan Akca
2023-10-30 17:16:56 +03:00
committed by Ali BARIN
parent 4ff824663b
commit be610c7fa9
13 changed files with 636 additions and 1 deletions

View File

@@ -0,0 +1,301 @@
export const fields = [
{
label: 'Subject',
key: 'subject',
type: 'string' as const,
required: true,
variables: true,
description: '',
},
{
label: 'Assignee',
key: 'assigneeId',
type: 'dropdown' as const,
required: false,
variables: true,
description:
'Note: An error occurs if the assignee is not in the default group (or the specific group chosen below).',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
{
name: 'parameters.showUserRole',
value: 'true',
},
{
name: 'parameters.includeAdmins',
value: 'true',
},
],
},
},
{
label: 'Collaborators',
key: 'collaborators',
type: 'dynamic' as const,
required: false,
description: '',
fields: [
{
label: 'Collaborator',
key: 'collaborator',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
{
name: 'parameters.includeAdmins',
value: 'true',
},
],
},
},
],
},
{
label: 'Collaborator Emails',
key: 'collaboratorEmails',
type: 'dynamic' as const,
required: false,
description:
'You have the option to include individuals who are not Zendesk users as Collaborators by adding their email addresses here.',
fields: [
{
label: 'Collaborator Email',
key: 'collaboratorEmail',
type: 'string' as const,
required: false,
variables: true,
description: '',
},
],
},
{
label: 'Group',
key: 'groupId',
type: 'dropdown' as const,
required: false,
variables: true,
description: 'Allocate this ticket to a specific group.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listGroups',
},
],
},
},
{
label: 'Requester Name',
key: 'requesterName',
type: 'string' as const,
required: false,
variables: true,
description:
'To specify the Requester, you need to fill in the Requester Name in this field and provide the Requestor Email in the next field.',
},
{
label: 'Requester Email',
key: 'requesterEmail',
type: 'string' as const,
required: false,
variables: true,
description:
'To specify the Requester, you need to fill in the Requester Email in this field and provide the Requestor Name in the previous field.',
},
{
label: 'First Comment/Description Format',
key: 'format',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Plain Text', value: 'Plain Text' },
{ label: 'HTML', value: 'HTML' },
],
},
{
label: 'First Comment/Description',
key: 'comment',
type: 'string' as const,
required: true,
variables: true,
description: '',
},
{
label: 'Should the first comment be public?',
key: 'publicOrNot',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Yes', value: 'yes' },
{ label: 'No', value: 'no' },
],
},
{
label: 'Tags',
key: 'tags',
type: 'string' as const,
required: false,
variables: true,
description: 'A comma separated list of tags.',
},
{
label: 'Status',
key: 'status',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'New', value: 'new' },
{ label: 'Open', value: 'open' },
{ label: 'Pending', value: 'pending' },
{ label: 'Hold', value: 'hold' },
{ label: 'Solved', value: 'solved' },
{ label: 'Closed', value: 'closed' },
],
},
{
label: 'Type',
key: 'type',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Problem', value: 'problem' },
{ label: 'Incident', value: 'incident' },
{ label: 'Question', value: 'question' },
{ label: 'Task', value: 'task' },
],
},
{
label: 'Due At',
key: 'dueAt',
type: 'string' as const,
required: false,
variables: true,
description: 'Limited to tickets typed as "task".',
},
{
label: 'Priority',
key: 'priority',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
options: [
{ label: 'Urgent', value: 'urgent' },
{ label: 'High', value: 'high' },
{ label: 'Normal', value: 'normal' },
{ label: 'Low', value: 'low' },
],
},
{
label: 'Submitter',
key: 'submitterId',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listUsers',
},
{
name: 'parameters.includeAdmins',
value: 'false',
},
],
},
},
{
label: 'Ticket Form',
key: 'ticketForm',
type: 'dropdown' as const,
required: false,
variables: true,
description:
'When chosen, this will configure the form displayed for this ticket. Note: This field is solely relevant for Zendesk enterprise accounts.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listTicketForms',
},
],
},
},
{
label: 'Sharing Agreements',
key: 'sharingAgreements',
type: 'dynamic' as const,
required: false,
description: '',
fields: [
{
label: 'Sharing Agreement',
key: 'sharingAgreement',
type: 'dropdown' as const,
required: false,
variables: true,
description: '',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listSharingAgreements',
},
],
},
},
],
},
{
label: 'Brand',
key: 'brandId',
type: 'dropdown' as const,
required: false,
variables: true,
description:
'This applies exclusively to Zendesk customers subscribed to plans that include multi-brand support.',
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listBrands',
},
],
},
},
];

View File

@@ -0,0 +1,101 @@
import { IJSONArray, IJSONObject } from '@automatisch/types';
import defineAction from '../../../../helpers/define-action';
import { fields } from './fields';
import isEmpty from 'lodash/isEmpty';
type Payload = {
ticket: IJSONObject;
};
export default defineAction({
name: 'Create ticket',
key: 'createTicket',
description: 'Creates a new ticket',
arguments: fields,
async run($) {
const {
subject,
assigneeId,
groupId,
requesterName,
requesterEmail,
format,
comment,
publicOrNot,
status,
type,
dueAt,
priority,
submitterId,
ticketForm,
brandId,
} = $.step.parameters;
const collaborators = $.step.parameters.collaborators as IJSONArray;
const collaboratorIds = collaborators?.map(
(collaborator: IJSONObject) => collaborator.collaborator
);
const collaboratorEmails = $.step.parameters
.collaboratorEmails as IJSONArray;
const formattedCollaboratorEmails = collaboratorEmails?.map(
(collaboratorEmail: IJSONObject) => collaboratorEmail.collaboratorEmail
);
const formattedCollaborators = [
...collaboratorIds,
...formattedCollaboratorEmails,
];
const sharingAgreements = $.step.parameters.sharingAgreements as IJSONArray;
const sharingAgreementIds = sharingAgreements
?.filter(isEmpty)
.map((sharingAgreement: IJSONObject) =>
Number(sharingAgreement.sharingAgreement)
);
const tags = $.step.parameters.tags as string;
const formattedTags = tags.split(',');
const payload: Payload = {
ticket: {
subject,
assignee_id: assigneeId,
collaborators: formattedCollaborators,
group_id: groupId,
is_public: publicOrNot,
tags: formattedTags,
status,
type,
due_at: dueAt,
priority,
submitter_id: submitterId,
ticket_form_id: ticketForm,
sharing_agreement_ids: sharingAgreementIds,
brand_id: brandId,
},
};
if (requesterName && requesterEmail) {
payload.ticket.requester = {
name: requesterName,
email: requesterEmail,
};
}
if (format === 'HTML') {
payload.ticket.comment = {
html_body: comment,
};
} else {
payload.ticket.comment = {
body: comment,
};
}
const response = await $.http.post('/api/v2/tickets', payload);
$.setActionItem({ raw: response.data });
},
});

View File

@@ -0,0 +1,3 @@
import createTicket from './create-ticket';
export default [createTicket];

View File

@@ -0,0 +1,13 @@
import listUsers from './list-users';
import listBrands from './list-brands';
import listGroups from './list-groups';
import listSharingAgreements from './list-sharing-agreements';
import listTicketForms from './list-ticket-forms';
export default [
listUsers,
listBrands,
listGroups,
listSharingAgreements,
listTicketForms,
];

View File

@@ -0,0 +1,38 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List brands',
key: 'listBrands',
async run($: IGlobalVariable) {
const brands: {
data: IJSONObject[];
} = {
data: [],
};
const params = {
page: 1,
per_page: 100,
};
let nextPage;
do {
const response = await $.http.get('/api/v2/brands', { params });
const allBrands = response?.data?.brands;
nextPage = response.data.next_page;
params.page = params.page + 1;
if (allBrands?.length) {
for (const brand of allBrands) {
brands.data.push({
value: brand.id,
name: brand.name,
});
}
}
} while (nextPage);
return brands;
},
};

View File

@@ -0,0 +1,38 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List groups',
key: 'listGroups',
async run($: IGlobalVariable) {
const groups: {
data: IJSONObject[];
} = {
data: [],
};
let hasMore;
const params = {
'page[size]': 100,
'page[after]': undefined as unknown as string,
};
do {
const response = await $.http.get('/api/v2/groups', { params });
const allGroups = response?.data?.groups;
hasMore = response?.data?.meta?.has_more;
params['page[after]'] = response.data.links?.after_cursor;
if (allGroups?.length) {
for (const group of allGroups) {
groups.data.push({
value: group.id,
name: group.name,
});
}
}
} while (hasMore);
return groups;
},
};

View File

@@ -0,0 +1,40 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List sharing agreements',
key: 'listSharingAgreements',
async run($: IGlobalVariable) {
const sharingAgreements: {
data: IJSONObject[];
} = {
data: [],
};
const params = {
page: 1,
per_page: 100,
};
let nextPage;
do {
const response = await $.http.get('/api/v2/sharing_agreements', {
params,
});
const allSharingAgreements = response?.data?.sharing_agreements;
nextPage = response.data.next_page;
params.page = params.page + 1;
if (allSharingAgreements?.length) {
for (const sharingAgreement of allSharingAgreements) {
sharingAgreements.data.push({
value: sharingAgreement.id,
name: sharingAgreement.name,
});
}
}
} while (nextPage);
return sharingAgreements;
},
};

View File

@@ -0,0 +1,38 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List ticket forms',
key: 'listTicketForms',
async run($: IGlobalVariable) {
const ticketForms: {
data: IJSONObject[];
} = {
data: [],
};
const params = {
page: 1,
per_page: 100,
};
let nextPage;
do {
const response = await $.http.get('/api/v2/ticket_forms', { params });
const allTicketForms = response?.data?.ticket_forms;
nextPage = response.data.next_page;
params.page = params.page + 1;
if (allTicketForms?.length) {
for (const ticketForm of allTicketForms) {
ticketForms.data.push({
value: ticketForm.id,
name: ticketForm.name,
});
}
}
} while (nextPage);
return ticketForms;
},
};

View File

@@ -0,0 +1,43 @@
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
export default {
name: 'List users',
key: 'listUsers',
async run($: IGlobalVariable) {
const users: {
data: IJSONObject[];
} = {
data: [],
};
let hasMore;
const showUserRole = $.step.parameters.showUserRole === 'true';
const includeAdmins = $.step.parameters.includeAdmins === 'true';
const role = includeAdmins ? ['admin', 'agent'] : ['agent'];
const params = {
'page[size]': 100,
role,
'page[after]': undefined as unknown as string,
};
do {
const response = await $.http.get('/api/v2/users', { params });
const allUsers = response?.data?.users;
hasMore = response?.data?.meta?.has_more;
params['page[after]'] = response.data.links?.after_cursor;
if (allUsers?.length) {
for (const user of allUsers) {
const name = showUserRole ? `${user.name} ${user.role}` : user.name;
users.data.push({
value: user.id,
name,
});
}
}
} while (hasMore);
return users;
},
};

View File

@@ -1,6 +1,8 @@
import defineApp from '../../helpers/define-app';
import addAuthHeader from './common/add-auth-headers';
import auth from './auth';
import actions from './actions';
import dynamicData from './dynamic-data';
export default defineApp({
name: 'Zendesk',
@@ -13,4 +15,6 @@ export default defineApp({
supportsConnections: true,
beforeRequest: [addAuthHeader],
auth,
actions,
dynamicData,
});

View File

@@ -442,7 +442,10 @@ export default defineConfig({
text: 'Zendesk',
collapsible: true,
collapsed: true,
items: [{ text: 'Connection', link: '/apps/zendesk/connection' }],
items: [
{ text: 'Actions', link: '/apps/zendesk/actions' },
{ text: 'Connection', link: '/apps/zendesk/connection' },
],
},
],
'/': [

View File

@@ -0,0 +1,12 @@
---
favicon: /favicons/zendesk.svg
items:
- name: Create ticket
desc: Creates a new ticket.
---
<script setup>
import CustomListing from '../../components/CustomListing.vue'
</script>
<CustomListing />

View File

@@ -46,3 +46,4 @@ The following integrations are currently supported by Automatisch.
- [Webhooks](/apps/webhooks/triggers)
- [WordPress](/apps/wordpress/triggers)
- [Youtube](/apps/youtube/triggers)
- [Zendesk](/apps/zendesk/actions)