@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 +0 -77
- package/__tests__/api.spec.js +100 -394
- package/docs/CHANGELOG.md +1 -1
- package/package.json +1 -1
- package/src/api.js +75 -102
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
|
|
package/__tests__/api.spec.js
CHANGED
|
@@ -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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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('
|
|
170
|
-
|
|
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
|
|
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('
|
|
311
|
-
mockPromiseResult.mockResolvedValueOnce([
|
|
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
|
|
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('
|
|
318
|
-
|
|
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.
|
|
322
|
-
expect(
|
|
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('
|
|
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
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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('
|
|
426
|
-
mockPromiseResult.
|
|
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('
|
|
460
|
-
await
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
526
|
-
|
|
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.
|
|
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.
|
|
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
|
-
*
|
|
194
|
-
* @param {
|
|
195
|
-
* @param {string
|
|
196
|
-
* @return {
|
|
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
|
-
|
|
221
|
-
return
|
|
222
|
-
|
|
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
|
-
*
|
|
228
|
-
* @param {
|
|
229
|
-
* @
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
241
|
-
|
|
218
|
+
if (typeof includeRefs === 'string') {
|
|
219
|
+
return [includeRefs];
|
|
220
|
+
}
|
|
242
221
|
|
|
243
|
-
|
|
244
|
-
|
|
222
|
+
if (Array.isArray(includeRefs)) {
|
|
223
|
+
return includeRefs;
|
|
224
|
+
}
|
|
245
225
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return addressForm;
|
|
226
|
+
if (includeRefs !== true) {
|
|
227
|
+
return [];
|
|
249
228
|
}
|
|
250
229
|
|
|
251
|
-
|
|
252
|
-
|
|
230
|
+
if (referenceFieldCache.has(contentType)) {
|
|
231
|
+
return referenceFieldCache.get(contentType);
|
|
232
|
+
}
|
|
253
233
|
|
|
254
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
282
|
-
|
|
255
|
+
if (resolvedIncludeRefs.length) {
|
|
256
|
+
query = query.includeReference(resolvedIncludeRefs);
|
|
257
|
+
}
|
|
283
258
|
|
|
284
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
311
|
-
|
|
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
|
-
|
|
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
|
}
|