@como/como-client 0.9.3 → 0.9.5

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.
Files changed (4) hide show
  1. package/README.md +29 -2
  2. package/lib/como.js +23 -16
  3. package/package.json +10 -8
  4. package/src/como.js +0 -1077
package/README.md CHANGED
@@ -6,10 +6,37 @@ A cross platform SDK for como backend API.
6
6
 
7
7
  - To run this project source code locally make sure you're running node 8.x.x or later.
8
8
  - To run the project example - `node ${workspaceFolder}/test.js`.
9
- - To run all unit tests run - `npm test`.
9
+ - Install dependencies with `corepack yarn install --frozen-lockfile`. Yarn is the
10
+ source of truth; the ignored local `package-lock.json` is not maintained.
11
+ - To build and run the offline dependency compatibility tests, run `npm test`.
12
+ This does not run the live QA example in `test.js`. The development test runner
13
+ requires Node 14 or later; this release was checked on Node 22.
10
14
  - To publish a new version to npm - `npm publish`. Make sure to increment the package version in `${workspaceFolder}/package.json`.
11
15
 
12
16
  ## Development in [vscode](https://code.visualstudio.com/)
13
17
 
14
18
  - The configuration is set in `${workspaceFolder}/.vscode/launch.json`
15
- - Use `Launch Program` pre-defined configuration to run.
19
+ - Use `Launch Program` pre-defined configuration to run.
20
+
21
+ ## Security release 0.9.5
22
+
23
+ - Axios is patched within the existing 0.x API line to `^0.34.0`.
24
+ - Mocha and Chai are development dependencies only, so consumers no longer
25
+ install the SDK's old Mocha 5 dependency tree, including its `inflight` path.
26
+ Build/test tooling can still have separate development-only advisories.
27
+ - `babel-runtime` is an explicit runtime dependency because the published
28
+ compiled files import it.
29
+ - The package entrypoint is `index.js`, which loads `lib/como.js`. `npm pack`
30
+ builds `lib/` through `prepack`; source-only `src/` paths are not published.
31
+ Existing `lib/como` and interceptor imports remain available.
32
+
33
+ The offline tests cover the service-consumed exports, HTTP JSON/redirect/error
34
+ handling, bearer-token refresh, credential updates, signed sign-on requests,
35
+ location state and resource URLs. They do not certify browser/mobile runtimes
36
+ or live authentication/CDN services.
37
+
38
+ Release this SDK before changing a consumer's registry dependency to `0.9.5`.
39
+ Do not commit a consumer lockfile referring to an unpublished registry version
40
+ or a machine-local sibling path. Review unrelated working-tree edits before
41
+ packing or publishing; a package includes the current source, not only staged
42
+ Git changes.
package/lib/como.js CHANGED
@@ -883,7 +883,7 @@ var Como = function () {
883
883
  key: 'login',
884
884
  value: function () {
885
885
  var _ref12 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee12(identifier, type, reCaptchaToken, recaptchaTokenType, devicePlatform, isWhatsapp) {
886
- var api, data, _error$response$data8, code, message;
886
+ var api, data, _error$response$data8, code, message, retryAfter, loginError;
887
887
 
888
888
  return _regenerator2.default.wrap(function _callee12$(_context12) {
889
889
  while (1) {
@@ -903,17 +903,25 @@ var Como = function () {
903
903
  _context12.t0 = _context12['catch'](1);
904
904
 
905
905
  if (!(_context12.t0.response && _context12.t0.response.data)) {
906
- _context12.next = 13;
906
+ _context12.next = 15;
907
907
  break;
908
908
  }
909
909
 
910
- _error$response$data8 = _context12.t0.response.data, code = _error$response$data8.code, message = _error$response$data8.message;
911
- throw new Error(code || message);
910
+ _error$response$data8 = _context12.t0.response.data, code = _error$response$data8.code, message = _error$response$data8.message, retryAfter = _error$response$data8.retryAfter;
911
+ loginError = new Error(code || message);
912
+ // When a request lands inside its OTP backoff the server states how long
913
+ // to wait. Only code and message used to survive this rethrow, which left
914
+ // the app inventing a countdown of its own.
912
915
 
913
- case 13:
916
+ if (typeof retryAfter === 'number') {
917
+ loginError.retryAfter = retryAfter;
918
+ }
919
+ throw loginError;
920
+
921
+ case 15:
914
922
  throw _context12.t0;
915
923
 
916
- case 14:
924
+ case 16:
917
925
  case 'end':
918
926
  return _context12.stop();
919
927
  }
@@ -942,7 +950,7 @@ var Como = function () {
942
950
  key: 'loginVerify',
943
951
  value: function () {
944
952
  var _ref13 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee13(identifier, type, code, reCaptchaToken, recaptchaTokenType, devicePlatform) {
945
- var api, _ref14, consent, _error$response$data9, errCode, message;
953
+ var api, _ref14, consent, _error$response$data9, errCode, message, retryAfter, verifyError;
946
954
 
947
955
  return _regenerator2.default.wrap(function _callee13$(_context13) {
948
956
  while (1) {
@@ -970,17 +978,16 @@ var Como = function () {
970
978
  break;
971
979
  }
972
980
 
973
- _error$response$data9 = _context13.t0.response.data, errCode = _error$response$data9.code, message = _error$response$data9.message;
981
+ _error$response$data9 = _context13.t0.response.data, errCode = _error$response$data9.code, message = _error$response$data9.message, retryAfter = _error$response$data9.retryAfter;
982
+ // The wrong-code case keeps reporting the message rather than the code,
983
+ // since callers match on that string.
974
984
 
975
- if (!(message && message === 'Invalid verification Code')) {
976
- _context13.next = 15;
977
- break;
978
- }
979
-
980
- throw new Error(message);
985
+ verifyError = message && message === 'Invalid verification Code' ? new Error(message) : new Error(errCode || message);
981
986
 
982
- case 15:
983
- throw new Error(errCode || message);
987
+ if (typeof retryAfter === 'number') {
988
+ verifyError.retryAfter = retryAfter;
989
+ }
990
+ throw verifyError;
984
991
 
985
992
  case 16:
986
993
  throw _context13.t0;
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@como/como-client",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Client module for Como's loaylty API",
5
- "main": "src/como.js",
5
+ "main": "index.js",
6
+ "packageManager": "yarn@1.22.22",
6
7
  "scripts": {
7
- "test": "mocha",
8
- "prepublishOnly": "node_modules/babel-cli/bin/babel.js src --out-dir lib"
8
+ "build": "babel src --out-dir lib",
9
+ "test": "npm run build && mocha --timeout 10000 \"test/**/*.test.js\"",
10
+ "prepack": "npm run build",
11
+ "prepublishOnly": "npm run build"
9
12
  },
10
13
  "files": [
11
14
  "/lib",
@@ -26,11 +29,10 @@
26
29
  },
27
30
  "homepage": "https://github.com/ConduitMobileRND/como-client-lib#readme",
28
31
  "dependencies": {
29
- "axios": "^0.30.2",
30
- "chai": "^4.1.2",
32
+ "axios": "^0.34.0",
33
+ "babel-runtime": "^6.26.0",
31
34
  "json-stable-stringify": "1.0.1",
32
35
  "jssha": "^3.2.0",
33
- "mocha": "^5.2.0",
34
36
  "moment-timezone": "^0.5.35",
35
37
  "ramda": "^0.25.0"
36
38
  },
@@ -45,7 +47,7 @@
45
47
  "eslint": "^4.19.1",
46
48
  "eslint-config-airbnb-base": "^12.1.0",
47
49
  "eslint-plugin-import": "^2.12.0",
48
- "mocha": "^5.2.0",
50
+ "mocha": "^10.8.2",
49
51
  "supertest": "^3.1.0",
50
52
  "webpack": "^4.8.3",
51
53
  "webpack-cli": "^2.1.4"
package/src/como.js DELETED
@@ -1,1077 +0,0 @@
1
- const moment = require('moment-timezone');
2
- const ramda = require('ramda');
3
- const environmentConfig = require('./config/env');
4
- const APIService = require('./services');
5
- const comoExternalUrlTranslator = require('./utils/comoExternalUrlTranslator');
6
- const resourceNames = require('./constants/resourceNames');
7
- const urlsUtils = require('./utils/urls');
8
-
9
- const INITIAL_COMO_DATA = {
10
- env: environmentConfig,
11
- token: null,
12
- locationId: null,
13
- locationState: null,
14
- installationId: null,
15
- membership: null,
16
- assets: null,
17
- reCaptchaSiteKey: null,
18
- mainResources: {},
19
- };
20
-
21
- class Como {
22
- constructor() {
23
- this.comoData = {};
24
- }
25
-
26
- async refreshTokenIfNeeded() {
27
- const { refresher, expiresIn } = this.comoData;
28
- if (!refresher) {
29
- console.log('No refresh for %o', this.comoData);
30
- } else if (expiresIn && moment(expiresIn).subtract(60, 'seconds').isBefore(moment())) {
31
- await refresher();
32
- }
33
- }
34
-
35
-
36
- /**
37
- * Sign the user and returns guest token & refresh token.
38
- * Requires init.
39
- */
40
- // async signon(source) {
41
- // const { InitApi: api } = this.comoData;
42
- // try {
43
- // const data = await api.signOn(this.comoData.installationId, this.comoData.locationId, this.comoData.refreshToken, source);
44
- // if (data && data.config && data.config.loyaltyCDN) {
45
- // this.comoData.env_params.services_root_cdn = data.config.loyaltyCDN;
46
- // }
47
- // return data;
48
- // } catch (error) {
49
- // if (error.response && error.response.data) {
50
- // const { code, message } = error.response.data;
51
- // throw new Error(code || message);
52
- // }
53
- // throw error;
54
- // }
55
- // }
56
-
57
- /**
58
- *
59
- * @param {Object} environment Environemnt object
60
- * @param {string} environment.id Environment Id
61
- * @param {string} environment.locationId Location Id
62
- * @param {string} environment.installationId Unique installation id
63
- * @param {Object} credentials Member credentials (Optional)
64
- * @param {string} accessToken session id string (Optional)
65
- * @param {string} refreshToken refresh token string (Optional)
66
- * @param {string} expiresIn access token expiration date formatted (Optional)
67
- * @param {function} refreshCallback an event callback which fires each time user's tokens gets refreshed
68
- * @param {boolean} enableSignon if disabled signOn api call will not be fired. Enabled by default.
69
- */
70
- async init(environment, credentials, refreshCallback, enableSignon = true) {
71
- const isExists = (x => !ramda.isNil(x) && !ramda.isEmpty(x));
72
- const {
73
- id: environmentId, locationId, installationId, source, binaryVersion, phoenixVersion,
74
- } = environment;
75
- if (!isExists(environmentId) || !isExists(locationId) || !isExists(installationId)) {
76
- throw new Error(`missing parameters: ${environment}`);
77
- }
78
-
79
- this.comoData = {
80
- ...INITIAL_COMO_DATA,
81
- ...credentials,
82
- installationId,
83
- locationId,
84
- source,
85
- env_params: {
86
- ...INITIAL_COMO_DATA.env[environmentId],
87
- },
88
- tempTokenData: {},
89
- config: {},
90
- };
91
-
92
- const updateCredentials = (creds, isNew = false) => {
93
- this.updateLocalCreds(creds);
94
- if (refreshCallback && isNew) {
95
- refreshCallback(creds);
96
- }
97
- };
98
-
99
- const getCredentials = this.getCredentials.bind(this);
100
- const { gateway } = this.comoData.env_params;
101
- const { service, refresher, initService } = APIService
102
- .getService(gateway, installationId, getCredentials, updateCredentials, source, binaryVersion, phoenixVersion);
103
- this.comoData.api = service;
104
- this.comoData.initApi = initService;
105
- this.comoData.refresher = refresher;
106
-
107
- const { accessToken, refreshToken, expiresIn } = credentials;
108
- let credExist = isExists(accessToken) && isExists(refreshToken);
109
- const isExpired = credExist && expiresIn && moment(expiresIn).isBefore(moment());
110
- let refreshFailed = false;
111
- if (isExpired) {
112
- try {
113
- const data = await this.comoData.initApi.refreshToken(installationId, refreshToken || 'none', locationId, source);
114
- this.comoData.config = data.config;
115
- console.log(`signOn with isExpired true result:${JSON.stringify(data)}`);
116
- updateCredentials(data, true);
117
- } catch (e) {
118
- console.error(`Failed to refresh token, installationId:${installationId}, refreshToken:${this.comoData.refreshToken}, locationId:${locationId}, source:${source}`);
119
- // refresh failed - need to revoke and signOn again
120
- initService.logError({
121
- method: 'refreshToken', installationId, locationId, source, refreshToken, error: e.message,
122
- });
123
- refreshFailed = true;
124
- }
125
- }
126
- let interval = 0;
127
- if (refreshFailed || !credExist) {
128
- while (!credExist && interval < 3000) {
129
- try {
130
- const data = await this.comoData.initApi.signOn(installationId, locationId, source);
131
- this.comoData.config = data.config;
132
- console.log(`signOn with refreshFailed result:${JSON.stringify(data)}`);
133
- updateCredentials(data, true);
134
- credExist = true;
135
- } catch (e) {
136
- console.error(`Failed to signOn, installationId:${installationId}, locationId:${locationId}, source:${source},interval:${interval}, error:${e}`);
137
- interval += 1000;
138
- initService.logError({
139
- method: 'signOn no accessToken', accessToken, isExpired, refreshFailed, credExist, installationId, locationId, source, refreshToken, interval, error: e.message,
140
- });
141
- await new Promise(r => setTimeout(r, interval));
142
- }
143
- }
144
- } else if (!isExpired) {
145
- let signOnSuccessfully = false;
146
- while (!signOnSuccessfully && interval < 3000) {
147
- try {
148
- console.log(`signOn with installationId:${installationId}, locationId:${locationId}, source:${source},accessToken:${accessToken}`);
149
- const data = await this.comoData.initApi.signOn(installationId, locationId, source, accessToken);
150
- this.comoData.config = data.config;
151
- console.log(`signOn with result " ${JSON.stringify(data)}`);
152
- if (data && data.accessToken) {
153
- console.log('signOn- updating credentials');
154
- updateCredentials(data, true);
155
- }
156
- if (data && data.config && data.config.recaptchaSiteKey) {
157
- this.comoData.reCaptchaSiteKey = data.config.recaptchaSiteKey;
158
- }
159
- signOnSuccessfully = true;
160
- } catch (e) {
161
- console.error(`Failed signOn with accessToken, installationId:${installationId}, locationId:${locationId}, source:${source},accessToken:${accessToken}, error:${e.message}`);
162
- interval += 1000;
163
- initService.logError({
164
- method: 'signOn with credentials', accessToken, isExpired, refreshFailed, credExist, installationId, locationId, source, refreshToken, interval, error: e.message,
165
- });
166
- await new Promise(r => setTimeout(r, interval));
167
- }
168
- }
169
- }
170
- if (!this.comoData.token) {
171
- this.comoData.token = this.comoData.accessToken;
172
- }
173
- return this.getCredentials();
174
- }
175
-
176
- /**
177
- * Updates the como.js local credentials settings.
178
- * Used when making api calls to janua.
179
- *
180
- * @param {Object} credentials credentials to update.
181
- * @param {Object} accessToken access token to update.
182
- * @param {Object} refreshToken refresh token to update.
183
- * @param {Object} expiresIn access token expiry date to update.
184
- */
185
- updateLocalCreds(credentials = {}) {
186
- this.comoData.accessToken = credentials.accessToken;
187
- this.comoData.token = credentials.accessToken;
188
- this.comoData.refreshToken = credentials.refreshToken;
189
- this.comoData.expiresIn = credentials.expiresIn;
190
- if (credentials && credentials.config) {
191
- this.comoData.reCaptchaSiteKey = credentials.config.recaptchaSiteKey;
192
- if (credentials.config.recaptcha) {
193
- this.comoData.recaptchaConfig = credentials.config.recaptcha;
194
- }
195
- }
196
- }
197
-
198
- getExternalUrlByID(itemID, gpsData, assetID, accessibilityData) {
199
- const locationData = this.comoData.mainResources[resourceNames.LOCATION_DATA];
200
- if (locationData && locationData.WebViews && locationData.WebViews[0].item) {
201
- const possibleWebViews = locationData.WebViews[0].item.filter(u => u.id === itemID);
202
- const url = possibleWebViews && possibleWebViews[0] && possibleWebViews[0].url;
203
- return this.translateExternalUrl(url, gpsData, assetID, accessibilityData);
204
- }
205
- return null;
206
- }
207
-
208
- async translateExternalUrl(url, gpsData, assetID, accessibilityData, devicePlatform = 'notImplemented') {
209
- const legacyUrl = await this.getAppModulesLegacyBaseUrl();
210
- let externalUrl = url;
211
- if (externalUrl && externalUrl.startsWith(legacyUrl)) {
212
- externalUrl = externalUrl.replace(legacyUrl, this.comoData.env_params.app_modules_root);
213
- }
214
- const {
215
- api, locationId, token, env_params: env, membership, assets,
216
- } = this.comoData;
217
- const locationData = this.comoData.mainResources && this.comoData.mainResources[resourceNames.LOCATION_DATA];
218
- const apiObjects = { api, servicesRoot: env.services_root };
219
- const dataToUse = {
220
- locationId, token, membership, locationData, assets, gpsData, assetID, accessibilityData, devicePlatform,
221
- };
222
- const translatedData = await comoExternalUrlTranslator.translateUrl(externalUrl, apiObjects, dataToUse);
223
- if (translatedData.tempTokenData.temporaryToken) {
224
- this.comoData.tempTokenData[translatedData.tempTokenData.temporaryToken] = translatedData.tempTokenData.expirationDate;
225
- }
226
- return translatedData && translatedData.translatedUrl && translatedData.translatedUrl.length ? translatedData.translatedUrl : '';
227
- }
228
-
229
- async getFeedbackFormData() {
230
- const {
231
- locationId, env_params: env, api,
232
- } = this.comoData;
233
- return api.getFeedbackFormData(env.zapp_root, locationId);
234
- }
235
-
236
- /**
237
- *
238
- * @param {Object} form registration form values
239
- * @param {Object} verification flow
240
- * @param {string} reCaptchaToken
241
- * @param {Object} source(registration origin)
242
- */
243
- async register(form, verification, reCaptchaToken, source) {
244
- const { api } = this.comoData;
245
- try {
246
- const data = await api.register(form, verification, reCaptchaToken, source);
247
- return data;
248
- } catch (error) {
249
- if (error.response && error.response.data) {
250
- const { code, message } = error.response.data;
251
- throw new Error(code || message);
252
- }
253
- throw error;
254
- }
255
- }
256
-
257
- /**
258
- *
259
- * @param {string} verification flow (code & verification step)
260
- * @param {string} type verification type
261
- * @param {Object} form registration form values
262
- * @param {string} reCaptchaToken
263
- */
264
- async registerVerify(verification, type, form, reCaptchaToken) {
265
- const { api } = this.comoData;
266
- try {
267
- const data = await api.registerVerify(verification, type, form, reCaptchaToken);
268
- return data;
269
- } catch (error) {
270
- if (error.response && error.response.data) {
271
- const { code, message } = error.response.data;
272
- throw new Error(code || message);
273
- }
274
- throw error;
275
- }
276
- }
277
-
278
- /**
279
- *
280
- * @param {Object} form update form values
281
- * @param {string} reCaptchaToken
282
- * @param {string} verification
283
- * @param {string} source request source
284
- */
285
- async updateProfile(form, reCaptchaToken, verification, source) {
286
- const { api } = this.comoData;
287
- try {
288
- const data = await api.updateProfile(form, reCaptchaToken, verification, source);
289
- return data;
290
- } catch (error) {
291
- if (error.response && error.response.data) {
292
- const { code, message } = error.response.data;
293
- throw new Error(code || message);
294
- }
295
- throw error;
296
- }
297
- }
298
-
299
- /**
300
- *
301
- * @param {string} verificationCode code verification sent by 3rd party
302
- * @param {string} type verification type
303
- * @param {Object} form update form values
304
- * @param {string} reCaptchaToken
305
- * @param {string} source request source
306
- */
307
- async updateProfileVerify(verificationCode, type, form, reCaptchaToken, source) {
308
- const { api } = this.comoData;
309
- try {
310
- const data = await api.updateProfileVerify(verificationCode, type, form, reCaptchaToken, source);
311
- return data;
312
- } catch (error) {
313
- if (error.response && error.response.data) {
314
- const { code, message } = error.response.data;
315
- throw new Error(code || message);
316
- }
317
- throw error;
318
- }
319
- }
320
-
321
- /**
322
- * @param {string} language language code
323
- */
324
- async updateLanguage(language) {
325
- const { api } = this.comoData;
326
- try {
327
- const data = await api.updateLanguage(language);
328
- return data;
329
- } catch (error) {
330
- if (error.response && error.response.data) {
331
- const { code, message } = error.response.data;
332
- throw new Error(code || message);
333
- }
334
- throw error;
335
- }
336
- }
337
-
338
- /**
339
- *
340
- * @param {string} unsubscribeSms unsubscribe user at location
341
- * @param {string} PhoneNumber phone number to identify user
342
- */
343
- async unsubscribeSms(PhoneNumber) {
344
- const { api, locationId } = this.comoData;
345
- try {
346
- const identifier = { PhoneNumber };
347
- const data = await api.unsubscribeSms(locationId, identifier);
348
- return data;
349
- } catch (error) {
350
- if (error.response && error.response.data) {
351
- const { code, message } = error.response.data;
352
- throw new Error(code || message);
353
- }
354
- throw error;
355
- }
356
- }
357
-
358
- /**
359
- *
360
- * @param {string} unsubscribeWhatsapp unsubscribe user at location
361
- * @param {string} PhoneNumber phone number to identify user
362
- */
363
- async unsubscribeWhatsapp(PhoneNumber) {
364
- const { api, locationId } = this.comoData;
365
- try {
366
- const identifier = { PhoneNumber };
367
- const data = await api.unsubscribeWhatsapp(locationId, identifier);
368
- return data;
369
- } catch (error) {
370
- if (error.response && error.response.data) {
371
- const { code, message } = error.response.data;
372
- throw new Error(code || message);
373
- }
374
- throw error;
375
- }
376
- }
377
-
378
- /**
379
- *
380
- * @param {Object} identifier an object which identifies the user by its credentials
381
- * @param {string} type login type i.e PhoneNumber/Email/etc
382
- * @param {reCaptchaToken} reCaptcha token for login chalenge
383
- * @param {recaptchaTokenType} recaptcha token type - score/human
384
- * @param {devicePlatform} device platform i.e. ios/android
385
- * @param {boolean} isWhatsapp optional flag for whatsapp login flow
386
- */
387
- async login(identifier, type, reCaptchaToken, recaptchaTokenType, devicePlatform, isWhatsapp) {
388
- const { api } = this.comoData;
389
- try {
390
- const data = await api.logIn(type, identifier, reCaptchaToken, recaptchaTokenType, devicePlatform, isWhatsapp);
391
- return data;
392
- } catch (error) {
393
- if (error.response && error.response.data) {
394
- const { code, message } = error.response.data;
395
- throw new Error(code || message);
396
- }
397
- throw error;
398
- }
399
- }
400
-
401
- /**
402
- *
403
- * @param {Object} identifier key-value identifiers
404
- * @param {*} type login type i.e PhoneNumber/Email/etc
405
- * @param {*} code a 2-factor verification code sent to the client
406
- * @param {reCaptchaToken} reCaptcha token for login challenge
407
- * @param {recaptchaTokenType} recaptcha token type - score/human
408
- * @param {devicePlatform} device platform i.e. ios/android
409
- */
410
- async loginVerify(identifier, type, code, reCaptchaToken, recaptchaTokenType, devicePlatform) {
411
- const { api } = this.comoData;
412
- try {
413
- const { consent } = await api.logInVerify(code, type, identifier, reCaptchaToken, recaptchaTokenType, devicePlatform);
414
- this.comoData.consent = consent;
415
- } catch (error) {
416
- if (error.response && error.response.data) {
417
- const { code: errCode, message } = error.response.data;
418
- if (message && message === 'Invalid verification Code') {
419
- throw new Error(message);
420
- }
421
- throw new Error(errCode || message);
422
- }
423
- throw error;
424
- }
425
-
426
- return this.getCredentials();
427
- }
428
-
429
- /**
430
- * Reload and sets the internal membership state.
431
- * To get the reloaded membership make sure to call getMembership();
432
- * @param onlyAssetKeys - include assets list full data or only the asset keys
433
- * @returns {Promise<void>}
434
- */
435
- async reloadMembership(onlyAssetKeys = false) {
436
- const {
437
- env_params: env, locationId, api,
438
- } = this.comoData;
439
- const membership = await api.getUserAtLocation(env.zapp_root, locationId, onlyAssetKeys);
440
- this.comoData.membership = membership;
441
- }
442
-
443
- /**
444
- * Send an Open app request to update server.
445
- */
446
- async openApp(data) {
447
- const {
448
- api, locationId, env_params: env,
449
- } = this.comoData;
450
-
451
- return api.openApp(env.zapp_root, locationId, data);
452
- }
453
-
454
- /**
455
- * Update membership status (permissions).
456
- * @param {boolean} pushEnable
457
- * @param {boolean} locationEnable
458
- */
459
- async clientUpdateMembership(pushEnable, locationEnable) {
460
- const {
461
- api, locationId, token, env_params: env,
462
- } = this.comoData;
463
- try {
464
- return api.clientUpdateMembership(env.services_root, locationId, pushEnable, locationEnable);
465
- } catch (e) {
466
- console.log(`clientUpdateMembership failed, token:${token}, locationId:${locationId}, error: ${e}`);
467
- }
468
- }
469
-
470
- /**
471
- * Downloads the main resource by its id and sets the resource in the main resource's map.
472
- * @param {string} resourceId Main resource id
473
- */
474
-
475
- async downloadMainResource(resourceId) {
476
- try {
477
- const resource = this.comoData.mainResourcesMap[resourceId];
478
- if (!resource) {
479
- const err = `resource ${resourceId} not found!`;
480
- throw new Error(err);
481
- }
482
-
483
- const { env_params: env, api } = this.comoData;
484
- const responseJson = await api.getLoyaltyResource(
485
- env.services_root_cdn,
486
- resource.id,
487
- ('Namespace' in resource) ? resource.Namespace : `location_${this.comoData.locationId}`,
488
- resource.Version,
489
- this.comoData.locationId,
490
- );
491
-
492
- this.comoData.mainResources[resourceId] = responseJson;
493
- } catch (e) {
494
- console.log(`downloadMainResource failed, resourceId:${resourceId}, locationId:${this.comoData.locationId}, this.comoData.mainResourcesMap: ${this.comoData.mainResourcesMap}, error: ${e}`);
495
- throw new Error(e);
496
- }
497
- }
498
-
499
-
500
- /**
501
- * Reload and sets the internal location state.
502
- * To get the reloaded location state make sure to call getLocationState();
503
- */
504
- async reloadLocationState() {
505
- const { api } = this.comoData;
506
- const state = await api.getLocationState(this.comoData.env_params.zapp_root, this.comoData.locationId);
507
- this.comoData.locationState = state;
508
- const mainResources = state.MainResources.Resource;
509
-
510
- this.comoData.mainResourcesMap = mainResources.reduce((total, obj) => {
511
- total[obj.id] = obj; // eslint-disable-line no-param-reassign
512
- return total;
513
- }, {});
514
- }
515
-
516
- /**
517
- * Requests an app client id by the user's location id.
518
- */
519
- async getAppClientID() {
520
- const {
521
- env_params: env, locationId, api,
522
- } = this.comoData;
523
- await this.refreshTokenIfNeeded();
524
- const responseJson = await api.getShortUserId(env.zapp_root, locationId);
525
- return responseJson.ID;
526
- }
527
-
528
- /**
529
- * Requests a generated payment code from the loyalty server
530
- */
531
- async getPaymentCode() {
532
- const {
533
- env_params: env, locationId, api,
534
- } = this.comoData;
535
- const responseJson = await api.getPaymentCode(env.zapp_root, locationId);
536
- return responseJson.VerificationCode;
537
- }
538
-
539
- /**
540
- * Logout the user identified by its accessToken
541
- */
542
- async logout() {
543
- const { api } = this.comoData;
544
- try {
545
- const response = await api.logOut();
546
- return response;
547
- } catch (error) {
548
- if (error.response && error.response.data) {
549
- const { code: errCode, message } = error.response.data;
550
- throw new Error(errCode || message);
551
- }
552
- throw error;
553
- }
554
- }
555
-
556
- /**
557
- *
558
- * @param {string} assetKey asset's id
559
- */
560
- async redeemAsset(assetKey) {
561
- const {
562
- env_params: env, locationId, api,
563
- } = this.comoData;
564
- await this.refreshTokenIfNeeded();
565
- const responseJson = await api.redeemAsset(env.zapp_root, locationId, assetKey);
566
- return responseJson;
567
- }
568
-
569
- /**
570
- *
571
- * @param {string} itemId item's id
572
- */
573
- async purchaseAsset(itemId) {
574
- const {
575
- env_params: env, locationId, api,
576
- } = this.comoData;
577
- const responseJson = await api.purchaseAsset(env.zapp_root, locationId, itemId);
578
- return responseJson;
579
- }
580
-
581
- async getUserPointShopDataAPI2() {
582
- const {
583
- env_params: env, locationId, api,
584
- } = this.comoData;
585
- const responseJson = await api.getUserPointShopDataAPI2(env.zapp_root, locationId);
586
- return responseJson;
587
- }
588
-
589
-
590
- async purchaseAssetAPI2(assetID) {
591
- const {
592
- env_params: env, locationId, api,
593
- } = this.comoData;
594
- const responseJson = await api.purchaseAssetAPI2(env.zapp_root, locationId, assetID);
595
- return responseJson;
596
- }
597
-
598
- /**
599
- * Reload and sets the internal assets (expired and valid) state.
600
- * To get the reloaded assets make sure to call como.getAssets();
601
- */
602
- async reloadAssets() {
603
- const {
604
- env_params: env, locationId, api,
605
- } = this.comoData;
606
- const responseJson = await api.memberAssets(env.services_root, locationId);
607
- this.comoData.assets = responseJson;
608
- }
609
-
610
- /**
611
- * Merge the default app text with the location specific app texts
612
- * Both DefaultAppTexts and AppTexts are a MainResource that needs to be downloaded before merge
613
- */
614
- mergeTexts() {
615
- if (this.comoData.mainResources.AppTexts && this.comoData.mainResources.DefaultAppTexts) {
616
- this.comoData.app_texts = this.comoData.mainResources.DefaultAppTexts;
617
- Object.keys(this.comoData.mainResources.AppTexts).forEach(key => {
618
- this.comoData.app_texts[key] = this.comoData.mainResources.AppTexts[key];
619
- });
620
- }
621
- }
622
-
623
- /**
624
- *
625
- * @param {string} key app text key
626
- */
627
- getTextForKey(key) { return this.comoData.app_texts[key]; }
628
-
629
- /**
630
- * Downloads the main resources:
631
- * - LocationData
632
- * - Menu
633
- * - Updates
634
- * - TCAlts
635
- * - AppTexts
636
- * - DefaultAppTexts
637
- */
638
- async downloadMainResources() {
639
- const resources = [
640
- 'LocationData',
641
- 'Menu',
642
- 'Updates',
643
- 'TCAlts',
644
- 'AppTexts',
645
- 'DefaultAppTexts',
646
- ];
647
- const downloadMainResource = this.downloadMainResource.bind(this);
648
- const downloads = resources.map(downloadMainResource);
649
- await Promise.all(downloads);
650
- this.mergeTexts();
651
- }
652
-
653
- /**
654
- *
655
- * @param {string} type form type
656
- */
657
- loadForm(type) {
658
- const { api, locationId } = this.comoData;
659
-
660
- return api.getForm(type, locationId);
661
- }
662
-
663
- /**
664
- *
665
- * @param {string} type configuration type
666
- */
667
- getConfigration(type) {
668
- const { api, locationId } = this.comoData;
669
- const locationState = this.getLocationState();
670
- let version = null;
671
- if (locationState) {
672
- version = locationState.PublishVersion;
673
- }
674
-
675
- return api.getConfiguration(type, locationId, version);
676
- }
677
-
678
- /**
679
- *
680
- * @param {string} type resource type
681
- */
682
- getResource(type) {
683
- const { api, locationId } = this.comoData;
684
- return api.getResource(type, locationId);
685
- }
686
-
687
- /**
688
- *
689
- * Get all legacy resources (Zapp)
690
- */
691
- getLegacyResources() {
692
- const { api, locationId } = this.comoData;
693
- return api.getZappResource(locationId);
694
- }
695
-
696
- /**
697
- * Generates a new referral code for friend-referral
698
- */
699
- generateReferralCode() {
700
- const {
701
- api, locationId, env_params: env,
702
- } = this.comoData;
703
-
704
- return api.generateReferralCode(env.services_root, locationId);
705
- }
706
-
707
- /**
708
- * Check that referral code is valid
709
- */
710
- verifyReferralCode(code) {
711
- const {
712
- api, locationId, env_params: env,
713
- } = this.comoData;
714
-
715
- return api.verifyReferralCode(env.services_root, locationId, code);
716
- }
717
-
718
- /**
719
- * Returns current accessToken and refreshToken
720
- * Caution: make sure to pass refresh callback to init to get the latest credentials
721
- */
722
- getCredentials() {
723
- const {
724
- accessToken, refreshToken, expiresIn, consent, locationId,
725
- } = this.comoData;
726
- return {
727
- accessToken, refreshToken, expiresIn, consent, locationId,
728
- };
729
- }
730
-
731
- /**
732
- * Returns the current accessToken
733
- * Caution: make sure to pass refresh callback to como.init to get the latest credentials
734
- */
735
- getToken() { return this.comoData.token; }
736
-
737
- /**
738
- * Returns the como.js initialized installation id
739
- */
740
- getInstallationId() { return this.comoData.installationId; }
741
-
742
- /**
743
- * Return the user membership object
744
- * Make sure to call reloadMembership() to reload the membership object.
745
- */
746
- getMembership() { return this.comoData.membership; }
747
-
748
- /**
749
- * Return the user assets object
750
- * Make sure to call reloadAssets() to reload the assets object.
751
- */
752
- getAssets() { return this.comoData.assets; }
753
-
754
- /**
755
- * Return service recaptch config
756
- */
757
- async getRecaptchaConfig() {
758
- await this.refreshTokenIfNeeded();
759
- return this.comoData.recaptchaConfig;
760
- }
761
-
762
- /**
763
- * Returns the location specific registration web url
764
- */
765
- async getRegistrationUrl(overrides) {
766
- await this.refreshTokenIfNeeded();
767
- const {
768
- token, installationId, locationId, env_params: env,
769
- } = this.comoData;
770
-
771
- return urlsUtils.appendQuery(
772
- `${env.app_modules_root}/register?token=${token}&locationid=${locationId}`,
773
- {
774
- ...this.getCredentials(),
775
- installationId,
776
- disableSuccessScreen: 'true',
777
- ...overrides,
778
- },
779
- );
780
- }
781
-
782
- /**
783
- * Returns the location specific profile web url
784
- */
785
- async getProfileUrl(overrides) {
786
- await this.refreshTokenIfNeeded();
787
- const {
788
- token, installationId, locationId, env_params: env,
789
- } = this.comoData;
790
-
791
- return urlsUtils.appendQuery(
792
- `${env.app_modules_root}/profile?token=${token}&locationid=${locationId}`,
793
- {
794
- ...this.getCredentials(),
795
- installationId,
796
- disableSuccessScreen: 'true',
797
- ...overrides,
798
- },
799
- );
800
- }
801
-
802
- /**
803
- * Returns the location specific login web url
804
- */
805
- async getLoginUrl() {
806
- await this.refreshTokenIfNeeded();
807
- const { token, locationId, env_params: env } = this.comoData;
808
- return `${env.app_modules_root}/login?token=${token}&locationid=${locationId}`;
809
- }
810
-
811
- /**
812
- * Returns the location data Portability url
813
- */
814
- async getDataPortabilityUrl(url, overrides) {
815
- await this.refreshTokenIfNeeded();
816
- const {
817
- installationId,
818
- } = this.comoData;
819
-
820
- return urlsUtils.appendQuery(
821
- `${url}`,
822
- {
823
- installationId,
824
- ...overrides,
825
- },
826
- );
827
- }
828
-
829
- /**
830
- * Returns app-modules legacy root url
831
- */
832
- async getAppModulesLegacyBaseUrl() {
833
- await this.refreshTokenIfNeeded();
834
- const { env_params: env } = this.comoData;
835
- return `${env.app_modules_legacy_root}`;
836
- }
837
-
838
- /**
839
- * Returns the location specific friend referral web url
840
- */
841
- getFriendReferralInviteLink(referral) {
842
- const { env_params: env, locationId } = this.comoData;
843
- const cpBaseUrl = env.cp_base_url.replace('{location_id}', locationId);
844
- return `${cpBaseUrl}/auth/signin?onboardingflow=register&ReferralCode=${referral}`;
845
- }
846
-
847
- /**
848
- * Return the location state
849
- * Make sure to call reloadLocationState() to reload the location state object.
850
- */
851
- getLocationState() { return this.comoData.locationState; }
852
-
853
- /**
854
- * Returns the location point shop items
855
- */
856
- getPointShopItems() { return this.comoData.mainResources.menu.gifts_shop_item; }
857
-
858
- /**
859
- *
860
- * @param {string} resourceName resource name
861
- */
862
- getMainResource(resourceName) { return this.comoData.mainResources[resourceName]; }
863
-
864
- /**
865
- *
866
- * @param {string} resourceId resource id
867
- * @param {string} namespace resource namespace
868
- * @param {string|number} version resource version
869
- */
870
- getResourceUrl(resourceId, namespace, version) {
871
- const { api, env_params: env, locationId } = this.comoData;
872
- return api.getResourceUrl(env.services_root_cdn, resourceId, namespace, version, locationId);
873
- }
874
-
875
- getPurchaseReportUrl() {
876
- const { env_params: env } = this.comoData;
877
- return `${env.purchase_connect_base_url}/Purchase/Report`;
878
- }
879
-
880
- getCdnUrl() {
881
- return this.comoData.env_params.services_root_cdn;
882
- }
883
-
884
- /**
885
- * @param {string} identifier fcm token created by firebase
886
- * @param {boolean} isFirebaseProjectMigrated is migrated to new firebase project
887
- */
888
- sendPushChannel(identifier, isFirebaseProjectMigrated = false) {
889
- const {
890
- api, locationId, env_params: env,
891
- } = this.comoData;
892
- return api.sendPushChannel(env.services_root, locationId, identifier, isFirebaseProjectMigrated);
893
- }
894
-
895
- async getRegionConfig() {
896
- const {
897
- api, locationId, env_params: env,
898
- } = this.comoData;
899
- return api.getClientBusinessBackend(env.zapp_root, locationId);
900
- }
901
-
902
- /**
903
- * @param {string} code coupon code entered by user on App
904
- */
905
- sendCouponCode(code) {
906
- const {
907
- api, locationId, env_params: env,
908
- } = this.comoData;
909
- return api.sendCouponCode(env.zapp_root, locationId, code);
910
- }
911
-
912
- async addPaymentMethod(cardToken) {
913
- const {
914
- api, locationId, token, env_params: env,
915
- } = this.comoData;
916
- return api.addPaymentMethod(env.services_root, locationId, token, cardToken);
917
- }
918
-
919
- async knowYourData(email) {
920
- const {
921
- api, locationId, env_params: env,
922
- } = this.comoData;
923
- return api.knowYourData(env.services_root, locationId, email);
924
- }
925
-
926
- async consentWithdraw(consentData) {
927
- const {
928
- api, locationId, env_params: env,
929
- } = this.comoData;
930
- return api.consentWithdraw(env.services_root, locationId, consentData);
931
- }
932
-
933
- async getPaymentMethods() {
934
- const {
935
- env_params: env, locationId, api,
936
- } = this.comoData;
937
- const responseJson = await api.getPaymentMethods(env.services_root, locationId);
938
- return responseJson;
939
- }
940
-
941
- /**
942
- * @param {Integer} amount to charge
943
- * @param {String} creditCardToken to charge from
944
- * @param {String} itemName for submit purchase
945
- * @param {String} itemNumber for submit purchase
946
- * @param {String} membershipKey for user identification
947
- */
948
- async payInApp(amount, creditCardToken, itemName, itemNumber, membershipKey) {
949
- const {
950
- env_params: env, token, locationId, api,
951
- } = this.comoData;
952
- const responseJson = await api.payInApp(env.services_root, locationId, token, amount, creditCardToken, itemName, itemNumber, membershipKey);
953
- return responseJson;
954
- }
955
-
956
- /**
957
- * @param {string} creditCardToken which card to delete
958
- */
959
- async deletePayment(creditCardToken) {
960
- const {
961
- env_params: env, token, locationId, api,
962
- } = this.comoData;
963
- const responseJson = await api.deletePayment(env.services_root, locationId, token, creditCardToken);
964
- return responseJson;
965
- }
966
- /**
967
- * @param {string} tag for Pop-up Message Trigger (Optional param)
968
- */
969
- async getMemberMessages(tag, limit, offset, fetchExpiredMessages = false) {
970
- const {
971
- api, locationId, env_params: env,
972
- } = this.comoData;
973
- await this.refreshTokenIfNeeded();
974
- return api.getMemberMessages(env.services_root, locationId, tag, limit, offset, fetchExpiredMessages);
975
- }
976
-
977
- /**
978
- * @param {string} keysList keys of the messages to mark as read
979
- */
980
- markAsReadMemberMessages(keysList) {
981
- const {
982
- api, locationId, env_params: env,
983
- } = this.comoData;
984
- return api.markAsReadMemberMessages(env.services_root, locationId, keysList);
985
- }
986
-
987
- /**
988
- * @param {String} receiver to share with
989
- * @param {String} sender name
990
- * @param {Integer} amount to share
991
- * @param {String} type of wallet
992
- */
993
- async budgetTransfer(receiver, sender, amount, wallet) {
994
- const {
995
- api, locationId, env_params: env,
996
- } = this.comoData;
997
- return api.budgetTransfer(env.services_root, locationId, receiver, sender, amount, wallet);
998
- }
999
-
1000
- /**
1001
- * @param {string} claimId key of the credit claim to read the data for
1002
- */
1003
- getCreditClaimData(claimId) {
1004
- const {
1005
- api, env_params: env,
1006
- } = this.comoData;
1007
- return api.getCreditClaimData(env.services_root, claimId);
1008
- }
1009
-
1010
-
1011
- /**
1012
- * @param {String} claimCode to use for budget transfer claim
1013
- */
1014
- async budgetTransferClaim(claimCode) {
1015
- const {
1016
- api, locationId, env_params: env,
1017
- } = this.comoData;
1018
- return api.budgetTransferClaim(env.services_root, locationId, claimCode);
1019
- }
1020
-
1021
- /**
1022
- * API call for claim generic action. To specify your trigger button, you need to associate it with a particular item, asset or tag.
1023
- * @param {string} assetKey - asset to claim
1024
- * @param {string} itemId - item id to claim
1025
- * @param {string} tag - claim all with tag
1026
- */
1027
- claim(assetKey, itemId, tag) {
1028
- const {
1029
- env_params: env, locationId, api,
1030
- } = this.comoData;
1031
- return api.claim(env.zapp_root, locationId, assetKey, itemId, tag);
1032
- }
1033
-
1034
- /**
1035
- * Log user action
1036
- * @param {string} action
1037
- * @param {string} logData - log information
1038
- */
1039
- async logUserAction(action, logData) {
1040
- const {
1041
- env_params: env, locationId, api,
1042
- } = this.comoData;
1043
- return api.logUserAction(env.zapp_root, locationId, action, logData);
1044
- }
1045
-
1046
- /**
1047
- * Member purchase history
1048
- * @param {string} limit - how manay items to return (if exists) - default value is 25
1049
- * @param {string} offset - default value is 0(next offset would be 25)
1050
- */
1051
- async getPurchaseHistory(limit, offset) {
1052
- const {
1053
- locationId, api, membership,
1054
- } = this.comoData;
1055
- return api.getPurchaseHistory(locationId, membership.Key, limit, offset);
1056
- }
1057
-
1058
- /**
1059
- * Get app texts for multi languages - only for apps that has project set in translation service
1060
- */
1061
- getAppTextsV2() {
1062
- const { api, locationId } = this.comoData;
1063
- return api.getAllTranslations(locationId);
1064
- }
1065
-
1066
- /**
1067
- * Generate a temporary token for the location
1068
- * @param {number} timeToLive time to live in seconds
1069
- * @returns {string} temporary token
1070
- */
1071
- async generateTemporaryToken(timeToLive = 60) {
1072
- const { api, locationId, env_params: env } = this.comoData;
1073
- return api.generateTemporaryToken(env.services_root, locationId, timeToLive);
1074
- }
1075
- }
1076
-
1077
- module.exports = Como;