@nuskin/contentstack-lib 2.1.0-pa-1117.9 → 2.1.0-pa-1117.11

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
@@ -7,7 +7,6 @@ This project extends the Contentstack delivery sdk, to be shared between the bac
7
7
  getSingletonEntries TAKES AN OPTION CALLED mergeWithFallback. IF THAT IS SET TO TRUE THEN IT WILL MERGE THE RESULTS
8
8
  FOR COMMON SPANISH AND MX-es FOR EXAMPLE.
9
9
  4. getSingletonEntries also allows you to convert the keys from snake case to camel case.
10
- 5. It extends the stack with getAddressForms and getShippingAddressForms for address form content types.
11
10
 
12
11
  ## Installing
13
12
 
@@ -55,82 +54,6 @@ const [
55
54
  mergeWithFallback: true
56
55
  });
57
56
 
58
- // Get address forms for a list of countries.
59
- // By default the returned keys are snake_case.
60
- const addressForms = await Stack.getAddressForms(['United States']);
61
-
62
- // Example result:
63
- // [
64
- // {
65
- // address_elements: [
66
- // {
67
- // field: 'address1',
68
- // width: 'full',
69
- // editable: true,
70
- // max_length: 40,
71
- // required: true
72
- // }
73
- // ],
74
- // postal_code_regex: '^[0-9]{5}(?:-[0-9]{4})?$',
75
- // verify_after_autocomplete: true,
76
- // wrap_address1_to_address2: false
77
- // }
78
- // ]
79
-
80
- // Pass true as the second parameter to camelCase the returned keys.
81
- const camelizedAddressForms = await Stack.getAddressForms(['United States'], true);
82
-
83
- // Get shipping address forms with referenced address form fields expanded.
84
- // countries is normalized to a list of { code, name } objects.
85
- const shippingAddressForms = await Stack.getShippingAddressForms(['United States']);
86
-
87
- // Example result:
88
- // [
89
- // {
90
- // countries: [
91
- // {
92
- // code: 'US',
93
- // name: 'United States'
94
- // }
95
- // ],
96
- // pre_address_fields: [
97
- // {
98
- // field: 'givenName',
99
- // width: 'half',
100
- // editable: true,
101
- // max_length: 30,
102
- // required: true
103
- // }
104
- // ],
105
- // address_form: {
106
- // address_elements: [
107
- // {
108
- // field: 'address1',
109
- // width: 'full',
110
- // editable: true,
111
- // max_length: 40,
112
- // required: true
113
- // }
114
- // ],
115
- // postal_code_regex: '^[0-9]{5}(?:-[0-9]{4})?$',
116
- // verify_after_autocomplete: true,
117
- // wrap_address1_to_address2: false
118
- // },
119
- // post_address_fields: [
120
- // {
121
- // field: 'mobilePhone',
122
- // width: 'full',
123
- // editable: true,
124
- // max_length: 20,
125
- // required: true
126
- // }
127
- // ]
128
- // }
129
- // ]
130
-
131
- // Pass true as the second parameter to camelCase the returned keys.
132
- const camelizedShippingAddressForms = await Stack.getShippingAddressForms(['United States'], true);
133
-
134
57
  // We could possibly add functionality to return the list of regions markets and languages in a single object structure
135
58
  ```
136
59
 
@@ -15,6 +15,9 @@ const {contentstack: prodEnvConfig} = require(`../config/prod.json`);
15
15
  const mockPromiseResult = jest.fn();
16
16
  const mockSinglePromiseResult = jest.fn();
17
17
  const mockStack = jest.fn();
18
+ const mockContainedIn = jest.fn();
19
+ const mockIncludeReference = jest.fn();
20
+ const mockFetch = jest.fn();
18
21
 
19
22
  function getConfig(env) {
20
23
  let config = {};
@@ -42,34 +45,76 @@ function getConfig(env) {
42
45
  }
43
46
 
44
47
  beforeEach(() => {
45
- mockStack.mockImplementation(() => ({
46
- ContentType: () => ({
47
- Query: () => ({
48
- language: () => ({
49
- includeFallback: () => ({
50
- toJSON: () => ({
51
- find: mockPromiseResult,
52
- findOne: mockSinglePromiseResult
53
- })
54
- })
55
- }),
56
- containedIn: () => ({
57
- includeReference: () => ({
58
- toJSON: () => ({
59
- find: mockPromiseResult
60
- })
61
- })
48
+ const mockQuery = {
49
+ language: jest.fn(() => ({
50
+ includeFallback: () => ({
51
+ toJSON: () => ({
52
+ find: mockPromiseResult,
53
+ findOne: mockSinglePromiseResult
62
54
  })
63
55
  })
56
+ })),
57
+ containedIn: mockContainedIn,
58
+ includeReference: mockIncludeReference,
59
+ toJSON: jest.fn(() => ({
60
+ find: mockPromiseResult
61
+ }))
62
+ };
63
+
64
+ mockContainedIn.mockImplementation(() => mockQuery);
65
+ mockIncludeReference.mockImplementation(() => mockQuery);
66
+ mockStack.mockImplementation(() => ({
67
+ ContentType: () => ({
68
+ Query: () => mockQuery,
69
+ fetch: mockFetch
64
70
  })
65
71
  }));
66
72
 
73
+ mockFetch.mockResolvedValue({
74
+ content_type: {
75
+ schema: [
76
+ {
77
+ uid: 'countries',
78
+ data_type: 'reference'
79
+ },
80
+ {
81
+ uid: 'details',
82
+ data_type: 'group',
83
+ schema: [
84
+ {
85
+ uid: 'address_form',
86
+ data_type: 'reference'
87
+ }
88
+ ]
89
+ },
90
+ {
91
+ uid: 'promo_blocks',
92
+ data_type: 'blocks',
93
+ blocks: [
94
+ {
95
+ uid: 'hero',
96
+ schema: [
97
+ {
98
+ uid: 'banner',
99
+ data_type: 'reference'
100
+ }
101
+ ]
102
+ }
103
+ ]
104
+ }
105
+ ]
106
+ }
107
+ });
108
+
67
109
  jest.mock("contentstack", () => ({Stack: mockStack}));
68
110
  });
69
111
 
70
112
  afterEach(() => {
71
113
  mockSinglePromiseResult.mockReset();
72
114
  mockPromiseResult.mockReset();
115
+ mockContainedIn.mockReset();
116
+ mockIncludeReference.mockReset();
117
+ mockFetch.mockReset();
73
118
  //jest.resetAllMocks(); don't reset the mockStack function
74
119
  });
75
120
 
@@ -78,28 +123,7 @@ const mockResults = () => {
78
123
  mockPromiseResult.mockResolvedValueOnce(checkoutAdrStrings);
79
124
  mockSinglePromiseResult.mockResolvedValueOnce(singleCheckoutAdrStrings);
80
125
  mockSinglePromiseResult.mockResolvedValueOnce(singleCheckoutCartStrings);
81
- }
82
-
83
- const addressFormFieldEntries = [
84
- {
85
- field: 'address1',
86
- width: 'full',
87
- editable: true,
88
- max_length: '40',
89
- required: true,
90
- api_mappings: ['address1'],
91
- autocomplete_token: 'address-line1'
92
- },
93
- {
94
- field: 'postalCode',
95
- width: 'half',
96
- editable: false,
97
- max_length: '10',
98
- required: false,
99
- api_mappings: ['postalCode'],
100
- validation_message: 'Invalid postal code'
101
- }
102
- ];
126
+ };
103
127
 
104
128
  describe("ContentstackApi", () => {
105
129
  test("getSingletonEntries", async () => {
@@ -166,380 +190,62 @@ describe("ContentstackApi", () => {
166
190
  });
167
191
  });
168
192
 
169
- test('extractAddressFormFields maps address form field entries', () => {
170
- const {getStack} = require('../src/api');
171
- const Stack = getStack('dev');
172
- expect(Stack.extractAddressFormFields(addressFormFieldEntries)).toStrictEqual([
173
- {
174
- field: 'address1',
175
- width: 'full',
176
- editable: true,
177
- max_length: 40,
178
- required: true,
179
- api_mappings: ['address1'],
180
- autocomplete_token: 'address-line1'
181
- },
182
- {
183
- field: 'postalCode',
184
- width: 'half',
185
- editable: false,
186
- max_length: 10,
187
- required: false,
188
- api_mappings: ['postalCode'],
189
- validation_message: 'Invalid postal code'
190
- }
191
- ]);
192
- });
193
-
194
- test('extractAddressFormFields camelcases address form field entries', () => {
195
- const {getStack} = require('../src/api');
196
- const Stack = getStack('dev');
197
- expect(Stack.extractAddressFormFields(addressFormFieldEntries, true)).toStrictEqual([
198
- {
199
- field: 'address1',
200
- width: 'full',
201
- editable: true,
202
- maxLength: 40,
203
- required: true,
204
- apiMappings: ['address1'],
205
- autocompleteToken: 'address-line1'
206
- },
207
- {
208
- field: 'postalCode',
209
- width: 'half',
210
- editable: false,
211
- maxLength: 10,
212
- required: false,
213
- apiMappings: ['postalCode'],
214
- validationMessage: 'Invalid postal code'
215
- }
216
- ]);
217
- });
218
-
219
- test('extractAddressFormFields returns an empty list when no entries are provided', () => {
220
- const {getStack} = require('../src/api');
221
- const Stack = getStack('dev');
222
- expect(Stack.extractAddressFormFields()).toStrictEqual([]);
223
- });
224
-
225
- test('getAddressForms maps address_elements', async () => {
226
- mockPromiseResult.mockResolvedValueOnce([[
227
- {
228
- title: 'United States',
229
- address_elements: addressFormFieldEntries,
230
- postal_code_regex: '^[0-9]{5}(?:-[0-9]{4})?$',
231
- verify_after_autocomplete: true,
232
- wrap_address1_to_address2: false,
233
- allowed_countries: ['US', 'CA']
234
- }
235
- ]]);
236
- const {getStack} = require('../src/api');
237
- const Stack = getStack('dev');
238
- await expect(Stack.getAddressForms(['United States'])).resolves.toStrictEqual([
239
- {
240
- address_elements: [
241
- {
242
- field: 'address1',
243
- width: 'full',
244
- editable: true,
245
- max_length: 40,
246
- required: true,
247
- api_mappings: ['address1'],
248
- autocomplete_token: 'address-line1'
249
- },
250
- {
251
- field: 'postalCode',
252
- width: 'half',
253
- editable: false,
254
- max_length: 10,
255
- required: false,
256
- api_mappings: ['postalCode'],
257
- validation_message: 'Invalid postal code'
258
- }
259
- ],
260
- postal_code_regex: '^[0-9]{5}(?:-[0-9]{4})?$',
261
- verify_after_autocomplete: true,
262
- wrap_address1_to_address2: false,
263
- allowed_countries: ['US', 'CA']
264
- }
265
- ]);
266
- });
267
-
268
- test('getAddressForms camelcases returned field names', async () => {
269
- mockPromiseResult.mockResolvedValueOnce([[
270
- {
271
- address_elements: addressFormFieldEntries,
272
- postal_code_regex: '^[0-9]{5}(?:-[0-9]{4})?$',
273
- verify_after_autocomplete: true,
274
- wrap_address1_to_address2: false,
275
- allowed_countries: ['US', 'CA']
276
- }
277
- ]]);
193
+ test('getSingletonEntries maps language code in to id', async () => {
194
+ mockSinglePromiseResult.mockResolvedValueOnce(singleCheckoutAdrStrings);
278
195
  const {getStack} = require('../src/api');
279
196
  const Stack = getStack('dev');
280
- await expect(Stack.getAddressForms(['United States'], true)).resolves.toStrictEqual([
281
- {
282
- addressElements: [
283
- {
284
- field: 'address1',
285
- width: 'full',
286
- editable: true,
287
- maxLength: 40,
288
- required: true,
289
- apiMappings: ['address1'],
290
- autocompleteToken: 'address-line1'
291
- },
292
- {
293
- field: 'postalCode',
294
- width: 'half',
295
- editable: false,
296
- maxLength: 10,
297
- required: false,
298
- apiMappings: ['postalCode'],
299
- validationMessage: 'Invalid postal code'
300
- }
301
- ],
302
- postalCodeRegex: '^[0-9]{5}(?:-[0-9]{4})?$',
303
- verifyAfterAutocomplete: true,
304
- wrapAddress1ToAddress2: false,
305
- allowedCountries: ['US', 'CA']
306
- }
307
- ]);
197
+ await Stack.getSingletonEntries({contentTypeUIDs: ['checkout_adr_strings'], language: 'in'});
198
+ expect(mockSinglePromiseResult).toHaveBeenCalled();
308
199
  });
309
200
 
310
- test('getAddressForms returns an empty list when no forms are found', async () => {
311
- mockPromiseResult.mockResolvedValueOnce([undefined]);
201
+ test('getMultiEntries does not include references when includeRefs is false', async () => {
202
+ mockPromiseResult.mockResolvedValueOnce([[{title: 'United States'}]]);
312
203
  const {getStack} = require('../src/api');
313
204
  const Stack = getStack('dev');
314
- await expect(Stack.getAddressForms(['Unknown'])).resolves.toStrictEqual([]);
205
+ await Stack.getMultiEntries('market', false, ['United States']);
206
+ expect(mockContainedIn).toHaveBeenCalledWith('title', ['United States']);
207
+ expect(mockIncludeReference).not.toHaveBeenCalled();
208
+ expect(mockFetch).not.toHaveBeenCalled();
315
209
  });
316
210
 
317
- test('getSingletonEntries maps language code in to id', async () => {
318
- mockSinglePromiseResult.mockResolvedValueOnce(singleCheckoutAdrStrings);
211
+ test('getMultiEntries includes explicit reference paths', async () => {
212
+ mockPromiseResult.mockResolvedValueOnce([[{title: 'United States'}]]);
319
213
  const {getStack} = require('../src/api');
320
214
  const Stack = getStack('dev');
321
- await Stack.getSingletonEntries({contentTypeUIDs: ['checkout_adr_strings'], language: 'in'});
322
- expect(mockSinglePromiseResult).toHaveBeenCalled();
215
+ await Stack.getMultiEntries('market', ['countries', 'address_form.address_elements'], ['United States']);
216
+ expect(mockIncludeReference).toHaveBeenCalledWith(['countries', 'address_form.address_elements']);
217
+ expect(mockFetch).not.toHaveBeenCalled();
323
218
  });
324
219
 
325
- test('getShippingAddressForms maps nested address form fields', async () => {
326
- mockPromiseResult.mockResolvedValueOnce([[
327
- {
328
- title: 'United States',
329
- countries: [
330
- {
331
- countries: [
332
- {
333
- country_code: 'US',
334
- country_name: 'United States'
335
- },
336
- {
337
- country_code: 'CA',
338
- country_name: 'Canada'
339
- }
340
- ]
341
- }
342
- ],
343
- pre_address_fields: [addressFormFieldEntries[0]],
344
- address_form: [
345
- {
346
- title: 'default',
347
- address_elements: addressFormFieldEntries,
348
- postal_code_regex: '^[0-9]{5}(?:-[0-9]{4})?$',
349
- verify_after_autocomplete: true,
350
- wrap_address1_to_address2: false,
351
- allowed_countries: ['US', 'CA']
352
- }
353
- ],
354
- post_address_fields: [addressFormFieldEntries[1]],
355
- display_name: 'North America'
356
- }
357
- ]]);
220
+ test('getMultiEntries discovers reference paths from schema when includeRefs is true', async () => {
221
+ mockPromiseResult.mockResolvedValueOnce([[{title: 'United States'}]]);
358
222
  const {getStack} = require('../src/api');
359
223
  const Stack = getStack('dev');
360
- await expect(Stack.getShippingAddressForms(['United States'])).resolves.toStrictEqual([
361
- {
362
- countries: [
363
- {
364
- code: 'US',
365
- name: 'United States'
366
- },
367
- {
368
- code: 'CA',
369
- name: 'Canada'
370
- }
371
- ],
372
- pre_address_fields: [
373
- {
374
- field: 'address1',
375
- width: 'full',
376
- editable: true,
377
- max_length: 40,
378
- required: true,
379
- api_mappings: ['address1'],
380
- autocomplete_token: 'address-line1'
381
- }
382
- ],
383
- address_form: {
384
- address_elements: [
385
- {
386
- field: 'address1',
387
- width: 'full',
388
- editable: true,
389
- max_length: 40,
390
- required: true,
391
- api_mappings: ['address1'],
392
- autocomplete_token: 'address-line1'
393
- },
394
- {
395
- field: 'postalCode',
396
- width: 'half',
397
- editable: false,
398
- max_length: 10,
399
- required: false,
400
- api_mappings: ['postalCode'],
401
- validation_message: 'Invalid postal code'
402
- }
403
- ],
404
- postal_code_regex: '^[0-9]{5}(?:-[0-9]{4})?$',
405
- verify_after_autocomplete: true,
406
- wrap_address1_to_address2: false,
407
- allowed_countries: ['US', 'CA']
408
- },
409
- post_address_fields: [
410
- {
411
- field: 'postalCode',
412
- width: 'half',
413
- editable: false,
414
- max_length: 10,
415
- required: false,
416
- api_mappings: ['postalCode'],
417
- validation_message: 'Invalid postal code'
418
- }
419
- ],
420
- display_name: 'North America'
421
- }
224
+ await Stack.getMultiEntries('shipping_address_form', true, ['United States']);
225
+ expect(mockFetch).toHaveBeenCalledTimes(1);
226
+ expect(mockIncludeReference).toHaveBeenCalledWith([
227
+ 'countries',
228
+ 'details.address_form',
229
+ 'promo_blocks.hero.banner'
422
230
  ]);
423
231
  });
424
232
 
425
- test('getShippingAddressForms camelcases returned field names', async () => {
426
- mockPromiseResult.mockResolvedValueOnce([[
427
- {
428
- title: 'United States',
429
- countries: [
430
- {
431
- countries: [
432
- {
433
- country_code: 'US',
434
- country_name: 'United States'
435
- },
436
- {
437
- country_code: 'CA',
438
- country_name: 'Canada'
439
- }
440
- ]
441
- }
442
- ],
443
- pre_address_fields: [addressFormFieldEntries[0]],
444
- address_form: [
445
- {
446
- title: 'default',
447
- address_elements: addressFormFieldEntries,
448
- postal_code_regex: '^[0-9]{5}(?:-[0-9]{4})?$',
449
- verify_after_autocomplete: true,
450
- wrap_address1_to_address2: false,
451
- allowed_countries: ['US', 'CA']
452
- }
453
- ],
454
- post_address_fields: [addressFormFieldEntries[1]],
455
- display_name: 'North America'
456
- }
457
- ]]);
233
+ test('getMultiEntries caches discovered reference paths per content type', async () => {
234
+ mockPromiseResult.mockResolvedValue([[{title: 'United States'}]]);
458
235
  const {getStack} = require('../src/api');
459
- const Stack = getStack('dev');
460
- await expect(Stack.getShippingAddressForms(['United States'], true)).resolves.toStrictEqual([
461
- {
462
- countries: [
463
- {
464
- code: 'US',
465
- name: 'United States'
466
- },
467
- {
468
- code: 'CA',
469
- name: 'Canada'
470
- }
471
- ],
472
- preAddressFields: [
473
- {
474
- field: 'address1',
475
- width: 'full',
476
- editable: true,
477
- maxLength: 40,
478
- required: true,
479
- apiMappings: ['address1'],
480
- autocompleteToken: 'address-line1'
481
- }
482
- ],
483
- addressForm: {
484
- addressElements: [
485
- {
486
- field: 'address1',
487
- width: 'full',
488
- editable: true,
489
- maxLength: 40,
490
- required: true,
491
- apiMappings: ['address1'],
492
- autocompleteToken: 'address-line1'
493
- },
494
- {
495
- field: 'postalCode',
496
- width: 'half',
497
- editable: false,
498
- maxLength: 10,
499
- required: false,
500
- apiMappings: ['postalCode'],
501
- validationMessage: 'Invalid postal code'
502
- }
503
- ],
504
- postalCodeRegex: '^[0-9]{5}(?:-[0-9]{4})?$',
505
- verifyAfterAutocomplete: true,
506
- wrapAddress1ToAddress2: false,
507
- allowedCountries: ['US', 'CA']
508
- },
509
- postAddressFields: [
510
- {
511
- field: 'postalCode',
512
- width: 'half',
513
- editable: false,
514
- maxLength: 10,
515
- required: false,
516
- apiMappings: ['postalCode'],
517
- validationMessage: 'Invalid postal code'
518
- }
519
- ],
520
- displayName: 'North America'
521
- }
236
+ const Stack = getStack('test');
237
+ await Stack.getMultiEntries('shipping_address_form', true, ['United States']);
238
+ await Stack.getMultiEntries('shipping_address_form', true, ['Canada']);
239
+ expect(mockFetch).toHaveBeenCalledTimes(1);
240
+ expect(mockIncludeReference).toHaveBeenNthCalledWith(1, [
241
+ 'countries',
242
+ 'details.address_form',
243
+ 'promo_blocks.hero.banner'
522
244
  ]);
523
- });
524
-
525
- test('getShippingAddressForms returns empty nested data when optional references are missing', async () => {
526
- mockPromiseResult.mockResolvedValueOnce([[
527
- {
528
- countries: [],
529
- pre_address_fields: undefined,
530
- address_form: [],
531
- post_address_fields: undefined
532
- }
533
- ]]);
534
- const {getStack} = require('../src/api');
535
- const Stack = getStack('dev');
536
- await expect(Stack.getShippingAddressForms(['Unknown'])).resolves.toStrictEqual([
537
- {
538
- countries: [],
539
- pre_address_fields: [],
540
- address_form: undefined,
541
- post_address_fields: []
542
- }
245
+ expect(mockIncludeReference).toHaveBeenNthCalledWith(2, [
246
+ 'countries',
247
+ 'details.address_form',
248
+ 'promo_blocks.hero.banner'
543
249
  ]);
544
250
  });
545
251
  });
package/docs/CHANGELOG.md CHANGED
@@ -1 +1 @@
1
- # [2.1.0-pa-1117.9](https://code.tls.nuskin.io/ns-am/utility/npm/contentstack-lib/compare/v2.1.0-pa-1117.8...v2.1.0-pa-1117.9) (2026-05-18)
1
+ # [2.1.0-pa-1117.11](https://code.tls.nuskin.io/ns-am/utility/npm/contentstack-lib/compare/v2.1.0-pa-1117.10...v2.1.0-pa-1117.11) (2026-05-21)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/contentstack-lib",
3
- "version": "2.1.0-pa-1117.9",
3
+ "version": "2.1.0-pa-1117.11",
4
4
  "description": "This project contains configuration and api code to access Contentstack, to be shared between the backend (AWS Lambda) and frontend (Vue, etc).",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/api.js CHANGED
@@ -8,6 +8,7 @@ const {contentstack: prodEnvConfig} = require(`../config/prod.json`);
8
8
 
9
9
  let env = null;
10
10
  let Stack = null;
11
+ const referenceFieldCache = new Map();
11
12
 
12
13
  const COMMON_LANGUAGES = {
13
14
  'da': 'da-dk',
@@ -22,22 +23,6 @@ const COMMON_LANGUAGES = {
22
23
  'tr': 'tr-tr'
23
24
  };
24
25
 
25
- const CONTENTSTACK_SYSTEM_FIELDS = new Set([
26
- '_content_type_uid',
27
- 'uid',
28
- 'title',
29
- 'tags',
30
- 'locale',
31
- 'created_by',
32
- 'updated_by',
33
- 'created_at',
34
- 'updated_at',
35
- 'ACL',
36
- '_version',
37
- '_in_progress',
38
- 'publish_details'
39
- ]);
40
-
41
26
  /**
42
27
  * Gets the correct contentstack config settings
43
28
  * @param {string} env
@@ -190,108 +175,99 @@ async function getSingletonEntries(options) {
190
175
  }
191
176
 
192
177
  /**
193
- * Gets all the specified entries for a multiple content type
194
- * @param {string} contentType
195
- * @param {string[]} values
196
- * @return {Promise<{Object}>} config
197
- */
198
- const _getMultiEntries = async (contentType, includeRefs, values) => {
199
- try {
200
- const [results] = await Stack
201
- .ContentType(contentType)
202
- .Query()
203
- .containedIn('title', values)
204
- .includeReference(includeRefs)
205
- .toJSON()
206
- .find();
207
-
208
- return results;
209
- } catch(err) {
210
- console.error(err);
211
- }
212
- }
213
-
214
- /**
215
- * Removes Contentstack-managed metadata fields while preserving custom content fields.
216
- * @param {Object} entry
217
- * @return {Object}
178
+ * Collect reference field paths recursively from a content type schema.
179
+ * @param {Object[]} schema
180
+ * @param {string=} parentPath
181
+ * @return {string[]}
218
182
  * @private
219
183
  */
220
- const _omitContentstackSystemFields = (entry = {}) => {
221
- return Object.fromEntries(
222
- Object.entries(entry).filter(([key]) => !CONTENTSTACK_SYSTEM_FIELDS.has(key))
223
- );
184
+ function _collectReferenceFieldPaths(schema = [], parentPath = '') {
185
+ return schema.flatMap((field) => {
186
+ const currentPath = parentPath ? `${parentPath}.${field.uid}` : field.uid;
187
+ const nestedSchemaPaths = Array.isArray(field.schema) ?
188
+ _collectReferenceFieldPaths(field.schema, currentPath) :
189
+ [];
190
+ const blockPaths = field.data_type === 'blocks' && Array.isArray(field.blocks) ?
191
+ field.blocks.flatMap((block) => {
192
+ return Array.isArray(block.schema) ?
193
+ _collectReferenceFieldPaths(block.schema, `${currentPath}.${block.uid}`) :
194
+ [];
195
+ }) :
196
+ [];
197
+
198
+ return [
199
+ ...(field.data_type === 'reference' ? [currentPath] : []),
200
+ ...nestedSchemaPaths,
201
+ ...blockPaths
202
+ ];
203
+ });
224
204
  }
225
205
 
226
206
  /**
227
- * Maps address form field entries into a small normalized dataset.
228
- * @param {Object[]} addressFormFields
229
- * @return {Object[]}
207
+ * Resolve reference include paths from a boolean, string, or array input.
208
+ * @param {string} contentType
209
+ * @param {boolean|string|string[]} includeRefs
210
+ * @return {Promise<string[]>}
230
211
  * @private
231
212
  */
232
- const _extractAddressFormFields = (addressFormFields = [], camelcase = false) => {
233
- const mappedFields = addressFormFields.map((addressFormField) => {
234
- const mappedField = _omitContentstackSystemFields(addressFormField);
235
-
236
- if (mappedField.max_length !== undefined && mappedField.max_length !== null) {
237
- mappedField.max_length = Number(mappedField.max_length);
238
- }
213
+ async function _resolveIncludeRefs(contentType, includeRefs) {
214
+ if (!includeRefs) {
215
+ return [];
216
+ }
239
217
 
240
- return mappedField;
241
- });
218
+ if (typeof includeRefs === 'string') {
219
+ return [includeRefs];
220
+ }
242
221
 
243
- return camelcase ? _snakeObjectToCamel(mappedFields) : mappedFields;
244
- }
222
+ if (Array.isArray(includeRefs)) {
223
+ return includeRefs;
224
+ }
245
225
 
246
- const _extractAddressForm = (addressForm, camelcase = false) => {
247
- if (!addressForm) {
248
- return addressForm;
226
+ if (includeRefs !== true) {
227
+ return [];
249
228
  }
250
229
 
251
- const mappedForm = _omitContentstackSystemFields(addressForm);
252
- mappedForm.address_elements = _extractAddressFormFields(addressForm.address_elements, camelcase);
230
+ if (referenceFieldCache.has(contentType)) {
231
+ return referenceFieldCache.get(contentType);
232
+ }
253
233
 
254
- return camelcase ? _snakeObjectToCamel(mappedForm) : mappedForm;
234
+ const contentTypeSchema = await Stack.ContentType(contentType).fetch();
235
+ const referenceFields = _collectReferenceFieldPaths(contentTypeSchema.content_type.schema);
236
+ referenceFieldCache.set(contentType, referenceFields);
237
+ return referenceFields;
255
238
  }
256
239
 
257
- const _extractShippingCountries = (countries = []) => {
258
- const shippingCountries = countries[0]?.countries ?? [];
259
-
260
- return shippingCountries.map(({country_code, country_name}) => ({
261
- code: country_code,
262
- name: country_name
263
- }));
264
- }
240
+ /**
241
+ * Gets all the specified entries for a multiple content type
242
+ * @param {string} contentType
243
+ * @param {string[]} values
244
+ * @return {Promise<{Object}>} config
245
+ */
246
+ const getMultiEntries = async (contentType, includeRefs, values) => {
247
+ try {
248
+ const resolvedIncludeRefs = await _resolveIncludeRefs(contentType, includeRefs);
265
249
 
266
- const getShippingAddressForms = async (forms, camelcase = false) => {
267
- const results = await _getMultiEntries(
268
- 'shipping_address_form',
269
- ['countries', 'pre_address_fields', 'address_form', 'address_form.address_elements', 'post_address_fields'],
270
- forms
271
- );
272
- const mappedResults = (results || []).map((result) => {
273
- const mappedResult = _omitContentstackSystemFields(result);
274
- mappedResult.countries = _extractShippingCountries(result.countries);
275
- mappedResult.pre_address_fields = _extractAddressFormFields(result.pre_address_fields, camelcase);
276
- mappedResult.address_form = _extractAddressForm(result.address_form[0], camelcase);
277
- mappedResult.post_address_fields = _extractAddressFormFields(result.post_address_fields, camelcase);
278
- return mappedResult;
279
- });
250
+ let query = Stack
251
+ .ContentType(contentType)
252
+ .Query()
253
+ .containedIn('title', values);
280
254
 
281
- return camelcase ? _snakeObjectToCamel(mappedResults) : mappedResults;
282
- }
255
+ if (resolvedIncludeRefs.length) {
256
+ query = query.includeReference(resolvedIncludeRefs);
257
+ }
283
258
 
284
- const getAddressForms = async (forms, camelcase = false) => {
285
- const results = await _getMultiEntries('address_form', 'address_elements', forms);
286
- const mappedResults = (results || []).map((result) => _extractAddressForm(result, camelcase));
259
+ const [results] = await query.toJSON().find();
287
260
 
288
- return camelcase ? _snakeObjectToCamel(mappedResults) : mappedResults;
289
- }
261
+ return results;
262
+ } catch(err) {
263
+ console.error(err);
264
+ }
265
+ };
290
266
 
291
267
  /**
292
268
  * Returns contentstack Stack object initialized with the api key, deliveryToken, and extended with custom functionality
293
269
  * @param {'dev' | 'test' | 'stage' | 'prod'} envrnmnt
294
- * @return {Object} Stack - contentstack Stack sdk object with additional getStrings function
270
+ * @return {Object} Stack - contentstack Stack sdk object with additional helper functions
295
271
  */
296
272
  function getStack(envrnmnt) {
297
273
  if (!envrnmnt) {
@@ -307,16 +283,13 @@ function getStack(envrnmnt) {
307
283
  }
308
284
 
309
285
  env = envrnmnt;
310
- const {apiKey: api_key, deliveryToken: delivery_token, environment} = getConfig(env)
311
- Stack = Contentstack.Stack({api_key, delivery_token, environment})
286
+ referenceFieldCache.clear();
287
+ const {apiKey: api_key, deliveryToken: delivery_token, environment} = getConfig(env);
288
+ Stack = Contentstack.Stack({api_key, delivery_token, environment});
312
289
 
313
290
  // extend the Stack with a custom function to get translations
314
291
  Stack.getSingletonEntries = getSingletonEntries;
315
- // extend the Stack with a custom function to get ShippingAddressForms
316
- Stack.getAddressForms = getAddressForms;
317
- // extend the Stack with a custom function to get ShippingAddressForms
318
- Stack.getShippingAddressForms = getShippingAddressForms;
319
- Stack.extractAddressFormFields = _extractAddressFormFields;
292
+ Stack.getMultiEntries = getMultiEntries;
320
293
 
321
294
  return Stack;
322
295
  }