@crmcom/self-service-sdk 2.1.2

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.
@@ -0,0 +1,41 @@
1
+ import { logger } from './logger';
2
+ let events = {};
3
+
4
+ try {
5
+ // Dynamic import for Vite / ES modules
6
+ const customConfigModule = import("../../../custom.config");
7
+ events = customConfigModule.events || {};
8
+ } catch (error) {
9
+ if (error.code === 'MODULE_NOT_FOUND') {
10
+ events = {};
11
+ } else {
12
+ throw error;
13
+ }
14
+ }
15
+
16
+ export const eventListener = {
17
+ handleEvent,
18
+ };
19
+
20
+ export const eventTypes = {
21
+ kochava: 'KOCHAVA',
22
+ };
23
+
24
+ export async function handleEvent({
25
+ type, // eg: 'kochava'
26
+ code, // event code
27
+ data,
28
+ object_type // object type, e.g., string or object
29
+ }) {
30
+ logger.debug("handleEvent type:", type);
31
+ logger.debug("handleEvent code:", code);
32
+ logger.debug("handleEvent object_type:", object_type);
33
+ logger.debug("handleEvent data:", data);
34
+
35
+ if (type === eventTypes.kochava) {
36
+ const kochava = events?.kochava ?? null;
37
+ if (kochava?.sendEvent) {
38
+ await kochava.sendEvent(object_type, code, data);
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,396 @@
1
+ /**
2
+ * class to handle HTTP Requests and Token refresh
3
+ */
4
+
5
+ //import querystring from 'querystring';
6
+ import { initOptionHeader } from '../../utils/common';
7
+ import {jwtDecode} from 'jwt-decode';
8
+ import { logger } from './logger';
9
+
10
+ export const httpBackOfficeUtil = {
11
+ setupChannel,
12
+ put,
13
+ post,
14
+ get,
15
+ sendDelete,
16
+ is200OK,
17
+ startSession,
18
+ getSession,
19
+ cleanObj,
20
+ cleanSession,
21
+ switchSession
22
+ };
23
+
24
+
25
+ /** local variables */
26
+
27
+ let _storeKVFn;
28
+ let _getKVFn;
29
+ let _sessionInvalidCallback;
30
+ let _fetchFn;
31
+
32
+ let _apiKey;
33
+ let _accessToken;
34
+ let _refreshToken;
35
+ let _host;
36
+ let _sessionData;
37
+
38
+ let _selfServicePath = 'self-service';
39
+ let _backofficePath = 'backoffice/v1/';
40
+ /**
41
+ *
42
+ * TODO: do we need a call back for no internet connection ?
43
+ * when no connection refresh token will not be available -> should not kick user out.
44
+ */
45
+
46
+ async function setupChannel({
47
+ storeKVFn, //function to store key value
48
+ getKVFn, //function to get value by key from the storage
49
+ sessionInvalidCallback, //function to call when api_key or token key is invalid and refresh token if available was failed
50
+ apiKey,
51
+ host,
52
+ fetchFn
53
+ }) {
54
+ _storeKVFn = storeKVFn;
55
+ _getKVFn = getKVFn;
56
+ _sessionInvalidCallback = sessionInvalidCallback;
57
+ _apiKey = apiKey;
58
+ _host = host;
59
+ if (fetchFn)
60
+ _fetchFn = fetchFn;
61
+ else
62
+ _fetchFn = fetch;
63
+
64
+ if (_getKVFn) {
65
+ _accessToken = await _getKVFn('access_token');
66
+ _refreshToken = await _getKVFn('refresh_token');
67
+ try {
68
+ if (_accessToken)
69
+ {
70
+ _sessionData = jwtDecode(_accessToken);
71
+ await refreshToken();
72
+ }
73
+ }catch (e) {
74
+ logger.warn('Failed to load session data:', e);
75
+ _sessionData = undefined;
76
+ }
77
+ }
78
+ //TODO add silent refresh token here
79
+ }
80
+
81
+ async function refreshToken() {
82
+ try {
83
+ // console.log('Session data:', _sessionData);
84
+ // console.log('_refreshToken:', _refreshToken);
85
+ let currentTime = Date.now();
86
+ let tokenLiveInSec = (_sessionData.exp - currentTime/1000);
87
+ // console.log("tokenLiveInSec:" + tokenLiveInSec);
88
+ if (tokenLiveInSec > 60*60)
89
+ return;
90
+
91
+ //try to refresh token
92
+
93
+ //console.log('API: ', contact)
94
+ let response = await post({
95
+ resourcePath: "users/refresh",
96
+ refreshToken: true,
97
+ logOutIfSessionInvalid: false,
98
+ });
99
+ if (response.code == "OK") {
100
+ startSession(response.data);
101
+ return;
102
+ }
103
+
104
+ } catch (e) {
105
+ logger.error("Refresh exception:", e);
106
+ return;
107
+ }
108
+ }
109
+
110
+ function startSession({access_token,refresh_token, exp, organisations}) {
111
+ //TODO handle expiration
112
+ _accessToken = access_token;
113
+ _refreshToken = refresh_token;
114
+ if (_storeKVFn) {
115
+ _storeKVFn("refresh_token", refresh_token)
116
+ _storeKVFn("access_token", access_token)
117
+ //_storeKVFn("organisations", JSON.stringify(organisations));
118
+ // _storeKVFn("exp", exp)
119
+ //parse and store session data
120
+ _sessionData = jwtDecode(_accessToken);
121
+ // console.log('Session data:', _sessionData);
122
+ }
123
+
124
+ }
125
+
126
+
127
+
128
+ function switchSession({access_token,refresh_token, exp},isMerchant) {
129
+ _sessionData = jwtDecode(access_token);
130
+ if(isMerchant){
131
+ var _sessionServiceOwnerData = jwtDecode(_accessToken);
132
+ _storeKVFn("merchant_id", _sessionData.current_organisation_id);
133
+ _storeKVFn("service_owner_id", _sessionServiceOwnerData.current_organisation_id)
134
+ }else{
135
+ _storeKVFn("merchant_id", null);
136
+ }
137
+
138
+ startSession({access_token,refresh_token, exp})
139
+ }
140
+
141
+ function cleanSession() {
142
+ //TODO handle expiration
143
+ _accessToken = undefined;
144
+ _refreshToken = undefined;
145
+ if (_storeKVFn) {
146
+ _storeKVFn("refresh_token", undefined)
147
+ _storeKVFn("access_token", undefined)
148
+ //parse and store session data
149
+ logger.debug('Session cleared');
150
+ }
151
+
152
+ }
153
+
154
+ function getSession() {
155
+ logger.debug("getSession called");
156
+ return _sessionData;
157
+ }
158
+
159
+ function getURI(isBackend, resourcePath) {
160
+ if (isBackend == true)
161
+ return _host + _backofficePath + resourcePath;
162
+ else
163
+ return _host + _selfServicePath + resourcePath;
164
+ }
165
+
166
+ function json2Obj(str) {
167
+ try {
168
+ return JSON.parse(str);
169
+ }catch (e){
170
+ return null;
171
+ }
172
+ }
173
+ async function post({
174
+ resourcePath,
175
+ body,
176
+ queryParams,
177
+ withAccessToken = false,
178
+ withoutApikey = false,
179
+ logOutIfSessionInvalid=true,
180
+ refreshToken
181
+ }) {
182
+ // let serverConfig = getServerConfig();
183
+ // let uri = getURI(isBackend, resourcePath);
184
+ let uri = _host + 'backoffice/v1/' + resourcePath;
185
+ var options = {};
186
+ options.headers = initOptionHeader();
187
+ options.method = "POST";
188
+ if (withAccessToken == true || logOutIfSessionInvalid==true || refreshToken==true) {
189
+ if (refreshToken)
190
+ options.headers['Authorization'] = 'Bearer ' + _refreshToken;
191
+ else
192
+ options.headers['Authorization'] = 'Bearer ' + _accessToken;
193
+ } else if(!withoutApikey)
194
+ options.headers['api_key'] = _apiKey;
195
+
196
+ // if (refreshToken) {
197
+ // options.headers['refresh_token'] = refreshToken;
198
+ // }
199
+ if (body)
200
+ options.body = JSON.stringify(body);
201
+ if (queryParams)
202
+ uri = _host + '?' + URLSearchParams(queryParams).toString();
203
+ logger.debug('POST:', uri);
204
+ let response = await _fetchFn(uri, options);
205
+ let bodyText = await response.text()
206
+ logger.debug('Response:', response.status);
207
+ if (response.status == '200') {
208
+ return { code: "OK", data: bodyText ? json2Obj(bodyText) : null };
209
+ } else
210
+ {
211
+ validateForceLogout(response,logOutIfSessionInvalid);
212
+ return { code: response.status, bodyText: bodyText, error: json2Obj(bodyText) };
213
+ }
214
+
215
+
216
+
217
+ //TODO: add token expire and validation later
218
+ // let accessToken;
219
+ // if (withAccessToken)
220
+ // {
221
+ // //check if token is not expired
222
+ // }
223
+
224
+ }
225
+
226
+ async function validateForceLogout(response,logOutIfSessionInvalid){
227
+ try {
228
+ if (logOutIfSessionInvalid == true && _sessionInvalidCallback && response.status == '401'){
229
+ await _sessionInvalidCallback();
230
+ cleanSession();
231
+ }
232
+ }catch (e) {
233
+ logger.error(e);
234
+ }
235
+ }
236
+
237
+ async function get({
238
+ resourcePath,
239
+ queryParams,
240
+ withAccessToken = false,
241
+ isBackend = true,
242
+ logOutIfSessionInvalid = true,
243
+ returnText=false
244
+ }) {
245
+ let uri = getURI(isBackend, resourcePath);
246
+ // let uri = _host + 'backoffice/v1/' + resourcePath;
247
+ var options = {};
248
+ options.headers = initOptionHeader();
249
+ options.method = "GET";
250
+ if (withAccessToken == true || logOutIfSessionInvalid==true)
251
+ options.headers['Authorization'] = 'Bearer ' + _accessToken;
252
+ else
253
+ options.headers['api_key'] = _apiKey;
254
+
255
+ if (queryParams)
256
+ {
257
+ const queryString = new URLSearchParams(
258
+ Object.fromEntries(
259
+ Object.entries(cleanObj(queryParams)).map(([k, v]) => [k, String(v)])
260
+ )
261
+ ).toString();
262
+ uri = uri + '?' + queryString;
263
+ }
264
+ logger.debug('GET:', uri);
265
+ let response = await _fetchFn(uri, options);
266
+ let bodyText = await response.text()
267
+ logger.debug('Response:', response.status);
268
+ if (response.status == '200') {
269
+ if(returnText)
270
+ return { code: "OK", data: bodyText};
271
+ return { code: "OK", data: bodyText ? json2Obj(bodyText) : null };
272
+ } else
273
+ {
274
+ validateForceLogout(response,logOutIfSessionInvalid);
275
+ return { code: response.status, bodyText: bodyText, error: json2Obj(bodyText) };
276
+ }
277
+
278
+ //TODO: add token expire and validation later
279
+ // let accessToken;
280
+ // if (withAccessToken)
281
+ // {
282
+ // //check if token is not expired
283
+ // }
284
+
285
+ }
286
+
287
+ async function sendDelete({
288
+ resourcePath,
289
+ queryParams,
290
+ withAccessToken = false,
291
+ isBackend = false,
292
+ logOutIfSessionInvalid = true
293
+ }) {
294
+ // let uri = getURI(isBackend, resourcePath);
295
+ let uri = _host + 'backoffice/v1/' + resourcePath;
296
+ var options = {};
297
+ options.headers = initOptionHeader();
298
+ options.method = "DELETE";
299
+ if (withAccessToken == true || logOutIfSessionInvalid==true)
300
+ options.headers['Authorization'] = 'Bearer ' + _accessToken;
301
+ else
302
+ options.headers['api_key'] = _apiKey;
303
+
304
+ if (queryParams)
305
+ {
306
+ const queryString = new URLSearchParams(
307
+ Object.fromEntries(
308
+ Object.entries(cleanObj(queryParams)).map(([k, v]) => [k, String(v)])
309
+ )
310
+ ).toString();
311
+ uri = uri + '?' + queryString;
312
+ }
313
+ logger.debug('GET:', uri);
314
+ let response = await _fetchFn(uri, options);
315
+ let bodyText = await response.text()
316
+ logger.debug('Response:', response.status);
317
+ if (response.status == '200') {
318
+ return { code: "OK", data: bodyText ? json2Obj(bodyText) : null };
319
+ } else
320
+ {
321
+ validateForceLogout(response,logOutIfSessionInvalid);
322
+ return { code: response.status, bodyText: bodyText, error: json2Obj(bodyText) };
323
+ }
324
+
325
+ //TODO: add token expire and validation later
326
+ // let accessToken;
327
+ // if (withAccessToken)
328
+ // {
329
+ // //check if token is not expired
330
+ // }
331
+
332
+ }
333
+
334
+ async function put({
335
+ resourcePath,
336
+ body,
337
+ queryParams,
338
+ withAccessToken = false,
339
+ isBackend = false,
340
+ logOutIfSessionInvalid = true
341
+ }) {
342
+ // let uri = getURI(isBackend, resourcePath);
343
+ let uri = _host + 'backoffice/v1/' + resourcePath;
344
+ var options = {};
345
+ options.headers = initOptionHeader();
346
+ options.method = "PUT";
347
+ if (withAccessToken == true || logOutIfSessionInvalid==true)
348
+ options.headers['Authorization'] = 'Bearer ' + _accessToken;
349
+ else
350
+ options.headers['api_key'] = _apiKey;
351
+
352
+ if (body)
353
+ options.body = JSON.stringify(body);
354
+ if (queryParams)
355
+ uri = uri + '?' + URLSearchParams(queryParams).toString();
356
+ logger.debug('PUT:', uri);
357
+ let response = await _fetchFn(uri, options);
358
+ let bodyText = await response.text()
359
+ logger.debug('Response:', response.status);
360
+ if (response.status == '200') {
361
+ return { code: "OK", data: bodyText ? json2Obj(bodyText) : null };
362
+ }
363
+ else
364
+ {
365
+ validateForceLogout(response,logOutIfSessionInvalid);
366
+ return { code: response.status, bodyText: bodyText, error: json2Obj(bodyText) };
367
+ }
368
+
369
+ //TODO: add token expire and validation later
370
+ // let accessToken;
371
+ // if (withAccessToken)
372
+ // {
373
+ // //check if token is not expired
374
+ // }
375
+
376
+ }
377
+
378
+ async function getAccessToken() {
379
+
380
+ }
381
+
382
+ function is200OK(result) {
383
+ if (result && result.status && result.status == '200')
384
+ return true;
385
+ else
386
+ return false;
387
+ }
388
+
389
+ function cleanObj(obj) {
390
+ for (var propName in obj) {
391
+ if (obj[propName] === null || obj[propName] === undefined) {
392
+ delete obj[propName];
393
+ }
394
+ }
395
+ return obj;
396
+ }
package/httpJCCUtil.js ADDED
@@ -0,0 +1,61 @@
1
+ import { parseString, processors } from 'xml2js';
2
+ import { logger } from './logger';
3
+
4
+ // var jccServer = 'https://test-apis.jccsecure.com/JCCLoyaltyService/JCCLoyaltyService?wsdl';
5
+ // var jccServer ='https://test-apis.crm.com/JCCLoyaltyService/JCCLoyaltyService?wsdl' //uat
6
+ export const jccServer = 'https://apis.jccsecure.com/JCCLoyaltyService/JCCLoyaltyService?wsdl'; // live
7
+
8
+ export const httpJCCUtil = {
9
+ post
10
+ };
11
+
12
+ function createPostOption(payload) {
13
+ return {
14
+ headers: {
15
+ 'Content-Type': 'text/xml',
16
+ strictSSL: true,
17
+ rejectUnauthorized: false,
18
+ },
19
+ body: payload,
20
+ method: 'POST',
21
+ };
22
+ }
23
+
24
+ export async function post(path, payload, urlEndPoint) {
25
+ const options = createPostOption(payload);
26
+ options.NODE_TLS_REJECT_UNAUTHORIZED = 0;
27
+
28
+ let response;
29
+ try {
30
+ response = await fetch(urlEndPoint, options);
31
+ logger.debug("Response received");
32
+ } catch (ex) {
33
+ logger.error("Fetch exception:", ex);
34
+ return { code: "NETWORK_ISSUE", data: {} };
35
+ }
36
+
37
+ let body;
38
+ try {
39
+ body = await response.text();
40
+ logger.debug("Response body received");
41
+ } catch (ex) {
42
+ logger.error("Server unexpected response:", ex);
43
+ return { code: 'SERVER_UNEXPECTED_ISSUE', data: ex };
44
+ }
45
+
46
+ try {
47
+ let bodyData = null;
48
+ parseString(
49
+ body,
50
+ { tagNameProcessors: [processors.stripPrefix] },
51
+ (err, result) => {
52
+ if (err) throw err;
53
+ bodyData = result;
54
+ }
55
+ );
56
+ return { code: 'OK', data: bodyData };
57
+ } catch (ex) {
58
+ logger.error('XML parse error:', ex);
59
+ return { code: "CLIENT_UNEXPECTED_ISSUE", data: {} };
60
+ }
61
+ }