@verii/data-loader 1.0.0-pre.1752076816

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 (55) hide show
  1. package/LICENSE +201 -0
  2. package/jest.config.js +20 -0
  3. package/package.json +45 -0
  4. package/src/batch-issuing/README.md +97 -0
  5. package/src/batch-issuing/constants.js +33 -0
  6. package/src/batch-issuing/fetchers.js +114 -0
  7. package/src/batch-issuing/file-readers.js +15 -0
  8. package/src/batch-issuing/file-writers.js +34 -0
  9. package/src/batch-issuing/index.js +124 -0
  10. package/src/batch-issuing/orchestrators.js +332 -0
  11. package/src/batch-issuing/prepare-data.js +167 -0
  12. package/src/batch-issuing/prompts.js +47 -0
  13. package/src/batch-issuing/validate-directory-exists.js +11 -0
  14. package/src/data-loader.js +14 -0
  15. package/src/helpers/common.js +30 -0
  16. package/src/helpers/compute-activation-date.js +12 -0
  17. package/src/helpers/index.js +8 -0
  18. package/src/helpers/load-csv.js +34 -0
  19. package/src/helpers/load-handlebars-template.js +9 -0
  20. package/src/helpers/parse-column.js +8 -0
  21. package/src/helpers/prepare-variable-sets.js +23 -0
  22. package/src/index.js +3 -0
  23. package/src/vendor-credentials/README.md +72 -0
  24. package/src/vendor-credentials/execute-update.js +32 -0
  25. package/src/vendor-credentials/index.js +49 -0
  26. package/src/vendor-credentials/orchestrator.js +22 -0
  27. package/src/vendor-credentials/prepare-data.js +36 -0
  28. package/test/batch-issuing.test.js +1523 -0
  29. package/test/data/badge-offer.template.json +22 -0
  30. package/test/data/batch-vars-offerids.csv +3 -0
  31. package/test/data/batch-vars.csv +3 -0
  32. package/test/data/batchissuing-variables.csv +3 -0
  33. package/test/data/driver-license-offer.template.json +10 -0
  34. package/test/data/driver-license-variables.csv +3 -0
  35. package/test/data/email-offer.template.json +10 -0
  36. package/test/data/email-variables.csv +3 -0
  37. package/test/data/id-document-offer.template.json +10 -0
  38. package/test/data/id-document-variables.csv +3 -0
  39. package/test/data/national-id-card-offer.template.json +10 -0
  40. package/test/data/national-id-card-variables.csv +3 -0
  41. package/test/data/offer.template.json +22 -0
  42. package/test/data/passport-offer.template.json +10 -0
  43. package/test/data/passport-variables.csv +3 -0
  44. package/test/data/person.template.json +15 -0
  45. package/test/data/phone-offer.template.json +10 -0
  46. package/test/data/phone-variables.csv +2 -0
  47. package/test/data/phones-batch-vars-offerids.csv +3 -0
  48. package/test/data/proof-of-age-offer.template.json +10 -0
  49. package/test/data/proof-of-age-variables.csv +3 -0
  50. package/test/data/resident-permit-offer.template.json +10 -0
  51. package/test/data/resident-permit-variables.csv +3 -0
  52. package/test/data/variables-no-did.csv +3 -0
  53. package/test/data/variables.csv +3 -0
  54. package/test/data/with-bom.csv +3 -0
  55. package/test/vendor-credentials.test.js +227 -0
@@ -0,0 +1,1523 @@
1
+ const { env } = require('node:process');
2
+ const path = require('path');
3
+ const nock = require('nock');
4
+ const got = require('got');
5
+
6
+ const gotExtendSpy = jest.spyOn(got, 'extend');
7
+ const { nanoid } = require('nanoid');
8
+ const { runBatchIssuing } = require('../src/batch-issuing/orchestrators');
9
+
10
+ const ISO_DATETIME_TZ_FORMAT =
11
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\+\d\d:\d\d|Z)$/;
12
+
13
+ describe('batch issuing test', () => {
14
+ beforeAll(() => {
15
+ nock.cleanAll();
16
+ });
17
+
18
+ afterAll(() => {
19
+ nock.cleanAll();
20
+ nock.restore();
21
+ });
22
+
23
+ beforeEach(() => {
24
+ jest.clearAllMocks();
25
+ });
26
+
27
+ it("should fail if options don't have credential type or type doesn't exist", async () => {
28
+ const options = {
29
+ csvFilename: path.join(__dirname, 'data/batch-vars-offerids.csv'),
30
+ offerTemplateFilename: path.join(
31
+ __dirname,
32
+ 'data/email-offer.template.json'
33
+ ),
34
+ tenant: 'foo',
35
+ termsUrl: 'http://example.com/terms.html',
36
+ idCredentialType: 'Mug2.1',
37
+ new: true,
38
+ dryrun: true,
39
+ };
40
+
41
+ await expect(() => runBatchIssuing(options)).rejects.toThrowError(
42
+ "Mug2.1 doesn't exist. Please use one of EmailV1.0,PhoneV1.0,DriversLicenseV1.0"
43
+ );
44
+ });
45
+ it("should fail if options doesn't have 'did' or `tenant'", async () => {
46
+ const options = {
47
+ csvFilename: path.join(__dirname, 'data/batch-vars-offerids.csv'),
48
+ offerTemplateFilename: path.join(
49
+ __dirname,
50
+ 'data/email-offer.template.json'
51
+ ),
52
+ termsUrl: 'http://example.com/terms.html',
53
+ idCredentialType: 'Mug2.1',
54
+ new: true,
55
+ dryrun: true,
56
+ };
57
+
58
+ await expect(() => runBatchIssuing(options)).rejects.toThrowError(
59
+ 'one of "--tenant" or "--did" is required'
60
+ );
61
+ });
62
+
63
+ it('should load the templates and use offerIds from the csv', async () => {
64
+ const options = {
65
+ csvFilename: path.join(__dirname, 'data/batch-vars-offerids.csv'),
66
+ offerTemplateFilename: path.join(
67
+ __dirname,
68
+ 'data/email-offer.template.json'
69
+ ),
70
+ tenant: 'foo',
71
+ termsUrl: 'http://example.com/terms.html',
72
+ new: true,
73
+ dryrun: true,
74
+ };
75
+
76
+ const updates = await runBatchIssuing(options);
77
+ expect(
78
+ new Date(updates.disclosureRequest.activationDate).getTime()
79
+ ).toBeLessThan(Date.now());
80
+
81
+ expect(updates).toEqual({
82
+ disclosureRequest: {
83
+ configurationType: 'issuing',
84
+ vendorEndpoint: 'integrated-issuing-identification',
85
+ types: [
86
+ {
87
+ type: 'EmailV1.0',
88
+ },
89
+ ],
90
+ identityMatchers: {
91
+ rules: [
92
+ {
93
+ valueIndex: 0,
94
+ path: ['$.emails'],
95
+ rule: 'pick',
96
+ },
97
+ ],
98
+ vendorUserIdIndex: 0,
99
+ },
100
+ vendorDisclosureId: expect.any(Number),
101
+ setIssuingDefault: true,
102
+ duration: '1h', // 1 hour by default
103
+ offerMode: 'preloaded',
104
+ purpose: 'Issuing Career Credential', // by default have a generic message
105
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
106
+ termsUrl: 'http://example.com/terms.html',
107
+ authTokenExpiresIn: 525600,
108
+ },
109
+ newExchangeOffers: [
110
+ {
111
+ newOffer: {
112
+ type: ['EmailV1.0'],
113
+ issuer: {
114
+ id: 'did to be determined at runtime',
115
+ },
116
+ credentialSubject: {
117
+ vendorUserId: 'joan.lee@sap.com',
118
+ email: 'joan.lee@sap.com',
119
+ },
120
+
121
+ offerId: '100',
122
+ },
123
+ newExchange: {
124
+ type: 'ISSUING',
125
+ identityMatcherValues: ['joan.lee@sap.com'],
126
+ },
127
+ },
128
+ {
129
+ newOffer: {
130
+ type: ['EmailV1.0'],
131
+ issuer: {
132
+ id: 'did to be determined at runtime',
133
+ },
134
+ credentialSubject: {
135
+ vendorUserId: 'john.smith@sap.com',
136
+ email: 'john.smith@sap.com',
137
+ },
138
+
139
+ offerId: '200',
140
+ },
141
+ newExchange: {
142
+ type: 'ISSUING',
143
+ identityMatcherValues: ['john.smith@sap.com'],
144
+ },
145
+ },
146
+ ],
147
+ });
148
+ });
149
+
150
+ it('should load the templates and use offerIds from the csv using phone for identifier matching using col index', async () => {
151
+ const options = {
152
+ csvFilename: path.join(__dirname, 'data/batch-vars-offerids.csv'),
153
+ offerTemplateFilename: path.join(
154
+ __dirname,
155
+ 'data/email-offer.template.json'
156
+ ),
157
+ tenant: 'foo',
158
+ termsUrl: 'http://example.com/terms.html',
159
+ idCredentialType: 'PhoneV1.0',
160
+ identifierMatchColumn: 1,
161
+ new: true,
162
+ dryrun: true,
163
+ };
164
+
165
+ const updates = await runBatchIssuing(options);
166
+ expect(
167
+ new Date(updates.disclosureRequest.activationDate).getTime()
168
+ ).toBeLessThan(Date.now());
169
+
170
+ expect(updates).toEqual({
171
+ disclosureRequest: {
172
+ configurationType: 'issuing',
173
+ vendorEndpoint: 'integrated-issuing-identification',
174
+ types: [
175
+ {
176
+ type: 'PhoneV1.0',
177
+ },
178
+ ],
179
+ identityMatchers: {
180
+ rules: [
181
+ {
182
+ valueIndex: 1,
183
+ path: ['$.phones'],
184
+ rule: 'pick',
185
+ },
186
+ ],
187
+ vendorUserIdIndex: 0,
188
+ },
189
+ vendorDisclosureId: expect.any(Number),
190
+ setIssuingDefault: true,
191
+ duration: '1h', // 1 hour by default
192
+ offerMode: 'preloaded',
193
+ purpose: 'Issuing Career Credential', // by default have a generic message
194
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
195
+ termsUrl: 'http://example.com/terms.html',
196
+ authTokenExpiresIn: 525600,
197
+ },
198
+ newExchangeOffers: [
199
+ {
200
+ newOffer: {
201
+ type: ['EmailV1.0'],
202
+ issuer: {
203
+ id: 'did to be determined at runtime',
204
+ },
205
+ credentialSubject: {
206
+ vendorUserId: 'joan.lee@sap.com',
207
+ email: 'joan.lee@sap.com',
208
+ },
209
+ offerId: '100',
210
+ },
211
+ newExchange: {
212
+ type: 'ISSUING',
213
+ identityMatcherValues: ['+16478275610'],
214
+ },
215
+ },
216
+ {
217
+ newOffer: {
218
+ type: ['EmailV1.0'],
219
+ issuer: {
220
+ id: 'did to be determined at runtime',
221
+ },
222
+ credentialSubject: {
223
+ vendorUserId: 'john.smith@sap.com',
224
+ email: 'john.smith@sap.com',
225
+ },
226
+ offerId: '200',
227
+ },
228
+ newExchange: {
229
+ type: 'ISSUING',
230
+ identityMatcherValues: ['+9711234567'],
231
+ },
232
+ },
233
+ ],
234
+ });
235
+ });
236
+
237
+ it('should load the templates and use offerIds from the csv using phone for identifier matching using col name', async () => {
238
+ const options = {
239
+ csvFilename: path.join(__dirname, 'data/batch-vars-offerids.csv'),
240
+ offerTemplateFilename: path.join(
241
+ __dirname,
242
+ 'data/email-offer.template.json'
243
+ ),
244
+ tenant: 'foo',
245
+ termsUrl: 'http://example.com/terms.html',
246
+ idCredentialType: 'PhoneV1.0',
247
+ identifierMatchColumn: 'phone',
248
+ new: true,
249
+ dryrun: true,
250
+ };
251
+
252
+ const updates = await runBatchIssuing(options);
253
+ expect(
254
+ new Date(updates.disclosureRequest.activationDate).getTime()
255
+ ).toBeLessThan(Date.now());
256
+
257
+ expect(updates).toEqual({
258
+ disclosureRequest: {
259
+ configurationType: 'issuing',
260
+ vendorEndpoint: 'integrated-issuing-identification',
261
+ types: [
262
+ {
263
+ type: 'PhoneV1.0',
264
+ },
265
+ ],
266
+ identityMatchers: {
267
+ rules: [
268
+ {
269
+ valueIndex: 1,
270
+ path: ['$.phones'],
271
+ rule: 'pick',
272
+ },
273
+ ],
274
+ vendorUserIdIndex: 0,
275
+ },
276
+ vendorDisclosureId: expect.any(Number),
277
+ setIssuingDefault: true,
278
+ duration: '1h', // 1 hour by default
279
+ offerMode: 'preloaded',
280
+ purpose: 'Issuing Career Credential', // by default have a generic message
281
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
282
+ termsUrl: 'http://example.com/terms.html',
283
+ authTokenExpiresIn: 525600,
284
+ },
285
+ newExchangeOffers: [
286
+ {
287
+ newOffer: {
288
+ type: ['EmailV1.0'],
289
+ issuer: {
290
+ id: 'did to be determined at runtime',
291
+ },
292
+ credentialSubject: {
293
+ vendorUserId: 'joan.lee@sap.com',
294
+ email: 'joan.lee@sap.com',
295
+ },
296
+ offerId: '100',
297
+ },
298
+ newExchange: {
299
+ type: 'ISSUING',
300
+ identityMatcherValues: ['+16478275610'],
301
+ },
302
+ },
303
+ {
304
+ newOffer: {
305
+ type: ['EmailV1.0'],
306
+ issuer: {
307
+ id: 'did to be determined at runtime',
308
+ },
309
+ credentialSubject: {
310
+ vendorUserId: 'john.smith@sap.com',
311
+ email: 'john.smith@sap.com',
312
+ },
313
+ offerId: '200',
314
+ },
315
+ newExchange: {
316
+ type: 'ISSUING',
317
+ identityMatcherValues: ['+9711234567'],
318
+ },
319
+ },
320
+ ],
321
+ });
322
+ });
323
+
324
+ it('should load the templates and use offerIds from the csv and use phone as the vendorUserId', async () => {
325
+ const options = {
326
+ csvFilename: path.join(__dirname, 'data/batch-vars-offerids.csv'),
327
+ offerTemplateFilename: path.join(
328
+ __dirname,
329
+ 'data/email-offer.template.json'
330
+ ),
331
+ tenant: 'foo',
332
+ termsUrl: 'http://example.com/terms.html',
333
+ idCredentialType: 'EmailV1.0',
334
+ vendorUseridColumn: 1,
335
+ new: true,
336
+ dryrun: true,
337
+ };
338
+
339
+ const updates = await runBatchIssuing(options);
340
+ expect(
341
+ new Date(updates.disclosureRequest.activationDate).getTime()
342
+ ).toBeLessThan(Date.now());
343
+
344
+ expect(updates).toEqual({
345
+ disclosureRequest: {
346
+ configurationType: 'issuing',
347
+ vendorEndpoint: 'integrated-issuing-identification',
348
+ types: [
349
+ {
350
+ type: 'EmailV1.0',
351
+ },
352
+ ],
353
+ identityMatchers: {
354
+ rules: [
355
+ {
356
+ valueIndex: 0,
357
+ path: ['$.emails'],
358
+ rule: 'pick',
359
+ },
360
+ ],
361
+ vendorUserIdIndex: 1,
362
+ },
363
+ vendorDisclosureId: expect.any(Number),
364
+ setIssuingDefault: true,
365
+ duration: '1h', // 1 hour by default
366
+ offerMode: 'preloaded',
367
+ purpose: 'Issuing Career Credential', // by default have a generic message
368
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
369
+ termsUrl: 'http://example.com/terms.html',
370
+ authTokenExpiresIn: 525600,
371
+ },
372
+ newExchangeOffers: [
373
+ {
374
+ newOffer: {
375
+ type: ['EmailV1.0'],
376
+ issuer: {
377
+ id: 'did to be determined at runtime',
378
+ },
379
+ credentialSubject: {
380
+ vendorUserId: '+16478275610',
381
+ email: 'joan.lee@sap.com',
382
+ },
383
+
384
+ offerId: '100',
385
+ },
386
+ newExchange: {
387
+ type: 'ISSUING',
388
+ identityMatcherValues: ['joan.lee@sap.com'],
389
+ },
390
+ },
391
+ {
392
+ newOffer: {
393
+ type: ['EmailV1.0'],
394
+ issuer: {
395
+ id: 'did to be determined at runtime',
396
+ },
397
+ credentialSubject: {
398
+ vendorUserId: '+9711234567',
399
+ email: 'john.smith@sap.com',
400
+ },
401
+
402
+ offerId: '200',
403
+ },
404
+ newExchange: {
405
+ type: 'ISSUING',
406
+ identityMatcherValues: ['john.smith@sap.com'],
407
+ },
408
+ },
409
+ ],
410
+ });
411
+ });
412
+ it('should load the templates and use offerIds by phone from the csv', async () => {
413
+ const options = {
414
+ csvFilename: path.join(__dirname, 'data/phones-batch-vars-offerids.csv'),
415
+ offerTemplateFilename: path.join(
416
+ __dirname,
417
+ 'data/phone-offer.template.json'
418
+ ),
419
+ tenant: 'foo',
420
+ termsUrl: 'http://example.com/terms.html',
421
+ idCredentialType: 'PhoneV1.0',
422
+ new: true,
423
+ dryrun: true,
424
+ };
425
+
426
+ const updates = await runBatchIssuing(options);
427
+ expect(
428
+ new Date(updates.disclosureRequest.activationDate).getTime()
429
+ ).toBeLessThan(Date.now());
430
+
431
+ expect(updates).toEqual({
432
+ disclosureRequest: {
433
+ configurationType: 'issuing',
434
+ vendorEndpoint: 'integrated-issuing-identification',
435
+ types: [
436
+ {
437
+ type: 'PhoneV1.0',
438
+ },
439
+ ],
440
+ identityMatchers: {
441
+ rules: [
442
+ {
443
+ valueIndex: 0,
444
+ path: ['$.phones'],
445
+ rule: 'pick',
446
+ },
447
+ ],
448
+ vendorUserIdIndex: 0,
449
+ },
450
+ vendorDisclosureId: expect.any(Number),
451
+ setIssuingDefault: true,
452
+ duration: '1h', // 1 hour by default
453
+ offerMode: 'preloaded',
454
+ purpose: 'Issuing Career Credential', // by default have a generic message
455
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
456
+ termsUrl: 'http://example.com/terms.html',
457
+ authTokenExpiresIn: 525600,
458
+ },
459
+ newExchangeOffers: [
460
+ {
461
+ newOffer: {
462
+ type: ['PhoneV1.0'],
463
+ issuer: {
464
+ id: 'did to be determined at runtime',
465
+ },
466
+ credentialSubject: {
467
+ vendorUserId: '+1234567890',
468
+ phone: '+1234567890',
469
+ },
470
+
471
+ offerId: '100',
472
+ },
473
+ newExchange: {
474
+ type: 'ISSUING',
475
+ identityMatcherValues: ['+1234567890'],
476
+ },
477
+ },
478
+ {
479
+ newOffer: {
480
+ type: ['PhoneV1.0'],
481
+ issuer: {
482
+ id: 'did to be determined at runtime',
483
+ },
484
+ credentialSubject: {
485
+ vendorUserId: '+2345678901',
486
+ phone: '+2345678901',
487
+ },
488
+
489
+ offerId: '200',
490
+ },
491
+ newExchange: {
492
+ type: 'ISSUING',
493
+ identityMatcherValues: ['+2345678901'],
494
+ },
495
+ },
496
+ ],
497
+ });
498
+ });
499
+ it('should load the templates and use offerIds by driver license from the csv', async () => {
500
+ const options = {
501
+ csvFilename: path.join(__dirname, 'data/driver-license-variables.csv'),
502
+ offerTemplateFilename: path.join(
503
+ __dirname,
504
+ 'data/driver-license-offer.template.json'
505
+ ),
506
+ tenant: 'foo',
507
+ termsUrl: 'http://example.com/terms.html',
508
+ idCredentialType: 'DriversLicenseV1.0',
509
+ new: true,
510
+ dryrun: true,
511
+ };
512
+
513
+ const updates = await runBatchIssuing(options);
514
+ expect(
515
+ new Date(updates.disclosureRequest.activationDate).getTime()
516
+ ).toBeLessThan(Date.now());
517
+
518
+ expect(updates).toEqual({
519
+ disclosureRequest: {
520
+ configurationType: 'issuing',
521
+ vendorEndpoint: 'integrated-issuing-identification',
522
+ types: [
523
+ {
524
+ type: 'DriversLicenseV1.0',
525
+ },
526
+ ],
527
+ identityMatchers: {
528
+ rules: [
529
+ {
530
+ valueIndex: 0,
531
+ path: ['$.idDocumentCredentials[*].credentialSubject.identifier'],
532
+ rule: 'pick',
533
+ },
534
+ ],
535
+ vendorUserIdIndex: 0,
536
+ },
537
+ vendorDisclosureId: expect.any(Number),
538
+ setIssuingDefault: true,
539
+ duration: '1h', // 1 hour by default
540
+ offerMode: 'preloaded',
541
+ purpose: 'Issuing Career Credential', // by default have a generic message
542
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
543
+ termsUrl: 'http://example.com/terms.html',
544
+ authTokenExpiresIn: 525600,
545
+ },
546
+ newExchangeOffers: [
547
+ {
548
+ newOffer: {
549
+ type: ['DriversLicenseV1.0'],
550
+ issuer: {
551
+ id: 'did to be determined at runtime',
552
+ },
553
+ credentialSubject: {
554
+ vendorUserId: 'vm123456',
555
+ identifier: 'vm123456',
556
+ },
557
+
558
+ offerId: expect.any(String),
559
+ },
560
+ newExchange: {
561
+ type: 'ISSUING',
562
+ identityMatcherValues: ['vm123456'],
563
+ },
564
+ },
565
+ {
566
+ newOffer: {
567
+ type: ['DriversLicenseV1.0'],
568
+ issuer: {
569
+ id: 'did to be determined at runtime',
570
+ },
571
+ credentialSubject: {
572
+ vendorUserId: 'as4523456',
573
+ identifier: 'as4523456',
574
+ },
575
+
576
+ offerId: expect.any(String),
577
+ },
578
+ newExchange: {
579
+ type: 'ISSUING',
580
+ identityMatcherValues: ['as4523456'],
581
+ },
582
+ },
583
+ ],
584
+ });
585
+ });
586
+
587
+ it('should load the templates and use offerIds by IdDocument from the csv', async () => {
588
+ const options = {
589
+ csvFilename: path.join(__dirname, 'data/id-document-variables.csv'),
590
+ offerTemplateFilename: path.join(
591
+ __dirname,
592
+ 'data/id-document-offer.template.json'
593
+ ),
594
+ tenant: 'foo',
595
+ termsUrl: 'http://example.com/terms.html',
596
+ idCredentialType: 'IdDocumentV1.0',
597
+ new: true,
598
+ dryrun: true,
599
+ };
600
+
601
+ const updates = await runBatchIssuing(options);
602
+ expect(
603
+ new Date(updates.disclosureRequest.activationDate).getTime()
604
+ ).toBeLessThan(Date.now());
605
+
606
+ expect(updates).toEqual({
607
+ disclosureRequest: {
608
+ configurationType: 'issuing',
609
+ vendorEndpoint: 'integrated-issuing-identification',
610
+ types: [
611
+ {
612
+ type: 'IdDocumentV1.0',
613
+ },
614
+ ],
615
+ identityMatchers: {
616
+ rules: [
617
+ {
618
+ valueIndex: 0,
619
+ path: ['$.idDocumentCredentials[*].credentialSubject.identifier'],
620
+ rule: 'pick',
621
+ },
622
+ ],
623
+ vendorUserIdIndex: 0,
624
+ },
625
+ vendorDisclosureId: expect.any(Number),
626
+ setIssuingDefault: true,
627
+ duration: '1h', // 1 hour by default
628
+ offerMode: 'preloaded',
629
+ purpose: 'Issuing Career Credential', // by default have a generic message
630
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
631
+ termsUrl: 'http://example.com/terms.html',
632
+ authTokenExpiresIn: 525600,
633
+ },
634
+ newExchangeOffers: [
635
+ {
636
+ newOffer: {
637
+ type: ['IdDocumentV1.0'],
638
+ issuer: {
639
+ id: 'did to be determined at runtime',
640
+ },
641
+ credentialSubject: {
642
+ vendorUserId: 'BR514345',
643
+ identifier: 'BR514345',
644
+ },
645
+
646
+ offerId: expect.any(String),
647
+ },
648
+ newExchange: {
649
+ type: 'ISSUING',
650
+ identityMatcherValues: ['BR514345'],
651
+ },
652
+ },
653
+ {
654
+ newOffer: {
655
+ type: ['IdDocumentV1.0'],
656
+ issuer: {
657
+ id: 'did to be determined at runtime',
658
+ },
659
+ credentialSubject: {
660
+ vendorUserId: 'BT678543',
661
+ identifier: 'BT678543',
662
+ },
663
+
664
+ offerId: expect.any(String),
665
+ },
666
+ newExchange: {
667
+ type: 'ISSUING',
668
+ identityMatcherValues: ['BT678543'],
669
+ },
670
+ },
671
+ ],
672
+ });
673
+ });
674
+
675
+ it('should load the templates and use offerIds by residentPermit from the csv', async () => {
676
+ const options = {
677
+ csvFilename: path.join(__dirname, 'data/resident-permit-variables.csv'),
678
+ offerTemplateFilename: path.join(
679
+ __dirname,
680
+ 'data/resident-permit-offer.template.json'
681
+ ),
682
+ tenant: 'foo',
683
+ termsUrl: 'http://example.com/terms.html',
684
+ idCredentialType: 'ResidentPermitV1.0',
685
+ new: true,
686
+ dryrun: true,
687
+ };
688
+
689
+ const updates = await runBatchIssuing(options);
690
+ expect(
691
+ new Date(updates.disclosureRequest.activationDate).getTime()
692
+ ).toBeLessThan(Date.now());
693
+
694
+ expect(updates).toEqual({
695
+ disclosureRequest: {
696
+ configurationType: 'issuing',
697
+ vendorEndpoint: 'integrated-issuing-identification',
698
+ types: [
699
+ {
700
+ type: 'ResidentPermitV1.0',
701
+ },
702
+ ],
703
+ identityMatchers: {
704
+ rules: [
705
+ {
706
+ valueIndex: 0,
707
+ path: ['$.idDocumentCredentials[*].credentialSubject.identifier'],
708
+ rule: 'pick',
709
+ },
710
+ ],
711
+ vendorUserIdIndex: 0,
712
+ },
713
+ vendorDisclosureId: expect.any(Number),
714
+ setIssuingDefault: true,
715
+ duration: '1h', // 1 hour by default
716
+ offerMode: 'preloaded',
717
+ purpose: 'Issuing Career Credential', // by default have a generic message
718
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
719
+ termsUrl: 'http://example.com/terms.html',
720
+ authTokenExpiresIn: 525600,
721
+ },
722
+ newExchangeOffers: [
723
+ {
724
+ newOffer: {
725
+ type: ['ResidentPermitV1.0'],
726
+ issuer: {
727
+ id: 'did to be determined at runtime',
728
+ },
729
+ credentialSubject: {
730
+ vendorUserId: 'ER514345',
731
+ identifier: 'ER514345',
732
+ },
733
+
734
+ offerId: expect.any(String),
735
+ },
736
+ newExchange: {
737
+ type: 'ISSUING',
738
+ identityMatcherValues: ['ER514345'],
739
+ },
740
+ },
741
+ {
742
+ newOffer: {
743
+ type: ['ResidentPermitV1.0'],
744
+ issuer: {
745
+ id: 'did to be determined at runtime',
746
+ },
747
+ credentialSubject: {
748
+ vendorUserId: 'RT678543',
749
+ identifier: 'RT678543',
750
+ },
751
+
752
+ offerId: expect.any(String),
753
+ },
754
+ newExchange: {
755
+ type: 'ISSUING',
756
+ identityMatcherValues: ['RT678543'],
757
+ },
758
+ },
759
+ ],
760
+ });
761
+ });
762
+
763
+ it('should load the templates and use offerIds by passport from the csv', async () => {
764
+ const options = {
765
+ csvFilename: path.join(__dirname, 'data/passport-variables.csv'),
766
+ offerTemplateFilename: path.join(
767
+ __dirname,
768
+ 'data/passport-offer.template.json'
769
+ ),
770
+ tenant: 'foo',
771
+ termsUrl: 'http://example.com/terms.html',
772
+ idCredentialType: 'PassportV1.0',
773
+ new: true,
774
+ dryrun: true,
775
+ };
776
+
777
+ const updates = await runBatchIssuing(options);
778
+ expect(
779
+ new Date(updates.disclosureRequest.activationDate).getTime()
780
+ ).toBeLessThan(Date.now());
781
+
782
+ expect(updates).toEqual({
783
+ disclosureRequest: {
784
+ configurationType: 'issuing',
785
+ vendorEndpoint: 'integrated-issuing-identification',
786
+ types: [
787
+ {
788
+ type: 'PassportV1.0',
789
+ },
790
+ ],
791
+ identityMatchers: {
792
+ rules: [
793
+ {
794
+ valueIndex: 0,
795
+ path: ['$.idDocumentCredentials[*].credentialSubject.identifier'],
796
+ rule: 'pick',
797
+ },
798
+ ],
799
+ vendorUserIdIndex: 0,
800
+ },
801
+ vendorDisclosureId: expect.any(Number),
802
+ setIssuingDefault: true,
803
+ duration: '1h', // 1 hour by default
804
+ offerMode: 'preloaded',
805
+ purpose: 'Issuing Career Credential', // by default have a generic message
806
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
807
+ termsUrl: 'http://example.com/terms.html',
808
+ authTokenExpiresIn: 525600,
809
+ },
810
+ newExchangeOffers: [
811
+ {
812
+ newOffer: {
813
+ type: ['PassportV1.0'],
814
+ issuer: {
815
+ id: 'did to be determined at runtime',
816
+ },
817
+ credentialSubject: {
818
+ vendorUserId: 'ER514345',
819
+ identifier: 'ER514345',
820
+ },
821
+
822
+ offerId: expect.any(String),
823
+ },
824
+ newExchange: {
825
+ type: 'ISSUING',
826
+ identityMatcherValues: ['ER514345'],
827
+ },
828
+ },
829
+ {
830
+ newOffer: {
831
+ type: ['PassportV1.0'],
832
+ issuer: {
833
+ id: 'did to be determined at runtime',
834
+ },
835
+ credentialSubject: {
836
+ vendorUserId: 'RT678543',
837
+ identifier: 'RT678543',
838
+ },
839
+
840
+ offerId: expect.any(String),
841
+ },
842
+ newExchange: {
843
+ type: 'ISSUING',
844
+ identityMatcherValues: ['RT678543'],
845
+ },
846
+ },
847
+ ],
848
+ });
849
+ });
850
+
851
+ it('should load the templates and use offerIds by NationalIdCard from the csv', async () => {
852
+ const options = {
853
+ csvFilename: path.join(__dirname, 'data/national-id-card-variables.csv'),
854
+ offerTemplateFilename: path.join(
855
+ __dirname,
856
+ 'data/national-id-card-offer.template.json'
857
+ ),
858
+ tenant: 'foo',
859
+ termsUrl: 'http://example.com/terms.html',
860
+ idCredentialType: 'NationalIdCardV1.0',
861
+ new: true,
862
+ dryrun: true,
863
+ };
864
+
865
+ const updates = await runBatchIssuing(options);
866
+ expect(
867
+ new Date(updates.disclosureRequest.activationDate).getTime()
868
+ ).toBeLessThan(Date.now());
869
+
870
+ expect(updates).toEqual({
871
+ disclosureRequest: {
872
+ configurationType: 'issuing',
873
+ vendorEndpoint: 'integrated-issuing-identification',
874
+ types: [
875
+ {
876
+ type: 'NationalIdCardV1.0',
877
+ },
878
+ ],
879
+ identityMatchers: {
880
+ rules: [
881
+ {
882
+ valueIndex: 0,
883
+ path: ['$.idDocumentCredentials[*].credentialSubject.identifier'],
884
+ rule: 'pick',
885
+ },
886
+ ],
887
+ vendorUserIdIndex: 0,
888
+ },
889
+ vendorDisclosureId: expect.any(Number),
890
+ setIssuingDefault: true,
891
+ duration: '1h', // 1 hour by default
892
+ offerMode: 'preloaded',
893
+ purpose: 'Issuing Career Credential', // by default have a generic message
894
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
895
+ termsUrl: 'http://example.com/terms.html',
896
+ authTokenExpiresIn: 525600,
897
+ },
898
+ newExchangeOffers: [
899
+ {
900
+ newOffer: {
901
+ type: ['NationalIdCardV1.0'],
902
+ issuer: {
903
+ id: 'did to be determined at runtime',
904
+ },
905
+ credentialSubject: {
906
+ vendorUserId: 'BR514345',
907
+ identifier: 'BR514345',
908
+ },
909
+
910
+ offerId: expect.any(String),
911
+ },
912
+ newExchange: {
913
+ type: 'ISSUING',
914
+ identityMatcherValues: ['BR514345'],
915
+ },
916
+ },
917
+ {
918
+ newOffer: {
919
+ type: ['NationalIdCardV1.0'],
920
+ issuer: {
921
+ id: 'did to be determined at runtime',
922
+ },
923
+ credentialSubject: {
924
+ vendorUserId: 'BT678543',
925
+ identifier: 'BT678543',
926
+ },
927
+
928
+ offerId: expect.any(String),
929
+ },
930
+ newExchange: {
931
+ type: 'ISSUING',
932
+ identityMatcherValues: ['BT678543'],
933
+ },
934
+ },
935
+ ],
936
+ });
937
+ });
938
+
939
+ it('should load the templates and use offerIds by ProofOfAge from the csv', async () => {
940
+ const options = {
941
+ csvFilename: path.join(__dirname, 'data/proof-of-age-variables.csv'),
942
+ offerTemplateFilename: path.join(
943
+ __dirname,
944
+ 'data/proof-of-age-offer.template.json'
945
+ ),
946
+ tenant: 'foo',
947
+ termsUrl: 'http://example.com/terms.html',
948
+ idCredentialType: 'ProofOfAgeV1.0',
949
+ new: true,
950
+ dryrun: true,
951
+ };
952
+
953
+ const updates = await runBatchIssuing(options);
954
+ expect(
955
+ new Date(updates.disclosureRequest.activationDate).getTime()
956
+ ).toBeLessThan(Date.now());
957
+
958
+ expect(updates).toEqual({
959
+ disclosureRequest: {
960
+ configurationType: 'issuing',
961
+ vendorEndpoint: 'integrated-issuing-identification',
962
+ types: [
963
+ {
964
+ type: 'ProofOfAgeV1.0',
965
+ },
966
+ ],
967
+ identityMatchers: {
968
+ rules: [
969
+ {
970
+ valueIndex: 0,
971
+ path: ['$.idDocumentCredentials[*].credentialSubject.identifier'],
972
+ rule: 'pick',
973
+ },
974
+ ],
975
+ vendorUserIdIndex: 0,
976
+ },
977
+ vendorDisclosureId: expect.any(Number),
978
+ setIssuingDefault: true,
979
+ duration: '1h', // 1 hour by default
980
+ offerMode: 'preloaded',
981
+ purpose: 'Issuing Career Credential', // by default have a generic message
982
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
983
+ termsUrl: 'http://example.com/terms.html',
984
+ authTokenExpiresIn: 525600,
985
+ },
986
+ newExchangeOffers: [
987
+ {
988
+ newOffer: {
989
+ type: ['ProofOfAgeV1.0'],
990
+ issuer: {
991
+ id: 'did to be determined at runtime',
992
+ },
993
+ credentialSubject: {
994
+ vendorUserId: 'ER514345',
995
+ identifier: 'ER514345',
996
+ },
997
+
998
+ offerId: expect.any(String),
999
+ },
1000
+ newExchange: {
1001
+ type: 'ISSUING',
1002
+ identityMatcherValues: ['ER514345'],
1003
+ },
1004
+ },
1005
+ {
1006
+ newOffer: {
1007
+ type: ['ProofOfAgeV1.0'],
1008
+ issuer: {
1009
+ id: 'did to be determined at runtime',
1010
+ },
1011
+ credentialSubject: {
1012
+ vendorUserId: 'RT678543',
1013
+ identifier: 'RT678543',
1014
+ },
1015
+
1016
+ offerId: expect.any(String),
1017
+ },
1018
+ newExchange: {
1019
+ type: 'ISSUING',
1020
+ identityMatcherValues: ['RT678543'],
1021
+ },
1022
+ },
1023
+ ],
1024
+ });
1025
+ });
1026
+
1027
+ it('should load the templates and csv', async () => {
1028
+ const options = {
1029
+ csvFilename: path.join(__dirname, 'data/variables.csv'),
1030
+ offerTemplateFilename: path.join(
1031
+ __dirname,
1032
+ 'data/email-offer.template.json'
1033
+ ),
1034
+ tenant: 'foo',
1035
+ termsUrl: 'http://example.com/terms.html',
1036
+ idCredentialType: 'EmailV1.0',
1037
+ vendorUseridColumn: 'email',
1038
+ new: true,
1039
+ dryrun: true,
1040
+ };
1041
+
1042
+ const updates = await runBatchIssuing(options);
1043
+ expect(
1044
+ new Date(updates.disclosureRequest.activationDate).getTime()
1045
+ ).toBeLessThan(Date.now());
1046
+
1047
+ expect(updates).toEqual({
1048
+ disclosureRequest: {
1049
+ configurationType: 'issuing',
1050
+ vendorEndpoint: 'integrated-issuing-identification',
1051
+ types: [
1052
+ {
1053
+ type: 'EmailV1.0',
1054
+ },
1055
+ ],
1056
+ identityMatchers: {
1057
+ rules: [
1058
+ {
1059
+ valueIndex: 0,
1060
+ path: ['$.emails'],
1061
+ rule: 'pick',
1062
+ },
1063
+ ],
1064
+ vendorUserIdIndex: 2,
1065
+ },
1066
+ vendorDisclosureId: expect.any(Number),
1067
+ setIssuingDefault: true,
1068
+ duration: '1h', // 1 hour by default
1069
+ offerMode: 'preloaded',
1070
+ purpose: 'Issuing Career Credential', // by default have a generic message
1071
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
1072
+ termsUrl: 'http://example.com/terms.html',
1073
+ authTokenExpiresIn: 525600,
1074
+ },
1075
+ newExchangeOffers: [
1076
+ {
1077
+ newOffer: {
1078
+ type: ['EmailV1.0'],
1079
+ issuer: {
1080
+ id: 'did to be determined at runtime',
1081
+ },
1082
+ credentialSubject: {
1083
+ vendorUserId: 'joan.lee@sap.com',
1084
+ email: 'joan.lee@sap.com',
1085
+ },
1086
+
1087
+ offerId: expect.any(String),
1088
+ },
1089
+ newExchange: {
1090
+ type: 'ISSUING',
1091
+ identityMatcherValues: ['joan.lee@sap.com'],
1092
+ },
1093
+ },
1094
+ {
1095
+ newOffer: {
1096
+ type: ['EmailV1.0'],
1097
+ issuer: {
1098
+ id: 'did to be determined at runtime',
1099
+ },
1100
+ credentialSubject: {
1101
+ vendorUserId: 'john.smith@sap.com',
1102
+ email: 'john.smith@sap.com',
1103
+ },
1104
+
1105
+ offerId: expect.any(String),
1106
+ },
1107
+ newExchange: {
1108
+ type: 'ISSUING',
1109
+ identityMatcherValues: ['john.smith@sap.com'],
1110
+ },
1111
+ },
1112
+ ],
1113
+ });
1114
+ });
1115
+
1116
+ it('should handle csv with BOM characters', async () => {
1117
+ const options = {
1118
+ csvFilename: path.join(__dirname, 'data/with-bom.csv'),
1119
+ offerTemplateFilename: path.join(
1120
+ __dirname,
1121
+ 'data/email-offer.template.json'
1122
+ ),
1123
+ tenant: 'foo',
1124
+ termsUrl: 'http://example.com/terms.html',
1125
+ idCredentialType: 'EmailV1.0',
1126
+ vendorUseridColumn: 'email',
1127
+ new: true,
1128
+ dryrun: true,
1129
+ };
1130
+
1131
+ const updates = await runBatchIssuing(options);
1132
+ expect(
1133
+ new Date(updates.disclosureRequest.activationDate).getTime()
1134
+ ).toBeLessThan(Date.now());
1135
+
1136
+ expect(updates).toEqual({
1137
+ disclosureRequest: {
1138
+ configurationType: 'issuing',
1139
+ vendorEndpoint: 'integrated-issuing-identification',
1140
+ types: [
1141
+ {
1142
+ type: 'EmailV1.0',
1143
+ },
1144
+ ],
1145
+ identityMatchers: {
1146
+ rules: [
1147
+ {
1148
+ valueIndex: 0,
1149
+ path: ['$.emails'],
1150
+ rule: 'pick',
1151
+ },
1152
+ ],
1153
+ vendorUserIdIndex: 2,
1154
+ },
1155
+ vendorDisclosureId: expect.any(Number),
1156
+ setIssuingDefault: true,
1157
+ duration: '1h', // 1 hour by default
1158
+ offerMode: 'preloaded',
1159
+ purpose: 'Issuing Career Credential', // by default have a generic message
1160
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
1161
+ termsUrl: 'http://example.com/terms.html',
1162
+ authTokenExpiresIn: 525600,
1163
+ },
1164
+ newExchangeOffers: [
1165
+ {
1166
+ newOffer: {
1167
+ type: ['EmailV1.0'],
1168
+ issuer: {
1169
+ id: 'did to be determined at runtime',
1170
+ },
1171
+ credentialSubject: {
1172
+ vendorUserId: 'joan.lee@sap.com',
1173
+ email: 'joan.lee@sap.com',
1174
+ },
1175
+
1176
+ offerId: expect.any(String),
1177
+ },
1178
+ newExchange: {
1179
+ type: 'ISSUING',
1180
+ identityMatcherValues: ['joan.lee@sap.com'],
1181
+ },
1182
+ },
1183
+ {
1184
+ newOffer: {
1185
+ type: ['EmailV1.0'],
1186
+ issuer: {
1187
+ id: 'did to be determined at runtime',
1188
+ },
1189
+ credentialSubject: {
1190
+ vendorUserId: 'john.smith@sap.com',
1191
+ email: 'john.smith@sap.com',
1192
+ },
1193
+
1194
+ offerId: expect.any(String),
1195
+ },
1196
+ newExchange: {
1197
+ type: 'ISSUING',
1198
+ identityMatcherValues: ['john.smith@sap.com'],
1199
+ },
1200
+ },
1201
+ ],
1202
+ });
1203
+ });
1204
+
1205
+ it('should load the templates and csv with vars', async () => {
1206
+ const options = {
1207
+ csvFilename: path.join(__dirname, 'data/variables.csv'),
1208
+ offerTemplateFilename: path.join(
1209
+ __dirname,
1210
+ 'data/email-offer.template.json'
1211
+ ),
1212
+ tenant: 'foo',
1213
+ termsUrl: 'http://example.com/terms.html',
1214
+ label: 'testLabel',
1215
+ offerMode: 'preloaded',
1216
+ purpose: 'Some Purpose',
1217
+ expiresInHours: 100,
1218
+ activatesInHours: 10,
1219
+ idCredentialType: 'EmailV1.0',
1220
+ vendorUseridColumn: 'email',
1221
+ new: true,
1222
+ dryrun: true,
1223
+ };
1224
+
1225
+ const updates = await runBatchIssuing(options);
1226
+
1227
+ expect(updates).toEqual({
1228
+ disclosureRequest: {
1229
+ configurationType: 'issuing',
1230
+ vendorEndpoint: 'integrated-issuing-identification',
1231
+ types: [
1232
+ {
1233
+ type: 'EmailV1.0',
1234
+ },
1235
+ ],
1236
+ identityMatchers: {
1237
+ rules: [
1238
+ {
1239
+ valueIndex: 0,
1240
+ path: ['$.emails'],
1241
+ rule: 'pick',
1242
+ },
1243
+ ],
1244
+ vendorUserIdIndex: 2,
1245
+ },
1246
+ vendorDisclosureId: expect.any(Number),
1247
+ setIssuingDefault: true,
1248
+ duration: '1h', // 1 hour by default
1249
+ offerMode: 'preloaded',
1250
+ purpose: options.purpose, // by default have a generic message
1251
+ activationDate: expect.stringMatching(ISO_DATETIME_TZ_FORMAT),
1252
+ termsUrl: 'http://example.com/terms.html',
1253
+ label: options.label,
1254
+ authTokenExpiresIn: 525600,
1255
+ },
1256
+ newExchangeOffers: [
1257
+ {
1258
+ newOffer: {
1259
+ type: ['EmailV1.0'],
1260
+ issuer: {
1261
+ id: 'did to be determined at runtime',
1262
+ },
1263
+ credentialSubject: {
1264
+ vendorUserId: 'joan.lee@sap.com',
1265
+ email: 'joan.lee@sap.com',
1266
+ },
1267
+
1268
+ offerId: expect.any(String),
1269
+ label: options.label,
1270
+ },
1271
+ newExchange: {
1272
+ type: 'ISSUING',
1273
+ identityMatcherValues: ['joan.lee@sap.com'],
1274
+ label: options.label,
1275
+ },
1276
+ },
1277
+ {
1278
+ newOffer: {
1279
+ type: ['EmailV1.0'],
1280
+ issuer: {
1281
+ id: 'did to be determined at runtime',
1282
+ },
1283
+ credentialSubject: {
1284
+ vendorUserId: 'john.smith@sap.com',
1285
+ email: 'john.smith@sap.com',
1286
+ },
1287
+
1288
+ offerId: expect.any(String),
1289
+ label: options.label,
1290
+ },
1291
+ newExchange: {
1292
+ type: 'ISSUING',
1293
+ identityMatcherValues: ['john.smith@sap.com'],
1294
+ label: options.label,
1295
+ },
1296
+ },
1297
+ ],
1298
+ });
1299
+
1300
+ expect(
1301
+ new Date(updates.disclosureRequest.activationDate).getTime()
1302
+ ).toBeGreaterThan(Date.now());
1303
+ });
1304
+
1305
+ describe('existing disclosure endpoints', () => {
1306
+ const existingDisclosures = [{ id: nanoid() }, { id: nanoid() }];
1307
+
1308
+ it('should have error if the disclosureRequest is not found', async () => {
1309
+ const agentUrl = 'https://exampleUrl';
1310
+ const tenant = '123';
1311
+ nock(agentUrl)
1312
+ .get(
1313
+ `/operator-api/v0.8/tenants/${tenant}/disclosures?vendorEndpoint=integrated-issuing-identification`
1314
+ )
1315
+ .reply(200, existingDisclosures);
1316
+
1317
+ const options = {
1318
+ csvFilename: path.join(__dirname, 'data/variables.csv'),
1319
+ offerTemplateFilename: path.join(
1320
+ __dirname,
1321
+ 'data/email-offer.template.json'
1322
+ ),
1323
+ tenant,
1324
+ termsUrl: 'http://example.com/terms.html',
1325
+ idCredentialType: 'EmailV1.0',
1326
+ disclosure: 'foo',
1327
+ dryrun: true,
1328
+ endpoint: agentUrl,
1329
+ authToken: 'fakeToken',
1330
+ };
1331
+
1332
+ await expect(() => runBatchIssuing(options)).rejects.toThrowError();
1333
+ });
1334
+
1335
+ it("should find the existing disclosure disclosureRequest with 'tenant' option", async () => {
1336
+ const agentUrl = 'https://exampleUrl';
1337
+ const tenant = '123';
1338
+ nock(agentUrl)
1339
+ .get(
1340
+ `/operator-api/v0.8/tenants/${tenant}/disclosures?vendorEndpoint=integrated-issuing-identification`
1341
+ )
1342
+ .reply(200, existingDisclosures);
1343
+
1344
+ const options = {
1345
+ csvFilename: path.join(__dirname, 'data/variables.csv'),
1346
+ offerTemplateFilename: path.join(
1347
+ __dirname,
1348
+ 'data/email-offer.template.json'
1349
+ ),
1350
+ tenant,
1351
+ termsUrl: 'http://example.com/terms.html',
1352
+ idCredentialType: 'EmailV1.0',
1353
+ vendorUseridColumn: 'email',
1354
+ disclosure: existingDisclosures[0].id,
1355
+ dryrun: true,
1356
+ endpoint: agentUrl,
1357
+ authToken: 'fakeToken',
1358
+ };
1359
+
1360
+ const updates = await runBatchIssuing(options);
1361
+ expect(updates).toEqual({
1362
+ disclosureRequest: existingDisclosures[0],
1363
+ newExchangeOffers: [
1364
+ {
1365
+ newOffer: {
1366
+ type: ['EmailV1.0'],
1367
+ issuer: {
1368
+ id: 'did to be determined at runtime',
1369
+ },
1370
+ credentialSubject: {
1371
+ vendorUserId: 'joan.lee@sap.com',
1372
+ email: 'joan.lee@sap.com',
1373
+ },
1374
+
1375
+ offerId: expect.any(String),
1376
+ },
1377
+ newExchange: {
1378
+ type: 'ISSUING',
1379
+ identityMatcherValues: ['joan.lee@sap.com'],
1380
+ },
1381
+ },
1382
+ {
1383
+ newOffer: {
1384
+ type: ['EmailV1.0'],
1385
+ issuer: {
1386
+ id: 'did to be determined at runtime',
1387
+ },
1388
+ credentialSubject: {
1389
+ vendorUserId: 'john.smith@sap.com',
1390
+ email: 'john.smith@sap.com',
1391
+ },
1392
+
1393
+ offerId: expect.any(String),
1394
+ },
1395
+ newExchange: {
1396
+ type: 'ISSUING',
1397
+ identityMatcherValues: ['john.smith@sap.com'],
1398
+ },
1399
+ },
1400
+ ],
1401
+ });
1402
+ });
1403
+ it("should find the existing disclosure disclosureRequest with 'did' option", async () => {
1404
+ const agentUrl = 'https://exampleUrl';
1405
+ const did = 'did:sap:123';
1406
+ nock(agentUrl)
1407
+ .get(
1408
+ `/operator-api/v0.8/tenants/${did}/disclosures?vendorEndpoint=integrated-issuing-identification`
1409
+ )
1410
+ .reply(200, existingDisclosures);
1411
+
1412
+ const options = {
1413
+ csvFilename: path.join(__dirname, 'data/variables.csv'),
1414
+ offerTemplateFilename: path.join(
1415
+ __dirname,
1416
+ 'data/email-offer.template.json'
1417
+ ),
1418
+ did,
1419
+ termsUrl: 'http://example.com/terms.html',
1420
+ idCredentialType: 'EmailV1.0',
1421
+ vendorUseridColumn: 'email',
1422
+ disclosure: existingDisclosures[0].id,
1423
+ dryrun: true,
1424
+ endpoint: agentUrl,
1425
+ authToken: 'fakeToken',
1426
+ };
1427
+
1428
+ const updates = await runBatchIssuing(options);
1429
+ expect(updates).toEqual({
1430
+ disclosureRequest: existingDisclosures[0],
1431
+ newExchangeOffers: [
1432
+ {
1433
+ newOffer: {
1434
+ type: ['EmailV1.0'],
1435
+ issuer: {
1436
+ id: did,
1437
+ },
1438
+ credentialSubject: {
1439
+ vendorUserId: 'joan.lee@sap.com',
1440
+ email: 'joan.lee@sap.com',
1441
+ },
1442
+
1443
+ offerId: expect.any(String),
1444
+ },
1445
+ newExchange: {
1446
+ type: 'ISSUING',
1447
+ identityMatcherValues: ['joan.lee@sap.com'],
1448
+ },
1449
+ },
1450
+ {
1451
+ newOffer: {
1452
+ type: ['EmailV1.0'],
1453
+ issuer: {
1454
+ id: did,
1455
+ },
1456
+ credentialSubject: {
1457
+ vendorUserId: 'john.smith@sap.com',
1458
+ email: 'john.smith@sap.com',
1459
+ },
1460
+
1461
+ offerId: expect.any(String),
1462
+ },
1463
+ newExchange: {
1464
+ type: 'ISSUING',
1465
+ identityMatcherValues: ['john.smith@sap.com'],
1466
+ },
1467
+ },
1468
+ ],
1469
+ });
1470
+ });
1471
+ });
1472
+
1473
+ describe('https.rejectUnauthorized test suite', () => {
1474
+ beforeEach(() => {
1475
+ env.NODE_TLS_REJECT_UNAUTHORIZED = '';
1476
+ });
1477
+ afterAll(() => {
1478
+ env.NODE_TLS_REJECT_UNAUTHORIZED = '';
1479
+ });
1480
+
1481
+ it('when NODE_TLS_REJECT_UNAUTHORIZED is "0", got client should be setup with https.rejectUnauthorized false', async () => {
1482
+ env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
1483
+ const options = {
1484
+ csvFilename: path.join(__dirname, 'data/batch-vars-offerids.csv'),
1485
+ offerTemplateFilename: path.join(
1486
+ __dirname,
1487
+ 'data/email-offer.template.json'
1488
+ ),
1489
+ tenant: 'foo',
1490
+ termsUrl: 'http://example.com/terms.html',
1491
+ new: true,
1492
+ dryrun: true,
1493
+ };
1494
+ await runBatchIssuing(options);
1495
+ expect(gotExtendSpy.mock.calls).toEqual([
1496
+ [{ https: { rejectUnauthorized: false } }],
1497
+ ]);
1498
+ });
1499
+
1500
+ it('when NODE_TLS_REJECT_UNAUTHORIZED is value other than "0", got client should be setup without https.rejectUnauthorized', async () => {
1501
+ const options = {
1502
+ csvFilename: path.join(__dirname, 'data/batch-vars-offerids.csv'),
1503
+ offerTemplateFilename: path.join(
1504
+ __dirname,
1505
+ 'data/email-offer.template.json'
1506
+ ),
1507
+ tenant: 'foo',
1508
+ termsUrl: 'http://example.com/terms.html',
1509
+ new: true,
1510
+ dryrun: true,
1511
+ };
1512
+ env.NODE_TLS_REJECT_UNAUTHORIZED = 'false';
1513
+ await runBatchIssuing(options);
1514
+ env.NODE_TLS_REJECT_UNAUTHORIZED = '';
1515
+ await runBatchIssuing(options);
1516
+ env.NODE_TLS_REJECT_UNAUTHORIZED = 'undefined';
1517
+ await runBatchIssuing(options);
1518
+ delete env.NODE_TLS_REJECT_UNAUTHORIZED;
1519
+ await runBatchIssuing(options);
1520
+ expect(gotExtendSpy.mock.calls).toEqual([[{}], [{}], [{}], [{}]]);
1521
+ });
1522
+ });
1523
+ });