@mspcopilot/n8n-nodes-connectwise 0.1.8 → 0.2.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/dist-community/credentials/ConnectWisePsaApi.credentials.js +1 -1
- package/dist-community/nodes/ConnectWise/PSA/ConnectWisePsa.node.js +25 -20
- package/dist-community/nodes/ConnectWise/PSA/descriptions/common.descriptions.js +3 -1
- package/dist-community/nodes/ConnectWise/PSA/helpers/hint-collector.js +187 -0
- package/dist-community/nodes/ConnectWise/PSA/helpers/utils.js +3 -4
- package/dist-community/nodes/ConnectWise/PSA/schema/converter/reference.generator.js +30 -10
- package/dist-community/nodes/ConnectWise/PSA/schema/references.js +2 -1
- package/dist-community/nodes/ConnectWise/PSA/schema/resources/company.schema.js +1 -10
- package/dist-community/nodes/ConnectWise/PSA/schema/resources/custom.schema.js +21 -1
- package/dist-community/nodes/ConnectWise/PSA/schema/resources/diagnostics.schema.js +5 -8
- package/dist-community/nodes/ConnectWise/PSA/schema/resources/ticket.schema.js +10 -1
- package/dist-community/nodes/ConnectWise/PSA/schema/static-fields.js +1 -1
- package/dist-community/nodes/ConnectWise/PSA/transport/cache.js +13 -3
- package/dist-community/nodes/ConnectWise/PSA/transport/client.js +12 -17
- package/dist-community/nodes/ConnectWise/PSA/transport/operations.js +19 -9
- package/dist-community/nodes/ConnectWise/PSA/transport/version-check.js +6 -8
- package/package.json +2 -2
|
@@ -117,7 +117,7 @@ class ConnectWisePsaApi {
|
|
|
117
117
|
name: "checkForUpdates",
|
|
118
118
|
type: "boolean",
|
|
119
119
|
default: !0,
|
|
120
|
-
hint: "Current version: 0.
|
|
120
|
+
hint: "Current version: 0.2.0 (community edition)",
|
|
121
121
|
description: "Check for new versions of this node package and show notifications when updates are available"
|
|
122
122
|
} ];
|
|
123
123
|
async authenticate(credentials, requestOptions) {
|
|
@@ -31,7 +31,7 @@ __export(ConnectWisePsa_node_exports, {
|
|
|
31
31
|
|
|
32
32
|
module.exports = __toCommonJS(ConnectWisePsa_node_exports);
|
|
33
33
|
|
|
34
|
-
var import_n8n_workflow = require("n8n-workflow"), import_methods = require("./methods"), import_fieldOptions = require("./methods/fieldOptions"), import_activity = require("./schema/resources/activity.schema"), import_company = require("./schema/resources/company.schema"), import_contact = require("./schema/resources/contact.schema"), import_diagnostics = require("./schema/resources/diagnostics.schema"), import_members = require("./schema/resources/members.schema"), import_reports = require("./schema/resources/reports.schema"), import_ticket = require("./schema/resources/ticket.schema"), import_ticketNote = require("./schema/resources/ticketNote.schema"), import_ticketTask = require("./schema/resources/ticketTask.schema"), import_timeEntry = require("./schema/resources/timeEntry.schema"), import_custom = require("./schema/resources/custom.schema");
|
|
34
|
+
var import_n8n_workflow = require("n8n-workflow"), import_methods = require("./methods"), import_hint_collector = require("./helpers/hint-collector"), import_fieldOptions = require("./methods/fieldOptions"), import_activity = require("./schema/resources/activity.schema"), import_company = require("./schema/resources/company.schema"), import_contact = require("./schema/resources/contact.schema"), import_diagnostics = require("./schema/resources/diagnostics.schema"), import_members = require("./schema/resources/members.schema"), import_reports = require("./schema/resources/reports.schema"), import_ticket = require("./schema/resources/ticket.schema"), import_ticketNote = require("./schema/resources/ticketNote.schema"), import_ticketTask = require("./schema/resources/ticketTask.schema"), import_timeEntry = require("./schema/resources/timeEntry.schema"), import_custom = require("./schema/resources/custom.schema");
|
|
35
35
|
|
|
36
36
|
const resources = {
|
|
37
37
|
activity: {
|
|
@@ -74,7 +74,7 @@ const resources = {
|
|
|
74
74
|
|
|
75
75
|
class ConnectWisePsa {
|
|
76
76
|
constructor() {
|
|
77
|
-
console.log(`🔌 ConnectWise PSA Node Loaded | 📦 v0.
|
|
77
|
+
console.log(`🔌 ConnectWise PSA Node Loaded | 📦 v0.2.0 | 🏗️ Build: ${"community".toUpperCase()} (${"prod".toUpperCase()} mode)`);
|
|
78
78
|
}
|
|
79
79
|
description={
|
|
80
80
|
displayName: "ConnectWise PSA",
|
|
@@ -159,26 +159,31 @@ class ConnectWisePsa {
|
|
|
159
159
|
};
|
|
160
160
|
async execute() {
|
|
161
161
|
const items = this.getInputData(), returnData = [], resource = this.getNodeParameter("resource", 0), operation = this.getNodeParameter("operation", 0);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
162
|
+
(0, import_hint_collector.initHints)(this);
|
|
163
|
+
try {
|
|
164
|
+
for (let i = 0; i < items.length; i++) try {
|
|
165
|
+
const resourceData = resources[resource];
|
|
166
|
+
if (!resourceData) throw new import_n8n_workflow.NodeOperationError(this.getNode(), `Resource ${resource} not found`);
|
|
167
|
+
const responseData = await resourceData.execute.call(this, operation, i);
|
|
168
|
+
returnData.push(...responseData);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (this.continueOnFail()) {
|
|
171
|
+
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray({
|
|
172
|
+
error: error.message
|
|
173
|
+
}), {
|
|
174
|
+
itemData: {
|
|
175
|
+
item: i
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
returnData.push(...executionData);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
throw error;
|
|
178
182
|
}
|
|
179
|
-
|
|
183
|
+
return [ returnData ];
|
|
184
|
+
} finally {
|
|
185
|
+
(0, import_hint_collector.flushHints)(this);
|
|
180
186
|
}
|
|
181
|
-
return [ returnData ];
|
|
182
187
|
}
|
|
183
188
|
}
|
|
184
189
|
|
|
@@ -29,18 +29,20 @@ __export(common_descriptions_exports, {
|
|
|
29
29
|
CHANGELOG_URL: () => CHANGELOG_URL,
|
|
30
30
|
DOCURL_PREFIX: () => DOCURL_PREFIX,
|
|
31
31
|
DOCURL_SUFFIX: () => DOCURL_SUFFIX,
|
|
32
|
+
SUPPORTER_URL: () => SUPPORTER_URL,
|
|
32
33
|
VERSION_CHECK_CACHE_TTL: () => VERSION_CHECK_CACHE_TTL,
|
|
33
34
|
VERSION_CHECK_URL: () => VERSION_CHECK_URL
|
|
34
35
|
});
|
|
35
36
|
|
|
36
37
|
module.exports = __toCommonJS(common_descriptions_exports);
|
|
37
38
|
|
|
38
|
-
const DOCURL_PREFIX = "mspcopilot.io/docs", DOCURL_SUFFIX = "?utm_source=n8n_node_connectwise", CHANGELOG_URL = "mspcopilot.io/changelog${DOCURL_SUFFIX}", VERSION_CHECK_URL = "https://
|
|
39
|
+
const DOCURL_PREFIX = "mspcopilot.io/docs/n8n-nodes-connectwise", DOCURL_SUFFIX = "?utm_source=n8n_node_connectwise", CHANGELOG_URL = "mspcopilot.io/changelog${DOCURL_SUFFIX}", SUPPORTER_URL = "mspcopilot.io/support?utm_source=n8n", VERSION_CHECK_URL = "https://api.mspcopilot.io/version-check/n8n-nodes-connectwise", VERSION_CHECK_CACHE_TTL = 36e5;
|
|
39
40
|
|
|
40
41
|
0 && (module.exports = {
|
|
41
42
|
CHANGELOG_URL: CHANGELOG_URL,
|
|
42
43
|
DOCURL_PREFIX: DOCURL_PREFIX,
|
|
43
44
|
DOCURL_SUFFIX: DOCURL_SUFFIX,
|
|
45
|
+
SUPPORTER_URL: SUPPORTER_URL,
|
|
44
46
|
VERSION_CHECK_CACHE_TTL: VERSION_CHECK_CACHE_TTL,
|
|
45
47
|
VERSION_CHECK_URL: VERSION_CHECK_URL
|
|
46
48
|
});
|
|
@@ -0,0 +1,187 @@
|
|
|
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 hint_collector_exports = {};
|
|
27
|
+
|
|
28
|
+
__export(hint_collector_exports, {
|
|
29
|
+
flushHints: () => flushHints,
|
|
30
|
+
hint: () => hint,
|
|
31
|
+
initHints: () => initHints
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
module.exports = __toCommonJS(hint_collector_exports);
|
|
35
|
+
|
|
36
|
+
var import_common = require("../descriptions/common.descriptions");
|
|
37
|
+
|
|
38
|
+
const HINT_TYPES = {
|
|
39
|
+
items_retrieved: {
|
|
40
|
+
aggregate: "sum",
|
|
41
|
+
template: (total, count) => count === 1 ? `Retrieved ${total} items` : `Retrieved ${total} items across ${count} API calls`
|
|
42
|
+
},
|
|
43
|
+
pagination_limit: {
|
|
44
|
+
aggregate: "dedupe",
|
|
45
|
+
template: data => `Pagination limit reached: Retrieved ${data.itemCount} items across ${data.maxPages} pages. Additional records may exist.`
|
|
46
|
+
},
|
|
47
|
+
cache_hit: {
|
|
48
|
+
aggregate: "count",
|
|
49
|
+
template: count => count === 1 ? "1 request served from cache" : `${count} requests served from cache`
|
|
50
|
+
},
|
|
51
|
+
cache_stored: {
|
|
52
|
+
aggregate: "latest",
|
|
53
|
+
template: ttl => `Response cached for ${ttl}`
|
|
54
|
+
},
|
|
55
|
+
cache_invalidated: {
|
|
56
|
+
aggregate: "count",
|
|
57
|
+
template: count => count === 1 ? "Cache entry invalidated" : `${count} cache entries invalidated`
|
|
58
|
+
},
|
|
59
|
+
cache_upgrade: {
|
|
60
|
+
aggregate: "dedupe",
|
|
61
|
+
template: () => `✨ Caching is available for supporters only. Learn more at <a href="https://${import_common.SUPPORTER_URL}">${import_common.SUPPORTER_URL.split("?")[0]}</a>`
|
|
62
|
+
},
|
|
63
|
+
cache_error: {
|
|
64
|
+
aggregate: "dedupe",
|
|
65
|
+
template: error => `⚠️ Cache error: ${error}`
|
|
66
|
+
},
|
|
67
|
+
version_update: {
|
|
68
|
+
aggregate: "dedupe",
|
|
69
|
+
template: data => `🎉 ConnectWise PSA Node v${data.version} is available! <a href="https://${import_common.CHANGELOG_URL}" target="_blank">View changelog</a>` + (data.message ? `<br>${data.message}` : "")
|
|
70
|
+
},
|
|
71
|
+
pro_upgrade: {
|
|
72
|
+
aggregate: "dedupe",
|
|
73
|
+
template: () => `✨ This helpful feature is available to supporters. See <a href="https://${import_common.SUPPORTER_URL}">${import_common.SUPPORTER_URL.split("?")[0]}</a> for other benefits.<br><br><hr><br>Simplify and speed up your development with these other functions. <ul><li>Smart Alert Consolidation</li><li>Reports</li><li>Ticket Management</li></ul>`
|
|
74
|
+
},
|
|
75
|
+
custom: {
|
|
76
|
+
aggregate: "dedupe",
|
|
77
|
+
template: message => message
|
|
78
|
+
}
|
|
79
|
+
}, collectors = /* @__PURE__ */ new WeakMap;
|
|
80
|
+
|
|
81
|
+
function initHints(ctx) {
|
|
82
|
+
collectors.set(ctx, {
|
|
83
|
+
values: /* @__PURE__ */ new Map
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function hint(ctx, type, value) {
|
|
88
|
+
const collector = collectors.get(ctx);
|
|
89
|
+
if (!collector) {
|
|
90
|
+
emitHintDirectly(ctx, type, value);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let values = collector.values.get(type);
|
|
94
|
+
switch (values || (values = [], collector.values.set(type, values)), (HINT_TYPES[type] || HINT_TYPES.custom).aggregate) {
|
|
95
|
+
case "dedupe":
|
|
96
|
+
values.length === 0 && values.push(value), type === "custom" && !values.includes(value) && values.push(value);
|
|
97
|
+
break;
|
|
98
|
+
|
|
99
|
+
case "count":
|
|
100
|
+
values.push(1);
|
|
101
|
+
break;
|
|
102
|
+
|
|
103
|
+
case "sum":
|
|
104
|
+
values.push(typeof value == "number" ? value : 0);
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case "latest":
|
|
108
|
+
values.length = 0, values.push(value);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function flushHints(ctx) {
|
|
114
|
+
const collector = collectors.get(ctx);
|
|
115
|
+
if (!collector) return;
|
|
116
|
+
if (!("addExecutionHints" in ctx)) {
|
|
117
|
+
collectors.delete(ctx);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const hints = [];
|
|
121
|
+
for (const [type, values] of collector.values) {
|
|
122
|
+
if (values.length === 0) continue;
|
|
123
|
+
const config = HINT_TYPES[type] || HINT_TYPES.custom;
|
|
124
|
+
let message;
|
|
125
|
+
switch (config.aggregate) {
|
|
126
|
+
case "dedupe":
|
|
127
|
+
if (type === "custom") {
|
|
128
|
+
for (const val of values) val !== void 0 && hints.push({
|
|
129
|
+
message: config.template(val),
|
|
130
|
+
location: "outputPane"
|
|
131
|
+
});
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
message = config.template(values[0]);
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
case "count":
|
|
138
|
+
message = config.template(values.length);
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
case "sum":
|
|
142
|
+
const total = values.reduce((acc, v) => acc + (typeof v == "number" ? v : 0), 0);
|
|
143
|
+
message = config.template(total, values.length);
|
|
144
|
+
break;
|
|
145
|
+
|
|
146
|
+
case "latest":
|
|
147
|
+
message = config.template(values[values.length - 1]);
|
|
148
|
+
break;
|
|
149
|
+
|
|
150
|
+
default:
|
|
151
|
+
message = config.template(values[0]);
|
|
152
|
+
}
|
|
153
|
+
hints.push({
|
|
154
|
+
message: message,
|
|
155
|
+
location: "outputPane"
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
hints.length > 0 && ctx.addExecutionHints(...hints), collectors.delete(ctx);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function emitHintDirectly(ctx, type, value) {
|
|
162
|
+
if (!("addExecutionHints" in ctx)) return;
|
|
163
|
+
const config = HINT_TYPES[type] || HINT_TYPES.custom;
|
|
164
|
+
let message;
|
|
165
|
+
switch (config.aggregate) {
|
|
166
|
+
case "count":
|
|
167
|
+
message = config.template(1);
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case "sum":
|
|
171
|
+
message = config.template(value ?? 0, 1);
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
default:
|
|
175
|
+
message = config.template(value);
|
|
176
|
+
}
|
|
177
|
+
ctx.addExecutionHints({
|
|
178
|
+
message: message,
|
|
179
|
+
location: "outputPane"
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
0 && (module.exports = {
|
|
184
|
+
flushHints: flushHints,
|
|
185
|
+
hint: hint,
|
|
186
|
+
initHints: initHints
|
|
187
|
+
});
|
|
@@ -35,13 +35,12 @@ __export(utils_exports, {
|
|
|
35
35
|
|
|
36
36
|
module.exports = __toCommonJS(utils_exports);
|
|
37
37
|
|
|
38
|
+
var import_hint_collector = require("./hint-collector");
|
|
39
|
+
|
|
38
40
|
const ms = require("ms");
|
|
39
41
|
|
|
40
42
|
function returnProOnlyMessage(context, itemIndex = 0) {
|
|
41
|
-
return
|
|
42
|
-
message: '✨ This helpful feature is available to supporters. See <a href="https://mspcopilot.io/support?utm_source=n8n">MSPCopilot.io/support</a> for other benefits.<br><br><hr><br>Simplify and speed up your development with these other functions. <ul><li>Smart Alert Consolidation</li><li>Reports</li><li>Ticket Management</li></ul>',
|
|
43
|
-
location: "outputPane"
|
|
44
|
-
}), context.helpers.constructExecutionMetaData(context.helpers.returnJsonArray([]), {
|
|
43
|
+
return (0, import_hint_collector.hint)(context, "pro_upgrade"), context.helpers.constructExecutionMetaData(context.helpers.returnJsonArray([]), {
|
|
45
44
|
itemData: {
|
|
46
45
|
item: itemIndex
|
|
47
46
|
}
|
|
@@ -36,6 +36,11 @@ module.exports = __toCommonJS(reference_generator_exports);
|
|
|
36
36
|
|
|
37
37
|
var import_transport = require("../../transport"), import_references = require("../references"), import_reports_descriptions = require("../reports-descriptions");
|
|
38
38
|
|
|
39
|
+
function buildRecordUrl(credentials, recordType, recordId) {
|
|
40
|
+
const isCustom = credentials.site === "custom", apiHost = isCustom ? credentials.customServer : credentials.site, hostname = !isCustom && apiHost.startsWith("api-") ? apiHost.replace("api-", "") : apiHost, version = (credentials.apiPath || import_transport.DEFAULT_API_PATH).split("/")[0];
|
|
41
|
+
return `https://${hostname}/${version}/services/system_io/router/openrecord.rails?recordType=${recordType}&recid=${recordId}&companyName=${credentials.companyId}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
const NOT_SPECIFIED_VALUE = "", NOT_SPECIFIED_DISPLAY = " ";
|
|
40
45
|
|
|
41
46
|
function getNestedProperty(obj, path) {
|
|
@@ -90,14 +95,20 @@ async function loadOptionsCore(config, referenceKey) {
|
|
|
90
95
|
}
|
|
91
96
|
let endpoint;
|
|
92
97
|
if (typeof configWithDefaults.endpoint == "function") {
|
|
93
|
-
endpoint = configWithDefaults.endpoint(allParams)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
if (endpoint = configWithDefaults.endpoint(allParams), endpoint.includes("/undefined/") || endpoint.includes("//") || endpoint.endsWith("/undefined")) return [ {
|
|
99
|
+
name: NOT_SPECIFIED_DISPLAY,
|
|
100
|
+
value: NOT_SPECIFIED_VALUE
|
|
101
|
+
} ];
|
|
102
|
+
const numericIdPatterns = [ /^\/service\/boards\/([^\/]+)/, /^\/project\/projects\/([^\/]+)/ ];
|
|
103
|
+
for (const pattern of numericIdPatterns) {
|
|
104
|
+
const match = endpoint.match(pattern);
|
|
105
|
+
if (match) {
|
|
106
|
+
const idValue = match[1];
|
|
107
|
+
if (isNaN(Number(idValue))) return [ {
|
|
108
|
+
name: NOT_SPECIFIED_DISPLAY,
|
|
109
|
+
value: NOT_SPECIFIED_VALUE
|
|
110
|
+
} ];
|
|
111
|
+
}
|
|
101
112
|
}
|
|
102
113
|
} else endpoint = configWithDefaults.endpoint;
|
|
103
114
|
const items = await import_transport.connectWiseApiRequest.call(this, "GET", endpoint, {}, queryParams, {
|
|
@@ -152,9 +163,18 @@ async function searchConnectWise(referenceKey, filter) {
|
|
|
152
163
|
const baseConditions = config.conditions ? config.conditions(params) : "";
|
|
153
164
|
return conditions && baseConditions ? `${baseConditions} and ${conditions}` : conditions || baseConditions;
|
|
154
165
|
}
|
|
155
|
-
};
|
|
166
|
+
}, options = await loadOptionsCore.call(this, searchConfig, referenceKey);
|
|
167
|
+
if (config.recordType) try {
|
|
168
|
+
const credentials = await this.getCredentials("connectWisePsaApi");
|
|
169
|
+
return {
|
|
170
|
+
results: options.map(option => option.value === "" ? option : {
|
|
171
|
+
...option,
|
|
172
|
+
url: buildRecordUrl(credentials, config.recordType, option.value)
|
|
173
|
+
})
|
|
174
|
+
};
|
|
175
|
+
} catch {}
|
|
156
176
|
return {
|
|
157
|
-
results:
|
|
177
|
+
results: options
|
|
158
178
|
};
|
|
159
179
|
}
|
|
160
180
|
|
|
@@ -45,7 +45,8 @@ const allReferences = {
|
|
|
45
45
|
conditions: () => "deletedFlag=false",
|
|
46
46
|
fields: "id,name,identifier",
|
|
47
47
|
pageSize: 300,
|
|
48
|
-
orderBy: "_info/lastUpdated desc"
|
|
48
|
+
orderBy: "_info/lastUpdated desc",
|
|
49
|
+
recordType: "CompanyFV"
|
|
49
50
|
},
|
|
50
51
|
company_countries: {
|
|
51
52
|
endpoint: "/company/countries"
|
|
@@ -115,16 +115,6 @@ const companyFields = [ {
|
|
|
115
115
|
searchable: !0
|
|
116
116
|
}
|
|
117
117
|
} ]
|
|
118
|
-
}, {
|
|
119
|
-
displayName: 'For information on how to create/update companies, check the <a href="temp">documentation</a>',
|
|
120
|
-
name: "importantNotice",
|
|
121
|
-
type: "notice",
|
|
122
|
-
default: "",
|
|
123
|
-
displayOptions: {
|
|
124
|
-
show: {
|
|
125
|
-
operation: [ "create", "update" ]
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
118
|
}, {
|
|
129
119
|
displayName: "Identifier",
|
|
130
120
|
name: "identifier",
|
|
@@ -681,6 +671,7 @@ const companyFields = [ {
|
|
|
681
671
|
description: "Manage ConnectWise companies",
|
|
682
672
|
identifierField: "identifier",
|
|
683
673
|
nameField: "name",
|
|
674
|
+
selectorField: "company",
|
|
684
675
|
fieldDetection: {
|
|
685
676
|
endpoint: "/company/companies",
|
|
686
677
|
sampleSize: 3
|
|
@@ -49,6 +49,26 @@ const customFields = [ {
|
|
|
49
49
|
value: "getAll",
|
|
50
50
|
description: "Get many via custom endpoint (GET)",
|
|
51
51
|
action: "Custom Get"
|
|
52
|
+
}, {
|
|
53
|
+
name: "POST (Create)",
|
|
54
|
+
value: "create",
|
|
55
|
+
description: "Create via custom endpoint (POST)",
|
|
56
|
+
action: "Custom Create"
|
|
57
|
+
}, {
|
|
58
|
+
name: "DELETE",
|
|
59
|
+
value: "delete",
|
|
60
|
+
description: "Delete via custom endpoint (DELETE)",
|
|
61
|
+
action: "Custom Delete"
|
|
62
|
+
}, {
|
|
63
|
+
name: "PUT (Replace)",
|
|
64
|
+
value: "replace",
|
|
65
|
+
description: "Replace via custom endpoint (PUT)",
|
|
66
|
+
action: "Custom Replace"
|
|
67
|
+
}, {
|
|
68
|
+
name: "PATCH (Update)",
|
|
69
|
+
value: "update",
|
|
70
|
+
description: "Update via custom endpoint (PATCH)",
|
|
71
|
+
action: "Custom Update"
|
|
52
72
|
} ],
|
|
53
73
|
default: "getAll"
|
|
54
74
|
}, {
|
|
@@ -62,7 +82,7 @@ const customFields = [ {
|
|
|
62
82
|
}
|
|
63
83
|
},
|
|
64
84
|
default: "",
|
|
65
|
-
hint: `View list of endpoints in the <a href="https://${import_common2.DOCURL_PREFIX}/
|
|
85
|
+
hint: `View list of endpoints in the <a href="https://${import_common2.DOCURL_PREFIX}/connectwise-api">documentation</a>`,
|
|
66
86
|
placeholder: "e.g., /company/companies/:id or https://api.connectwise.com/...",
|
|
67
87
|
description: "The API endpoint. Can be a full URL or a partial path. Include any parameters like the ID directly in the URL."
|
|
68
88
|
}, {
|
|
@@ -38,6 +38,8 @@ __export(diagnostics_schema_exports, {
|
|
|
38
38
|
|
|
39
39
|
module.exports = __toCommonJS(diagnostics_schema_exports);
|
|
40
40
|
|
|
41
|
+
var import_hint_collector = require("../../helpers/hint-collector");
|
|
42
|
+
|
|
41
43
|
const diagnosticsFields = [ {
|
|
42
44
|
displayName: "Operation",
|
|
43
45
|
name: "operation",
|
|
@@ -70,10 +72,8 @@ async function execute(operation, i) {
|
|
|
70
72
|
clearedCount: clearedCount,
|
|
71
73
|
message: `Cleared ${clearedCount} cache entries`
|
|
72
74
|
};
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
location: "outputPane"
|
|
76
|
-
}), this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(result), {
|
|
75
|
+
return (0, import_hint_collector.hint)(this, "custom", `Cache cleared: ${clearedCount} entries removed`),
|
|
76
|
+
this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(result), {
|
|
77
77
|
itemData: {
|
|
78
78
|
item: i
|
|
79
79
|
}
|
|
@@ -88,10 +88,7 @@ async function execute(operation, i) {
|
|
|
88
88
|
error: originalError,
|
|
89
89
|
message: friendlyMessage
|
|
90
90
|
};
|
|
91
|
-
return this.
|
|
92
|
-
message: friendlyMessage,
|
|
93
|
-
location: "outputPane"
|
|
94
|
-
}), this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(result), {
|
|
91
|
+
return (0, import_hint_collector.hint)(this, "custom", friendlyMessage), this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(result), {
|
|
95
92
|
itemData: {
|
|
96
93
|
item: i
|
|
97
94
|
}
|
|
@@ -160,6 +160,10 @@ const ticketFields = [ {
|
|
|
160
160
|
}
|
|
161
161
|
},
|
|
162
162
|
modes: [ {
|
|
163
|
+
displayName: "ID",
|
|
164
|
+
name: "id",
|
|
165
|
+
type: "string"
|
|
166
|
+
}, {
|
|
163
167
|
displayName: "From List",
|
|
164
168
|
name: "list",
|
|
165
169
|
type: "list",
|
|
@@ -256,7 +260,12 @@ const ticketFields = [ {
|
|
|
256
260
|
},
|
|
257
261
|
displayOptions: {
|
|
258
262
|
show: {
|
|
259
|
-
operation: [ "create", "update" ]
|
|
263
|
+
operation: [ "create", "update" ],
|
|
264
|
+
board: [ {
|
|
265
|
+
_cnd: {
|
|
266
|
+
exists: !0
|
|
267
|
+
}
|
|
268
|
+
} ]
|
|
260
269
|
}
|
|
261
270
|
},
|
|
262
271
|
x_detectField: "auto"
|
|
@@ -408,7 +408,7 @@ const COMMON_FIELDS_NAME = "Common Fields", staticFieldDefinitions = {
|
|
|
408
408
|
value: "__ALL_FIELDS_OVERRIDE__"
|
|
409
409
|
}, {
|
|
410
410
|
name: COMMON_FIELDS_NAME,
|
|
411
|
-
value: "id,summary,recordType,board/id,board/name,status/id,status/name,company/id,company/identifier,company/name,agreement/name,agreement/type,contact/name,contactEmailAddress,team/name,priority/name,source/name,customerUpdatedFlag,closedFlag,location/name,department/identifier,sla/name,_info/lastUpdated,_info/updatedBy,_info/dateEntered,__customFields,$$customFields,type/name,subType/name,item/name,owner/identifier,budgetHours,actualHours,resources,closedDate,closedBy,project/id,project/name,phase/name"
|
|
411
|
+
value: "id,summary,recordType,board/id,board/name,status/id,status/name,company/id,company/identifier,company/name,agreement/name,agreement/type,contact/id,contact/name,contactEmailAddress,team/name,priority/name,source/name,customerUpdatedFlag,closedFlag,location/name,department/identifier,sla/name,_info/lastUpdated,_info/updatedBy,_info/dateEntered,__customFields,$$customFields,type/name,subType/name,item/name,owner/identifier,budgetHours,actualHours,resources,closedDate,closedBy,project/id,project/name,phase/name"
|
|
412
412
|
}, {
|
|
413
413
|
name: "ID",
|
|
414
414
|
value: "id"
|
|
@@ -37,17 +37,27 @@ module.exports = __toCommonJS(cache_exports);
|
|
|
37
37
|
|
|
38
38
|
var import_utils = require("../helpers/utils");
|
|
39
39
|
|
|
40
|
-
let cacheClient = null, Redis = null;
|
|
40
|
+
let cacheClient = null, Redis = null, lastConnectionError = null;
|
|
41
|
+
|
|
42
|
+
function getBestErrorMessage(operationError) {
|
|
43
|
+
return lastConnectionError || operationError;
|
|
44
|
+
}
|
|
41
45
|
|
|
42
46
|
async function getCacheClient() {
|
|
43
47
|
return null;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
async function getCached(key) {
|
|
47
|
-
return
|
|
51
|
+
return {
|
|
52
|
+
data: null
|
|
53
|
+
};
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
async function setCached(key, data, ttl) {
|
|
56
|
+
async function setCached(key, data, ttl) {
|
|
57
|
+
return {
|
|
58
|
+
success: !1
|
|
59
|
+
};
|
|
60
|
+
}
|
|
51
61
|
|
|
52
62
|
async function deleteCached(key) {
|
|
53
63
|
return !1;
|
|
@@ -41,7 +41,7 @@ __export(client_exports, {
|
|
|
41
41
|
|
|
42
42
|
module.exports = __toCommonJS(client_exports);
|
|
43
43
|
|
|
44
|
-
var import_n8n_workflow = require("n8n-workflow"), import_logging = require("../helpers/logging");
|
|
44
|
+
var import_n8n_workflow = require("n8n-workflow"), import_logging = require("../helpers/logging"), import_hint_collector = require("../helpers/hint-collector");
|
|
45
45
|
|
|
46
46
|
const DEFAULT_API_PATH = "v4_6_release/apis/3.0";
|
|
47
47
|
|
|
@@ -92,7 +92,7 @@ async function connectWiseApiRequest(method, endpoint, body = {}, qs = {}, optio
|
|
|
92
92
|
console.warn("Version check module load failed:", error);
|
|
93
93
|
}
|
|
94
94
|
if ("getNodeParameter" in this && method === "GET") try {
|
|
95
|
-
this.getNodeParameter("cacheResponse", 0, !1)
|
|
95
|
+
const cacheResponse = this.getNodeParameter("cacheResponse", 0, !1);
|
|
96
96
|
} catch {}
|
|
97
97
|
return await connectWiseHttpRequest.call(this, method, endpoint, body, qs, options);
|
|
98
98
|
}
|
|
@@ -134,9 +134,9 @@ async function connectWiseHttpRequest(method, endpoint, body = {}, qs = {}, opti
|
|
|
134
134
|
if (pageNumber > maxPages) {
|
|
135
135
|
const itemCount = isReportFormat ? reportResult?.row_values?.length || 0 : returnData.length;
|
|
136
136
|
console.log(`⚠️ Reached maximum page limit (${maxPages} pages, ${itemCount} records). Some results may be missing.`),
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
(0, import_hint_collector.hint)(this, "pagination_limit", {
|
|
138
|
+
itemCount: itemCount,
|
|
139
|
+
maxPages: maxPages
|
|
140
140
|
});
|
|
141
141
|
break;
|
|
142
142
|
}
|
|
@@ -162,23 +162,18 @@ async function connectWiseHttpRequest(method, endpoint, body = {}, qs = {}, opti
|
|
|
162
162
|
hasMultiplePages = !0;
|
|
163
163
|
}
|
|
164
164
|
if (hasMultiplePages) {
|
|
165
|
-
const currentPageCount = isReportFormat ? responseData.row_values?.length || 0 : responseData.length,
|
|
166
|
-
console.log(`✅ Page ${pageNumber}: received ${currentPageCount} items (total so far: ${
|
|
165
|
+
const currentPageCount = isReportFormat ? responseData.row_values?.length || 0 : responseData.length, totalCount2 = isReportFormat ? reportResult.row_values.length : returnData.length;
|
|
166
|
+
console.log(`✅ Page ${pageNumber}: received ${currentPageCount} items (total so far: ${totalCount2})`);
|
|
167
167
|
}
|
|
168
168
|
pageNumber++;
|
|
169
169
|
} while (isReportFormat ? responseData.row_values && responseData.row_values.length === pageSize : responseData.length === pageSize);
|
|
170
170
|
if (hasMultiplePages) {
|
|
171
|
-
const
|
|
172
|
-
console.log(`📊 Pagination complete: ${
|
|
171
|
+
const totalCount2 = isReportFormat ? reportResult.row_values.length : returnData.length;
|
|
172
|
+
console.log(`📊 Pagination complete: ${totalCount2} total items\n`);
|
|
173
173
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
message: `Retrieved ${totalCount} items${pageNumber > 2 ? ` across ${pageNumber - 1} pages` : ""}`,
|
|
178
|
-
location: "outputPane"
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
return isReportFormat ? reportResult : returnData;
|
|
174
|
+
const totalCount = isReportFormat ? reportResult?.row_values?.length || 0 : returnData.length;
|
|
175
|
+
return totalCount > 0 && (0, import_hint_collector.hint)(this, "items_retrieved", totalCount),
|
|
176
|
+
isReportFormat ? reportResult : returnData;
|
|
182
177
|
} else {
|
|
183
178
|
options.limit && (qs.pageSize = options.limit), Object.keys(qs).length && (requestOptions.qs = qs);
|
|
184
179
|
try {
|
|
@@ -28,6 +28,7 @@ var operations_exports = {};
|
|
|
28
28
|
__export(operations_exports, {
|
|
29
29
|
buildEndpointUrl: () => buildEndpointUrl,
|
|
30
30
|
extractOperationParams: () => extractOperationParams,
|
|
31
|
+
fetchRecordById: () => fetchRecordById,
|
|
31
32
|
recordCreate: () => recordCreate,
|
|
32
33
|
recordDelete: () => recordDelete,
|
|
33
34
|
recordGet: () => recordGet,
|
|
@@ -84,24 +85,29 @@ function buildEndpointUrl(schema, operation, params = {}) {
|
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
async function fetchRecordById(context, schema, params, qs) {
|
|
89
|
+
const {method: method, endpoint: endpoint} = buildEndpointUrl(schema, "get", params);
|
|
90
|
+
return import_client.connectWiseApiRequest.call(context, method, endpoint, {}, qs);
|
|
91
|
+
}
|
|
92
|
+
|
|
87
93
|
async function recordGet(context, schema, params, qs) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
const selectorField = schema.selectorField || "id", selectorValue = params[selectorField];
|
|
95
|
+
if (selectorValue && typeof selectorValue == "object" && "mode" in selectorValue) {
|
|
96
|
+
const locator = selectorValue;
|
|
97
|
+
if (locator.mode === "identifier") {
|
|
98
|
+
const identifierField = schema.identifierField || "id", searchCondition = `${identifierField}="${locator.value}"`, searchQs = {
|
|
92
99
|
...qs,
|
|
93
100
|
conditions: searchCondition
|
|
94
101
|
}, getAllEndpoint = buildEndpointUrl(schema, "getAll", params), results = await import_client.connectWiseApiRequest.call(context, getAllEndpoint.method, getAllEndpoint.endpoint, {}, searchQs);
|
|
95
|
-
if (!results || results.length === 0) throw new Error(`No ${schema.resource} found with ${identifierField}: ${
|
|
102
|
+
if (!results || results.length === 0) throw new Error(`No ${schema.resource} found with ${identifierField}: ${locator.value}`);
|
|
96
103
|
return results[0];
|
|
97
104
|
} else {
|
|
98
105
|
let id;
|
|
99
|
-
if (
|
|
106
|
+
if (locator.mode === "id") id = locator.value; else if (locator.mode === "list" && locator.value) id = typeof locator.value == "object" && "value" in locator.value ? locator.value.value : locator.value; else throw new Error(`Please select a ${schema.resource} from the list or enter an ID`);
|
|
100
107
|
params.id = id;
|
|
101
108
|
}
|
|
102
109
|
}
|
|
103
|
-
|
|
104
|
-
return await import_client.connectWiseApiRequest.call(context, method, endpoint, {}, qs);
|
|
110
|
+
return fetchRecordById(context, schema, params, qs);
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
async function recordGetAll(context, schema, params, qs, options) {
|
|
@@ -136,7 +142,10 @@ async function recordUpdate(context, schema, endpoint, index, qs) {
|
|
|
136
142
|
let existingCustomFields = [];
|
|
137
143
|
if (existingRecord) existingCustomFields = existingRecord.customFields || []; else {
|
|
138
144
|
const params = extractOperationParams(context, index);
|
|
139
|
-
existingRecord = await
|
|
145
|
+
existingRecord = await fetchRecordById(context, schema, {
|
|
146
|
+
id: params.id,
|
|
147
|
+
ticketType: params.ticketType
|
|
148
|
+
}, {}), existingCustomFields = existingRecord.customFields || [];
|
|
140
149
|
}
|
|
141
150
|
const fieldsArray = customFields && typeof customFields == "object" && "field" in customFields ? customFields.field || [] : Array.isArray(customFields) ? customFields : [], customFieldPatch = [ {
|
|
142
151
|
op: "replace",
|
|
@@ -159,6 +168,7 @@ async function recordDelete(context, endpoint) {
|
|
|
159
168
|
0 && (module.exports = {
|
|
160
169
|
buildEndpointUrl: buildEndpointUrl,
|
|
161
170
|
extractOperationParams: extractOperationParams,
|
|
171
|
+
fetchRecordById: fetchRecordById,
|
|
162
172
|
recordCreate: recordCreate,
|
|
163
173
|
recordDelete: recordDelete,
|
|
164
174
|
recordGet: recordGet,
|
|
@@ -37,14 +37,12 @@ __export(version_check_exports, {
|
|
|
37
37
|
|
|
38
38
|
module.exports = __toCommonJS(version_check_exports);
|
|
39
39
|
|
|
40
|
-
var fs = __toESM(require("fs/promises")), path = __toESM(require("path")), os = __toESM(require("os")), import_common = require("../descriptions/common.descriptions");
|
|
40
|
+
var fs = __toESM(require("fs/promises")), path = __toESM(require("path")), os = __toESM(require("os")), import_common = require("../descriptions/common.descriptions"), import_hint_collector = require("../helpers/hint-collector");
|
|
41
41
|
|
|
42
|
-
const CACHE_FILE_PATH = path.join(os.tmpdir(), "connectwise-n8n-version-cache.json"), CURRENT_VERSION = "0.
|
|
42
|
+
const CACHE_FILE_PATH = path.join(os.tmpdir(), "connectwise-n8n-version-cache.json"), CURRENT_VERSION = "0.2.0";
|
|
43
43
|
|
|
44
44
|
let inMemoryCache = null, cacheLoadPromise = null, fetchInProgress = !1;
|
|
45
45
|
|
|
46
|
-
const notificationShown = /* @__PURE__ */ new WeakSet;
|
|
47
|
-
|
|
48
46
|
async function initializeCache() {
|
|
49
47
|
try {
|
|
50
48
|
const fileContent = await fs.readFile(CACHE_FILE_PATH, "utf-8"), cacheEntry = JSON.parse(fileContent);
|
|
@@ -112,10 +110,10 @@ function checkForUpdatesAsync() {
|
|
|
112
110
|
async function checkAndNotifyUpdate(context) {
|
|
113
111
|
try {
|
|
114
112
|
if (!(await context.getCredentials("connectWisePsaApi")).checkForUpdates) return;
|
|
115
|
-
checkForUpdatesAsync(), inMemoryCache?.updateAvailable &&
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
})
|
|
113
|
+
checkForUpdatesAsync(), inMemoryCache?.updateAvailable && (0, import_hint_collector.hint)(context, "version_update", {
|
|
114
|
+
version: inMemoryCache.latest,
|
|
115
|
+
message: inMemoryCache.message
|
|
116
|
+
});
|
|
119
117
|
} catch (error) {
|
|
120
118
|
console.warn("⚠️ Version check failed:", error);
|
|
121
119
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mspcopilot/n8n-nodes-connectwise",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "n8n node to integrate with ConnectWise PSA",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package"
|
|
@@ -67,4 +67,4 @@
|
|
|
67
67
|
"registry": "https://registry.npmjs.org",
|
|
68
68
|
"access": "public"
|
|
69
69
|
}
|
|
70
|
-
}
|
|
70
|
+
}
|