@veritree/services 2.81.0 → 2.82.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veritree/services",
3
- "version": "2.81.0",
3
+ "version": "2.82.1",
4
4
  "description": "A collection of javascript functions/services to talk to veritree API",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -11,7 +11,9 @@
11
11
  "registry": "https://registry.npmjs.org"
12
12
  },
13
13
  "scripts": {
14
- "release": "np --no-tests --no-2fa"
14
+ "release": "np --no-tests --no-2fa",
15
+ "release-v2": "yarn publish && yarn push-tag",
16
+ "push-tag": "git push origin v$(node -p \"require('./package.json').version\")"
15
17
  },
16
18
  "devDependencies": {
17
19
  "np": "^8.0.4",
@@ -1,6 +1,12 @@
1
- import { getCookie } from './cookies';
2
- import { createParamsStringFromArgs } from '../utils/args';
3
- import { getSession } from './session';
1
+ import { getCookie } from "./cookies";
2
+ import { createParamsStringFromArgs } from "../utils/args";
3
+ import {
4
+ extractOrgPublicIdFromPath,
5
+ getMe,
6
+ getOrgType,
7
+ getSession,
8
+ resolveUserOrg,
9
+ } from "./session";
4
10
 
5
11
  /**
6
12
  * Returns the runtime configuration object for Nuxt 3.
@@ -43,20 +49,20 @@ import { getSession } from './session';
43
49
  */
44
50
  function addVersionParam(url) {
45
51
  // If URL is invalid or already has the result parameter, return it as is
46
- if (!url || url.includes('_result=1')) {
52
+ if (!url || url.includes("_result=1")) {
47
53
  return url;
48
54
  }
49
55
 
50
56
  // Check if URL already has the version parameter
51
- if (url.includes('_v=')) {
57
+ if (url.includes("_v=")) {
52
58
  return url;
53
59
  }
54
60
 
55
61
  // Use environment variable if available, otherwise use default version
56
- const version = '25.0.0';
62
+ const version = "25.0.0";
57
63
 
58
64
  // Append version parameter to URL
59
- const urlVersionParam = url.includes('?')
65
+ const urlVersionParam = url.includes("?")
60
66
  ? `&_v=${version}`
61
67
  : `?_v=${version}`;
62
68
 
@@ -71,10 +77,10 @@ function addVersionParam(url) {
71
77
  * @returns {object} data
72
78
  */
73
79
  function getConfig(method, data, as) {
74
- const isGet = method === 'get';
80
+ const isGet = method === "get";
75
81
  const isSpoofing = as;
76
82
  const isFormData = data instanceof FormData;
77
- const accessToken = `Bearer ${getCookie('access_token')}`;
83
+ const accessToken = `Bearer ${getCookie("access_token")}`;
78
84
 
79
85
  const config = {
80
86
  method,
@@ -84,7 +90,7 @@ function getConfig(method, data, as) {
84
90
  };
85
91
 
86
92
  if (!isFormData) {
87
- config.headers['Content-Type'] = 'application/json';
93
+ config.headers["Content-Type"] = "application/json";
88
94
  }
89
95
 
90
96
  // TODO: improve this ifs and elses
@@ -92,7 +98,7 @@ function getConfig(method, data, as) {
92
98
  if (!data) data = {};
93
99
 
94
100
  if (isFormData) {
95
- if (isSpoofing) data.set('_method', as.toUpperCase());
101
+ if (isSpoofing) data.set("_method", as.toUpperCase());
96
102
  config.body = data;
97
103
  } else {
98
104
  if (isSpoofing) data._method = as.toUpperCase();
@@ -115,7 +121,7 @@ export default class Api {
115
121
  // this.baseUrl = url + "/api";
116
122
 
117
123
  this.baseUrl = process.env ? `${process.env.API_VERITREE_URL}/api` : null;
118
- this.resource = resource ? resource : '';
124
+ this.resource = resource ? resource : "";
119
125
  this.orgId = null;
120
126
  this.orgType = null;
121
127
  }
@@ -153,7 +159,6 @@ export default class Api {
153
159
  return await this.post(null, data, null, args);
154
160
  }
155
161
 
156
-
157
162
  /**
158
163
  * Updates a resource with the specified ID.
159
164
  *
@@ -163,7 +168,7 @@ export default class Api {
163
168
  * @param {object} args - Additional arguments for the update request.
164
169
  * @returns {Promise<any>} - A promise that resolves to the response from the update request.
165
170
  */
166
- async update(id, data, as = 'put', args) {
171
+ async update(id, data, as = "put", args) {
167
172
  const url = `${this.getUrl(id)}${this.getUrlParams(args)}`;
168
173
  return await this.post(url, data, as);
169
174
  }
@@ -177,7 +182,7 @@ export default class Api {
177
182
  */
178
183
  async delete(id, args) {
179
184
  const url = `${this.getUrl(id)}${this.getUrlParams(args)}`;
180
- return await this.post(url, null, 'delete');
185
+ return await this.post(url, null, "delete");
181
186
  }
182
187
 
183
188
  /**
@@ -197,7 +202,7 @@ export default class Api {
197
202
  */
198
203
  async post(url, data, as, args) {
199
204
  if (!url) url = `${this.getUrl()}${this.getUrlParams(args)}`;
200
- return await this.unWrap(url, 'post', data, as);
205
+ return await this.unWrap(url, "post", data, as);
201
206
  }
202
207
 
203
208
  // ----------
@@ -208,7 +213,7 @@ export default class Api {
208
213
  * @returns
209
214
  */
210
215
  getUrl(id) {
211
- id = id ? `/${id}` : '';
216
+ id = id ? `/${id}` : "";
212
217
  return `${this.baseUrl}/${this.resource}${id}`;
213
218
  }
214
219
 
@@ -222,15 +227,15 @@ export default class Api {
222
227
  let isOrgLess = false;
223
228
  let isOrgIdAs = false;
224
229
  let isOrgTypeAs = false;
225
- let orgIdParam = '';
226
- let orgTypeParam = '';
230
+ let orgIdParam = "";
231
+ let orgTypeParam = "";
227
232
  let argsClone = structuredClone(args); // avoids mutating object
228
233
 
229
234
  // while most of endpoints require an org id and type, some endpoints do not
230
235
  if (argsClone) {
231
- isOrgLess = Object.hasOwn(argsClone, 'orgless');
232
- isOrgIdAs = Object.hasOwn(argsClone, 'org_id_as');
233
- isOrgTypeAs = Object.hasOwn(argsClone, 'org_type_as');
236
+ isOrgLess = Object.hasOwn(argsClone, "orgless");
237
+ isOrgIdAs = Object.hasOwn(argsClone, "org_id_as");
238
+ isOrgTypeAs = Object.hasOwn(argsClone, "org_type_as");
234
239
  }
235
240
 
236
241
  // a super admin user can create/edit data from other orgs
@@ -257,7 +262,7 @@ export default class Api {
257
262
  }
258
263
 
259
264
  return `?${orgIdParam}${orgTypeParam}${createParamsStringFromArgs(
260
- argsClone
265
+ argsClone,
261
266
  )}`;
262
267
  }
263
268
 
@@ -269,7 +274,7 @@ export default class Api {
269
274
  * @param {object} data
270
275
  * @returns {object} envelope
271
276
  */
272
- async unWrap(url, method = 'get', data, as) {
277
+ async unWrap(url, method = "get", data, as) {
273
278
  url = addVersionParam(url);
274
279
  const config = getConfig(method, data, as);
275
280
  const response = await fetch(url, config);
@@ -280,27 +285,44 @@ export default class Api {
280
285
  return;
281
286
  }
282
287
 
283
- const headerContentType = response.headers.get('content-type');
288
+ const headerContentType = response.headers.get("content-type");
284
289
 
285
- if (headerContentType.includes('json')) {
290
+ if (headerContentType.includes("json")) {
286
291
  response.json().then((json) => {
287
292
  response.ok ? resolve(json) : reject(json);
288
293
  });
289
- } else if (headerContentType.includes('csv')) {
294
+ } else if (headerContentType.includes("csv")) {
290
295
  response.text().then((res) => {
291
296
  response.ok ? resolve(res) : reject(res);
292
297
  });
293
298
  } else {
294
- throw new Error('This response header content-type is not handled.');
299
+ throw new Error("This response header content-type is not handled.");
295
300
  }
296
301
  });
297
302
  }
298
303
 
299
304
  setOrg() {
300
305
  const session = getSession();
301
- if (!session) return;
302
- const { orgId, orgType } = session;
303
- this.orgId = orgId;
304
- this.orgType = orgType;
306
+
307
+ if (Object.keys(session).length) {
308
+ const { orgId, orgType } = session;
309
+ this.orgId = orgId;
310
+ this.orgType = orgType;
311
+ return;
312
+ }
313
+
314
+ const me = getMe();
315
+
316
+ // console.log("API setOrg called...", window.location.pathname);
317
+
318
+ if (Object.keys(me).length) {
319
+ const orgPublicId = extractOrgPublicIdFromPath(window.location.pathname);
320
+ const org = resolveUserOrg(me, orgPublicId);
321
+
322
+ this.orgId = org ? org.id : null;
323
+ this.orgType = org ? getOrgType(org.org_type) : null;
324
+ }
325
+
326
+ return {};
305
327
  }
306
328
  }
@@ -1,3 +1,157 @@
1
1
  export const getSession = () => {
2
- return JSON.parse(localStorage.getItem('session'));
2
+ return JSON.parse(localStorage.getItem('session') || '{}');
3
3
  };
4
+
5
+ export const getMe = () => {
6
+ return JSON.parse(localStorage.getItem('me') || '{}');
7
+ }
8
+
9
+ /**
10
+ * Returns reseller org based on:
11
+ * - if has veritree org, return veritree org
12
+ * - if no veritree org, return first reseller org
13
+ *
14
+ * @param {Array<Object>} resellers
15
+ * @returns {object} org
16
+ */
17
+ export const getResellerOrg = (resellers) => {
18
+ if (hasVeritreeOrg(resellers)) {
19
+ return getVeritreeOrg(resellers);
20
+ }
21
+
22
+ return resellers[0]?.org || {};
23
+ };
24
+
25
+ /**
26
+ * Returns the veritree org
27
+ *
28
+ * TODO: improve the veritree org id to be related to env (dev = 3, prod = 1)
29
+ *
30
+ * @param {Array<Object>} resellers
31
+ * @returns {object} org
32
+ */
33
+ export const getVeritreeOrg = (resellers) => {
34
+ return resellers.find(({ org }) => org.id === 3).org;
35
+ };
36
+
37
+ /**
38
+ * Checks if reseller has veritree org
39
+ *
40
+ * @param {Array<Object>} resellers
41
+ * @returns {boolean}
42
+ */
43
+ export const hasVeritreeOrg = (resellers) => {
44
+ return resellers.some(({ org }) => org.id === 3);
45
+ };
46
+
47
+ export function getOrgType(orgType) {
48
+ let orgTypeFormatted = null;
49
+
50
+ switch (orgType) {
51
+ case 'owner':
52
+ orgTypeFormatted = 'ownerAccount';
53
+ break;
54
+ case 'organization':
55
+ orgTypeFormatted = 'orgAccount';
56
+ break;
57
+ case 'sponsor':
58
+ orgTypeFormatted = 'sponsorAccount';
59
+ break;
60
+ case 'reseller':
61
+ orgTypeFormatted = 'resellerAccount';
62
+ break;
63
+ }
64
+
65
+ return orgTypeFormatted;
66
+ }
67
+
68
+ /**
69
+ * Gets the correct org to be used based on the following priority:
70
+ * - If orgPublicId is provided, searches for a matching org across all user's organizations
71
+ * - If no match or orgPublicId not provided, follows hierarchy:
72
+ * 1. First owner org (user_owners)
73
+ * 2. First reseller org (user_resellers)
74
+ * 3. First corporate org (user_orgs)
75
+ * 4. First sponsor org (user_sponsors)
76
+ *
77
+ * @param {Object} me - User object containing organization relationships
78
+ * @param {Array<Object>} me.user_owners - Array of owner organization relationships
79
+ * @param {Array<Object>} me.user_resellers - Array of reseller organization relationships
80
+ * @param {Array<Object>} me.user_orgs - Array of corporate organization relationships
81
+ * @param {Array<Object>} me.user_sponsors - Array of sponsor organization relationships
82
+ * @param {String} orgPublicId - Optional public ID to find specific org
83
+ * @returns {Object} org - The selected organization
84
+ */
85
+ export const resolveUserOrg = (me, orgPublicId) => {
86
+ const { user_owners, user_resellers, user_orgs, user_sponsors } = me;
87
+
88
+ if (orgPublicId) {
89
+ const orgs = getUserOrganizations(me);
90
+ const org = orgs.find(({ public_id }) => public_id === orgPublicId);
91
+
92
+ if (org) {
93
+ return org;
94
+ }
95
+ }
96
+
97
+ if (user_owners && user_owners.length) {
98
+ return user_owners[0].org;
99
+ }
100
+
101
+ if (user_resellers && user_resellers.length) {
102
+ return getResellerOrg(user_resellers);
103
+ }
104
+
105
+ if (user_orgs && user_orgs.length) {
106
+ return user_orgs[0].org;
107
+ }
108
+
109
+ if (user_sponsors && user_sponsors.length) {
110
+ return user_sponsors[0].org;
111
+ }
112
+
113
+ // Return empty object if no organization is found
114
+ return {};
115
+ };
116
+
117
+ /**
118
+ * Aggregates all organizations a user belongs to across all membership types.
119
+ * Combines organizations from owner, reseller, corporate, and sponsor relationships.
120
+ *
121
+ * @param {Object} me - User object containing organization relationships
122
+ * @param {Array<Object>} me.user_owners - Array of owner organization relationships
123
+ * @param {Array<Object>} me.user_resellers - Array of reseller organization relationships
124
+ * @param {Array<Object>} me.user_orgs - Array of corporate organization relationships
125
+ * @param {Array<Object>} me.user_sponsors - Array of sponsor organization relationships
126
+ * @returns {Array<Object>} Array of organization objects from all membership types
127
+ */
128
+ export const getUserOrganizations = (me) => {
129
+ if (!me) {
130
+ return [];
131
+ }
132
+
133
+ return [
134
+ ...(me.user_owners ? me.user_owners.map((uo) => uo.org).filter(Boolean) : []),
135
+ ...(me.user_resellers ? me.user_resellers.map((ur) => ur.org).filter(Boolean) : []),
136
+ ...(me.user_orgs ? me.user_orgs.map((uo) => uo.org).filter(Boolean) : []),
137
+ ...(me.user_sponsors ? me.user_sponsors.map((us) => us.org).filter(Boolean) : []),
138
+ ];
139
+ };
140
+
141
+ /**
142
+ * Extracts the organization public ID from the first segment of a URL path.
143
+ * TODO: handle edge case where path is just '/' by getting an org like is done getOrg in auth.js
144
+ *
145
+ * @param {string} path - The URL path to extract from (e.g., '/veritree/dashboard')
146
+ * @returns {string|null} The organization public ID, or null if path is empty
147
+ */
148
+ export const extractOrgPublicIdFromPath = (path) => {
149
+ if (!path) {
150
+ return null;
151
+ }
152
+
153
+ const url = new URL(path, location.origin);
154
+ const pathSegments = url.pathname.split('/').filter(Boolean);
155
+
156
+ return pathSegments.length > 0 ? pathSegments[0] : null;
157
+ };