@nuskin/ns-product-lib 2.7.2-cx24-3964.1 → 2.8.0-cx12-7493.1

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,13 @@
1
- ## [2.7.2-cx24-3964.1](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/compare/v2.7.1...v2.7.2-cx24-3964.1) (2023-04-28)
1
+ # [2.8.0-cx12-7493.1](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/compare/v2.7.1...v2.8.0-cx12-7493.1) (2023-05-02)
2
2
 
3
3
 
4
4
  ### Fix
5
5
 
6
- * Products in sales event not showing adjusted price and sales ribbon #CX24-3964 ([1242901](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/124290157798010c82b5e30a273b342fe707e884)), closes [#CX24-3964](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/issues/CX24-3964)
6
+ * lint and error fixes (#CX12-7493) ([632e74e](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/632e74ebefca8279cf5dceeb608fc9427f352096)), closes [#CX12-7493](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/issues/CX12-7493)
7
+
8
+ ### New
9
+
10
+ * Begin work on new nsProduct.js (#CX12-7493) ([e2a4ec2](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/e2a4ec29ee495869ee6463a7f9a6896d1aa0f95b)), closes [#CX12-7493](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/issues/CX12-7493)
7
11
 
8
12
  ## [2.7.1](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/compare/v2.7.0...v2.7.1) (2023-04-15)
9
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/ns-product-lib",
3
- "version": "2.7.2-cx24-3964.1",
3
+ "version": "2.8.0-cx12-7493.1",
4
4
  "description": "This project contains shared Product models and code between the backend and frontend.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "@nuskin/configuration-sdk": "2.2.3",
35
35
  "@nuskin/ns-common-lib": "1.4.5",
36
- "@nuskin/ns-util": "4.4.0",
36
+ "@nuskin/ns-util": "4.3.2",
37
37
  "axios": "0.27.2",
38
38
  "qs": "6.11.0"
39
39
  },
@@ -8,17 +8,21 @@ const config = {
8
8
  dev: {
9
9
  ...baseConfig,
10
10
  accessToken: 'cs74599c107573ea7f6c7a1413',
11
- env: 'public-dev'
11
+ env: 'public-dev',
12
+ graphqlUrl: 'https://dev.nuskin.com/product-api/graphql'
12
13
  },
13
14
  prod: {
14
15
  ...baseConfig,
15
16
  accessToken: 'cs7029c5e32f0e99978efc683d',
16
- env: 'public-prod'
17
+ env: 'public-prod',
18
+ graphqlUrl: 'https://www.nuskin.com/product-api/graphql'
19
+
17
20
  },
18
21
  test: {
19
22
  ...baseConfig,
20
23
  accessToken: 'cs67e9fdd60eb15c301add40ea',
21
- env: 'public-test'
24
+ env: 'public-test',
25
+ graphqlUrl: 'https://product.api.test.nuskin.com/graphql'
22
26
  }
23
27
  };
24
28
 
@@ -26,7 +30,7 @@ const config = {
26
30
  * getCredentials returns contentstack config.
27
31
  *
28
32
  * @param {string} env
29
- * @return {{accessToken: string; apiKey: string; env: string; url: string;}}
33
+ * @return {{accessToken: string; apiKey: string; env: string; graphqlUrl: string; url: string; }}
30
34
  */
31
35
  function getCredentials(env) {
32
36
  if (config[env] === undefined) {
package/src/index.js CHANGED
@@ -15,16 +15,20 @@ const ProductStatusMapper = require("./productStatusMapper.js");
15
15
  const ProductUtils = require("./productUtils.js");
16
16
  const ProductData = require("./productData.js");
17
17
  const CustomerTypes = require('./models/customerTypes');
18
+ const NsProduct = require('./nsProduct.js')
19
+ const GraphQLQueries = require('./models/productGraphQLQueries')
18
20
 
19
21
  module.exports = {
20
22
  Agelocme,
21
23
  Contentstack,
24
+ CustomerTypes,
25
+ GraphQLQueries,
26
+ NsProduct,
22
27
  PriceType,
23
28
  Product,
24
29
  ProductContentMapper,
25
30
  ProductStatus,
26
31
  ProductStatusMapper,
27
32
  ProductUtils,
28
- ProductData,
29
- CustomerTypes
33
+ ProductData
30
34
  };
@@ -0,0 +1,396 @@
1
+ 'use strict'
2
+
3
+ const VariantQuery = `
4
+ id
5
+ globalId
6
+ sku
7
+ slug
8
+ variantLabel
9
+ variantColor
10
+ title
11
+ salesLabel
12
+ salesText
13
+ description
14
+ nettoWeight
15
+ size
16
+ isExclusive
17
+ availableQuantity
18
+ maxQuantity
19
+ purchaseTypes {
20
+ buyOnce
21
+ subscription
22
+ }
23
+ matchingVariant
24
+ shadeable
25
+ productType
26
+ primaryBrand
27
+ brandFamily
28
+ marketAttributes {
29
+ discount
30
+ redeem
31
+ earn
32
+ }
33
+ disclaimers
34
+ restrictedMarkets
35
+ searchKeywords
36
+ excludeFromSearch
37
+ availableChannels
38
+ customerTypes
39
+ scanQualifiedCount
40
+ chargeShipping
41
+ dangerousGoods
42
+ channels
43
+ foreignOrderLimit
44
+ orderType
45
+ salesDisclaimer
46
+ productImages {
47
+ url
48
+ alt
49
+ thumbnail
50
+ }
51
+ productDetails {
52
+ description
53
+ includedItems
54
+ highlights {
55
+ iconUrl
56
+ label
57
+ }
58
+ originCountry
59
+ importer
60
+ warnings
61
+ }
62
+ ingredients {
63
+ keyIngredients {
64
+ name
65
+ description
66
+ image {
67
+ url
68
+ alt
69
+ thumbnail
70
+ }
71
+ }
72
+ nutritionInformationImage {
73
+ url
74
+ alt
75
+ thumbnail
76
+ }
77
+ productName
78
+ allIngredients
79
+ otherIngredients
80
+ activeIngredients
81
+ inactiveIngredients
82
+ ingredientDisclaimers
83
+ markdown
84
+ }
85
+ disclaimerWarnings {
86
+ icon {
87
+ url
88
+ alt
89
+ thumbnail
90
+ }
91
+ markdown
92
+ }
93
+ features {
94
+ image {
95
+ url
96
+ alt
97
+ thumbnail
98
+ }
99
+ subtitle
100
+ features
101
+ backgroundColor
102
+ textColor
103
+ }
104
+ benefits {
105
+ benefits
106
+ youTubeVideoId
107
+ image {
108
+ url
109
+ alt
110
+ thumbnail
111
+ }
112
+ }
113
+ results {
114
+ summary
115
+ results {
116
+ percentage
117
+ text
118
+ }
119
+ report {
120
+ url
121
+ text
122
+ }
123
+ youTubeVideoId
124
+ image {
125
+ url
126
+ alt
127
+ thumbnail
128
+ }
129
+ }
130
+ usage {
131
+ steps
132
+ recommendations
133
+ warnings
134
+ additionalText
135
+ youTubeVideoId
136
+ image {
137
+ url
138
+ alt
139
+ thumbnail
140
+ }
141
+ markdown
142
+ }
143
+ resources {
144
+ title
145
+ url
146
+ image {
147
+ url
148
+ alt
149
+ thumbnail
150
+ }
151
+ }
152
+ faqs {
153
+ question
154
+ answers
155
+ }
156
+ status {
157
+ status
158
+ isBackordered
159
+ backorderedAvailableDate
160
+ }
161
+ `;
162
+
163
+ const BundleProduct = `
164
+ type
165
+ price {
166
+ currencyCode
167
+ retail
168
+ wholesale
169
+ retailSales
170
+ wholesaleSales
171
+ retailSubscription
172
+ wholesaleSubscription
173
+ }
174
+ retailDiscount
175
+ wholesaleDiscount
176
+ pvDiscount
177
+ cvDiscount
178
+ sbDiscount
179
+ grpDiscount
180
+ availableChannels
181
+ customerTypes
182
+ purchaseTypes {
183
+ buyOnce
184
+ subscription
185
+ }
186
+ status {
187
+ status
188
+ isBackordered
189
+ backorderedAvailableDate
190
+ }
191
+ availableQuantity
192
+ chargeShipping
193
+ dangerousGoods
194
+ excludeFromSearch
195
+ isExclusive
196
+ marketAttributes {
197
+ discount
198
+ redeem
199
+ earn
200
+ }
201
+ restrictedMarkets
202
+ scanQualifiedCount
203
+ kitProducts {
204
+ quantity
205
+ isMandatory
206
+ product {
207
+ id
208
+ }
209
+ }
210
+ `;
211
+
212
+ const ProductQuery = `
213
+ id
214
+ slug
215
+ variantSelectLabel
216
+ title
217
+ productImages {
218
+ url
219
+ alt
220
+ thumbnail
221
+ }
222
+ salesLabel
223
+ salesText
224
+ description
225
+ salesDisclaimer
226
+ features {
227
+ image {
228
+ url
229
+ alt
230
+ thumbnail
231
+ }
232
+ subtitle
233
+ features
234
+ backgroundColor
235
+ textColor
236
+ }
237
+ productDetails {
238
+ description
239
+ includedItems
240
+ highlights {
241
+ iconUrl
242
+ label
243
+ }
244
+ originCountry
245
+ importer
246
+ warnings
247
+ }
248
+ benefits {
249
+ benefits
250
+ youTubeVideoId
251
+ image {
252
+ url
253
+ alt
254
+ thumbnail
255
+ }
256
+ }
257
+ results {
258
+ summary
259
+ results {
260
+ percentage
261
+ text
262
+ }
263
+ report {
264
+ url
265
+ text
266
+ }
267
+ youTubeVideoId
268
+ image {
269
+ url
270
+ alt
271
+ thumbnail
272
+ }
273
+ }
274
+ sustainability {
275
+ youTubeVideoId
276
+ description
277
+ highlights {
278
+ description
279
+ image {
280
+ url
281
+ alt
282
+ thumbnail
283
+ }
284
+ }
285
+ image {
286
+ url
287
+ alt
288
+ thumbnail
289
+ }
290
+ }
291
+ usage {
292
+ steps
293
+ recommendations
294
+ warnings
295
+ additionalText
296
+ youTubeVideoId
297
+ image {
298
+ url
299
+ alt
300
+ thumbnail
301
+ }
302
+ markdown
303
+ }
304
+ resources {
305
+ title
306
+ url
307
+ image {
308
+ url
309
+ alt
310
+ thumbnail
311
+ }
312
+ }
313
+ faqs {
314
+ question
315
+ answers
316
+ }
317
+ ingredients {
318
+ keyIngredients {
319
+ name
320
+ description
321
+ image {
322
+ url
323
+ alt
324
+ thumbnail
325
+ }
326
+ }
327
+ nutritionInformationImage {
328
+ url
329
+ alt
330
+ thumbnail
331
+ }
332
+ productName
333
+ allIngredients
334
+ otherIngredients
335
+ activeIngredients
336
+ inactiveIngredients
337
+ ingredientDisclaimers
338
+ markdown
339
+ }
340
+ disclaimers
341
+ disclaimerWarnings {
342
+ icon {
343
+ url
344
+ alt
345
+ thumbnail
346
+ }
347
+ markdown
348
+ }
349
+ seoInformation {
350
+ metaDescription
351
+ metaTitle
352
+ canonicalURL
353
+ ogImage {
354
+ url
355
+ alt
356
+ thumbnail
357
+ }
358
+ }
359
+ refundPolicy
360
+ shippingText
361
+ thirdPartyScripts
362
+ categories {
363
+ displayName
364
+ id
365
+ subcategories {
366
+ displayName
367
+ id
368
+ }
369
+ }
370
+ warranty
371
+ secondaryTitle
372
+ variants {
373
+ ${VariantQuery}
374
+ }
375
+ bundle {
376
+ ${BundleProduct}
377
+ }
378
+ productDataSource {
379
+ source
380
+ webBaseUrl
381
+ apiBaseUrl
382
+ storeId
383
+ }
384
+ error {
385
+ name
386
+ errors
387
+ lines
388
+ message
389
+ status
390
+ }
391
+ `;
392
+
393
+ module.exports = {
394
+ VariantQuery,
395
+ ProductQuery
396
+ }
@@ -0,0 +1,108 @@
1
+ 'use strict'
2
+
3
+ const axios = require('axios')
4
+ const {ProductQuery} = require('./models/productGraphQLQueries')
5
+ const config = require('./contentstack/environment')
6
+
7
+ let envConfig = {}
8
+
9
+ const processProps = (props) => {
10
+ if (!props) {
11
+ return "empty"
12
+ } else if (props.fromId) {
13
+ if(!props.env || !['dev', 'test', 'prod'].includes(props.env)) {
14
+ return "missingEnv"
15
+ }
16
+ else {
17
+ return "id"
18
+ }
19
+ } else if (props.fromSlug) {
20
+ if(!props.env || !['dev', 'test', 'prod'].includes(props.env)) {
21
+ return "missingEnv"
22
+ } else {
23
+ return "slug"
24
+ }
25
+ } else if (props.data) {
26
+ return "data"
27
+ }
28
+ }
29
+
30
+ const getProductById = async(id, market, language) => {
31
+ const payload = {variables:{market, language, id}}
32
+ payload.operationName = 'ProductById'
33
+ payload.query = `query ProductById($id: String!, $market: String, $language: String) {
34
+ productById(id: $id, market: $market, language: $language) {
35
+ ${ProductQuery}}}`
36
+ try {
37
+ const idRes = await axios.post(`${envConfig.graphqlUrl}`, payload);
38
+ return ((idRes.data || {}).data || {}).productById || idRes.error;
39
+ } catch(ex) {
40
+ console.error(ex)
41
+ }
42
+ }
43
+
44
+ const getProductBySlug = async(slug, market, language) => {
45
+ const payload = {variables:{market, language, slug}}
46
+ payload.operationName = 'ProductBySlug'
47
+ payload.query = `query ProductBySlug($slug: String!, $market: String, $language: String) {
48
+ productBySlug(slug: $slug, market: $market, language: $language) {
49
+ ${ProductQuery}}}`
50
+ try {
51
+ const idRes = await axios.post(`${envConfig.graphqlUrl}`, payload);
52
+ return ((idRes.data || {}).data || {}).productBySlug || idRes.error;
53
+ } catch(ex) {
54
+ console.error(ex)
55
+ }
56
+ }
57
+
58
+ /*
59
+ This is the "constructor" for a nuskin product object.
60
+ You send it an object that will be used to determine what to do when it initializes
61
+ Possible options:
62
+ {} - Create an empty nsProduct object with no data
63
+ {data: Object} - Create an object using the data previously retrived from the product graphql
64
+ {fromId: String, market: String, language: String, env: String} - Create an object using data retrieved using the ID, market, language, environment sent
65
+ {fromSlug: String, market: String, language: String, env: String} - Create an object using data retrieved using the slug, market, language, environment sent
66
+
67
+ Example usage:
68
+ const product = await nsProduct({fromSlug: 'tegreen', market: 'US', language: 'en', env: 'dev'});
69
+ const fromSku = await nsProduct({fromId: '01003440', market: 'US', language: 'en', env: 'dev'});
70
+ const fromUid = await nsProduct({fromId: 'blta50746fc33f83470', market: 'US', language: 'en', env: 'dev'});
71
+ */
72
+ const nsProduct = async (props) =>
73
+ {
74
+ let data = {}
75
+ // Validate props passed in
76
+ const constructorType = processProps(props)
77
+ if (constructorType === 'missingEnv') {
78
+ data.error = "Missing env in props, please include the environment and re-initialize"
79
+ } else if (['id', 'slug'].includes(constructorType)) {
80
+ // initialize configs and libs
81
+ envConfig = config.getCredentials(props.env);
82
+ }
83
+ // Initialize NsProduct based on constructor used
84
+ switch (constructorType) {
85
+ case "id":
86
+ data = await getProductById(props.fromId, props.market || 'US', props.language || 'en') || {}
87
+ break
88
+ case "slug":
89
+ data = await getProductBySlug(props.fromSlug, props.market || 'US', props.language || 'en') || {}
90
+ break
91
+ case "data":
92
+ data = props.data
93
+ break
94
+ }
95
+
96
+ const getPricing = async() => {
97
+ data.pricing = await "hello"
98
+ }
99
+
100
+ return {
101
+ data,
102
+ getPricing
103
+ }
104
+ }
105
+
106
+ module.exports = {
107
+ nsProduct
108
+ }