n8n-nodes-smartcontact 1.0.1 → 1.0.2

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/README.md CHANGED
@@ -19,5 +19,5 @@ version: bump `version`, then, with a short-lived granular publish token from Da
19
19
  npm run build
20
20
  npm publish --access public --userconfig <scratch npmrc>
21
21
 
22
- n8n verification (n8n Cloud) needs publishing from GitHub Actions with provenance: see docs/guides/D2-n8n.md.
22
+ n8n verification (n8n Cloud) needs publishing from GitHub Actions with provenance: see docs/BACKLOG.md #84 (needs a SmartContact GitHub account from Dave, then Claude does the rest).
23
23
  Self-hosted n8n users install it from Settings › Community Nodes as `n8n-nodes-smartcontact`.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SmartContactApi = void 0;
4
+ const urls_1 = require("../nodes/SmartContact/urls");
4
5
  class SmartContactApi {
5
6
  name = 'smartContactApi';
6
7
  displayName = 'SmartContact API';
@@ -16,17 +17,18 @@ class SmartContactApi {
16
17
  description: 'In SmartContact: Settings › API keys & agents › add a key of kind "Integration". Its role decides what n8n can read and change.',
17
18
  },
18
19
  {
19
- displayName: 'API URL',
20
+ displayName: 'Environment',
20
21
  name: 'baseUrl',
21
- type: 'string',
22
- default: 'https://app.smartcontact.online/v1',
23
- description: 'Leave as it is unless SmartContact support gives you another address',
22
+ type: 'options',
23
+ default: urls_1.API_URL,
24
+ options: urls_1.API_URLS.map((value) => ({ name: value === urls_1.API_URL ? 'Production' : value.includes('-dev.') ? 'Development' : 'Test', value })),
25
+ description: 'Leave on Production unless you are testing with SmartContact',
24
26
  },
25
27
  ];
26
28
  authenticate = {
27
29
  type: 'generic',
28
30
  properties: { headers: { Authorization: '=Bearer {{$credentials.apiKey}}' } },
29
31
  };
30
- test = { request: { baseURL: '={{$credentials.baseUrl}}', url: '/me' } };
32
+ test = { request: { baseURL: urls_1.BASE_URL_EXPRESSION, url: '/me' } };
31
33
  }
32
34
  exports.SmartContactApi = SmartContactApi;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SmartContact = void 0;
4
+ // SmartContact actions for n8n: create, get, find and update companies, contacts, deals, tasks and notes.
5
+ // Declarative style: each operation maps straight to one call on the public API (/v1/openapi.json).
6
+ const urls_1 = require("./urls");
4
7
  const show = (resource, operation) => ({ show: { resource: [resource], ...(operation ? { operation } : {}) } });
5
8
  const idField = (resource, name, operations) => ({
6
9
  displayName: `${name} ID`,
@@ -110,7 +113,7 @@ class SmartContact {
110
113
  inputs: ['main'],
111
114
  outputs: ['main'],
112
115
  credentials: [{ name: 'smartContactApi', required: true }],
113
- requestDefaults: { baseURL: '={{$credentials.baseUrl}}', headers: { Accept: 'application/json', 'Content-Type': 'application/json' } },
116
+ requestDefaults: { baseURL: urls_1.BASE_URL_EXPRESSION, headers: { Accept: 'application/json', 'Content-Type': 'application/json' } },
114
117
  properties: [
115
118
  {
116
119
  displayName: 'Resource',
@@ -3,8 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SmartContactTrigger = void 0;
4
4
  // Instant SmartContact trigger for n8n. Activating the workflow subscribes this node's webhook URL to the chosen events
5
5
  // (POST /v1/webhooks, which returns a signing secret); deactivating removes it. Each delivery is checked against that
6
- // secret, then the record is fetched from api_url so the workflow gets the full company, contact, deal or task.
6
+ // secret, then the record is fetched by its type and id so the workflow gets the full company, contact, deal or task.
7
7
  const signature_1 = require("./signature");
8
+ const urls_1 = require("./urls");
8
9
  const EVENTS = [
9
10
  ['company.created', 'Company added'],
10
11
  ['company.updated', 'Company changed'],
@@ -60,7 +61,7 @@ class SmartContactTrigger {
60
61
  const data = this.getWorkflowStaticData('node');
61
62
  if (!data.webhookId)
62
63
  return false;
63
- const { baseUrl } = (await this.getCredentials('smartContactApi'));
64
+ const baseUrl = (0, urls_1.safeBaseUrl)((await this.getCredentials('smartContactApi')).baseUrl);
64
65
  const list = (await this.helpers.httpRequestWithAuthentication.call(this, 'smartContactApi', { method: 'GET', url: `${baseUrl}/webhooks`, json: true }));
65
66
  if (list.data.some((w) => w.id === data.webhookId && w.status === 'active'))
66
67
  return true;
@@ -69,7 +70,7 @@ class SmartContactTrigger {
69
70
  return false;
70
71
  },
71
72
  async create() {
72
- const { baseUrl } = (await this.getCredentials('smartContactApi'));
73
+ const baseUrl = (0, urls_1.safeBaseUrl)((await this.getCredentials('smartContactApi')).baseUrl);
73
74
  const created = (await this.helpers.httpRequestWithAuthentication.call(this, 'smartContactApi', {
74
75
  method: 'POST',
75
76
  url: `${baseUrl}/webhooks`,
@@ -84,7 +85,7 @@ class SmartContactTrigger {
84
85
  async delete() {
85
86
  const data = this.getWorkflowStaticData('node');
86
87
  if (data.webhookId) {
87
- const { baseUrl } = (await this.getCredentials('smartContactApi'));
88
+ const baseUrl = (0, urls_1.safeBaseUrl)((await this.getCredentials('smartContactApi')).baseUrl);
88
89
  try {
89
90
  await this.helpers.httpRequestWithAuthentication.call(this, 'smartContactApi', { method: 'DELETE', url: `${baseUrl}/webhooks/${data.webhookId}` });
90
91
  }
@@ -108,10 +109,11 @@ class SmartContactTrigger {
108
109
  }
109
110
  const event = req.body;
110
111
  let record;
111
- const { baseUrl } = (await this.getCredentials('smartContactApi'));
112
- if (this.getNodeParameter('fetch') && event.api_url && String(event.api_url).startsWith(`${baseUrl}/`)) {
112
+ const baseUrl = (0, urls_1.safeBaseUrl)((await this.getCredentials('smartContactApi')).baseUrl);
113
+ const url = this.getNodeParameter('fetch') ? (0, urls_1.recordUrl)(baseUrl, event.type, event.data?.id) : null;
114
+ if (url) {
113
115
  try {
114
- record = (await this.helpers.httpRequestWithAuthentication.call(this, 'smartContactApi', { method: 'GET', url: String(event.api_url), json: true }));
116
+ record = (await this.helpers.httpRequestWithAuthentication.call(this, 'smartContactApi', { method: 'GET', url, json: true, disableFollowRedirect: true }));
115
117
  }
116
118
  catch {
117
119
  record = undefined; // e.g. deleted since; the event still goes through
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ // Where the API key may be sent. Kept apart from the node so it can be tested without n8n installed.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.BASE_URL_EXPRESSION = exports.safeBaseUrl = exports.API_URLS = exports.API_URL = void 0;
5
+ exports.recordUrl = recordUrl;
6
+ exports.API_URL = 'https://app.smartcontact.online/v1';
7
+ exports.API_URLS = [exports.API_URL, 'https://app-dev.smartcontact.online/v1', 'https://app-test.smartcontact.online/v1'];
8
+ /** The credential's API URL if it is one of ours; anything else (a value saved by an older version) falls back to production. */
9
+ const safeBaseUrl = (url) => (exports.API_URLS.includes(String(url)) ? String(url) : exports.API_URL);
10
+ exports.safeBaseUrl = safeBaseUrl;
11
+ /** The same check as safeBaseUrl, as an n8n expression for declarative requests and the credential test. */
12
+ exports.BASE_URL_EXPRESSION = `={{ ${JSON.stringify(exports.API_URLS)}.includes($credentials.baseUrl) ? $credentials.baseUrl : ${JSON.stringify(exports.API_URL)} }}`;
13
+ const PATHS = { company: 'companies', contact: 'contacts', deal: 'deals', task: 'tasks', meeting: 'meetings', activity: 'activities', quote: 'quotes' };
14
+ /** Builds the record's address from the event's type and id. A URL in the payload is never followed. Erased records can't be fetched. */
15
+ function recordUrl(baseUrl, type, id) {
16
+ if (typeof type !== 'string' || typeof id !== 'string' || !id || type.endsWith('.erased'))
17
+ return null;
18
+ const path = Object.prototype.hasOwnProperty.call(PATHS, type.split('.')[0]) ? PATHS[type.split('.')[0]] : undefined;
19
+ return path ? `${(0, exports.safeBaseUrl)(baseUrl)}/${path}/${encodeURIComponent(id)}` : null;
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-smartcontact",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "SmartContact CRM nodes for n8n: instant triggers from SmartContact webhooks, and actions on companies, contacts, deals, tasks and notes",
5
5
  "keywords": ["n8n-community-node-package", "smartcontact", "crm"],
6
6
  "license": "MIT",