From 3426e134af67d2c9ed8711f9cee7470ebac07629 Mon Sep 17 00:00:00 2001 From: Jorge Cortes Date: Thu, 23 Oct 2025 11:54:34 -0500 Subject: [PATCH] [Components] Topdesk - new components --- .../create-incident/create-incident.mjs | 411 +++++++++++++++++ .../actions/get-incident/get-incident.mjs | 39 ++ .../actions/get-incidents/get-incidents.mjs | 87 ++++ .../get-knowledge-item-statuses.mjs | 42 ++ .../get-knowledge-items.mjs | 122 +++++ .../update-incident/update-incident.mjs | 435 ++++++++++++++++++ components/topdesk/package.json | 7 +- components/topdesk/topdesk.app.mjs | 249 +++++++++- pnpm-lock.yaml | 12 +- 9 files changed, 1393 insertions(+), 11 deletions(-) create mode 100644 components/topdesk/actions/create-incident/create-incident.mjs create mode 100644 components/topdesk/actions/get-incident/get-incident.mjs create mode 100644 components/topdesk/actions/get-incidents/get-incidents.mjs create mode 100644 components/topdesk/actions/get-knowledge-item-statuses/get-knowledge-item-statuses.mjs create mode 100644 components/topdesk/actions/get-knowledge-items/get-knowledge-items.mjs create mode 100644 components/topdesk/actions/update-incident/update-incident.mjs diff --git a/components/topdesk/actions/create-incident/create-incident.mjs b/components/topdesk/actions/create-incident/create-incident.mjs new file mode 100644 index 0000000000000..b89325a18e31b --- /dev/null +++ b/components/topdesk/actions/create-incident/create-incident.mjs @@ -0,0 +1,411 @@ +import app from "../../topdesk.app.mjs"; + +export default { + key: "topdesk-create-incident", + name: "Create Incident", + description: "Creates a new incident. [See the documentation](https://developers.topdesk.com/explorer/?page=incident#/incident/createIncident)", + version: "0.0.1", + type: "action", + props: { + app, + callerLookupId: { + type: "string", + label: "Caller Lookup ID", + description: "Lookup value for filling in a registered caller's contact details (UUID). **Required** - you must specify either this field or provide caller details manually.", + propDefinition: [ + app, + "personId", + ], + }, + status: { + type: "string", + label: "Status", + description: "Status of the incident. Can only be set by operators.", + optional: true, + options: [ + "firstLine", + "secondLine", + "partial", + ], + }, + briefDescription: { + type: "string", + label: "Brief Description", + description: "Brief description of the incident (max 80 characters)", + optional: true, + }, + request: { + type: "string", + label: "Request", + description: "The initial request text. HTML tags are supported: ``, ``, ``, ``, ``, `
`, `
`, `
`", + optional: true, + }, + action: { + type: "string", + label: "Action", + description: "The initial action text. HTML tags are supported: ``, ``, ``, ``, ``, `
`, `
`, `
`", + optional: true, + }, + actionInvisibleForCaller: { + type: "boolean", + label: "Action Invisible For Caller", + description: "Whether the initial action is invisible for persons. Can only be set by operators.", + optional: true, + }, + categoryId: { + type: "string", + label: "Category ID", + description: "The UUID of the category", + optional: true, + }, + subcategoryId: { + type: "string", + label: "Subcategory ID", + description: "The UUID of the subcategory", + optional: true, + }, + callTypeId: { + type: "string", + label: "Call Type ID", + description: "The UUID of the call type", + optional: true, + }, + entryTypeId: { + type: "string", + label: "Entry Type ID", + description: "The UUID of the entry type. Can only be set by operators.", + optional: true, + }, + externalNumber: { + type: "string", + label: "External Number", + description: "External number (max 60 characters). Can only be set by operators.", + optional: true, + }, + objectId: { + type: "string", + label: "Object ID", + description: "The UUID of the object (asset). Can only be set by operators.", + optional: true, + }, + locationId: { + type: "string", + label: "Location ID", + description: "The UUID of the location. Can only be set by operators.", + optional: true, + }, + branchId: { + type: "string", + label: "Branch ID", + description: "The UUID of the branch. Can only be set by operators.", + optional: true, + }, + mainIncidentId: { + type: "string", + label: "Main Incident ID", + description: "Main incident UUID, required for creating a partial incident. Can only be set by operators.", + optional: true, + }, + impact: { + type: "string", + label: "Impact ID", + description: "The UUID of the impact. Can only be set by operators.", + optional: true, + }, + urgency: { + type: "string", + label: "Urgency ID", + description: "The UUID of the urgency. Can only be set by operators.", + optional: true, + }, + priority: { + type: "string", + label: "Priority ID", + description: "The UUID of the priority. Can only be set by operators.", + optional: true, + }, + duration: { + type: "string", + label: "Duration ID", + description: "The UUID of the duration. Can only be set by operators.", + optional: true, + }, + targetDate: { + type: "string", + label: "Target Date", + description: "Target date in ISO 8601 format (e.g., `2024-08-01T12:00:00.000+0200`). Can only be set by operators.", + optional: true, + }, + slaId: { + type: "string", + label: "SLA ID", + description: "The UUID of the SLA. Can only be set by operators.", + optional: true, + }, + onHold: { + type: "boolean", + label: "On Hold", + description: "Whether the incident is on hold. Can only be set by operators.", + optional: true, + }, + operatorId: { + propDefinition: [ + app, + "operatorId", + ], + label: "Operator", + description: "The operator assigned to the incident. Can only be set by operators.", + optional: true, + }, + operatorGroupId: { + type: "string", + label: "Operator Group ID", + description: "The UUID of the operator group assigned to the incident", + optional: true, + }, + supplierId: { + type: "string", + label: "Supplier ID", + description: "The UUID of the supplier. Can only be set by operators.", + optional: true, + }, + processingStatusId: { + type: "string", + label: "Processing Status ID", + description: "The UUID of the processing status. Can only be set by operators.", + optional: true, + }, + responded: { + type: "boolean", + label: "Responded", + description: "Whether the incident is responded. SLM-licence is needed. Can only be set by operators.", + optional: true, + }, + responseDate: { + type: "string", + label: "Response Date", + description: "Response date in ISO 8601 format. SLM-licence is needed. Can only be set by operators.", + optional: true, + }, + completed: { + type: "boolean", + label: "Completed", + description: "Whether the incident is completed. Can only be set by operators.", + optional: true, + }, + completedDate: { + type: "string", + label: "Completed Date", + description: "Completed date in ISO 8601 format. Can only be set by operators.", + optional: true, + }, + closed: { + type: "boolean", + label: "Closed", + description: "Whether the incident is closed. Can only be set by operators.", + optional: true, + }, + closedDate: { + type: "string", + label: "Closed Date", + description: "Closed date in ISO 8601 format. Can only be set by operators.", + optional: true, + }, + closureCodeId: { + type: "string", + label: "Closure Code ID", + description: "The UUID of the closure code. Can only be set by operators.", + optional: true, + }, + majorCall: { + type: "boolean", + label: "Major Call", + description: "Whether the incident is a major call. Can only be set by operators.", + optional: true, + }, + publishToSsd: { + type: "boolean", + label: "Publish To SSD", + description: "Whether to publish the incident to Self Service Desk. Can only be set by operators.", + optional: true, + }, + optionalFields1: { + type: "object", + label: "Optional Fields 1", + description: "Optional fields tab 1 as a JSON object", + optional: true, + }, + optionalFields2: { + type: "object", + label: "Optional Fields 2", + description: "Optional fields tab 2 as a JSON object", + optional: true, + }, + }, + annotations: { + readOnlyHint: false, + destructiveHint: false, + openWorldHint: true, + idempotentHint: false, + }, + async run({ $ }) { + const { + app, + callerLookupId, + status, + briefDescription, + request, + action, + actionInvisibleForCaller, + categoryId, + subcategoryId, + callTypeId, + entryTypeId, + externalNumber, + objectId, + locationId, + branchId, + mainIncidentId, + impact, + urgency, + priority, + duration, + targetDate, + slaId, + onHold, + operatorId, + operatorGroupId, + supplierId, + processingStatusId, + responded, + responseDate, + completed, + completedDate, + closed, + closedDate, + closureCodeId, + majorCall, + publishToSsd, + optionalFields1, + optionalFields2, + } = this; + + // Fields that take { id: value } structure + const idFields = [ + { + value: callerLookupId, + key: "callerLookup", + }, + { + value: categoryId, + key: "category", + }, + { + value: subcategoryId, + key: "subcategory", + }, + { + value: callTypeId, + key: "callType", + }, + { + value: entryTypeId, + key: "entryType", + }, + { + value: objectId, + key: "object", + }, + { + value: locationId, + key: "location", + }, + { + value: branchId, + key: "branch", + }, + { + value: mainIncidentId, + key: "mainIncident", + }, + { + value: impact, + key: "impact", + }, + { + value: urgency, + key: "urgency", + }, + { + value: priority, + key: "priority", + }, + { + value: duration, + key: "duration", + }, + { + value: slaId, + key: "sla", + }, + { + value: operatorId, + key: "operator", + }, + { + value: operatorGroupId, + key: "operatorGroup", + }, + { + value: supplierId, + key: "supplier", + }, + { + value: processingStatusId, + key: "processingStatus", + }, + { + value: closureCodeId, + key: "closureCode", + }, + ]; + + const response = await app.createIncident({ + $, + data: { + status, + briefDescription, + request, + action, + actionInvisibleForCaller, + externalNumber, + targetDate, + onHold, + responded, + responseDate, + completed, + completedDate, + closed, + closedDate, + majorCall, + publishToSsd, + optionalFields1, + optionalFields2, + ...idFields.reduce((acc, { + value, key, + }) => ({ + ...acc, + ...value && { + [key]: { + id: value, + }, + }, + }), {}), + }, + }); + + $.export("$summary", `Successfully created incident with ID \`${response.id}\``); + + return response; + }, +}; diff --git a/components/topdesk/actions/get-incident/get-incident.mjs b/components/topdesk/actions/get-incident/get-incident.mjs new file mode 100644 index 0000000000000..a9f339105b878 --- /dev/null +++ b/components/topdesk/actions/get-incident/get-incident.mjs @@ -0,0 +1,39 @@ +import app from "../../topdesk.app.mjs"; + +export default { + key: "topdesk-get-incident", + name: "Get Incident", + description: "Returns an incident by ID. [See the documentation](https://developers.topdesk.com/explorer/?page=incident#/incident/getIncidentById)", + version: "0.0.1", + type: "action", + props: { + app, + incidentId: { + propDefinition: [ + app, + "incidentId", + ], + }, + }, + annotations: { + readOnlyHint: true, + destructiveHint: false, + openWorldHint: true, + idempotentHint: true, + }, + async run({ $ }) { + const { + app, + incidentId, + } = this; + + const response = await app.getIncident({ + $, + incidentId, + }); + + $.export("$summary", `Successfully retrieved incident with ID \`${response.id}\``); + + return response; + }, +}; diff --git a/components/topdesk/actions/get-incidents/get-incidents.mjs b/components/topdesk/actions/get-incidents/get-incidents.mjs new file mode 100644 index 0000000000000..a833c15ee621d --- /dev/null +++ b/components/topdesk/actions/get-incidents/get-incidents.mjs @@ -0,0 +1,87 @@ +import app from "../../topdesk.app.mjs"; + +export default { + key: "topdesk-get-incidents", + name: "Get Incidents", + description: "Returns a list of incidents. [See the documentation](https://developers.topdesk.com/explorer/?page=incident#/incident/getIncidents)", + version: "0.0.1", + type: "action", + props: { + app, + maxResults: { + type: "integer", + label: "Max Results", + description: "Maximum number of incidents to return. Leave empty to return all incidents.", + optional: true, + }, + sort: { + type: "string", + label: "Sort", + description: "The sort order of the returned incidents (e.g., `callDate:asc,creationDate:desc`)", + optional: true, + }, + query: { + type: "string", + label: "Query", + description: "A FIQL string to select which incidents should be returned. [See the documentation](https://developers.topdesk.com/tutorial.html#query)", + optional: true, + }, + fields: { + type: "string", + label: "Fields", + description: "A comma-separated list of which fields should be returned. By default all fields will be returned.", + optional: true, + }, + all: { + type: "boolean", + label: "All", + description: "When set to true, will return all incidents including partials and archived. Otherwise only firstLine and secondLine incidents are returned.", + optional: true, + default: false, + }, + }, + annotations: { + readOnlyHint: true, + destructiveHint: false, + openWorldHint: true, + idempotentHint: true, + }, + async run({ $ }) { + const { + app, + maxResults, + sort, + query, + fields, + all, + } = this; + + const incidents = []; + const paginator = app.paginate({ + fn: app.listIncidents, + fnArgs: { + $, + params: { + sort, + query, + fields: Array.isArray(fields) && fields?.length + ? fields.join(",") + : typeof fields === "string" && fields.length + ? fields + : undefined, + all, + page_size: 100, + }, + }, + maxResults, + }); + + for await (const incident of paginator) { + incidents.push(incident); + } + + $.export("$summary", `Successfully retrieved \`${incidents.length}\` incident(s)`); + + return incidents; + }, +}; diff --git a/components/topdesk/actions/get-knowledge-item-statuses/get-knowledge-item-statuses.mjs b/components/topdesk/actions/get-knowledge-item-statuses/get-knowledge-item-statuses.mjs new file mode 100644 index 0000000000000..862e17b771770 --- /dev/null +++ b/components/topdesk/actions/get-knowledge-item-statuses/get-knowledge-item-statuses.mjs @@ -0,0 +1,42 @@ +import app from "../../topdesk.app.mjs"; + +export default { + key: "topdesk-get-knowledge-item-statuses", + name: "Get Knowledge Item Statuses", + description: "Returns the list of possible Knowledge Item statuses. [See the documentation](https://developers.topdesk.com/explorer/?page=knowledge-base#/Searchlists/getStatuses)", + version: "0.0.1", + type: "action", + props: { + app, + archived: { + type: "boolean", + label: "Archived", + description: "Whether to show archived entries. Leave unset for all entries, or specify true/false for only archived or only active entries, respectively.", + optional: true, + }, + }, + annotations: { + readOnlyHint: true, + destructiveHint: false, + openWorldHint: true, + idempotentHint: true, + }, + async run({ $ }) { + const { + app, + archived, + } = this; + + const response = await app.listKnowledgeItemStatuses({ + $, + params: { + archived, + }, + }); + + const statusCount = response.results?.length || 0; + $.export("$summary", `Successfully retrieved \`${statusCount}\` knowledge item status(es)`); + + return response; + }, +}; diff --git a/components/topdesk/actions/get-knowledge-items/get-knowledge-items.mjs b/components/topdesk/actions/get-knowledge-items/get-knowledge-items.mjs new file mode 100644 index 0000000000000..3e5c91b45741d --- /dev/null +++ b/components/topdesk/actions/get-knowledge-items/get-knowledge-items.mjs @@ -0,0 +1,122 @@ +import app from "../../topdesk.app.mjs"; + +export default { + key: "topdesk-get-knowledge-items", + name: "Get Knowledge Items", + description: "Returns a list of Knowledge Items. [See the documentation](https://developers.topdesk.com/explorer/?page=knowledge-base#/Knowledge%20Items/getKnowledgeItems)", + version: "0.0.1", + type: "action", + props: { + app, + maxResults: { + type: "integer", + label: "Max Results", + description: "Maximum number of knowledge items to return. Leave empty to return all items.", + optional: true, + }, + fields: { + type: "string[]", + label: "Fields", + description: "Additional fields to include in the response. ID and number are always included.", + optional: true, + options: [ + "parent", + "visibility", + "urls", + "news", + "manager", + "status", + "standardSolution", + "externalLink", + "language", + "title", + "description", + "content", + "commentsForOperators", + "keywords", + "creator", + "modifier", + "creationDate", + "modificationDate", + "translation.creator", + "translation.modifier", + "translation.creationDate", + "translation.modificationDate", + "availableTranslations", + ], + }, + query: { + type: "string", + label: "Query", + description: `A FIQL query to filter the response. Use semicolons to combine multiple conditions. + +**Available filter fields:** +- \`parent.id\`, \`parent.number\` +- \`visibility.sspVisibility\`, \`visibility.sspVisibleFrom\`, \`visibility.sspVisibleUntil\` +- \`visibility.sspVisibilityFilteredOnBranches\`, \`visibility.operatorVisibilityFilteredOnBranches\` +- \`visibility.publicKnowledgeItem\`, \`visibility.branches.id\`, \`visibility.branches.name\` +- \`manager.id\`, \`manager.name\` +- \`status.id\`, \`status.name\` +- \`standardSolution.id\`, \`standardSolution.name\` +- \`externalLink.id\`, \`externalLink.type\`, \`externalLink.date\` +- \`archived\`, \`news\` + +**Operators:** +- \`==\` (equals), \`!=\` (not equals) +- \`=gt=\` (greater than), \`=ge=\` (greater than or equal) +- \`=lt=\` (less than), \`=le=\` (less than or equal) +- \`=in=\` (in list), \`=out=\` (not in list) + +**Example:** \`parent.id==3fa85f64-5717-4562-b3fc-2c963f66afa6;externalLink.id=in=(oneTool,otherTool)\``, + optional: true, + }, + language: { + type: "string", + label: "Language", + description: "The language of the Knowledge Item content, in BCP 47 format (e.g., `en`)", + optional: true, + }, + }, + annotations: { + readOnlyHint: true, + destructiveHint: false, + openWorldHint: true, + }, + async run({ $ }) { + const { + app, + maxResults, + fields, + query, + language, + } = this; + + const items = []; + const paginator = app.paginate({ + fn: app.listKnowledgeItems, + fnArgs: { + $, + params: { + fields: Array.isArray(fields) && fields?.length + ? fields.join(",") + : typeof fields === "string" && fields.length + ? fields + : undefined, + query, + language, + page_size: 100, + }, + }, + maxResults, + dataField: "item", + }); + + for await (const item of paginator) { + items.push(item); + } + + $.export("$summary", `Successfully retrieved \`${items.length}\` knowledge item(s)`); + + return items; + }, +}; diff --git a/components/topdesk/actions/update-incident/update-incident.mjs b/components/topdesk/actions/update-incident/update-incident.mjs new file mode 100644 index 0000000000000..875bc266e1985 --- /dev/null +++ b/components/topdesk/actions/update-incident/update-incident.mjs @@ -0,0 +1,435 @@ +import app from "../../topdesk.app.mjs"; + +export default { + key: "topdesk-update-incident", + name: "Update Incident", + description: "Updates an existing incident. [See the documentation](https://developers.topdesk.com/explorer/?page=incident#/incident/patchIncident)", + version: "0.0.1", + type: "action", + props: { + app, + incidentId: { + propDefinition: [ + app, + "incidentId", + ], + }, + callerLookupId: { + type: "string", + label: "Caller Lookup ID", + description: "Lookup value for filling in a registered caller's contact details (UUID). Can only be set by operators.", + optional: true, + }, + briefDescription: { + type: "string", + label: "Brief Description", + description: "Brief description of the incident (max 80 characters)", + optional: true, + }, + request: { + type: "string", + label: "Request", + description: "The request text. HTML tags are supported: ``, ``, ``, ``, ``, `
`, `
`, `
`", + optional: true, + }, + action: { + type: "string", + label: "Action", + description: "The action text to add. HTML tags are supported: ``, ``, ``, ``, ``, `
`, `
`, `
`", + optional: true, + }, + actionInvisibleForCaller: { + type: "boolean", + label: "Action Invisible For Caller", + description: "Whether the action is invisible for persons. Can only be set by operators.", + optional: true, + default: false, + }, + categoryId: { + type: "string", + label: "Category ID", + description: "The UUID of the category", + optional: true, + }, + subcategoryId: { + type: "string", + label: "Subcategory ID", + description: "The UUID of the subcategory", + optional: true, + }, + callTypeId: { + type: "string", + label: "Call Type ID", + description: "The UUID of the call type", + optional: true, + }, + callDate: { + type: "string", + label: "Call Date", + description: "The date when this call was registered in ISO 8601 format (e.g., `2024-08-01T12:00:00.000+0200`). Can only be set by operators.", + optional: true, + }, + entryTypeId: { + type: "string", + label: "Entry Type ID", + description: "The UUID of the entry type. Can only be set by operators.", + optional: true, + }, + externalNumber: { + type: "string", + label: "External Number", + description: "External number (max 60 characters). Can only be set by operators.", + optional: true, + }, + objectId: { + type: "string", + label: "Object ID", + description: "The UUID of the object (asset). Can only be set by operators.", + optional: true, + }, + locationId: { + type: "string", + label: "Location ID", + description: "The UUID of the location. Can only be set by operators.", + optional: true, + }, + branchId: { + type: "string", + label: "Branch ID", + description: "The UUID of the branch. Can only be set by operators.", + optional: true, + }, + impact: { + type: "string", + label: "Impact ID", + description: "The UUID of the impact. Can only be set by operators.", + optional: true, + }, + urgency: { + type: "string", + label: "Urgency ID", + description: "The UUID of the urgency. Can only be set by operators.", + optional: true, + }, + priority: { + type: "string", + label: "Priority ID", + description: "The UUID of the priority. Can only be set by operators.", + optional: true, + }, + duration: { + type: "string", + label: "Duration ID", + description: "The UUID of the duration. Can only be set by operators.", + optional: true, + }, + targetDate: { + type: "string", + label: "Target Date", + description: "Target date in ISO 8601 format (e.g., `2024-08-01T12:00:00.000+0200`). Can only be set by operators.", + optional: true, + }, + slaId: { + type: "string", + label: "SLA ID", + description: "The UUID of the SLA. Can only be set by operators.", + optional: true, + }, + onHold: { + type: "boolean", + label: "On Hold", + description: "Whether the incident is on hold. Can only be set by operators.", + optional: true, + }, + operatorId: { + propDefinition: [ + app, + "operatorId", + ], + label: "Operator", + description: "The operator assigned to the incident. Can only be set by operators.", + optional: true, + }, + operatorGroupId: { + type: "string", + label: "Operator Group ID", + description: "The UUID of the operator group assigned to the incident", + optional: true, + }, + supplierId: { + type: "string", + label: "Supplier ID", + description: "The UUID of the supplier. Can only be set by operators.", + optional: true, + }, + processingStatusId: { + type: "string", + label: "Processing Status ID", + description: "The UUID of the processing status. Can only be set by operators.", + optional: true, + }, + responded: { + type: "boolean", + label: "Responded", + description: "Whether the incident is responded. SLM-licence is needed. Can only be set by operators.", + optional: true, + }, + responseDate: { + type: "string", + label: "Response Date", + description: "Response date in ISO 8601 format. SLM-licence is needed. Can only be set by operators.", + optional: true, + }, + completed: { + type: "boolean", + label: "Completed", + description: "Whether the incident is completed. Can only be set by operators.", + optional: true, + }, + completedDate: { + type: "string", + label: "Completed Date", + description: "Completed date in ISO 8601 format. Can only be set by operators.", + optional: true, + }, + closed: { + type: "boolean", + label: "Closed", + description: "Whether the incident is closed. Can only be set by operators.", + optional: true, + }, + closedDate: { + type: "string", + label: "Closed Date", + description: "Closed date in ISO 8601 format. Can only be set by operators.", + optional: true, + }, + closureCodeId: { + type: "string", + label: "Closure Code ID", + description: "The UUID of the closure code. Can only be set by operators.", + optional: true, + }, + costs: { + type: "string", + label: "Costs", + description: "Costs as a decimal number. Can only be set by operators.", + optional: true, + }, + feedbackMessage: { + type: "string", + label: "Feedback Message", + description: "Feedback message from the caller", + optional: true, + }, + feedbackRating: { + type: "integer", + label: "Feedback Rating", + description: "Feedback rating (1-5). Can be set by persons.", + optional: true, + min: 1, + max: 5, + }, + majorCall: { + type: "boolean", + label: "Major Call", + description: "Whether the incident is a major call. Can only be set by operators.", + optional: true, + }, + majorCallObject: { + type: "object", + label: "Major Call Object", + description: "Major call details as a JSON object. Can only be set by operators.", + optional: true, + }, + publishToSsd: { + type: "boolean", + label: "Publish To SSD", + description: "Whether to publish the incident to Self Service Desk. Can only be set by operators.", + optional: true, + }, + optionalFields1: { + type: "object", + label: "Optional Fields 1", + description: "Optional fields tab 1 as a JSON object", + optional: true, + }, + optionalFields2: { + type: "object", + label: "Optional Fields 2", + description: "Optional fields tab 2 as a JSON object", + optional: true, + }, + }, + annotations: { + readOnlyHint: false, + destructiveHint: false, + openWorldHint: true, + idempotentHint: false, + }, + async run({ $ }) { + const { + app, + incidentId, + callerLookupId, + briefDescription, + request, + action, + actionInvisibleForCaller, + categoryId, + subcategoryId, + callTypeId, + callDate, + entryTypeId, + externalNumber, + objectId, + locationId, + branchId, + impact, + urgency, + priority, + duration, + targetDate, + slaId, + onHold, + operatorId, + operatorGroupId, + supplierId, + processingStatusId, + responded, + responseDate, + completed, + completedDate, + closed, + closedDate, + closureCodeId, + costs, + feedbackMessage, + feedbackRating, + majorCall, + majorCallObject, + publishToSsd, + optionalFields1, + optionalFields2, + } = this; + + // Fields that take { id: value } structure + const idFields = [ + { + value: callerLookupId, + key: "callerLookup", + }, + { + value: categoryId, + key: "category", + }, + { + value: subcategoryId, + key: "subcategory", + }, + { + value: callTypeId, + key: "callType", + }, + { + value: entryTypeId, + key: "entryType", + }, + { + value: objectId, + key: "object", + }, + { + value: locationId, + key: "location", + }, + { + value: branchId, + key: "branch", + }, + { + value: impact, + key: "impact", + }, + { + value: urgency, + key: "urgency", + }, + { + value: priority, + key: "priority", + }, + { + value: duration, + key: "duration", + }, + { + value: slaId, + key: "sla", + }, + { + value: operatorId, + key: "operator", + }, + { + value: operatorGroupId, + key: "operatorGroup", + }, + { + value: supplierId, + key: "supplier", + }, + { + value: processingStatusId, + key: "processingStatus", + }, + { + value: closureCodeId, + key: "closureCode", + }, + ]; + + const response = await app.updateIncident({ + $, + incidentId, + data: { + briefDescription, + request, + action, + actionInvisibleForCaller, + callDate, + externalNumber, + targetDate, + onHold, + responded, + responseDate, + completed, + completedDate, + closed, + closedDate, + costs, + feedbackMessage, + feedbackRating, + majorCall, + majorCallObject, + publishToSsd, + optionalFields1, + optionalFields2, + ...idFields.reduce((acc, { + value, key, + }) => ({ + ...acc, + ...value && { + [key]: { + id: value, + }, + }, + }), {}), + }, + }); + + $.export("$summary", `Successfully updated incident with ID \`${response.id}\``); + + return response; + }, +}; diff --git a/components/topdesk/package.json b/components/topdesk/package.json index 055578ef33969..c29f2269782f0 100644 --- a/components/topdesk/package.json +++ b/components/topdesk/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/topdesk", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream TOPdesk Components", "main": "topdesk.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.1.0" } -} \ No newline at end of file +} diff --git a/components/topdesk/topdesk.app.mjs b/components/topdesk/topdesk.app.mjs index c0c7553307b0c..5b039f7bc0ed7 100644 --- a/components/topdesk/topdesk.app.mjs +++ b/components/topdesk/topdesk.app.mjs @@ -1,11 +1,252 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "topdesk", - propDefinitions: {}, + propDefinitions: { + incidentId: { + type: "string", + label: "Incident ID", + description: "The UUID of the incident", + async options({ prevContext }) { + const { pageStart } = prevContext; + if (pageStart === null) { + return []; + } + const incidents = await this.listIncidents({ + params: { + pageStart, + pageSize: 100, + }, + }); + return { + options: incidents?.map((incident) => ({ + label: incident.briefDescription + ? `${incident.number} - ${incident.briefDescription}` + : incident.number, + value: incident.id, + })) || [], + context: { + pageStart: incidents.length === 100 + ? (pageStart || 0) + 100 + : null, + }, + }; + }, + }, + knowledgeItemId: { + type: "string", + label: "Knowledge Item ID", + description: "The UUID or number of the knowledge item", + async options({ prevContext }) { + const response = await this.listKnowledgeItems({ + params: { + start: prevContext?.start || 0, + page_size: 100, + }, + }); + const items = response.item || []; + return { + options: items.map((item) => ({ + label: `${item.number} - ${item.translation?.content?.title || "Untitled"}`, + value: item.id, + })), + context: { + start: response.next + ? (prevContext?.start || 0) + 100 + : null, + }, + }; + }, + }, + operatorId: { + type: "string", + label: "Operator ID", + description: "The UUID of the operator", + async options({ prevContext }) { + const operators = await this.listOperators({ + params: { + start: prevContext?.start || 0, + page_size: 100, + }, + }); + return { + options: operators?.map((operator) => ({ + label: operator.dynamicName || `${operator.firstName} ${operator.surName}`, + value: operator.id, + })) || [], + context: { + start: operators?.length === 100 + ? (prevContext?.start || 0) + 100 + : null, + }, + }; + }, + }, + personId: { + type: "string", + label: "Person ID", + description: "The UUID of the person", + async options({ prevContext }) { + const persons = await this.listPersons({ + params: { + start: prevContext?.start || 0, + page_size: 100, + }, + }); + return persons.map((person) => ({ + label: `${person.firstName} ${person.surName}`, + value: person.id, + })) || []; + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + getUrl(path) { + return `${this.$auth.api_url}${path}`; + }, + getAuth() { + const { + username, + app_token: password, + } = this.$auth; + return { + username, + password, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this.getUrl(path), + auth: this.getAuth(), + ...opts, + }); + }, + post(opts = {}) { + return this._makeRequest({ + method: "POST", + ...opts, + }); + }, + patch(opts = {}) { + return this._makeRequest({ + method: "PATCH", + ...opts, + }); + }, + listIncidents(opts = {}) { + return this._makeRequest({ + path: "/tas/api/incidents", + ...opts, + }); + }, + getIncident({ + incidentId, ...opts + } = {}) { + return this._makeRequest({ + path: `/tas/api/incidents/id/${incidentId}`, + ...opts, + }); + }, + createIncident(opts = {}) { + return this.post({ + path: "/tas/api/incidents", + ...opts, + }); + }, + updateIncident({ + incidentId, ...opts + } = {}) { + return this.patch({ + path: `/tas/api/incidents/id/${incidentId}`, + ...opts, + }); + }, + listKnowledgeItems(opts = {}) { + return this._makeRequest({ + path: "/services/knowledge-base-v1/knowledgeItems", + ...opts, + }); + }, + getKnowledgeItem({ + itemId, ...opts + } = {}) { + return this._makeRequest({ + path: `/services/knowledge-base-v1/knowledgeItems/${itemId}`, + ...opts, + }); + }, + listKnowledgeItemStatuses(opts = {}) { + return this._makeRequest({ + path: "/services/knowledge-base-v1/knowledgeItemStatuses", + ...opts, + }); + }, + listOperators(opts = {}) { + return this._makeRequest({ + path: "/tas/api/operators", + ...opts, + }); + }, + listPersons(opts = {}) { + return this._makeRequest({ + path: "/tas/api/persons", + ...opts, + }); + }, + async *paginate({ + fn, + fnArgs = {}, + maxResults = 600, + dataField, + }) { + let resourcesCount = 0; + let start = 0; + + while (true) { + const response = await fn({ + ...fnArgs, + params: { + ...fnArgs.params, + start: start + (fnArgs.params?.page_size || 100), + page_size: (fnArgs.params?.page_size || 100), + }, + }); + + // Extract items from response based on dataField or use response directly + const items = dataField + ? (response[dataField] || []) + : (Array.isArray(response) + ? response + : []); + + if (!items.length) { + console.log("No items found"); + return; + } + + for (const item of items) { + yield item; + resourcesCount++; + + if (maxResults && resourcesCount >= maxResults) { + console.log("Reached max results"); + return; + } + } + + const hasNextPage = response.next || (items.length === 100); + + if (!hasNextPage) { + console.log("No more pages found"); + return; + } + + // Auto-increment pagination parameters + start += (fnArgs.params?.page_size || 100); + } }, }, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc1b2c546bcb0..92ed34f11ad62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2870,8 +2870,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 - components/cloud_66: - specifiers: {} + components/cloud_66: {} components/cloud_convert: dependencies: @@ -3400,8 +3399,7 @@ importers: components/cronly: {} - components/crossmint: - specifiers: {} + components/crossmint: {} components/crove_app: dependencies: @@ -14807,7 +14805,11 @@ importers: specifier: ^0.1.4 version: 0.1.6 - components/topdesk: {} + components/topdesk: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 components/topmessage: dependencies: