@heines/n8n-nodes-pax8 0.1.0 → 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/README.md CHANGED
@@ -28,7 +28,6 @@ Use the **Pax8 OAuth2 (Client Credentials)** credential:
28
28
  - Token URL: `https://token-manager.pax8.com/oauth/token`
29
29
  - Audience: `https://api.pax8.com`
30
30
 
31
- ### Access Token
32
31
  If you already have an access token, use **Pax8 API Token** and paste it.
33
32
 
34
33
  ## Node usage
@@ -3,6 +3,5 @@ export declare class Pax8OAuth2Api implements ICredentialType {
3
3
  name: string;
4
4
  displayName: string;
5
5
  documentationUrl: string;
6
- extends: string[];
7
6
  properties: INodeProperties[];
8
7
  }
@@ -5,8 +5,7 @@ class Pax8OAuth2Api {
5
5
  constructor() {
6
6
  this.name = 'pax8OAuth2Api';
7
7
  this.displayName = 'Pax8 OAuth2 (Client Credentials)';
8
- this.documentationUrl = 'https://developer.pax8.com/'; // adjust if different
9
- this.extends = ['oAuth2Api'];
8
+ this.documentationUrl = 'https://devx.pax8.com/reference/createaccesstoken';
10
9
  this.properties = [
11
10
  {
12
11
  displayName: 'Client ID',
@@ -23,39 +22,21 @@ class Pax8OAuth2Api {
23
22
  default: '',
24
23
  required: true,
25
24
  },
26
- {
27
- displayName: 'Token URL',
28
- name: 'accessTokenUrl',
29
- type: 'string',
30
- default: 'https://token-manager.pax8.com/oauth/token',
31
- required: true,
32
- },
33
25
  {
34
26
  displayName: 'Audience',
35
27
  name: 'audience',
36
28
  type: 'string',
37
29
  default: 'https://api.pax8.com',
38
- description: 'Pax8 API audience to request in the token call.',
39
30
  required: true,
31
+ description: 'Audience to request in the token call. For Pax8 Partner API use https://api.pax8.com',
40
32
  },
41
33
  {
42
- displayName: 'Scope',
43
- name: 'scope',
34
+ displayName: 'Token URL',
35
+ name: 'tokenUrl',
44
36
  type: 'string',
45
- default: '',
46
- description: 'Optional. Leave blank unless Pax8 provided you scopes.',
47
- },
48
- {
49
- displayName: 'Grant Type',
50
- name: 'grantType',
51
- type: 'hidden',
52
- default: 'clientCredentials',
53
- },
54
- {
55
- displayName: 'Authentication',
56
- name: 'authentication',
57
- type: 'hidden',
58
- default: 'body',
37
+ default: 'https://api.pax8.com/v1/token',
38
+ required: true,
39
+ description: 'Pax8 access token endpoint.',
59
40
  },
60
41
  ];
61
42
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export * from './nodes/Pax8/Pax8.node';
2
2
  export * from './credentials/Pax8OAuth2Api.credentials';
3
- export * from './credentials/Pax8ApiToken.credentials';
package/dist/index.js CHANGED
@@ -16,4 +16,3 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./nodes/Pax8/Pax8.node"), exports);
18
18
  __exportStar(require("./credentials/Pax8OAuth2Api.credentials"), exports);
19
- __exportStar(require("./credentials/Pax8ApiToken.credentials"), exports);
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Pax8 = void 0;
4
- const OP_DEFS = {
5
- 'createAccessToken': { resource: 'accessToken', method: 'POST', path: '/token', params: [], hasBody: true },
6
- 'createCompany': { resource: 'companies', method: 'POST', path: '/companies', params: [], hasBody: true },
4
+ async function pax8HttpRequest(ctx, options) {
5
+ const token = await getPax8AccessToken(ctx);
6
+ options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}`, accept: 'application/json' };
7
+ return ctx.helpers.httpRequest(options);
8
+ }
9
+ const OP_DEFS = { 'createCompany': { resource: 'companies', method: 'POST', path: '/companies', params: [], hasBody: true },
7
10
  'get_companies_companyId': { resource: 'companies', method: 'GET', path: '/companies/{companyId}', params: [{ name: 'companyId', in: 'path', required: true }], hasBody: false },
8
11
  'findCompanies': { resource: 'companies', method: 'GET', path: '/companies', params: [{ name: 'page', in: 'query', required: false }, { name: 'size', in: 'query', required: false }, { name: 'sort', in: 'query', required: false }, { name: 'city', in: 'query', required: false }, { name: 'country', in: 'query', required: false }, { name: 'stateOrProvince', in: 'query', required: false }, { name: 'postalCode', in: 'query', required: false }, { name: 'selfServiceAllowed', in: 'query', required: false }, { name: 'billOnBehalfOfEnabled', in: 'query', required: false }, { name: 'orderApprovalRequired', in: 'query', required: false }, { name: 'status', in: 'query', required: false }], hasBody: false },
9
12
  'updateCompany': { resource: 'companies', method: 'PATCH', path: '/companies/{companyId}', params: [{ name: 'companyId', in: 'path', required: true }], hasBody: true },
@@ -104,11 +107,50 @@ const OP_DEFS = {
104
107
  'updateConfiguration': { resource: 'webhooks', method: 'POST', path: '/webhooks/{id}/configuration', params: [{ name: 'id', in: 'path', required: true }], hasBody: true },
105
108
  'updateStatus': { resource: 'webhooks', method: 'POST', path: '/webhooks/{id}/status', params: [{ name: 'id', in: 'path', required: true }], hasBody: true },
106
109
  };
110
+ function pax8TokenExpiryMs(r) {
111
+ if (r.expires_at)
112
+ return new Date(r.expires_at).getTime();
113
+ if (typeof r.expires_in === 'number')
114
+ return Date.now() + r.expires_in * 1000;
115
+ return Date.now() + 23 * 60 * 60 * 1000;
116
+ }
117
+ async function getPax8AccessToken(ctx) {
118
+ const creds = (await ctx.getCredentials('pax8OAuth2Api'));
119
+ const tokenUrl = creds.tokenUrl || 'https://api.pax8.com/v1/token';
120
+ const audience = creds.audience || 'https://api.pax8.com';
121
+ const clientId = creds.clientId;
122
+ const clientSecret = creds.clientSecret;
123
+ const staticData = ctx.getWorkflowStaticData('global');
124
+ const cachedToken = staticData.pax8AccessToken;
125
+ const cachedExpiry = staticData.pax8AccessTokenExpiresAt;
126
+ // 60s safety margin
127
+ if (cachedToken && cachedExpiry && cachedExpiry - 60_000 > Date.now()) {
128
+ return cachedToken;
129
+ }
130
+ const tokenResp = (await ctx.helpers.httpRequest({
131
+ method: 'POST',
132
+ url: tokenUrl,
133
+ headers: {
134
+ accept: 'application/json',
135
+ 'content-type': 'application/json',
136
+ },
137
+ body: {
138
+ audience,
139
+ grant_type: 'client_credentials',
140
+ client_id: clientId,
141
+ client_secret: clientSecret,
142
+ },
143
+ json: true,
144
+ }));
145
+ staticData.pax8AccessToken = tokenResp.access_token;
146
+ staticData.pax8AccessTokenExpiresAt = pax8TokenExpiryMs(tokenResp);
147
+ return tokenResp.access_token;
148
+ }
107
149
  function applyPathParams(endpoint, params) {
108
150
  let result = endpoint;
109
151
  for (const [key, value] of Object.entries(params)) {
110
- result = result.replaceAll(`{${key}}`, encodeURIComponent(String(value)));
111
- result = result.replaceAll(`:${key}`, encodeURIComponent(String(value)));
152
+ result = result.split(`{${key}}`).join(encodeURIComponent(String(value)));
153
+ result = result.split(`:${key}`).join(encodeURIComponent(String(value)));
112
154
  }
113
155
  return result;
114
156
  }
@@ -161,21 +203,12 @@ class Pax8 {
161
203
  inputs: ['main'],
162
204
  outputs: ['main'],
163
205
  credentials: [
164
- { name: 'pax8OAuth2Api', required: false },
165
- { name: 'pax8ApiToken', required: false },
166
- ],
167
- properties: [
168
206
  {
169
- displayName: 'Authentication',
170
- name: 'authMode',
171
- type: 'options',
172
- default: 'oauth2',
173
- options: [
174
- { name: 'OAuth2 (Client Credentials)', value: 'oauth2' },
175
- { name: 'Access Token', value: 'token' },
176
- ],
177
- description: 'Choose how the node should authenticate',
207
+ name: 'pax8OAuth2Api',
208
+ required: true,
178
209
  },
210
+ ],
211
+ properties: [
179
212
  {
180
213
  displayName: 'Base URL',
181
214
  name: 'baseUrl',
@@ -187,9 +220,8 @@ class Pax8 {
187
220
  displayName: 'Resource',
188
221
  name: 'resource',
189
222
  type: 'options',
190
- default: 'accessToken',
223
+ default: 'companies',
191
224
  options: [
192
- { name: 'Access Token', value: 'accessToken' },
193
225
  { name: 'Companies', value: 'companies' },
194
226
  { name: 'Contacts', value: 'contacts' },
195
227
  { name: 'Invoices', value: 'invoices' },
@@ -2880,7 +2912,6 @@ class Pax8 {
2880
2912
  this.methods = {
2881
2913
  loadOptions: {
2882
2914
  async getCompanies() {
2883
- const authMode = this.getCurrentNodeParameter('authMode') || 'oauth2';
2884
2915
  const baseUrl = String(this.getCurrentNodeParameter('baseUrl') || 'https://api.pax8.com').replace(/\/$/, '');
2885
2916
  const items = [];
2886
2917
  let page = 0;
@@ -2888,14 +2919,7 @@ class Pax8 {
2888
2919
  for (let guard = 0; guard < 20; guard++) {
2889
2920
  const url = `${baseUrl}/companies`;
2890
2921
  const options = { method: 'GET', url, json: true, qs: { page, size } };
2891
- const res = authMode === 'oauth2'
2892
- ? await this.helpers.httpRequestWithAuthentication.call(this, 'pax8OAuth2Api', options)
2893
- : await (async () => {
2894
- const creds = await this.getCredentials('pax8ApiToken');
2895
- const token = creds.accessToken;
2896
- options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}` };
2897
- return this.helpers.httpRequest(options);
2898
- })();
2922
+ const res = await pax8HttpRequest(this, options);
2899
2923
  const batch = extractListItems(res) || [];
2900
2924
  items.push(...batch);
2901
2925
  if (batch.length < size)
@@ -2908,7 +2932,6 @@ class Pax8 {
2908
2932
  })).filter((o) => o.value);
2909
2933
  },
2910
2934
  async getProducts() {
2911
- const authMode = this.getCurrentNodeParameter('authMode') || 'oauth2';
2912
2935
  const baseUrl = String(this.getCurrentNodeParameter('baseUrl') || 'https://api.pax8.com').replace(/\/$/, '');
2913
2936
  const items = [];
2914
2937
  let page = 0;
@@ -2916,14 +2939,7 @@ class Pax8 {
2916
2939
  for (let guard = 0; guard < 20; guard++) {
2917
2940
  const url = `${baseUrl}/products`;
2918
2941
  const options = { method: 'GET', url, json: true, qs: { page, size } };
2919
- const res = authMode === 'oauth2'
2920
- ? await this.helpers.httpRequestWithAuthentication.call(this, 'pax8OAuth2Api', options)
2921
- : await (async () => {
2922
- const creds = await this.getCredentials('pax8ApiToken');
2923
- const token = creds.accessToken;
2924
- options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}` };
2925
- return this.helpers.httpRequest(options);
2926
- })();
2942
+ const res = await pax8HttpRequest(this, options);
2927
2943
  const batch = extractListItems(res) || [];
2928
2944
  items.push(...batch);
2929
2945
  if (batch.length < size)
@@ -2936,7 +2952,6 @@ class Pax8 {
2936
2952
  })).filter((o) => o.value);
2937
2953
  },
2938
2954
  async getSubscriptions() {
2939
- const authMode = this.getCurrentNodeParameter('authMode') || 'oauth2';
2940
2955
  const baseUrl = String(this.getCurrentNodeParameter('baseUrl') || 'https://api.pax8.com').replace(/\/$/, '');
2941
2956
  const companyId = this.getCurrentNodeParameter('companyId');
2942
2957
  const items = [];
@@ -2948,14 +2963,7 @@ class Pax8 {
2948
2963
  if (companyId)
2949
2964
  qs.companyId = companyId;
2950
2965
  const options = { method: 'GET', url, json: true, qs };
2951
- const res = authMode === 'oauth2'
2952
- ? await this.helpers.httpRequestWithAuthentication.call(this, 'pax8OAuth2Api', options)
2953
- : await (async () => {
2954
- const creds = await this.getCredentials('pax8ApiToken');
2955
- const token = creds.accessToken;
2956
- options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}` };
2957
- return this.helpers.httpRequest(options);
2958
- })();
2966
+ const res = await pax8HttpRequest(this, options);
2959
2967
  const batch = extractListItems(res) || [];
2960
2968
  items.push(...batch);
2961
2969
  if (batch.length < size)
@@ -2968,7 +2976,6 @@ class Pax8 {
2968
2976
  })).filter((o) => o.value);
2969
2977
  },
2970
2978
  async getInvoices() {
2971
- const authMode = this.getCurrentNodeParameter('authMode') || 'oauth2';
2972
2979
  const baseUrl = String(this.getCurrentNodeParameter('baseUrl') || 'https://api.pax8.com').replace(/\/$/, '');
2973
2980
  const companyId = this.getCurrentNodeParameter('companyId');
2974
2981
  const items = [];
@@ -2980,14 +2987,7 @@ class Pax8 {
2980
2987
  if (companyId)
2981
2988
  qs.companyId = companyId;
2982
2989
  const options = { method: 'GET', url, json: true, qs };
2983
- const res = authMode === 'oauth2'
2984
- ? await this.helpers.httpRequestWithAuthentication.call(this, 'pax8OAuth2Api', options)
2985
- : await (async () => {
2986
- const creds = await this.getCredentials('pax8ApiToken');
2987
- const token = creds.accessToken;
2988
- options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}` };
2989
- return this.helpers.httpRequest(options);
2990
- })();
2990
+ const res = await pax8HttpRequest(this, options);
2991
2991
  const batch = extractListItems(res) || [];
2992
2992
  items.push(...batch);
2993
2993
  if (batch.length < size)
@@ -3000,7 +3000,6 @@ class Pax8 {
3000
3000
  })).filter((o) => o.value);
3001
3001
  },
3002
3002
  async getQuotes() {
3003
- const authMode = this.getCurrentNodeParameter('authMode') || 'oauth2';
3004
3003
  const baseUrl = String(this.getCurrentNodeParameter('baseUrl') || 'https://api.pax8.com').replace(/\/$/, '');
3005
3004
  const items = [];
3006
3005
  let page = 0;
@@ -3008,14 +3007,7 @@ class Pax8 {
3008
3007
  for (let guard = 0; guard < 20; guard++) {
3009
3008
  const url = `${baseUrl}/v2/quotes`;
3010
3009
  const options = { method: 'GET', url, json: true, qs: { page, limit } };
3011
- const res = authMode === 'oauth2'
3012
- ? await this.helpers.httpRequestWithAuthentication.call(this, 'pax8OAuth2Api', options)
3013
- : await (async () => {
3014
- const creds = await this.getCredentials('pax8ApiToken');
3015
- const token = creds.accessToken;
3016
- options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}` };
3017
- return this.helpers.httpRequest(options);
3018
- })();
3010
+ const res = await pax8HttpRequest(this, options);
3019
3011
  const batch = extractListItems(res) || [];
3020
3012
  items.push(...batch);
3021
3013
  if (batch.length < limit)
@@ -3028,7 +3020,6 @@ class Pax8 {
3028
3020
  })).filter((o) => o.value);
3029
3021
  },
3030
3022
  async getWebhooks() {
3031
- const authMode = this.getCurrentNodeParameter('authMode') || 'oauth2';
3032
3023
  const baseUrl = String(this.getCurrentNodeParameter('baseUrl') || 'https://api.pax8.com').replace(/\/$/, '');
3033
3024
  const items = [];
3034
3025
  let page = 0;
@@ -3036,14 +3027,7 @@ class Pax8 {
3036
3027
  for (let guard = 0; guard < 20; guard++) {
3037
3028
  const url = `${baseUrl}/webhooks`;
3038
3029
  const options = { method: 'GET', url, json: true, qs: { page, size } };
3039
- const res = authMode === 'oauth2'
3040
- ? await this.helpers.httpRequestWithAuthentication.call(this, 'pax8OAuth2Api', options)
3041
- : await (async () => {
3042
- const creds = await this.getCredentials('pax8ApiToken');
3043
- const token = creds.accessToken;
3044
- options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}` };
3045
- return this.helpers.httpRequest(options);
3046
- })();
3030
+ const res = await pax8HttpRequest(this, options);
3047
3031
  const batch = extractListItems(res) || [];
3048
3032
  items.push(...batch);
3049
3033
  if (batch.length < size)
@@ -3056,7 +3040,6 @@ class Pax8 {
3056
3040
  })).filter((o) => o.value);
3057
3041
  },
3058
3042
  async getContacts() {
3059
- const authMode = this.getCurrentNodeParameter('authMode') || 'oauth2';
3060
3043
  const baseUrl = String(this.getCurrentNodeParameter('baseUrl') || 'https://api.pax8.com').replace(/\/$/, '');
3061
3044
  const companyId = this.getCurrentNodeParameter('companyId');
3062
3045
  if (!companyId)
@@ -3067,14 +3050,7 @@ class Pax8 {
3067
3050
  for (let guard = 0; guard < 20; guard++) {
3068
3051
  const url = `${baseUrl}/companies/${encodeURIComponent(companyId)}/contacts`;
3069
3052
  const options = { method: 'GET', url, json: true, qs: { page, size } };
3070
- const res = authMode === 'oauth2'
3071
- ? await this.helpers.httpRequestWithAuthentication.call(this, 'pax8OAuth2Api', options)
3072
- : await (async () => {
3073
- const creds = await this.getCredentials('pax8ApiToken');
3074
- const token = creds.accessToken;
3075
- options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}` };
3076
- return this.helpers.httpRequest(options);
3077
- })();
3053
+ const res = await pax8HttpRequest(this, options);
3078
3054
  const batch = extractListItems(res) || [];
3079
3055
  items.push(...batch);
3080
3056
  if (batch.length < size)
@@ -3093,7 +3069,6 @@ class Pax8 {
3093
3069
  const items = this.getInputData();
3094
3070
  const returnData = [];
3095
3071
  for (let i = 0; i < items.length; i++) {
3096
- const authMode = this.getNodeParameter('authMode', i);
3097
3072
  const baseUrl = this.getNodeParameter('baseUrl', i).replace(/\/$/, '');
3098
3073
  const resource = this.getNodeParameter('resource', i);
3099
3074
  const operation = this.getNodeParameter('operation', i);
@@ -3129,20 +3104,18 @@ class Pax8 {
3129
3104
  url,
3130
3105
  json: true,
3131
3106
  qs: cleanQuery(qs),
3107
+ headers: {
3108
+ accept: 'application/json',
3109
+ },
3132
3110
  };
3133
3111
  if (def.hasBody) {
3134
3112
  const body = this.getNodeParameter('body', i);
3135
3113
  options.body = body;
3136
3114
  }
3137
- if (authMode === 'oauth2') {
3138
- return this.helpers.httpRequestWithAuthentication.call(this, 'pax8OAuth2Api', options);
3139
- }
3140
- else {
3141
- const creds = await this.getCredentials('pax8ApiToken');
3142
- const token = creds.accessToken;
3143
- options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}` };
3144
- return this.helpers.httpRequest(options);
3145
- }
3115
+ // Pax8 OAuth2 token (JSON body token endpoint) -> Bearer header
3116
+ const token = await getPax8AccessToken(this);
3117
+ options.headers = { ...(options.headers || {}), Authorization: `Bearer ${token}` };
3118
+ return this.helpers.httpRequest(options);
3146
3119
  };
3147
3120
  if (pagination && returnAll) {
3148
3121
  const all = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heines/n8n-nodes-pax8",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "n8n community node for the Pax8 API (Partner endpoints).",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
@@ -37,11 +37,13 @@
37
37
  "n8n": {
38
38
  "n8nNodesApiVersion": 1,
39
39
  "credentials": [
40
- "dist/credentials/Pax8OAuth2Api.credentials.js",
41
- "dist/credentials/Pax8ApiToken.credentials.js"
40
+ "dist/credentials/Pax8OAuth2Api.credentials.js"
42
41
  ],
43
42
  "nodes": [
44
43
  "dist/nodes/Pax8/Pax8.node.js"
45
44
  ]
45
+ },
46
+ "engines": {
47
+ "node": ">=18 <23"
46
48
  }
47
- }
49
+ }