@mspcopilot/n8n-nodes-connectwise 0.2.1-beta.1 → 0.2.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/{dist-community → dist-beta}/credentials/ConnectWisePsaApi.credentials.js +18 -4
  2. package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/ConnectWisePsa.node.js +14 -2
  3. package/dist-beta/nodes/ConnectWise/PSA/ConnectWisePsaTrigger.node.js +506 -0
  4. package/dist-beta/nodes/ConnectWise/PSA/IfSchedule.node.js +332 -0
  5. package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/descriptions/common.properties.js +17 -2
  6. package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/helpers/hint-collector.js +17 -3
  7. package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/methods/fieldOptions.js +1 -0
  8. package/dist-beta/nodes/ConnectWise/PSA/schema/resources/alert-consolidation.js +451 -0
  9. package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/custom.schema.js +38 -1
  10. package/dist-beta/nodes/ConnectWise/PSA/schema/resources/diagnostics.schema.js +103 -0
  11. package/dist-beta/nodes/ConnectWise/PSA/schema/resources/reports.schema.js +206 -0
  12. package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/ticket.schema.js +11 -1
  13. package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/transport/cache.js +84 -6
  14. package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/transport/client.js +46 -5
  15. package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/transport/version-check.js +7 -7
  16. package/package.json +7 -5
  17. package/dist-community/nodes/ConnectWise/PSA/schema/resources/alert-consolidation.js +0 -42
  18. /package/{dist-community → dist-beta}/LICENSE.md +0 -0
  19. /package/{dist-community → dist-beta}/credentials/connectwise-dark.svg +0 -0
  20. /package/{dist-community → dist-beta}/credentials/connectwise-light.svg +0 -0
  21. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/connectwise-dark.svg +0 -0
  22. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/connectwise-light.svg +0 -0
  23. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/descriptions/common.descriptions.js +0 -0
  24. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/descriptions/index.js +0 -0
  25. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/helpers/customFields.js +0 -0
  26. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/helpers/field-collector.js +0 -0
  27. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/helpers/field-conditions.js +0 -0
  28. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/helpers/index.js +0 -0
  29. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/helpers/logging.js +0 -0
  30. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/helpers/utils.js +0 -0
  31. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/methods/generated.js +0 -0
  32. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/methods/index.js +0 -0
  33. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/converter/index.js +0 -0
  34. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/converter/reference.generator.js +0 -0
  35. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/index.js +0 -0
  36. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/references.js +0 -0
  37. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/reports-descriptions.js +0 -0
  38. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/activity.schema.js +0 -0
  39. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/company.schema.js +0 -0
  40. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/contact.schema.js +0 -0
  41. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/index.js +0 -0
  42. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/members.schema.js +0 -0
  43. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/ticketNote.schema.js +0 -0
  44. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/ticketTask.schema.js +0 -0
  45. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/resources/timeEntry.schema.js +0 -0
  46. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/static-fields.js +0 -0
  47. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/transformer.js +0 -0
  48. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/types/index.js +0 -0
  49. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/types/reference.types.js +0 -0
  50. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/schema/types/resource.types.js +0 -0
  51. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/transport/access-key.js +0 -0
  52. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/transport/index.js +0 -0
  53. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/transport/operations.js +0 -0
  54. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/transport/response-transformer.js +0 -0
  55. /package/{dist-community → dist-beta}/nodes/ConnectWise/PSA/types/n8n-augmentation.d.js +0 -0
  56. /package/{dist-community → dist-beta}/nodes/ConnectWisePsa.node.json +0 -0
@@ -36,7 +36,7 @@ var import_common = require("../nodes/ConnectWise/PSA/descriptions/common.descri
36
36
  class ConnectWisePsaApi {
37
37
  name="connectWisePsaApi";
38
38
  displayName="ConnectWise PSA API";
39
- documentationUrl=`https://${import_common.DOCURL_PREFIX}/n8n-nodes-connectwise/credentials${import_common.DOCURL_SUFFIX}`;
39
+ documentationUrl=`https://${import_common.DOCURL_PREFIX}/credentials${import_common.DOCURL_SUFFIX}`;
40
40
  icon={
41
41
  light: "file:connectwise-light.svg",
42
42
  dark: "file:connectwise-dark.svg"
@@ -112,14 +112,28 @@ class ConnectWisePsaApi {
112
112
  placeholder: import_transport.DEFAULT_API_PATH,
113
113
  hint: `Leave blank to use the default API path (${import_transport.DEFAULT_API_PATH})`,
114
114
  description: "Custom API version path. Leave empty to use the default."
115
- }, {
115
+ }, ...process.env.MSPCOPILOT_BETA !== "true" ? [ {
116
116
  displayName: "Check for Updates",
117
117
  name: "checkForUpdates",
118
118
  type: "boolean",
119
119
  default: !0,
120
- hint: "Current version: 0.2.1-beta.1 (community edition)",
120
+ hint: "Current version: 0.2.1-beta.3 (beta edition)",
121
121
  description: "Check for new versions of this node package and show notifications when updates are available"
122
- } ];
122
+ } ] : [], ...process.env.MSPCOPILOT_BETA === "true" ? [ {
123
+ displayName: "Current version: 0.2.1-beta.3 (beta)",
124
+ name: "versionInfo",
125
+ type: "notice",
126
+ default: ""
127
+ } ] : [], ...process.env.MSPCOPILOT_BETA === "true" ? [ {
128
+ displayName: "Access Key",
129
+ name: "accessKey",
130
+ type: "string",
131
+ typeOptions: {
132
+ password: !0
133
+ },
134
+ default: "",
135
+ hint: "To enable beta or server-side features"
136
+ } ] : [] ];
123
137
  async authenticate(credentials, requestOptions) {
124
138
  const serverUrl = credentials.site === "custom" ? credentials.customServer : credentials.site, base64Auth = Buffer.from(`${credentials.companyId}+${credentials.publicKey}:${credentials.privateKey}`).toString("base64");
125
139
  return requestOptions.baseURL || (requestOptions.baseURL = `https://${serverUrl}`),
@@ -46,10 +46,20 @@ const resources = {
46
46
  schema: import_contact.contactSchema,
47
47
  execute: import_contact.execute
48
48
  },
49
+ ...process.env.MSPCOPILOT_BETA === "true" ? {
50
+ diagnostics: {
51
+ schema: require("./schema/resources/diagnostics.schema").diagnosticsSchema,
52
+ execute: require("./schema/resources/diagnostics.schema").execute
53
+ }
54
+ } : {},
49
55
  members: {
50
56
  schema: import_members.membersSchema,
51
57
  execute: import_members.execute
52
58
  },
59
+ reports: {
60
+ schema: require("./schema/resources/reports.schema").reportsSchema,
61
+ execute: require("./schema/resources/reports.schema").execute
62
+ },
53
63
  ticket: {
54
64
  schema: import_ticket.ticketSchema,
55
65
  execute: import_ticket.execute
@@ -66,6 +76,7 @@ const resources = {
66
76
  schema: import_timeEntry.timeEntrySchema,
67
77
  execute: import_timeEntry.execute
68
78
  },
79
+ ...process.env.MSPCOPILOT_BETA === "true" ? {} : {},
69
80
  custom: {
70
81
  schema: import_custom.customSchema,
71
82
  execute: import_custom.execute
@@ -74,7 +85,7 @@ const resources = {
74
85
 
75
86
  class ConnectWisePsa {
76
87
  constructor() {
77
- console.log(`🔌 ConnectWise PSA Node Loaded | 📦 v0.2.1-beta.1 | 🏗️ Build: ${"community".toUpperCase()} (${"prod".toUpperCase()} mode)`);
88
+ console.log(`🔌 ConnectWise PSA Node Loaded | 📦 v0.2.1-beta.3 | 🏗️ Build: ${"beta".toUpperCase()} (${"prod".toUpperCase()} mode)`);
78
89
  }
79
90
  description={
80
91
  displayName: "ConnectWise PSA",
@@ -152,7 +163,8 @@ class ConnectWisePsa {
152
163
  ...import_methods.loadOptions,
153
164
  loadFieldsOptions: import_fieldOptions.loadFieldsOptions,
154
165
  loadOrderByFieldsOptions: import_fieldOptions.loadOrderByFieldsOptions,
155
- loadCustomFieldsOptions: import_fieldOptions.loadCustomFieldsOptions
166
+ loadCustomFieldsOptions: import_fieldOptions.loadCustomFieldsOptions,
167
+ loadReportColumns: require("./schema/resources/reports.schema").loadReportColumns
156
168
  },
157
169
  listSearch: import_methods.listSearch
158
170
  };
@@ -0,0 +1,506 @@
1
+ var __defProp = Object.defineProperty;
2
+
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+
9
+ var __export = (target, all) => {
10
+ for (var name in all) __defProp(target, name, {
11
+ get: all[name],
12
+ enumerable: !0
13
+ });
14
+ }, __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from == "object" || typeof from == "function") for (let key of __getOwnPropNames(from)) !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, {
16
+ get: () => from[key],
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ return to;
20
+ };
21
+
22
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
23
+ value: !0
24
+ }), mod);
25
+
26
+ var ConnectWisePsaTrigger_node_exports = {};
27
+
28
+ __export(ConnectWisePsaTrigger_node_exports, {
29
+ ConnectWisePsaTrigger: () => ConnectWisePsaTrigger
30
+ });
31
+
32
+ module.exports = __toCommonJS(ConnectWisePsaTrigger_node_exports);
33
+
34
+ var import_n8n_workflow = require("n8n-workflow"), import_methods = require("./methods"), import_client = require("./transport/client"), import_common = require("./descriptions/common.descriptions");
35
+
36
+ const WEBHOOK_REGISTRY_URL = "https://api.mspcopilot.io/watchtower/api/n8n/triggers";
37
+
38
+ function toWatchtowerQuery(query) {
39
+ const keyMap = {
40
+ boardIds: "board.id",
41
+ statusIds: "status.id",
42
+ companyIds: "company.id",
43
+ priorityIds: "priority.id",
44
+ typeIds: "type.id",
45
+ subtypeIds: "subtype.id",
46
+ itemIds: "item.id",
47
+ sourceIds: "source.id"
48
+ }, result = {};
49
+ for (const [key, value] of Object.entries(query)) {
50
+ const mappedKey = keyMap[key] ?? key;
51
+ result[mappedKey] = value;
52
+ }
53
+ return result;
54
+ }
55
+
56
+ function formatWebhookError(error) {
57
+ const e = error, status = e.response?.status, serverMessage = e.response?.data?.message;
58
+ return serverMessage ? status ? `[${status}] ${serverMessage}` : serverMessage : status === 401 ? "[401] Authentication failed. Check your Access Key in credentials." : status === 403 ? "[403] Access denied. Check that your Access Key is valid and the Watchtower service is reachable." : status === 502 || status === 503 || status === 504 ? `[${status}] Watchtower service is temporarily unavailable. Try again in a moment.` : status ? `[${status}] ${e.message ?? "Request failed"}` : e.message ?? "Unknown error";
59
+ }
60
+
61
+ async function notifyWebhookAction(context, options) {
62
+ const {action: action, webhookUrl: webhookUrl, webhookId: webhookId, type: type, query: query, options: triggerOptions, isTest: isTest, metadata: metadata} = options, credentials = await context.getCredentials("connectWisePsaApi"), companyId = credentials.companyId, accessKey = credentials.accessKey;
63
+ if (!accessKey) throw new Error("Access Key is required for trigger functionality. Please add an Access Key in your ConnectWise PSA credentials.");
64
+ await context.helpers.httpRequest({
65
+ method: "POST",
66
+ url: WEBHOOK_REGISTRY_URL,
67
+ headers: {
68
+ "X-Company-Id": companyId,
69
+ Authorization: `Bearer ${accessKey}`
70
+ },
71
+ body: {
72
+ action: action,
73
+ webhookId: webhookId,
74
+ webhookUrl: webhookUrl,
75
+ type: type,
76
+ query: action === "delete" ? void 0 : query ? toWatchtowerQuery(query) : void 0,
77
+ options: action === "delete" ? void 0 : triggerOptions,
78
+ isTest: isTest,
79
+ metadata: {
80
+ workflowId: metadata?.workflowId,
81
+ workflowName: metadata?.workflowName
82
+ }
83
+ },
84
+ json: !0
85
+ });
86
+ }
87
+
88
+ function getFilterParameters(context) {
89
+ const getParam = (name, defaultValue) => {
90
+ try {
91
+ return context.getNodeParameter(name, defaultValue);
92
+ } catch {
93
+ return defaultValue;
94
+ }
95
+ }, resource = getParam("resource", "ticket"), query = {}, boardIds = getParam("boardIds", []);
96
+ boardIds.length > 0 && (query.boardIds = boardIds);
97
+ const statusIds = getParam("statusIds", []);
98
+ statusIds.length > 0 && (query.statusIds = statusIds);
99
+ const priorityIds = getParam("priorityIds", []);
100
+ priorityIds.length > 0 && (query.priorityIds = priorityIds);
101
+ const sourceIds = getParam("sourceIds", []);
102
+ sourceIds.length > 0 && (query.sourceIds = sourceIds);
103
+ const options = {}, timingConditions = getParam("timingConditions", {});
104
+ timingConditions.minTicketAge && (options.minTicketAge = timingConditions.minTicketAge),
105
+ timingConditions.minTimeInStatus && (options.minTimeInStatus = timingConditions.minTimeInStatus);
106
+ const triggerOn = getParam("triggerOn", [ "enter" ]);
107
+ if (options.onEnter = triggerOn.includes("enter"), options.onExit = triggerOn.includes("exit"),
108
+ options.onCreate = triggerOn.includes("create"), options.onUpdate = triggerOn.includes("update"),
109
+ options.onDelete = triggerOn.includes("delete"), triggerOn.includes("repeat")) {
110
+ const repeatEvery = getParam("repeatEvery", "");
111
+ if (!repeatEvery) throw new Error("Repeat Every is required when Repeat lifecycle event is selected");
112
+ options.repeatEvery = repeatEvery;
113
+ const maxRepeats = getParam("maxRepeats", "");
114
+ options.maxRepeats = maxRepeats !== "" && Number(maxRepeats) > 0 ? Number(maxRepeats) : 0;
115
+ }
116
+ const skipExistingMatches = getParam("skipExistingMatches", !0);
117
+ options.skipExistingMatches = skipExistingMatches;
118
+ const rearm = getParam("rearm", "never");
119
+ if (options.rearm = rearm, rearm === "cooldown") {
120
+ const cooldown = getParam("cooldown", "");
121
+ if (!cooldown) throw new Error("Cooldown Duration is required when rearm is set to Notify After Cooldown");
122
+ options.cooldown = cooldown;
123
+ }
124
+ return {
125
+ resource: resource,
126
+ query: query,
127
+ options: options
128
+ };
129
+ }
130
+
131
+ class ConnectWisePsaTrigger {
132
+ description={
133
+ hidden: process.env.MSPCOPILOT_BETA !== "true",
134
+ displayName: "ConnectWise PSA Trigger",
135
+ name: "connectWisePsaTrigger",
136
+ icon: {
137
+ light: "file:connectwise-light.svg",
138
+ dark: "file:connectwise-dark.svg"
139
+ },
140
+ group: [ "trigger" ],
141
+ version: 1,
142
+ subtitle: '={{{"ticket": "Ticket"}[$parameter["resource"]]}}',
143
+ description: "Trigger on ConnectWise PSA events",
144
+ defaults: {
145
+ name: "ConnectWise PSA Trigger"
146
+ },
147
+ inputs: [],
148
+ outputs: [ import_n8n_workflow.NodeConnectionType.Main ],
149
+ credentials: [ {
150
+ name: "connectWisePsaApi",
151
+ required: !0
152
+ } ],
153
+ webhooks: [ {
154
+ name: "default",
155
+ httpMethod: "POST",
156
+ responseMode: "onReceived",
157
+ path: "webhook"
158
+ } ],
159
+ properties: [ {
160
+ displayName: `Powered by Watchtower — see the <a href="https://${import_common.DOCURL_PREFIX}/ticket-trigger${import_common.DOCURL_SUFFIX}" target="_blank">Ticket Trigger Guide</a> for setup and details.`,
161
+ name: "triggerNotice",
162
+ type: "notice",
163
+ default: ""
164
+ }, {
165
+ displayName: "Trigger On",
166
+ name: "resource",
167
+ type: "options",
168
+ noDataExpression: !0,
169
+ options: [ {
170
+ name: "Ticket",
171
+ value: "ticket",
172
+ description: "Trigger on service ticket events"
173
+ } ],
174
+ default: "ticket"
175
+ }, {
176
+ displayName: "Lifecycle Events",
177
+ name: "triggerOn",
178
+ type: "multiOptions",
179
+ displayOptions: {
180
+ show: {
181
+ resource: [ "ticket" ]
182
+ }
183
+ },
184
+ options: [ {
185
+ name: "Enter",
186
+ value: "enter",
187
+ description: "When a ticket first matches conditions"
188
+ }, {
189
+ name: "Repeat",
190
+ value: "repeat",
191
+ description: "Periodically while a ticket continues matching conditions"
192
+ }, {
193
+ name: "Exit",
194
+ value: "exit",
195
+ description: "When a ticket stops matching conditions"
196
+ }, {
197
+ name: "Create",
198
+ value: "create",
199
+ description: "When a ticket is first created and matches conditions"
200
+ }, {
201
+ name: "Update",
202
+ value: "update",
203
+ description: "Immediately when a matching ticket is updated in ConnectWise"
204
+ }, {
205
+ name: "Delete",
206
+ value: "delete",
207
+ description: "Immediately when a matching ticket is deleted from ConnectWise"
208
+ } ],
209
+ default: [ "enter" ],
210
+ description: "Which lifecycle events should fire the trigger"
211
+ }, {
212
+ displayName: "Ticket Type",
213
+ name: "ticketType",
214
+ type: "options",
215
+ options: [ {
216
+ name: "Service Ticket",
217
+ value: "service"
218
+ } ],
219
+ default: "service",
220
+ description: "Type of ticket to monitor"
221
+ }, {
222
+ displayName: "Board Names or IDs",
223
+ name: "boardIds",
224
+ type: "multiOptions",
225
+ typeOptions: {
226
+ loadOptionsMethod: "loadOptions_service_boards",
227
+ loadOptionsDependsOn: [ "ticketType" ]
228
+ },
229
+ displayOptions: {
230
+ show: {
231
+ resource: [ "ticket" ]
232
+ }
233
+ },
234
+ default: [],
235
+ description: 'Filter by boards. Leave empty for all boards. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.'
236
+ }, {
237
+ displayName: "Status Names or IDs",
238
+ name: "statusIds",
239
+ type: "multiOptions",
240
+ typeOptions: {
241
+ loadOptionsMethod: "loadOptions_service_statuses_all",
242
+ loadOptionsDependsOn: [ "boardIds" ]
243
+ },
244
+ displayOptions: {
245
+ show: {
246
+ resource: [ "ticket" ]
247
+ }
248
+ },
249
+ default: [],
250
+ description: 'Filter by statuses (select boards first to filter). Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.'
251
+ }, {
252
+ displayName: "Priority Names or IDs",
253
+ name: "priorityIds",
254
+ type: "multiOptions",
255
+ typeOptions: {
256
+ loadOptionsMethod: "loadOptions_service_priorities"
257
+ },
258
+ displayOptions: {
259
+ show: {
260
+ resource: [ "ticket" ]
261
+ }
262
+ },
263
+ default: [],
264
+ description: 'Filter by priorities. Leave empty for all priorities. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.'
265
+ }, {
266
+ displayName: "Source Names or IDs",
267
+ name: "sourceIds",
268
+ type: "multiOptions",
269
+ typeOptions: {
270
+ loadOptionsMethod: "loadOptions_service_sources"
271
+ },
272
+ displayOptions: {
273
+ show: {
274
+ resource: [ "ticket" ]
275
+ }
276
+ },
277
+ default: [],
278
+ description: 'Filter by ticket sources. Leave empty for all sources. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.'
279
+ }, {
280
+ displayName: "Timing Conditions",
281
+ name: "timingConditions",
282
+ type: "collection",
283
+ placeholder: "Add Condition",
284
+ displayOptions: {
285
+ show: {
286
+ resource: [ "ticket" ]
287
+ }
288
+ },
289
+ default: {},
290
+ options: [ {
291
+ displayName: "Minimum Ticket Age",
292
+ name: "minTicketAge",
293
+ type: "string",
294
+ default: "",
295
+ placeholder: "15m, 2h, 3d",
296
+ description: "Only trigger for tickets older than this duration"
297
+ }, {
298
+ displayName: "Minimum Time in Current Status",
299
+ name: "minTimeInStatus",
300
+ type: "string",
301
+ default: "",
302
+ placeholder: "30m, 1h, 1d",
303
+ description: "Only trigger for tickets in current status longer than this"
304
+ } ]
305
+ }, {
306
+ displayName: "Repeat Every",
307
+ name: "repeatEvery",
308
+ type: "string",
309
+ displayOptions: {
310
+ show: {
311
+ resource: [ "ticket" ],
312
+ triggerOn: [ "repeat" ]
313
+ }
314
+ },
315
+ default: "15m",
316
+ placeholder: "15m, 1h, 2d",
317
+ description: "How often to send repeat notifications"
318
+ }, {
319
+ displayName: "Maximum Repeats",
320
+ name: "maxRepeats",
321
+ type: "number",
322
+ displayOptions: {
323
+ show: {
324
+ resource: [ "ticket" ],
325
+ triggerOn: [ "repeat" ]
326
+ }
327
+ },
328
+ typeOptions: {
329
+ minValue: 1
330
+ },
331
+ default: "",
332
+ placeholder: "Unlimited",
333
+ description: "Maximum repeat notifications per ticket. Leave blank for unlimited."
334
+ }, {
335
+ displayName: "If Ticket Re-Enters",
336
+ name: "rearm",
337
+ type: "options",
338
+ displayOptions: {
339
+ show: {
340
+ resource: [ "ticket" ]
341
+ }
342
+ },
343
+ options: [ {
344
+ name: "Never Notify Again",
345
+ value: "never",
346
+ description: "Once triggered, will not trigger again even if it re-enters"
347
+ }, {
348
+ name: "Notify Immediately",
349
+ value: "immediate",
350
+ description: "Trigger again immediately when ticket re-enters"
351
+ }, {
352
+ name: "Notify After Cooldown",
353
+ value: "cooldown",
354
+ description: "Wait for cooldown period before allowing re-trigger"
355
+ } ],
356
+ default: "never",
357
+ description: "Only applies to Enter events. What happens if a ticket exits and later re-enters conditions.",
358
+ hint: "This setting controls re-entry behavior for Enter events only"
359
+ }, {
360
+ displayName: "Cooldown Duration",
361
+ name: "cooldown",
362
+ type: "string",
363
+ displayOptions: {
364
+ show: {
365
+ resource: [ "ticket" ],
366
+ rearm: [ "cooldown" ]
367
+ }
368
+ },
369
+ default: "15m",
370
+ placeholder: "15m, 1h, 2d",
371
+ description: "How long to wait before ticket can re-trigger"
372
+ }, {
373
+ displayName: "Skip Existing Matches",
374
+ name: "skipExistingMatches",
375
+ type: "boolean",
376
+ displayOptions: {
377
+ show: {
378
+ resource: [ "ticket" ]
379
+ }
380
+ },
381
+ default: !0,
382
+ description: "On creation or update, don't trigger for existing matches"
383
+ } ]
384
+ };
385
+ methods={
386
+ loadOptions: {
387
+ ...import_methods.loadOptions,
388
+ async loadOptions_service_statuses_all() {
389
+ let selectedBoardIds = [];
390
+ try {
391
+ const boardIds = this.getNodeParameter("boardIds", []);
392
+ Array.isArray(boardIds) && boardIds.length > 0 && (selectedBoardIds = boardIds);
393
+ } catch {}
394
+ let boards;
395
+ if (selectedBoardIds.length > 0) {
396
+ const boardConditions = selectedBoardIds.map(id => `id=${id}`).join(" or ");
397
+ boards = await import_client.connectWiseApiRequest.call(this, "GET", "/service/boards", {}, {
398
+ conditions: `(${boardConditions})`,
399
+ fields: "id,name",
400
+ orderBy: "name",
401
+ pageSize: 100
402
+ }, {
403
+ optionsCache: !0
404
+ });
405
+ } else boards = await import_client.connectWiseApiRequest.call(this, "GET", "/service/boards", {}, {
406
+ conditions: "inactiveFlag=false and projectFlag=false",
407
+ fields: "id,name",
408
+ orderBy: "name",
409
+ pageSize: 100
410
+ }, {
411
+ optionsCache: !0
412
+ });
413
+ const statusPromises = boards.map(async board => {
414
+ try {
415
+ return (await import_client.connectWiseApiRequest.call(this, "GET", `/service/boards/${board.id}/statuses`, {}, {
416
+ fields: "id,name",
417
+ orderBy: "sortOrder",
418
+ pageSize: 100
419
+ }, {
420
+ optionsCache: !0
421
+ })).map(status => ({
422
+ statusName: status.name,
423
+ boardName: board.name,
424
+ value: status.id
425
+ }));
426
+ } catch {
427
+ return [];
428
+ }
429
+ }), allStatuses = (await Promise.all(statusPromises)).flat();
430
+ return allStatuses.sort((a, b) => {
431
+ const nameCompare = a.statusName.localeCompare(b.statusName);
432
+ return nameCompare !== 0 ? nameCompare : a.boardName.localeCompare(b.boardName);
433
+ }), allStatuses.map(s => ({
434
+ name: s.statusName,
435
+ value: s.value,
436
+ description: s.boardName
437
+ }));
438
+ }
439
+ }
440
+ };
441
+ webhookMethods={
442
+ default: {
443
+ async checkExists() {
444
+ return !1;
445
+ },
446
+ async create() {
447
+ const webhookUrl = this.getNodeWebhookUrl("default") ?? "", {resource: resource, query: query, options: options} = getFilterParameters(this), node = this.getNode(), workflow = this.getWorkflow(), isTest = webhookUrl.includes("/webhook-test/");
448
+ try {
449
+ await notifyWebhookAction(this, {
450
+ action: "create",
451
+ webhookUrl: webhookUrl,
452
+ webhookId: node.webhookId,
453
+ type: resource,
454
+ query: query,
455
+ options: options,
456
+ isTest: isTest,
457
+ metadata: {
458
+ workflowId: workflow.id,
459
+ workflowName: workflow.name
460
+ }
461
+ });
462
+ } catch (error) {
463
+ throw console.error("Webhook registration failed:", formatWebhookError(error)),
464
+ new import_n8n_workflow.NodeOperationError(node, formatWebhookError(error));
465
+ }
466
+ return !0;
467
+ },
468
+ async delete() {
469
+ const webhookUrl = this.getNodeWebhookUrl("default") ?? "", isTest = webhookUrl.includes("/webhook-test/");
470
+ if (isTest) return !0;
471
+ const {resource: resource} = getFilterParameters(this), node = this.getNode(), workflow = this.getWorkflow();
472
+ try {
473
+ await notifyWebhookAction(this, {
474
+ action: "delete",
475
+ webhookUrl: webhookUrl,
476
+ webhookId: node.webhookId,
477
+ type: resource,
478
+ isTest: isTest,
479
+ metadata: {
480
+ workflowId: workflow.id,
481
+ workflowName: workflow.name
482
+ }
483
+ });
484
+ } catch (error) {
485
+ console.error("Webhook deregistration failed:", formatWebhookError(error));
486
+ }
487
+ return !0;
488
+ }
489
+ }
490
+ };
491
+ async webhook() {
492
+ const req = this.getRequestObject(), workflow = this.getWorkflow(), executionId = this.getExecutionId(), baseUrl = this.getInstanceBaseUrl();
493
+ return {
494
+ workflowData: [ this.helpers.returnJsonArray(req.body) ],
495
+ webhookResponse: {
496
+ executionId: executionId,
497
+ workflowId: workflow.id,
498
+ executionUrl: executionId ? `${baseUrl}workflow/${workflow.id}/executions/${executionId}` : `${baseUrl}workflow/${workflow.id}/executions`
499
+ }
500
+ };
501
+ }
502
+ }
503
+
504
+ 0 && (module.exports = {
505
+ ConnectWisePsaTrigger: ConnectWisePsaTrigger
506
+ });