@thegraphitelab/n8n-nodes-servicetitan 0.9.5 → 0.10.1
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/ServiceTitanApi.credentials.d.ts +1 -1
- package/dist/credentials/ServiceTitanApi.credentials.js +63 -67
- package/dist/credentials/ServiceTitanApi.credentials.js.map +1 -1
- package/dist/credentials/customAppSecrets.d.ts +12 -0
- package/dist/credentials/customAppSecrets.js +43 -0
- package/dist/credentials/customAppSecrets.js.map +1 -0
- package/dist/nodes/ServiceTitan/ServiceTitan.node.d.ts +3 -19
- package/dist/nodes/ServiceTitan/ServiceTitan.node.js +9 -61
- package/dist/nodes/ServiceTitan/ServiceTitan.node.js.map +1 -1
- package/dist/nodes/ServiceTitan/ServiceTitanTrigger.node.d.ts +3 -9
- package/dist/nodes/ServiceTitan/ServiceTitanTrigger.node.js +7 -267
- package/dist/nodes/ServiceTitan/ServiceTitanTrigger.node.js.map +1 -1
- package/dist/nodes/ServiceTitan/v1/ServiceTitanTriggerV1.node.d.ts +11 -0
- package/dist/nodes/ServiceTitan/v1/ServiceTitanTriggerV1.node.js +22 -0
- package/dist/nodes/ServiceTitan/v1/ServiceTitanTriggerV1.node.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/ServiceTitanV1.node.d.ts +6 -0
- package/dist/nodes/ServiceTitan/v1/ServiceTitanV1.node.js +19 -0
- package/dist/nodes/ServiceTitan/v1/ServiceTitanV1.node.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/_generated/get.d.ts +6 -0
- package/dist/nodes/ServiceTitan/v1/actions/_generated/get.js +6524 -0
- package/dist/nodes/ServiceTitan/v1/actions/_generated/get.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/_generated/triggers.d.ts +6 -0
- package/dist/nodes/ServiceTitan/v1/actions/_generated/triggers.js +3174 -0
- package/dist/nodes/ServiceTitan/v1/actions/_generated/triggers.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/get/export.d.ts +5 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/get/export.js +44 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/get/export.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/get/get.d.ts +6 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/get/get.js +30 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/get/get.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/get/getMany.d.ts +6 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/get/getMany.js +93 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/get/getMany.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/patch.d.ts +5 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/patch.js +10 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/patch.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/poll.d.ts +11 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/poll.js +207 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/poll.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/post.d.ts +5 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/post.js +9 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/post.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/put.d.ts +5 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/put.js +10 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/put.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/utils.d.ts +6 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/utils.js +65 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/utils.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/write.d.ts +6 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/write.js +199 -0
- package/dist/nodes/ServiceTitan/v1/actions/general/write.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/pollRouter.d.ts +2 -0
- package/dist/nodes/ServiceTitan/v1/actions/pollRouter.js +19 -0
- package/dist/nodes/ServiceTitan/v1/actions/pollRouter.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/router.d.ts +2 -0
- package/dist/nodes/ServiceTitan/v1/actions/router.js +39 -0
- package/dist/nodes/ServiceTitan/v1/actions/router.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/actions/types.d.ts +55 -0
- package/dist/nodes/ServiceTitan/v1/actions/types.js +3 -0
- package/dist/nodes/ServiceTitan/v1/actions/types.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/descriptions/node.description.d.ts +20 -0
- package/dist/nodes/ServiceTitan/v1/descriptions/node.description.js +96 -0
- package/dist/nodes/ServiceTitan/v1/descriptions/node.description.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/descriptions/trigger.description.d.ts +2 -0
- package/dist/nodes/ServiceTitan/v1/descriptions/trigger.description.js +44 -0
- package/dist/nodes/ServiceTitan/v1/descriptions/trigger.description.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/helpers/array.d.ts +1 -0
- package/dist/nodes/ServiceTitan/v1/helpers/array.js +7 -0
- package/dist/nodes/ServiceTitan/v1/helpers/array.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/helpers/customFields.d.ts +13 -0
- package/dist/nodes/ServiceTitan/v1/helpers/customFields.js +36 -0
- package/dist/nodes/ServiceTitan/v1/helpers/customFields.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/helpers/reduce.d.ts +5 -0
- package/dist/nodes/ServiceTitan/v1/helpers/reduce.js +20 -0
- package/dist/nodes/ServiceTitan/v1/helpers/reduce.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/helpers/string.d.ts +2 -0
- package/dist/nodes/ServiceTitan/v1/helpers/string.js +21 -0
- package/dist/nodes/ServiceTitan/v1/helpers/string.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/methods/methods.d.ts +6 -0
- package/dist/nodes/ServiceTitan/v1/methods/methods.js +21 -0
- package/dist/nodes/ServiceTitan/v1/methods/methods.js.map +1 -0
- package/dist/nodes/ServiceTitan/v1/transport/index.d.ts +31 -0
- package/dist/nodes/ServiceTitan/v1/transport/index.js +103 -0
- package/dist/nodes/ServiceTitan/v1/transport/index.js.map +1 -0
- package/dist/package.json +1 -1
- package/package.json +1 -1
- package/dist/nodes/ServiceTitan/GenericFunctions.d.ts +0 -32
- package/dist/nodes/ServiceTitan/GenericFunctions.js +0 -89
- package/dist/nodes/ServiceTitan/GenericFunctions.js.map +0 -1
- package/dist/nodes/ServiceTitan/resources/customer/get.d.ts +0 -2
- package/dist/nodes/ServiceTitan/resources/customer/get.js +0 -26
- package/dist/nodes/ServiceTitan/resources/customer/get.js.map +0 -1
- package/dist/nodes/ServiceTitan/resources/customer/index.d.ts +0 -3
- package/dist/nodes/ServiceTitan/resources/customer/index.js +0 -28
- package/dist/nodes/ServiceTitan/resources/customer/index.js.map +0 -1
- package/dist/nodes/ServiceTitan/resources/index.d.ts +0 -18
- package/dist/nodes/ServiceTitan/resources/index.js +0 -28
- package/dist/nodes/ServiceTitan/resources/index.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IAuthenticateGeneric, ICredentialDataDecryptedObject, ICredentialTestRequest, ICredentialType, IHttpRequestHelper, INodeProperties } from
|
|
1
|
+
import type { IAuthenticateGeneric, ICredentialDataDecryptedObject, ICredentialTestRequest, ICredentialType, IHttpRequestHelper, INodeProperties } from "n8n-workflow";
|
|
2
2
|
export declare function getApiHost(environment: string): string;
|
|
3
3
|
export declare class ServiceTitanApi implements ICredentialType {
|
|
4
4
|
name: string;
|
|
@@ -2,125 +2,123 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ServiceTitanApi = void 0;
|
|
4
4
|
exports.getApiHost = getApiHost;
|
|
5
|
+
const customAppSecrets_1 = require("./customAppSecrets");
|
|
6
|
+
// Label of the TGL_CUSTOM_APP_SECRETS entry holding the TGL-owned ST-App-Key.
|
|
7
|
+
const SERVICETITAN_APP_KEY_LABEL = "TGL_SERVICETITAN_API_KEY";
|
|
5
8
|
/**
|
|
6
9
|
* ServiceTitan auth hosts by environment.
|
|
7
10
|
* Integration ("Sandbox") and Production are entirely separate ST environments —
|
|
8
11
|
* different app registrations, different tenant IDs, different credentials.
|
|
9
12
|
*/
|
|
10
13
|
function getAuthHost(environment) {
|
|
11
|
-
return environment ===
|
|
12
|
-
?
|
|
13
|
-
:
|
|
14
|
+
return environment === "production"
|
|
15
|
+
? "auth.servicetitan.io"
|
|
16
|
+
: "auth-integration.servicetitan.io";
|
|
14
17
|
}
|
|
15
18
|
function getApiHost(environment) {
|
|
16
|
-
return environment ===
|
|
17
|
-
?
|
|
18
|
-
:
|
|
19
|
+
return environment === "production"
|
|
20
|
+
? "api.servicetitan.io"
|
|
21
|
+
: "api-integration.servicetitan.io";
|
|
19
22
|
}
|
|
20
23
|
class ServiceTitanApi {
|
|
21
|
-
name =
|
|
22
|
-
displayName =
|
|
23
|
-
icon =
|
|
24
|
-
documentationUrl =
|
|
24
|
+
name = "serviceTitanApi";
|
|
25
|
+
displayName = "ServiceTitan API";
|
|
26
|
+
icon = "file:../nodes/ServiceTitan/servicetitan.svg";
|
|
27
|
+
documentationUrl = "https://developer.servicetitan.io/";
|
|
25
28
|
properties = [
|
|
26
29
|
{
|
|
27
|
-
displayName:
|
|
28
|
-
name:
|
|
29
|
-
type:
|
|
30
|
+
displayName: "Environment",
|
|
31
|
+
name: "environment",
|
|
32
|
+
type: "options",
|
|
30
33
|
options: [
|
|
31
|
-
{ name:
|
|
32
|
-
{ name:
|
|
34
|
+
{ name: "Integration (Sandbox)", value: "integration" },
|
|
35
|
+
{ name: "Production", value: "production" },
|
|
33
36
|
],
|
|
34
|
-
default:
|
|
35
|
-
description:
|
|
37
|
+
default: "integration",
|
|
38
|
+
description: "Which ServiceTitan environment to connect to. Integration and Production are entirely separate — they need different app registrations and credentials.",
|
|
36
39
|
},
|
|
37
40
|
{
|
|
38
|
-
displayName:
|
|
39
|
-
name:
|
|
40
|
-
type:
|
|
41
|
-
default:
|
|
41
|
+
displayName: "Tenant ID",
|
|
42
|
+
name: "tenantId",
|
|
43
|
+
type: "string",
|
|
44
|
+
default: "",
|
|
42
45
|
required: true,
|
|
43
|
-
description:
|
|
46
|
+
description: "Found in ServiceTitan → Settings → API Application Access. Treated as a string in URL paths.",
|
|
44
47
|
},
|
|
45
48
|
{
|
|
46
|
-
displayName:
|
|
47
|
-
name:
|
|
48
|
-
type:
|
|
49
|
-
default:
|
|
49
|
+
displayName: "Client ID",
|
|
50
|
+
name: "clientId",
|
|
51
|
+
type: "string",
|
|
52
|
+
default: "",
|
|
50
53
|
required: true,
|
|
51
54
|
description: "Per-client OAuth client ID from ServiceTitan → Settings → API Application Access.",
|
|
52
55
|
},
|
|
53
56
|
{
|
|
54
|
-
displayName:
|
|
55
|
-
name:
|
|
56
|
-
type:
|
|
57
|
+
displayName: "Client Secret",
|
|
58
|
+
name: "clientSecret",
|
|
59
|
+
type: "string",
|
|
57
60
|
typeOptions: { password: true },
|
|
58
|
-
default:
|
|
61
|
+
default: "",
|
|
59
62
|
required: true,
|
|
60
63
|
description: "Per-client OAuth client secret from ServiceTitan → Settings → API Application Access.",
|
|
61
64
|
},
|
|
62
65
|
// Both fields below are populated by preAuthentication, not user input.
|
|
63
|
-
// appKey
|
|
66
|
+
// appKey comes from the TGL_CUSTOM_APP_SECRETS array (label
|
|
67
|
+
// "TGL_SERVICETITAN_API_KEY") on the n8n service.
|
|
64
68
|
// `expirable: true` on accessToken is REQUIRED — n8n's credentials-helper
|
|
65
69
|
// short-circuits and never invokes preAuthentication unless at least one
|
|
66
70
|
// hidden property has expirable:true. Without it, both fields stay '' and
|
|
67
71
|
// the ST-App-Key header goes out empty → 401 from ServiceTitan.
|
|
68
72
|
{
|
|
69
|
-
displayName:
|
|
70
|
-
name:
|
|
71
|
-
type:
|
|
73
|
+
displayName: "Access Token",
|
|
74
|
+
name: "accessToken",
|
|
75
|
+
type: "hidden",
|
|
72
76
|
typeOptions: { password: true, expirable: true },
|
|
73
|
-
default:
|
|
77
|
+
default: "",
|
|
74
78
|
},
|
|
75
79
|
{
|
|
76
|
-
displayName:
|
|
77
|
-
name:
|
|
78
|
-
type:
|
|
80
|
+
displayName: "App Key",
|
|
81
|
+
name: "appKey",
|
|
82
|
+
type: "hidden",
|
|
79
83
|
typeOptions: { password: true },
|
|
80
|
-
default:
|
|
84
|
+
default: "",
|
|
81
85
|
},
|
|
82
86
|
];
|
|
83
87
|
async preAuthentication(credentials) {
|
|
84
|
-
// The ST-App-Key is TGL-owned and identical across all clients, so it
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
const appKey =
|
|
88
|
-
|
|
89
|
-
throw new Error('[ServiceTitan auth] TGL_SERVICETITAN_APP_KEY env var is not set on this n8n service. Set it on BOTH the n8n primary and worker services, then redeploy.');
|
|
90
|
-
}
|
|
91
|
-
const environment = credentials.environment ?? 'integration';
|
|
88
|
+
// The ST-App-Key is TGL-owned and identical across all clients, so it lives
|
|
89
|
+
// at the n8n instance level in the shared TGL_CUSTOM_APP_SECRETS array
|
|
90
|
+
// (keyed by label), not on every credential. Throws if unset/missing.
|
|
91
|
+
const appKey = (0, customAppSecrets_1.getCustomAppSecret)(SERVICETITAN_APP_KEY_LABEL);
|
|
92
|
+
const environment = credentials.environment ?? "integration";
|
|
92
93
|
const clientId = credentials.clientId;
|
|
93
94
|
const clientSecret = credentials.clientSecret;
|
|
94
95
|
const tenantId = credentials.tenantId;
|
|
95
96
|
const missing = [];
|
|
96
97
|
if (!clientId)
|
|
97
|
-
missing.push(
|
|
98
|
+
missing.push("Client ID");
|
|
98
99
|
if (!clientSecret)
|
|
99
|
-
missing.push(
|
|
100
|
+
missing.push("Client Secret");
|
|
100
101
|
if (!tenantId)
|
|
101
|
-
missing.push(
|
|
102
|
+
missing.push("Tenant ID");
|
|
102
103
|
if (missing.length > 0) {
|
|
103
|
-
throw new Error(`[ServiceTitan auth] Missing required credential field(s): ${missing.join(
|
|
104
|
+
throw new Error(`[ServiceTitan auth] Missing required credential field(s): ${missing.join(", ")}. Fill them in on the n8n credential and save.`);
|
|
104
105
|
}
|
|
105
106
|
const authHost = getAuthHost(environment);
|
|
106
107
|
const tokenUrl = `https://${authHost}/connect/token`;
|
|
107
108
|
// Diagnostics — env, host, clientId prefix (NOT secret), App Key prefix
|
|
108
109
|
const clientIdPreview = `${clientId.slice(0, 6)}…${clientId.slice(-4)}`;
|
|
109
|
-
const appKeyPreview = `${appKey.slice(0, 6)}…${appKey.slice(-4)}`;
|
|
110
|
-
// eslint-disable-next-line no-console
|
|
111
|
-
console.log(`[ServiceTitan auth] env=${environment} tokenUrl=${tokenUrl} tenantId=${tenantId} clientId=${clientIdPreview} appKey=${appKeyPreview}`);
|
|
112
110
|
const body = new URLSearchParams({
|
|
113
|
-
grant_type:
|
|
111
|
+
grant_type: "client_credentials",
|
|
114
112
|
client_id: clientId,
|
|
115
113
|
client_secret: clientSecret,
|
|
116
114
|
}).toString();
|
|
117
115
|
let response;
|
|
118
116
|
try {
|
|
119
117
|
response = (await this.helpers.httpRequest({
|
|
120
|
-
method:
|
|
118
|
+
method: "POST",
|
|
121
119
|
url: tokenUrl,
|
|
122
120
|
headers: {
|
|
123
|
-
|
|
121
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
124
122
|
},
|
|
125
123
|
body,
|
|
126
124
|
}));
|
|
@@ -131,33 +129,31 @@ class ServiceTitanApi {
|
|
|
131
129
|
const status = e.response?.status;
|
|
132
130
|
const statusText = e.response?.statusText;
|
|
133
131
|
const data = e.response?.data;
|
|
134
|
-
const bodyStr = typeof data ===
|
|
135
|
-
const detail = bodyStr ? ` body=${bodyStr.slice(0, 500)}` :
|
|
136
|
-
throw new Error(`[ServiceTitan auth] Token fetch failed: ${status ??
|
|
132
|
+
const bodyStr = typeof data === "string" ? data : JSON.stringify(data);
|
|
133
|
+
const detail = bodyStr ? ` body=${bodyStr.slice(0, 500)}` : "";
|
|
134
|
+
throw new Error(`[ServiceTitan auth] Token fetch failed: ${status ?? "?"} ${statusText ?? ""}${detail}. ` +
|
|
137
135
|
`URL=${tokenUrl} env=${environment} clientId=${clientIdPreview}. ` +
|
|
138
136
|
`Verify Client ID/Secret are correct for the ${environment} environment.`);
|
|
139
137
|
}
|
|
140
138
|
if (!response?.access_token) {
|
|
141
139
|
throw new Error(`[ServiceTitan auth] Token endpoint returned 2xx but no access_token. Response: ${JSON.stringify(response).slice(0, 500)}`);
|
|
142
140
|
}
|
|
143
|
-
// eslint-disable-next-line no-console
|
|
144
|
-
console.log(`[ServiceTitan auth] Token fetch OK. expires_in=${response.expires_in}s`);
|
|
145
141
|
return { accessToken: response.access_token, appKey };
|
|
146
142
|
}
|
|
147
143
|
authenticate = {
|
|
148
|
-
type:
|
|
144
|
+
type: "generic",
|
|
149
145
|
properties: {
|
|
150
146
|
headers: {
|
|
151
|
-
Authorization:
|
|
152
|
-
|
|
147
|
+
Authorization: "=Bearer {{$credentials.accessToken}}",
|
|
148
|
+
"ST-App-Key": "={{$credentials.appKey}}",
|
|
153
149
|
},
|
|
154
150
|
},
|
|
155
151
|
};
|
|
156
152
|
test = {
|
|
157
153
|
request: {
|
|
158
154
|
baseURL: '=https://{{$credentials.environment === "production" ? "api.servicetitan.io" : "api-integration.servicetitan.io"}}',
|
|
159
|
-
url:
|
|
160
|
-
method:
|
|
155
|
+
url: "=/settings/v2/tenant/{{$credentials.tenantId}}/user-roles",
|
|
156
|
+
method: "GET",
|
|
161
157
|
qs: {
|
|
162
158
|
page: 1,
|
|
163
159
|
pageSize: 1,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServiceTitanApi.credentials.js","sourceRoot":"","sources":["../../credentials/ServiceTitanApi.credentials.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"ServiceTitanApi.credentials.js","sourceRoot":"","sources":["../../credentials/ServiceTitanApi.credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TGL custom app secrets live in a single n8n env var, TGL_CUSTOM_APP_SECRETS,
|
|
3
|
+
* holding a JSON array of { label, value } entries. Each TGL integration reads
|
|
4
|
+
* its own instance-level secret by label, keeping these values out of
|
|
5
|
+
* client-visible credential fields and out of per-integration env vars.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Return the `value` of the TGL_CUSTOM_APP_SECRETS entry whose `label` matches.
|
|
9
|
+
* Fails hard (no fallback) with a distinct error for each failure mode so the
|
|
10
|
+
* cause is obvious in the n8n logs.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getCustomAppSecret(label: string): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TGL custom app secrets live in a single n8n env var, TGL_CUSTOM_APP_SECRETS,
|
|
4
|
+
* holding a JSON array of { label, value } entries. Each TGL integration reads
|
|
5
|
+
* its own instance-level secret by label, keeping these values out of
|
|
6
|
+
* client-visible credential fields and out of per-integration env vars.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getCustomAppSecret = getCustomAppSecret;
|
|
10
|
+
const ENV_VAR = 'TGL_CUSTOM_APP_SECRETS';
|
|
11
|
+
/**
|
|
12
|
+
* Return the `value` of the TGL_CUSTOM_APP_SECRETS entry whose `label` matches.
|
|
13
|
+
* Fails hard (no fallback) with a distinct error for each failure mode so the
|
|
14
|
+
* cause is obvious in the n8n logs.
|
|
15
|
+
*/
|
|
16
|
+
function getCustomAppSecret(label) {
|
|
17
|
+
// eslint-disable-next-line @n8n/community-nodes/no-restricted-globals
|
|
18
|
+
const raw = process.env[ENV_VAR];
|
|
19
|
+
if (!raw) {
|
|
20
|
+
throw new Error(`[TGL secrets] ${ENV_VAR} env var is not set on this n8n service. Set it ` +
|
|
21
|
+
`on BOTH the n8n primary and worker services (a JSON array of ` +
|
|
22
|
+
`{ "label", "value" } objects), then redeploy.`);
|
|
23
|
+
}
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
throw new Error(`[TGL secrets] ${ENV_VAR} could not be parsed as JSON. It must be a JSON ` +
|
|
30
|
+
`array of { "label", "value" } objects.`);
|
|
31
|
+
}
|
|
32
|
+
if (!Array.isArray(parsed)) {
|
|
33
|
+
throw new Error(`[TGL secrets] ${ENV_VAR} must be a JSON array of { "label", "value" } objects.`);
|
|
34
|
+
}
|
|
35
|
+
const entry = parsed.find((s) => s?.label === label);
|
|
36
|
+
const value = entry?.value;
|
|
37
|
+
if (!value) {
|
|
38
|
+
throw new Error(`[TGL secrets] ${ENV_VAR} has no entry with a non-empty value for label ` +
|
|
39
|
+
`"${label}". Add { "label": "${label}", "value": "<secret>" } to the array.`);
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=customAppSecrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customAppSecrets.js","sourceRoot":"","sources":["../../credentials/customAppSecrets.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAcH,gDAqCC;AA5CD,MAAM,OAAO,GAAG,wBAAwB,CAAC;AAEzC;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,KAAa;IAC/C,sEAAsE;IACtE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,GAAG,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACd,iBAAiB,OAAO,kDAAkD;YACzE,+DAA+D;YAC/D,+CAA+C,CAChD,CAAC;IACH,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,KAAK,CACd,iBAAiB,OAAO,kDAAkD;YACzE,wCAAwC,CACzC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACd,iBAAiB,OAAO,wDAAwD,CAChF,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAI,MAA4B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,KAAK,CAAC,CAAC;IAC5E,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,CAAC;IAC3B,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACd,iBAAiB,OAAO,iDAAiD;YACxE,IAAI,KAAK,sBAAsB,KAAK,wCAAwC,CAC7E,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC"}
|
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* Naming convention notes:
|
|
6
|
-
* - File basename `ServiceTitan.node.ts` must match the exported class name
|
|
7
|
-
* `ServiceTitan` — n8n's community-node loader does
|
|
8
|
-
* `new require(file)[fileBasename]()` to instantiate.
|
|
9
|
-
* - `description.name` is `'serviceTitan'` (camelCase of the file basename).
|
|
10
|
-
* n8n's picker merges trigger+action into one app tile iff
|
|
11
|
-
* `triggerName.replace('Trigger', '') === actionName`, so the trigger MUST
|
|
12
|
-
* be `'serviceTitanTrigger'` for them to render as a single ServiceTitan tile.
|
|
13
|
-
* - Collision risk with a future official n8n ServiceTitan node is handled by
|
|
14
|
-
* the npm scope: community packages are addressed as
|
|
15
|
-
* `<packageName>.<name>`, so `@thegraphitelab/n8n-nodes-servicetitan.serviceTitan`
|
|
16
|
-
* never collides with `n8n-nodes-base.serviceTitan` if n8n ever ships one.
|
|
17
|
-
*/
|
|
18
|
-
export declare class ServiceTitan implements INodeType {
|
|
19
|
-
description: INodeTypeDescription;
|
|
1
|
+
import { VersionedNodeType } from "n8n-workflow";
|
|
2
|
+
export declare class ServiceTitan extends VersionedNodeType {
|
|
3
|
+
constructor();
|
|
20
4
|
}
|
|
@@ -2,67 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ServiceTitan = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* n8n's picker merges trigger+action into one app tile iff
|
|
15
|
-
* `triggerName.replace('Trigger', '') === actionName`, so the trigger MUST
|
|
16
|
-
* be `'serviceTitanTrigger'` for them to render as a single ServiceTitan tile.
|
|
17
|
-
* - Collision risk with a future official n8n ServiceTitan node is handled by
|
|
18
|
-
* the npm scope: community packages are addressed as
|
|
19
|
-
* `<packageName>.<name>`, so `@thegraphitelab/n8n-nodes-servicetitan.serviceTitan`
|
|
20
|
-
* never collides with `n8n-nodes-base.serviceTitan` if n8n ever ships one.
|
|
21
|
-
*/
|
|
22
|
-
class ServiceTitan {
|
|
23
|
-
description = {
|
|
24
|
-
displayName: 'ServiceTitan',
|
|
25
|
-
name: 'serviceTitan',
|
|
26
|
-
icon: {
|
|
27
|
-
light: 'file:servicetitan.svg',
|
|
28
|
-
dark: 'file:servicetitan.dark.svg',
|
|
29
|
-
},
|
|
30
|
-
group: ['transform'],
|
|
31
|
-
version: 1,
|
|
32
|
-
subtitle: '={{$parameter["operation"] || $parameter["event"]}}: {{$parameter["resource"]}}',
|
|
33
|
-
description: 'Interact with the ServiceTitan API',
|
|
34
|
-
defaults: {
|
|
35
|
-
name: 'ServiceTitan',
|
|
36
|
-
},
|
|
37
|
-
usableAsTool: true,
|
|
38
|
-
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
39
|
-
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
40
|
-
credentials: [
|
|
41
|
-
{
|
|
42
|
-
name: 'serviceTitanApi',
|
|
43
|
-
required: true,
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
requestDefaults: {
|
|
47
|
-
baseURL: '=https://{{$credentials.environment === "production" ? "api.servicetitan.io" : "api-integration.servicetitan.io"}}',
|
|
48
|
-
headers: {
|
|
49
|
-
'Content-Type': 'application/json',
|
|
50
|
-
Accept: 'application/json',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
properties: [
|
|
54
|
-
{
|
|
55
|
-
displayName: 'Resource',
|
|
56
|
-
name: 'resource',
|
|
57
|
-
type: 'options',
|
|
58
|
-
noDataExpression: true,
|
|
59
|
-
options: resources_1.resourceSelectOptions,
|
|
60
|
-
default: 'customer',
|
|
61
|
-
},
|
|
62
|
-
...resources_1.allResources.flatMap((r) => r.resourceOperations),
|
|
63
|
-
...resources_1.allResources.flatMap((r) => r.resourceFields),
|
|
64
|
-
],
|
|
65
|
-
};
|
|
5
|
+
const node_description_1 = require("./v1/descriptions/node.description");
|
|
6
|
+
const ServiceTitanV1_node_1 = require("./v1/ServiceTitanV1.node");
|
|
7
|
+
class ServiceTitan extends n8n_workflow_1.VersionedNodeType {
|
|
8
|
+
constructor() {
|
|
9
|
+
const nodeVersions = {
|
|
10
|
+
1: new ServiceTitanV1_node_1.ServiceTitanV1(node_description_1.baseDescription),
|
|
11
|
+
};
|
|
12
|
+
super(nodeVersions, node_description_1.baseDescription);
|
|
13
|
+
}
|
|
66
14
|
}
|
|
67
15
|
exports.ServiceTitan = ServiceTitan;
|
|
68
16
|
//# sourceMappingURL=ServiceTitan.node.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServiceTitan.node.js","sourceRoot":"","sources":["../../../nodes/ServiceTitan/ServiceTitan.node.ts"],"names":[],"mappings":";;;AAAA,+
|
|
1
|
+
{"version":3,"file":"ServiceTitan.node.js","sourceRoot":"","sources":["../../../nodes/ServiceTitan/ServiceTitan.node.ts"],"names":[],"mappings":";;;AAAA,+CAAqE;AACrE,yEAAqE;AACrE,kEAA0D;AAE1D,MAAa,YAAa,SAAQ,gCAAiB;IACjD;QACE,MAAM,YAAY,GAAuC;YACvD,CAAC,EAAE,IAAI,oCAAc,CAAC,kCAAe,CAAC;SACvC,CAAC;QAEF,KAAK,CAAC,YAAY,EAAE,kCAAe,CAAC,CAAC;IACvC,CAAC;CACF;AARD,oCAQC"}
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare class ServiceTitanTrigger
|
|
3
|
-
|
|
4
|
-
methods: {
|
|
5
|
-
loadOptions: {
|
|
6
|
-
getBusinessUnits(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
7
|
-
};
|
|
8
|
-
};
|
|
9
|
-
poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
|
|
1
|
+
import { VersionedNodeType } from "n8n-workflow";
|
|
2
|
+
export declare class ServiceTitanTrigger extends VersionedNodeType {
|
|
3
|
+
constructor();
|
|
10
4
|
}
|