n8n-nodes-clientify 0.2.13 → 0.2.15
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/credentials/ClientifyApi.credentials.d.ts +2 -2
- package/dist/credentials/ClientifyApi.credentials.js +20 -22
- package/dist/nodes/ClientifyApi/ClientifyApi.node.d.ts +1 -1
- package/dist/nodes/ClientifyApi/ClientifyApi.node.js +49 -34
- package/dist/nodes/ClientifyApi/ClientifyApiCatalog.d.ts +14 -2
- package/dist/nodes/ClientifyApi/ClientifyApiCatalog.js +57 -9
- package/dist/nodes/ClientifyApi/clientify.png +0 -0
- package/dist/nodes/ClientifyApi/clientify.svg +3 -5
- package/dist/nodes/ClientifyApi/operations.generated.js +2991 -1
- package/dist/nodes/ClientifyTrigger/clientify.png +0 -0
- package/dist/nodes/ClientifyTrigger/clientify.svg +3 -5
- package/dist/package.json +4 -6
- package/package.json +4 -6
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ICredentialTestRequest, ICredentialType, INodeProperties } from
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from "n8n-workflow";
|
|
2
2
|
export declare class ClientifyApi implements ICredentialType {
|
|
3
3
|
name: string;
|
|
4
4
|
displayName: string;
|
|
5
5
|
documentationUrl: string;
|
|
6
|
-
authenticate:
|
|
6
|
+
authenticate: IAuthenticateGeneric;
|
|
7
7
|
test: ICredentialTestRequest;
|
|
8
8
|
properties: INodeProperties[];
|
|
9
9
|
}
|
|
@@ -3,46 +3,44 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ClientifyApi = void 0;
|
|
4
4
|
class ClientifyApi {
|
|
5
5
|
constructor() {
|
|
6
|
-
this.name =
|
|
7
|
-
this.displayName =
|
|
8
|
-
this.documentationUrl =
|
|
6
|
+
this.name = "clientifyApi";
|
|
7
|
+
this.displayName = "Clientify API";
|
|
8
|
+
this.documentationUrl = "https://newapi.clientify.com/";
|
|
9
9
|
this.authenticate = {
|
|
10
|
-
type:
|
|
10
|
+
type: "generic",
|
|
11
11
|
properties: {
|
|
12
12
|
headers: {
|
|
13
|
-
Authorization:
|
|
13
|
+
Authorization: "=Token {{$credentials.apiKey}}",
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
};
|
|
17
17
|
this.test = {
|
|
18
18
|
request: {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
url: '/me/?fields=id,email',
|
|
23
|
-
method: 'GET',
|
|
19
|
+
baseURL: "={{$credentials.baseUrl}}",
|
|
20
|
+
url: "/me/?fields=id,email",
|
|
21
|
+
method: "GET",
|
|
24
22
|
},
|
|
25
23
|
};
|
|
26
24
|
this.properties = [
|
|
27
25
|
{
|
|
28
|
-
displayName:
|
|
29
|
-
name:
|
|
30
|
-
type:
|
|
26
|
+
displayName: "API Key",
|
|
27
|
+
name: "apiKey",
|
|
28
|
+
type: "string",
|
|
31
29
|
typeOptions: {
|
|
32
30
|
password: true,
|
|
33
31
|
},
|
|
34
|
-
default:
|
|
32
|
+
default: "",
|
|
35
33
|
required: true,
|
|
36
|
-
placeholder:
|
|
37
|
-
description: 'Clientify API key (used as
|
|
34
|
+
placeholder: "Enter your Clientify API key",
|
|
35
|
+
description: 'Clientify API key (used as "Authorization: Token <apiKey>")',
|
|
38
36
|
},
|
|
39
37
|
{
|
|
40
|
-
displayName:
|
|
41
|
-
name:
|
|
42
|
-
type:
|
|
43
|
-
default:
|
|
44
|
-
placeholder:
|
|
45
|
-
description:
|
|
38
|
+
displayName: "Base URL",
|
|
39
|
+
name: "baseUrl",
|
|
40
|
+
type: "string",
|
|
41
|
+
default: "https://api-plus.clientify.com/v2",
|
|
42
|
+
placeholder: "https://api-plus.clientify.com/v2",
|
|
43
|
+
description: "Optional override for the Clientify API base URL",
|
|
46
44
|
},
|
|
47
45
|
];
|
|
48
46
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
|
|
2
2
|
export declare class ClientifyApi implements INodeType {
|
|
3
3
|
description: INodeTypeDescription;
|
|
4
4
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
@@ -6,35 +6,45 @@ const ClientifyApiCatalog_1 = require("./ClientifyApiCatalog");
|
|
|
6
6
|
class ClientifyApi {
|
|
7
7
|
constructor() {
|
|
8
8
|
this.description = {
|
|
9
|
-
displayName:
|
|
10
|
-
name:
|
|
11
|
-
icon:
|
|
12
|
-
group: [
|
|
9
|
+
displayName: "Clientify",
|
|
10
|
+
name: "clientifyApi",
|
|
11
|
+
icon: "file:clientify.svg",
|
|
12
|
+
group: ["transform"],
|
|
13
13
|
version: 1,
|
|
14
|
-
subtitle: '={{$parameter["operation"]}}',
|
|
15
|
-
description:
|
|
14
|
+
subtitle: '={{$parameter["resource"] + " · " + $parameter["operation"]}}',
|
|
15
|
+
description: "Clientify CRM (direct REST API)",
|
|
16
16
|
defaults: {
|
|
17
|
-
name:
|
|
17
|
+
name: "Clientify",
|
|
18
18
|
},
|
|
19
|
-
inputs: [
|
|
20
|
-
outputs: [
|
|
19
|
+
inputs: ["main"],
|
|
20
|
+
outputs: ["main"],
|
|
21
21
|
credentials: [
|
|
22
22
|
{
|
|
23
|
-
name:
|
|
23
|
+
name: "clientifyApi",
|
|
24
24
|
required: true,
|
|
25
25
|
},
|
|
26
26
|
],
|
|
27
27
|
properties: (() => {
|
|
28
28
|
const props = [
|
|
29
29
|
{
|
|
30
|
-
displayName:
|
|
31
|
-
name:
|
|
32
|
-
type:
|
|
30
|
+
displayName: "Resource",
|
|
31
|
+
name: "resource",
|
|
32
|
+
type: "options",
|
|
33
|
+
options: ClientifyApiCatalog_1.resourceOptions,
|
|
34
|
+
default: "auto",
|
|
35
|
+
required: true,
|
|
36
|
+
noDataExpression: true,
|
|
37
|
+
description: "Choose a resource first, then pick an action",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
displayName: "Action",
|
|
41
|
+
name: "operation",
|
|
42
|
+
type: "options",
|
|
33
43
|
options: ClientifyApiCatalog_1.operationOptions,
|
|
34
|
-
default:
|
|
44
|
+
default: "GetCurrentUser",
|
|
35
45
|
required: true,
|
|
36
46
|
noDataExpression: true,
|
|
37
|
-
description:
|
|
47
|
+
description: "Select the Clientify action to execute (mirrors the AppMixer connector action list)",
|
|
38
48
|
},
|
|
39
49
|
...ClientifyApiCatalog_1.operationFields,
|
|
40
50
|
];
|
|
@@ -45,11 +55,11 @@ class ClientifyApi {
|
|
|
45
55
|
async execute() {
|
|
46
56
|
const items = this.getInputData();
|
|
47
57
|
const returnData = [];
|
|
48
|
-
const credentials = await this.getCredentials(
|
|
49
|
-
const
|
|
50
|
-
const baseUrl = credentials.baseUrl || 'https://api-plus.clientify.com/v2';
|
|
58
|
+
const credentials = await this.getCredentials("clientifyApi");
|
|
59
|
+
const baseUrl = credentials.baseUrl || "https://api-plus.clientify.com/v2";
|
|
51
60
|
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
52
|
-
const
|
|
61
|
+
const selectedResource = this.getNodeParameter("resource", itemIndex, "auto");
|
|
62
|
+
const operation = this.getNodeParameter("operation", itemIndex);
|
|
53
63
|
const def = ClientifyApiCatalog_1.operationDefinitions[operation];
|
|
54
64
|
if (!def) {
|
|
55
65
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation "${operation}". Ensure the operation catalog is present in this node package build output.`);
|
|
@@ -65,17 +75,18 @@ class ClientifyApi {
|
|
|
65
75
|
continue;
|
|
66
76
|
}
|
|
67
77
|
// Apply per-operation defaults if user left the field empty.
|
|
68
|
-
if ((value ===
|
|
78
|
+
if ((value === "" || value === null || value === undefined) &&
|
|
79
|
+
fieldName in def.fieldDefaults) {
|
|
69
80
|
value = def.fieldDefaults[fieldName];
|
|
70
81
|
}
|
|
71
82
|
// Avoid accidentally sending empty optional values (n8n defaults).
|
|
72
83
|
const isRequired = def.requiredFieldNames.includes(fieldName);
|
|
73
84
|
if (!isRequired) {
|
|
74
|
-
if (value ===
|
|
85
|
+
if (value === "" || value === null || value === undefined)
|
|
75
86
|
continue;
|
|
76
|
-
if (typeof value ===
|
|
87
|
+
if (typeof value === "number" && value === 0)
|
|
77
88
|
continue;
|
|
78
|
-
if (typeof value ===
|
|
89
|
+
if (typeof value === "boolean" && value === false)
|
|
79
90
|
continue;
|
|
80
91
|
}
|
|
81
92
|
input[fieldName] = value;
|
|
@@ -83,35 +94,39 @@ class ClientifyApi {
|
|
|
83
94
|
// Validate required fields are present (avoid confusing API errors).
|
|
84
95
|
for (const fieldName of def.requiredFieldNames) {
|
|
85
96
|
const value = input[fieldName];
|
|
86
|
-
if (value === undefined || value === null || value ===
|
|
97
|
+
if (value === undefined || value === null || value === "") {
|
|
87
98
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${fieldName} is required`);
|
|
88
99
|
}
|
|
89
|
-
if (typeof value ===
|
|
100
|
+
if (typeof value === "number" && value <= 0) {
|
|
90
101
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${fieldName} must be a positive number`);
|
|
91
102
|
}
|
|
92
103
|
}
|
|
93
104
|
const url = (0, ClientifyApiCatalog_1.renderPathTemplate)(def.pathTemplate, input);
|
|
94
105
|
const rest = (0, ClientifyApiCatalog_1.omitKeys)(input, def.pathParamNames);
|
|
95
|
-
const isQueryMethod = def.method ===
|
|
96
|
-
const qs = isQueryMethod
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
const isQueryMethod = def.method === "GET" || def.method === "DELETE";
|
|
107
|
+
const qs = isQueryMethod
|
|
108
|
+
? Object.assign(Object.assign({}, (def.fixedQuery || {})), rest)
|
|
109
|
+
: undefined;
|
|
110
|
+
const body = !isQueryMethod && Object.keys(rest).length > 0
|
|
111
|
+
? rest
|
|
112
|
+
: undefined;
|
|
113
|
+
const result = await this.helpers.httpRequestWithAuthentication.call(this, "clientifyApi", {
|
|
99
114
|
method: def.method,
|
|
100
115
|
url: `${baseUrl}${url}`,
|
|
101
116
|
qs,
|
|
102
117
|
body,
|
|
103
118
|
json: true,
|
|
104
|
-
headers: {
|
|
105
|
-
Authorization: `Token ${apiKey}`,
|
|
106
|
-
},
|
|
107
119
|
});
|
|
108
|
-
const normalized = result === undefined || result === null || result ===
|
|
120
|
+
const normalized = result === undefined || result === null || result === ""
|
|
109
121
|
? { ok: true }
|
|
110
|
-
: typeof result ===
|
|
122
|
+
: typeof result === "object"
|
|
111
123
|
? result
|
|
112
124
|
: { data: result };
|
|
113
125
|
returnData.push({
|
|
114
126
|
json: Object.assign(Object.assign({}, normalized), { _meta: {
|
|
127
|
+
resource: selectedResource === "auto"
|
|
128
|
+
? (0, ClientifyApiCatalog_1.getResourceForOperation)(operation)
|
|
129
|
+
: selectedResource,
|
|
115
130
|
operation,
|
|
116
131
|
method: def.method,
|
|
117
132
|
path: url,
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import { INodeProperties } from
|
|
2
|
-
import { operationDefinitions } from
|
|
1
|
+
import { INodeProperties } from "n8n-workflow";
|
|
2
|
+
import { operationDefinitions } from "./operations.generated";
|
|
3
|
+
type ClientifyResource = "auto" | "companies" | "contacts" | "tasks" | "users" | "misc";
|
|
4
|
+
export declare function getResourceForOperation(operation: string): Exclude<ClientifyResource, "auto">;
|
|
5
|
+
export declare const resourceOptions: {
|
|
6
|
+
name: string;
|
|
7
|
+
value: string;
|
|
8
|
+
description: string;
|
|
9
|
+
}[];
|
|
3
10
|
export declare const operationOptions: {
|
|
4
11
|
name: string;
|
|
5
12
|
value: string;
|
|
6
13
|
description: string;
|
|
14
|
+
displayOptions: {
|
|
15
|
+
show: {
|
|
16
|
+
resource: string[];
|
|
17
|
+
};
|
|
18
|
+
};
|
|
7
19
|
}[];
|
|
8
20
|
export declare const operationFields: INodeProperties[];
|
|
9
21
|
export declare function renderPathTemplate(pathTemplate: string, input: Record<string, unknown>): string;
|
|
@@ -1,28 +1,75 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.operationDefinitions = exports.operationFields = exports.operationOptions = void 0;
|
|
3
|
+
exports.operationDefinitions = exports.operationFields = exports.operationOptions = exports.resourceOptions = void 0;
|
|
4
|
+
exports.getResourceForOperation = getResourceForOperation;
|
|
4
5
|
exports.renderPathTemplate = renderPathTemplate;
|
|
5
6
|
exports.omitKeys = omitKeys;
|
|
6
7
|
const operations_generated_1 = require("./operations.generated");
|
|
7
8
|
Object.defineProperty(exports, "operationDefinitions", { enumerable: true, get: function () { return operations_generated_1.operationDefinitions; } });
|
|
8
9
|
function mapInspectorTypeToN8nType(type) {
|
|
9
|
-
if (type ===
|
|
10
|
-
return
|
|
11
|
-
if (type ===
|
|
12
|
-
return
|
|
13
|
-
return
|
|
10
|
+
if (type === "number")
|
|
11
|
+
return "number";
|
|
12
|
+
if (type === "toggle")
|
|
13
|
+
return "boolean";
|
|
14
|
+
return "string";
|
|
14
15
|
}
|
|
16
|
+
function inferResourceFromPath(pathTemplate) {
|
|
17
|
+
if (pathTemplate.startsWith("/companies/"))
|
|
18
|
+
return "companies";
|
|
19
|
+
if (pathTemplate.startsWith("/contacts/"))
|
|
20
|
+
return "contacts";
|
|
21
|
+
if (pathTemplate.startsWith("/tasks/"))
|
|
22
|
+
return "tasks";
|
|
23
|
+
if (pathTemplate.startsWith("/users/"))
|
|
24
|
+
return "users";
|
|
25
|
+
return "misc";
|
|
26
|
+
}
|
|
27
|
+
const resourceLabels = {
|
|
28
|
+
companies: "Companies",
|
|
29
|
+
contacts: "Contacts",
|
|
30
|
+
tasks: "Tasks",
|
|
31
|
+
users: "Users",
|
|
32
|
+
misc: "Misc",
|
|
33
|
+
};
|
|
34
|
+
const operationResourceMap = Object.fromEntries(Object.values(operations_generated_1.operationDefinitions).map((def) => [
|
|
35
|
+
def.operation,
|
|
36
|
+
inferResourceFromPath(def.pathTemplate),
|
|
37
|
+
]));
|
|
38
|
+
function getResourceForOperation(operation) {
|
|
39
|
+
var _a;
|
|
40
|
+
return (_a = operationResourceMap[operation]) !== null && _a !== void 0 ? _a : "misc";
|
|
41
|
+
}
|
|
42
|
+
exports.resourceOptions = [
|
|
43
|
+
{
|
|
44
|
+
name: "Auto (Legacy Workflows)",
|
|
45
|
+
value: "auto",
|
|
46
|
+
description: "Shows all actions and keeps legacy operation-only workflows compatible",
|
|
47
|
+
},
|
|
48
|
+
...Array.from(new Set(Object.values(operationResourceMap)))
|
|
49
|
+
.sort((a, b) => resourceLabels[a].localeCompare(resourceLabels[b]))
|
|
50
|
+
.map((resource) => ({
|
|
51
|
+
name: resourceLabels[resource],
|
|
52
|
+
value: resource,
|
|
53
|
+
description: `Actions related to ${resourceLabels[resource].toLowerCase()}`,
|
|
54
|
+
})),
|
|
55
|
+
];
|
|
15
56
|
exports.operationOptions = Object.values(operations_generated_1.operationDefinitions)
|
|
16
57
|
.sort((a, b) => a.label.localeCompare(b.label))
|
|
17
58
|
.map((def) => ({
|
|
18
59
|
name: def.label,
|
|
19
60
|
value: def.operation,
|
|
20
61
|
description: def.description,
|
|
62
|
+
displayOptions: {
|
|
63
|
+
show: {
|
|
64
|
+
resource: [getResourceForOperation(def.operation), "auto"],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
21
67
|
}));
|
|
22
68
|
exports.operationFields = (() => {
|
|
23
69
|
var _a;
|
|
24
70
|
const fields = [];
|
|
25
71
|
for (const def of Object.values(operations_generated_1.operationDefinitions)) {
|
|
72
|
+
const resource = getResourceForOperation(def.operation);
|
|
26
73
|
const inspectorInputs = def.inspectorInputs || {};
|
|
27
74
|
const sortedFieldEntries = Object.entries(inspectorInputs).sort((a, b) => {
|
|
28
75
|
var _a, _b, _c, _d;
|
|
@@ -33,16 +80,17 @@ exports.operationFields = (() => {
|
|
|
33
80
|
for (const [fieldName, inputDef] of sortedFieldEntries) {
|
|
34
81
|
const isRequired = def.requiredFieldNames.includes(fieldName);
|
|
35
82
|
const n8nType = mapInspectorTypeToN8nType(inputDef === null || inputDef === void 0 ? void 0 : inputDef.type);
|
|
36
|
-
const defaultValue = (_a = def.fieldDefaults[fieldName]) !== null && _a !== void 0 ? _a : (n8nType ===
|
|
83
|
+
const defaultValue = (_a = def.fieldDefaults[fieldName]) !== null && _a !== void 0 ? _a : (n8nType === "number" ? 0 : n8nType === "boolean" ? false : "");
|
|
37
84
|
fields.push({
|
|
38
85
|
displayName: (inputDef === null || inputDef === void 0 ? void 0 : inputDef.label) || fieldName,
|
|
39
86
|
name: fieldName,
|
|
40
87
|
type: n8nType,
|
|
41
88
|
default: defaultValue,
|
|
42
89
|
required: isRequired,
|
|
43
|
-
description: (inputDef === null || inputDef === void 0 ? void 0 : inputDef.tooltip) ||
|
|
90
|
+
description: (inputDef === null || inputDef === void 0 ? void 0 : inputDef.tooltip) || "",
|
|
44
91
|
displayOptions: {
|
|
45
92
|
show: {
|
|
93
|
+
resource: [resource, "auto"],
|
|
46
94
|
operation: [def.operation],
|
|
47
95
|
},
|
|
48
96
|
},
|
|
@@ -56,7 +104,7 @@ function renderPathTemplate(pathTemplate, input) {
|
|
|
56
104
|
const key = String(expr).trim();
|
|
57
105
|
const value = input[key];
|
|
58
106
|
if (value === undefined || value === null)
|
|
59
|
-
return
|
|
107
|
+
return "";
|
|
60
108
|
return encodeURIComponent(String(value));
|
|
61
109
|
});
|
|
62
110
|
}
|
|
Binary file
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" fill="none">
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
<path d="M30 23C26.686 23 24 25.686 24 29C24 32.314 26.686 35 30 35C33.314 35 36 32.314 36 29C36 25.686 33.314 23 30 23Z" fill="white"/>
|
|
5
|
-
</svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60" fill="none">
|
|
2
|
+
<image width="60" height="60" href="data:image/png;base64,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" />
|
|
3
|
+
</svg>
|