@thegraphitelab/n8n-nodes-servicetitan 0.2.0 → 0.4.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.
@@ -61,11 +61,15 @@ class ServiceTitanApi {
61
61
  },
62
62
  // Both fields below are populated by preAuthentication, not user input.
63
63
  // appKey is read from the TGL_SERVICETITAN_APP_KEY env var on the n8n service.
64
+ // `expirable: true` on accessToken is REQUIRED — n8n's credentials-helper
65
+ // short-circuits and never invokes preAuthentication unless at least one
66
+ // hidden property has expirable:true. Without it, both fields stay '' and
67
+ // the ST-App-Key header goes out empty → 401 from ServiceTitan.
64
68
  {
65
69
  displayName: 'Access Token',
66
70
  name: 'accessToken',
67
71
  type: 'hidden',
68
- typeOptions: { password: true },
72
+ typeOptions: { password: true, expirable: true },
69
73
  default: '',
70
74
  },
71
75
  {
@@ -82,24 +86,62 @@ class ServiceTitanApi {
82
86
  // eslint-disable-next-line @n8n/community-nodes/no-restricted-globals
83
87
  const appKey = process.env.TGL_SERVICETITAN_APP_KEY;
84
88
  if (!appKey) {
85
- throw new Error('TGL_SERVICETITAN_APP_KEY environment variable is not set. Configure it on both the n8n primary and worker services in Railway, then redeploy.');
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.');
86
90
  }
87
91
  const environment = credentials.environment ?? 'integration';
88
92
  const clientId = credentials.clientId;
89
93
  const clientSecret = credentials.clientSecret;
94
+ const tenantId = credentials.tenantId;
95
+ const missing = [];
96
+ if (!clientId)
97
+ missing.push('Client ID');
98
+ if (!clientSecret)
99
+ missing.push('Client Secret');
100
+ if (!tenantId)
101
+ missing.push('Tenant ID');
102
+ if (missing.length > 0) {
103
+ throw new Error(`[ServiceTitan auth] Missing required credential field(s): ${missing.join(', ')}. Fill them in on the n8n credential and save.`);
104
+ }
105
+ const authHost = getAuthHost(environment);
106
+ const tokenUrl = `https://${authHost}/connect/token`;
107
+ // Diagnostics — env, host, clientId prefix (NOT secret), App Key prefix
108
+ 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}`);
90
112
  const body = new URLSearchParams({
91
113
  grant_type: 'client_credentials',
92
114
  client_id: clientId,
93
115
  client_secret: clientSecret,
94
116
  }).toString();
95
- const response = (await this.helpers.httpRequest({
96
- method: 'POST',
97
- url: `https://${getAuthHost(environment)}/connect/token`,
98
- headers: {
99
- 'Content-Type': 'application/x-www-form-urlencoded',
100
- },
101
- body,
102
- }));
117
+ let response;
118
+ try {
119
+ response = (await this.helpers.httpRequest({
120
+ method: 'POST',
121
+ url: tokenUrl,
122
+ headers: {
123
+ 'Content-Type': 'application/x-www-form-urlencoded',
124
+ },
125
+ body,
126
+ }));
127
+ }
128
+ catch (err) {
129
+ // Surface ServiceTitan's actual error response so users can fix it
130
+ const e = err;
131
+ const status = e.response?.status;
132
+ const statusText = e.response?.statusText;
133
+ const data = e.response?.data;
134
+ const bodyStr = typeof data === 'string' ? data : JSON.stringify(data);
135
+ const detail = bodyStr ? ` body=${bodyStr.slice(0, 500)}` : '';
136
+ throw new Error(`[ServiceTitan auth] Token fetch failed: ${status ?? '?'} ${statusText ?? ''}${detail}. ` +
137
+ `URL=${tokenUrl} env=${environment} clientId=${clientIdPreview}. ` +
138
+ `Verify Client ID/Secret are correct for the ${environment} environment.`);
139
+ }
140
+ if (!response?.access_token) {
141
+ throw new Error(`[ServiceTitan auth] Token endpoint returned 2xx but no access_token. Response: ${JSON.stringify(response).slice(0, 500)}`);
142
+ }
143
+ // eslint-disable-next-line no-console
144
+ console.log(`[ServiceTitan auth] Token fetch OK. expires_in=${response.expires_in}s`);
103
145
  return { accessToken: response.access_token, appKey };
104
146
  }
105
147
  authenticate = {
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceTitanApi.credentials.js","sourceRoot":"","sources":["../../credentials/ServiceTitanApi.credentials.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ServiceTitanApi.credentials.js","sourceRoot":"","sources":["../../credentials/ServiceTitanApi.credentials.ts"],"names":[],"mappings":"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"}
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegraphitelab/n8n-nodes-servicetitan",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "n8n custom node for ServiceTitan — TGL internal use.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegraphitelab/n8n-nodes-servicetitan",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "n8n custom node for ServiceTitan — TGL internal use.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",