cozy-harvest-lib 7.2.4 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [7.3.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@7.2.4...cozy-harvest-lib@7.3.0) (2022-02-18)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **deps:** pin dependencies ([e53d065](https://github.com/cozy/cozy-libs/commit/e53d065090224ea340b2c25c3afd14f223f4d119))
12
+
13
+
14
+ ### Features
15
+
16
+ * Do not allow multiple accounts with same BI connection ID ([55b5f23](https://github.com/cozy/cozy-libs/commit/55b5f23f0adbc308c3b70fa287c3938ee1b0a4cc))
17
+
18
+
19
+
20
+
21
+
6
22
  ## [7.2.4](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@7.2.3...cozy-harvest-lib@7.2.4) (2022-02-15)
7
23
 
8
24
 
@@ -36,7 +36,8 @@ var TERMS_VERSION_MISMATCH = 'TERMS_VERSION_MISMATCH';
36
36
  var UNKNOWN_ERROR = 'UNKNOWN_ERROR';
37
37
  var USER_ACTION_NEEDED = 'USER_ACTION_NEEDED';
38
38
  var VENDOR_DOWN = 'VENDOR_DOWN';
39
- var KNOWN_ERRORS = [CHALLENGE_ASKED, DISK_QUOTA_EXCEEDED, LOGIN_FAILED, MAINTENANCE, NOT_EXISTING_DIRECTORY, TERMS_VERSION_MISMATCH, USER_ACTION_NEEDED, VENDOR_DOWN];
39
+ var ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED = 'ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED';
40
+ var KNOWN_ERRORS = [CHALLENGE_ASKED, DISK_QUOTA_EXCEEDED, LOGIN_FAILED, MAINTENANCE, NOT_EXISTING_DIRECTORY, TERMS_VERSION_MISMATCH, USER_ACTION_NEEDED, VENDOR_DOWN, ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED];
40
41
  var USER_ERRORS = [CHALLENGE_ASKED, DISK_QUOTA_EXCEEDED, LOGIN_FAILED, NOT_EXISTING_DIRECTORY, USER_ACTION_NEEDED];
41
42
  var sanitizeAccountIdentifierRx = /\//g;
42
43
  /**
@@ -204,6 +204,10 @@
204
204
  "VENDOR_DOWN.LINXO_DOWN": {
205
205
  "title": "Unavailable service",
206
206
  "description": "It seems that we are experiencing overload with our bank konnectors at the moment. Please rerun the connector later."
207
+ },
208
+ "ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED": {
209
+ "title": "This account already exists",
210
+ "description": "You already have configured an account with these identifiers."
207
211
  }
208
212
  }
209
213
  },
@@ -204,6 +204,10 @@
204
204
  "VENDOR_DOWN.LINXO_DOWN": {
205
205
  "title": "Service non disponible",
206
206
  "description": "Il semble que le service [%{name}](%{link}) ne nous ait pas répondu dans les temps. Vous pouvez tenter de le relancer manuellement maintenant ou ultérieurement."
207
+ },
208
+ "ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED": {
209
+ "title": "Ce compte est déjà configuré",
210
+ "description": "Vous avez déjà configuré un compte avec ces identifiants."
207
211
  }
208
212
  }
209
213
  },
@@ -1,3 +1,4 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
1
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
3
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
4
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
@@ -23,7 +24,9 @@ import get from 'lodash/get';
23
24
  import omit from 'lodash/omit';
24
25
  import clone from 'lodash/clone';
25
26
  import set from 'lodash/set';
27
+ import keyBy from 'lodash/keyBy';
26
28
  import defaults from 'lodash/defaults';
29
+ import { Q } from 'cozy-client';
27
30
  import { waitForRealtimeEvent } from './jobUtils';
28
31
  import { getBIConnection, createBIConnection, updateBIConnection, getBIUserConfig, updateBIUserConfig, setBIConnectionSyncStatus } from './bi-http';
29
32
  import assert from '../assert';
@@ -51,7 +54,8 @@ var getBIIdFromContract = function getBIIdFromContract(bankAccount) {
51
54
 
52
55
 
53
56
  var extraBIErrorMap = {
54
- config: 'LOGIN_FAILED'
57
+ config: 'LOGIN_FAILED',
58
+ ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED: 'ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED'
55
59
  };
56
60
  /**
57
61
  * Converts and chains error
@@ -144,7 +148,7 @@ export var getBIConfig = function getBIConfig(flow) {
144
148
 
145
149
  export var createOrUpdateBIConnection = /*#__PURE__*/function () {
146
150
  var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref3) {
147
- var account, client, konnector, flow, connId, biConfig, tempToken, config, credentials, credsToSend, biConnection, connection;
151
+ var account, client, konnector, flow, connId, biConfig, tempToken, config, credentials, credsToSend, biConnection, isUpdate, connection, sameAccount, err;
148
152
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
149
153
  while (1) {
150
154
  switch (_context2.prev = _context2.next) {
@@ -212,25 +216,52 @@ export var createOrUpdateBIConnection = /*#__PURE__*/function () {
212
216
 
213
217
  case 31:
214
218
  logger.info('Creating or updating connection...');
215
- _context2.next = 34;
216
- return connId ? updateBIConnection(config, connId, credsToSend, tempToken) : createBIConnection(config, credsToSend, tempToken);
219
+ isUpdate = Boolean(connId);
220
+ _context2.next = 35;
221
+ return isUpdate ? updateBIConnection(config, connId, credsToSend, tempToken) : createBIConnection(config, credsToSend, tempToken);
217
222
 
218
- case 34:
223
+ case 35:
219
224
  connection = _context2.sent;
225
+
226
+ if (isUpdate) {
227
+ _context2.next = 44;
228
+ break;
229
+ }
230
+
231
+ _context2.next = 39;
232
+ return findAccountWithBiConnection({
233
+ client: client,
234
+ konnector: konnector,
235
+ connectionId: connection.id
236
+ });
237
+
238
+ case 39:
239
+ sameAccount = _context2.sent;
240
+
241
+ if (!sameAccount) {
242
+ _context2.next = 44;
243
+ break;
244
+ }
245
+
246
+ err = new KonnectorJobError('ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED');
247
+ err.accountId = sameAccount._id;
248
+ throw err;
249
+
250
+ case 44:
220
251
  logger.info("Created or updated connection ".concat(connection.id));
221
252
  return _context2.abrupt("return", connection);
222
253
 
223
- case 39:
224
- _context2.prev = 39;
254
+ case 48:
255
+ _context2.prev = 48;
225
256
  _context2.t1 = _context2["catch"](15);
226
257
  return _context2.abrupt("return", convertBIErrortoKonnectorJobError(_context2.t1));
227
258
 
228
- case 42:
259
+ case 51:
229
260
  case "end":
230
261
  return _context2.stop();
231
262
  }
232
263
  }
233
- }, _callee2, null, [[15, 39], [17, 27]]);
264
+ }, _callee2, null, [[15, 48], [17, 27]]);
234
265
  }));
235
266
 
236
267
  return function createOrUpdateBIConnection(_x2) {
@@ -761,7 +792,64 @@ export var fetchExtraOAuthUrlParams = /*#__PURE__*/function () {
761
792
  var shouldResumeConnection = function shouldResumeConnection(error) {
762
793
  return error === DECOUPLED_ERROR || error === ADDITIONAL_INFORMATION_NEEDED_ERROR;
763
794
  };
795
+ /**
796
+ * Tries to find an existing account, associated to an existing trigger
797
+ * with the given bi connection id
798
+ *
799
+ * @param {CozyClient} options.client - Cozy client
800
+ * @param {Object} options.konnector - Konnector manifest
801
+ * @param {Integer} options.connectionId - BI connection id
802
+ * @return {Account|null} An account with a trigger with the same identifier if any
803
+ */
804
+
805
+
806
+ export var findAccountWithBiConnection = /*#__PURE__*/function () {
807
+ var _ref22 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11(_ref21) {
808
+ var client, konnector, connectionId, _yield$Promise$all, _yield$Promise$all2, accountsResult, triggersResult, accountsIndex, trigger;
809
+
810
+ return _regeneratorRuntime.wrap(function _callee11$(_context11) {
811
+ while (1) {
812
+ switch (_context11.prev = _context11.next) {
813
+ case 0:
814
+ client = _ref21.client, konnector = _ref21.konnector, connectionId = _ref21.connectionId;
815
+ _context11.next = 3;
816
+ return Promise.all([client.query(Q('io.cozy.accounts').where({
817
+ data: {
818
+ auth: {
819
+ bi: {
820
+ connId: connectionId
821
+ }
822
+ }
823
+ }
824
+ }).indexFields(['data.auth.bi.connId'])), client.query(Q('io.cozy.triggers').where({
825
+ message: {
826
+ konnector: konnector.slug
827
+ }
828
+ }).indexFields(['message.konnector']))]);
829
+
830
+ case 3:
831
+ _yield$Promise$all = _context11.sent;
832
+ _yield$Promise$all2 = _slicedToArray(_yield$Promise$all, 2);
833
+ accountsResult = _yield$Promise$all2[0];
834
+ triggersResult = _yield$Promise$all2[1];
835
+ accountsIndex = keyBy(accountsResult.data, '_id');
836
+ trigger = triggersResult.data.find(function (t) {
837
+ return accountsIndex[get(t, 'message.account')];
838
+ });
839
+ return _context11.abrupt("return", trigger ? accountsIndex[trigger.message.account] : null);
764
840
 
841
+ case 10:
842
+ case "end":
843
+ return _context11.stop();
844
+ }
845
+ }
846
+ }, _callee11);
847
+ }));
848
+
849
+ return function findAccountWithBiConnection(_x12) {
850
+ return _ref22.apply(this, arguments);
851
+ };
852
+ }();
765
853
  export var konnectorPolicy = {
766
854
  name: 'budget-insight',
767
855
  match: isBudgetInsightConnector,
@@ -772,5 +860,6 @@ export var konnectorPolicy = {
772
860
  fetchExtraOAuthUrlParams: fetchExtraOAuthUrlParams,
773
861
  getAdditionalInformationNeeded: getAdditionalInformationNeeded,
774
862
  handleOAuthAccount: handleOAuthAccount,
775
- setSync: setSync
863
+ setSync: setSync,
864
+ findAccountWithBiConnection: findAccountWithBiConnection
776
865
  };
@@ -449,6 +449,22 @@ describe('createOrUpdateBIConnection', function () {
449
449
  }
450
450
  }, _callee10);
451
451
  })));
452
+ jest.spyOn(client, 'query').mockImplementation( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
453
+ return _regeneratorRuntime.wrap(function _callee11$(_context11) {
454
+ while (1) {
455
+ switch (_context11.prev = _context11.next) {
456
+ case 0:
457
+ return _context11.abrupt("return", {
458
+ data: []
459
+ });
460
+
461
+ case 1:
462
+ case "end":
463
+ return _context11.stop();
464
+ }
465
+ }
466
+ }, _callee11);
467
+ })));
452
468
  createBIConnection.mockReset().mockResolvedValue({
453
469
  id: 'created-bi-connection-id-789'
454
470
  });
@@ -461,15 +477,15 @@ describe('createOrUpdateBIConnection', function () {
461
477
  };
462
478
  };
463
479
 
464
- it('should accept a connector without parameter and find bank id in the account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
480
+ it('should accept a connector without parameter and find bank id in the account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
465
481
  var _setup7, client, flow, connection;
466
482
 
467
- return _regeneratorRuntime.wrap(function _callee11$(_context11) {
483
+ return _regeneratorRuntime.wrap(function _callee12$(_context12) {
468
484
  while (1) {
469
- switch (_context11.prev = _context11.next) {
485
+ switch (_context12.prev = _context12.next) {
470
486
  case 0:
471
487
  _setup7 = setup(), client = _setup7.client, flow = _setup7.flow;
472
- _context11.next = 3;
488
+ _context12.next = 3;
473
489
  return createOrUpdateBIConnection({
474
490
  client: client,
475
491
  account: account,
@@ -478,7 +494,7 @@ describe('createOrUpdateBIConnection', function () {
478
494
  });
479
495
 
480
496
  case 3:
481
- connection = _context11.sent;
497
+ connection = _context12.sent;
482
498
  expect(waitForRealtimeEvent).toHaveBeenCalledWith(client, {
483
499
  _id: 'job-id-1337'
484
500
  }, 'result', 60000);
@@ -494,20 +510,20 @@ describe('createOrUpdateBIConnection', function () {
494
510
 
495
511
  case 8:
496
512
  case "end":
497
- return _context11.stop();
513
+ return _context12.stop();
498
514
  }
499
515
  }
500
- }, _callee11);
516
+ }, _callee12);
501
517
  })));
502
- it('should create a BI connection if no connection id in account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12() {
518
+ it('should create a BI connection if no connection id in account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
503
519
  var _setup8, client, flow, connection;
504
520
 
505
- return _regeneratorRuntime.wrap(function _callee12$(_context12) {
521
+ return _regeneratorRuntime.wrap(function _callee13$(_context13) {
506
522
  while (1) {
507
- switch (_context12.prev = _context12.next) {
523
+ switch (_context13.prev = _context13.next) {
508
524
  case 0:
509
525
  _setup8 = setup(), client = _setup8.client, flow = _setup8.flow;
510
- _context12.next = 3;
526
+ _context13.next = 3;
511
527
  return createOrUpdateBIConnection({
512
528
  client: client,
513
529
  account: account,
@@ -516,7 +532,7 @@ describe('createOrUpdateBIConnection', function () {
516
532
  });
517
533
 
518
534
  case 3:
519
- connection = _context12.sent;
535
+ connection = _context13.sent;
520
536
  expect(waitForRealtimeEvent).toHaveBeenCalledWith(client, {
521
537
  _id: 'job-id-1337'
522
538
  }, 'result', 60000);
@@ -532,17 +548,17 @@ describe('createOrUpdateBIConnection', function () {
532
548
 
533
549
  case 8:
534
550
  case "end":
535
- return _context12.stop();
551
+ return _context13.stop();
536
552
  }
537
553
  }
538
- }, _callee12);
554
+ }, _callee13);
539
555
  })));
540
- it('should update the BI connection if connection id in account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13() {
556
+ it('should update the BI connection if connection id in account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
541
557
  var _setup9, client, flow, connection;
542
558
 
543
- return _regeneratorRuntime.wrap(function _callee13$(_context13) {
559
+ return _regeneratorRuntime.wrap(function _callee14$(_context14) {
544
560
  while (1) {
545
- switch (_context13.prev = _context13.next) {
561
+ switch (_context14.prev = _context14.next) {
546
562
  case 0:
547
563
  _setup9 = setup(), client = _setup9.client, flow = _setup9.flow;
548
564
  getBIConnection.mockReset().mockResolvedValue({
@@ -551,7 +567,7 @@ describe('createOrUpdateBIConnection', function () {
551
567
  updateBIConnection.mockReset().mockResolvedValue({
552
568
  id: 'updated-bi-connection-id-789'
553
569
  });
554
- _context13.next = 5;
570
+ _context14.next = 5;
555
571
  return createOrUpdateBIConnection({
556
572
  client: client,
557
573
  flow: flow,
@@ -568,7 +584,7 @@ describe('createOrUpdateBIConnection', function () {
568
584
  });
569
585
 
570
586
  case 5:
571
- connection = _context13.sent;
587
+ connection = _context14.sent;
572
588
  expect(waitForRealtimeEvent).toHaveBeenCalledWith(client, {
573
589
  _id: 'job-id-1337'
574
590
  }, 'result', 60000);
@@ -590,21 +606,21 @@ describe('createOrUpdateBIConnection', function () {
590
606
 
591
607
  case 11:
592
608
  case "end":
593
- return _context13.stop();
609
+ return _context14.stop();
594
610
  }
595
611
  }
596
- }, _callee13);
612
+ }, _callee14);
597
613
  })));
598
- it('should create the BI connection if connection id in the account does not exist', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14() {
614
+ it('should create the BI connection if connection id in the account does not exist', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
599
615
  var _setup10, client, flow, connection;
600
616
 
601
- return _regeneratorRuntime.wrap(function _callee14$(_context14) {
617
+ return _regeneratorRuntime.wrap(function _callee15$(_context15) {
602
618
  while (1) {
603
- switch (_context14.prev = _context14.next) {
619
+ switch (_context15.prev = _context15.next) {
604
620
  case 0:
605
621
  _setup10 = setup(), client = _setup10.client, flow = _setup10.flow;
606
622
  getBIConnection.mockRejectedValueOnce();
607
- _context14.next = 4;
623
+ _context15.next = 4;
608
624
  return createOrUpdateBIConnection({
609
625
  client: client,
610
626
  flow: flow,
@@ -621,7 +637,7 @@ describe('createOrUpdateBIConnection', function () {
621
637
  });
622
638
 
623
639
  case 4:
624
- connection = _context14.sent;
640
+ connection = _context15.sent;
625
641
  expect(waitForRealtimeEvent).toHaveBeenCalledWith(client, {
626
642
  _id: 'job-id-1337'
627
643
  }, 'result', 60000);
@@ -643,24 +659,24 @@ describe('createOrUpdateBIConnection', function () {
643
659
 
644
660
  case 10:
645
661
  case "end":
646
- return _context14.stop();
662
+ return _context15.stop();
647
663
  }
648
664
  }
649
- }, _callee14);
665
+ }, _callee15);
650
666
  })));
651
- it('should convert wrongpass correctly', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15() {
667
+ it('should convert wrongpass correctly', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16() {
652
668
  var _setup11, client, flow, err;
653
669
 
654
- return _regeneratorRuntime.wrap(function _callee15$(_context15) {
670
+ return _regeneratorRuntime.wrap(function _callee16$(_context16) {
655
671
  while (1) {
656
- switch (_context15.prev = _context15.next) {
672
+ switch (_context16.prev = _context16.next) {
657
673
  case 0:
658
674
  _setup11 = setup(), client = _setup11.client, flow = _setup11.flow;
659
675
  err = new Error();
660
676
  err.code = 'wrongpass';
661
677
  getBIConnection.mockReset().mockResolvedValueOnce({});
662
678
  updateBIConnection.mockReset().mockRejectedValue(err);
663
- _context15.next = 7;
679
+ _context16.next = 7;
664
680
  return expect(createOrUpdateBIConnection({
665
681
  client: client,
666
682
  flow: flow,
@@ -678,24 +694,24 @@ describe('createOrUpdateBIConnection', function () {
678
694
 
679
695
  case 7:
680
696
  case "end":
681
- return _context15.stop();
697
+ return _context16.stop();
682
698
  }
683
699
  }
684
- }, _callee15);
700
+ }, _callee16);
685
701
  })));
686
- it('should convert config correctly', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16() {
702
+ it('should convert config correctly', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee17() {
687
703
  var _setup12, client, flow, err;
688
704
 
689
- return _regeneratorRuntime.wrap(function _callee16$(_context16) {
705
+ return _regeneratorRuntime.wrap(function _callee17$(_context17) {
690
706
  while (1) {
691
- switch (_context16.prev = _context16.next) {
707
+ switch (_context17.prev = _context17.next) {
692
708
  case 0:
693
709
  _setup12 = setup(), client = _setup12.client, flow = _setup12.flow;
694
710
  err = new Error();
695
711
  err.code = 'config';
696
712
  getBIConnection.mockReset().mockResolvedValueOnce({});
697
713
  updateBIConnection.mockReset().mockRejectedValue(err);
698
- _context16.next = 7;
714
+ _context17.next = 7;
699
715
  return expect(createOrUpdateBIConnection({
700
716
  client: client,
701
717
  flow: flow,
@@ -713,24 +729,24 @@ describe('createOrUpdateBIConnection', function () {
713
729
 
714
730
  case 7:
715
731
  case "end":
716
- return _context16.stop();
732
+ return _context17.stop();
717
733
  }
718
734
  }
719
- }, _callee16);
735
+ }, _callee17);
720
736
  })));
721
- it('should convert SCARequired correctly', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee17() {
737
+ it('should convert SCARequired correctly', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18() {
722
738
  var _setup13, client, flow, err;
723
739
 
724
- return _regeneratorRuntime.wrap(function _callee17$(_context17) {
740
+ return _regeneratorRuntime.wrap(function _callee18$(_context18) {
725
741
  while (1) {
726
- switch (_context17.prev = _context17.next) {
742
+ switch (_context18.prev = _context18.next) {
727
743
  case 0:
728
744
  _setup13 = setup(), client = _setup13.client, flow = _setup13.flow;
729
745
  err = new Error();
730
746
  err.code = 'SCARequired';
731
747
  getBIConnection.mockReset().mockResolvedValueOnce({});
732
748
  updateBIConnection.mockReset().mockRejectedValue(err);
733
- _context17.next = 7;
749
+ _context18.next = 7;
734
750
  return expect(createOrUpdateBIConnection({
735
751
  client: client,
736
752
  flow: flow,
@@ -748,17 +764,17 @@ describe('createOrUpdateBIConnection', function () {
748
764
 
749
765
  case 7:
750
766
  case "end":
751
- return _context17.stop();
767
+ return _context18.stop();
752
768
  }
753
769
  }
754
- }, _callee17);
770
+ }, _callee18);
755
771
  })));
756
- it('should remove sensible data from account and create bi connection', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18() {
772
+ it('should remove sensible data from account and create bi connection', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19() {
757
773
  var _setup14, client, flow, account, konnector, createOrUpdateBIConnection, accountToSave;
758
774
 
759
- return _regeneratorRuntime.wrap(function _callee18$(_context18) {
775
+ return _regeneratorRuntime.wrap(function _callee19$(_context19) {
760
776
  while (1) {
761
- switch (_context18.prev = _context18.next) {
777
+ switch (_context19.prev = _context19.next) {
762
778
  case 0:
763
779
  _setup14 = setup(), client = _setup14.client, flow = _setup14.flow;
764
780
  account = {
@@ -780,7 +796,7 @@ describe('createOrUpdateBIConnection', function () {
780
796
  createOrUpdateBIConnection = jest.fn().mockResolvedValue({
781
797
  id: 'created-bi-connection-id'
782
798
  });
783
- _context18.next = 7;
799
+ _context19.next = 7;
784
800
  return onBIAccountCreation({
785
801
  client: client,
786
802
  flow: flow,
@@ -790,7 +806,7 @@ describe('createOrUpdateBIConnection', function () {
790
806
  });
791
807
 
792
808
  case 7:
793
- accountToSave = _context18.sent;
809
+ accountToSave = _context19.sent;
794
810
  expect(createOrUpdateBIConnection).toHaveBeenCalledWith(expect.objectContaining({
795
811
  account: {
796
812
  _id: 'created-account-id',
@@ -825,10 +841,209 @@ describe('createOrUpdateBIConnection', function () {
825
841
 
826
842
  case 11:
827
843
  case "end":
828
- return _context18.stop();
844
+ return _context19.stop();
829
845
  }
830
846
  }
831
- }, _callee18);
847
+ }, _callee19);
848
+ })));
849
+ it('should refuse to create an account with a bi connection id which already exists', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21() {
850
+ var _setup15, client, flow, account, konnector;
851
+
852
+ return _regeneratorRuntime.wrap(function _callee21$(_context21) {
853
+ while (1) {
854
+ switch (_context21.prev = _context21.next) {
855
+ case 0:
856
+ _setup15 = setup(), client = _setup15.client, flow = _setup15.flow;
857
+ account = {
858
+ auth: {
859
+ login: '1234',
860
+ password: '4567',
861
+ dob: '20/12/1890',
862
+ bankId: '100000'
863
+ }
864
+ };
865
+ konnector = {
866
+ slug: 'bankingconnectortest'
867
+ };
868
+ jest.spyOn(flow, 'saveAccount').mockImplementation(function (account) {
869
+ return _objectSpread({
870
+ _id: 'created-account-id'
871
+ }, account);
872
+ });
873
+ jest.spyOn(client, 'query').mockImplementation( /*#__PURE__*/function () {
874
+ var _ref22 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20(_ref21) {
875
+ var doctype;
876
+ return _regeneratorRuntime.wrap(function _callee20$(_context20) {
877
+ while (1) {
878
+ switch (_context20.prev = _context20.next) {
879
+ case 0:
880
+ doctype = _ref21.doctype;
881
+
882
+ if (!(doctype === 'io.cozy.accounts')) {
883
+ _context20.next = 5;
884
+ break;
885
+ }
886
+
887
+ return _context20.abrupt("return", {
888
+ data: [{
889
+ _id: 'account_id',
890
+ auth: {
891
+ bi: {
892
+ connId: 12
893
+ }
894
+ }
895
+ }]
896
+ });
897
+
898
+ case 5:
899
+ if (!(doctype === 'io.cozy.triggers')) {
900
+ _context20.next = 9;
901
+ break;
902
+ }
903
+
904
+ return _context20.abrupt("return", {
905
+ data: [{
906
+ message: {
907
+ account: 'account_id'
908
+ }
909
+ }]
910
+ });
911
+
912
+ case 9:
913
+ throw new Error('unexpected doctype ' + doctype);
914
+
915
+ case 10:
916
+ case "end":
917
+ return _context20.stop();
918
+ }
919
+ }
920
+ }, _callee20);
921
+ }));
922
+
923
+ return function (_x2) {
924
+ return _ref22.apply(this, arguments);
925
+ };
926
+ }());
927
+ _context21.next = 7;
928
+ return expect(onBIAccountCreation({
929
+ client: client,
930
+ flow: flow,
931
+ account: account,
932
+ konnector: konnector
933
+ })).rejects.toEqual(new Error('ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED'));
934
+
935
+ case 7:
936
+ case "end":
937
+ return _context21.stop();
938
+ }
939
+ }
940
+ }, _callee21);
941
+ })));
942
+ it('should create an account if bi connection is not already used', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23() {
943
+ var _setup16, client, flow, account, konnector;
944
+
945
+ return _regeneratorRuntime.wrap(function _callee23$(_context23) {
946
+ while (1) {
947
+ switch (_context23.prev = _context23.next) {
948
+ case 0:
949
+ _setup16 = setup(), client = _setup16.client, flow = _setup16.flow;
950
+ account = {
951
+ auth: {
952
+ login: '1234',
953
+ password: '4567',
954
+ dob: '20/12/1890',
955
+ bankId: '100000'
956
+ }
957
+ };
958
+ konnector = {
959
+ slug: 'bankingconnectortest'
960
+ };
961
+ jest.spyOn(flow, 'saveAccount').mockImplementation(function (account) {
962
+ return _objectSpread({
963
+ _id: 'created-account-id'
964
+ }, account);
965
+ });
966
+ jest.spyOn(client, 'query').mockImplementation( /*#__PURE__*/function () {
967
+ var _ref25 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22(_ref24) {
968
+ var doctype;
969
+ return _regeneratorRuntime.wrap(function _callee22$(_context22) {
970
+ while (1) {
971
+ switch (_context22.prev = _context22.next) {
972
+ case 0:
973
+ doctype = _ref24.doctype;
974
+
975
+ if (!(doctype === 'io.cozy.accounts')) {
976
+ _context22.next = 5;
977
+ break;
978
+ }
979
+
980
+ return _context22.abrupt("return", {
981
+ data: [{
982
+ _id: 'account_id',
983
+ auth: {
984
+ bi: {
985
+ connId: 12
986
+ }
987
+ }
988
+ }]
989
+ });
990
+
991
+ case 5:
992
+ if (!(doctype === 'io.cozy.triggers')) {
993
+ _context22.next = 9;
994
+ break;
995
+ }
996
+
997
+ return _context22.abrupt("return", {
998
+ data: [{
999
+ message: {
1000
+ account: 'other_account_id'
1001
+ }
1002
+ }]
1003
+ });
1004
+
1005
+ case 9:
1006
+ throw new Error('unexpected doctype ' + doctype);
1007
+
1008
+ case 10:
1009
+ case "end":
1010
+ return _context22.stop();
1011
+ }
1012
+ }
1013
+ }, _callee22);
1014
+ }));
1015
+
1016
+ return function (_x3) {
1017
+ return _ref25.apply(this, arguments);
1018
+ };
1019
+ }());
1020
+ _context23.next = 7;
1021
+ return expect(onBIAccountCreation({
1022
+ client: client,
1023
+ flow: flow,
1024
+ account: account,
1025
+ konnector: konnector
1026
+ })).resolves.toEqual({
1027
+ _id: 'created-account-id',
1028
+ auth: {
1029
+ bankId: '100000',
1030
+ login: '1234'
1031
+ },
1032
+ data: {
1033
+ auth: {
1034
+ bi: {
1035
+ connId: 'created-bi-connection-id-789'
1036
+ }
1037
+ }
1038
+ }
1039
+ });
1040
+
1041
+ case 7:
1042
+ case "end":
1043
+ return _context23.stop();
1044
+ }
1045
+ }
1046
+ }, _callee23);
832
1047
  })));
833
1048
  });
834
1049
  describe('fetchExtraOAuthUrlParams', function () {
@@ -843,13 +1058,13 @@ describe('fetchExtraOAuthUrlParams', function () {
843
1058
  }
844
1059
  }
845
1060
  });
846
- waitForRealtimeEvent.mockImplementation( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee19() {
847
- return _regeneratorRuntime.wrap(function _callee19$(_context19) {
1061
+ waitForRealtimeEvent.mockImplementation( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee24() {
1062
+ return _regeneratorRuntime.wrap(function _callee24$(_context24) {
848
1063
  while (1) {
849
- switch (_context19.prev = _context19.next) {
1064
+ switch (_context24.prev = _context24.next) {
850
1065
  case 0:
851
1066
  sleep(2);
852
- return _context19.abrupt("return", {
1067
+ return _context24.abrupt("return", {
853
1068
  data: {
854
1069
  result: {
855
1070
  code: 'bi-temporary-access-token-121212',
@@ -862,24 +1077,24 @@ describe('fetchExtraOAuthUrlParams', function () {
862
1077
 
863
1078
  case 2:
864
1079
  case "end":
865
- return _context19.stop();
1080
+ return _context24.stop();
866
1081
  }
867
1082
  }
868
- }, _callee19);
1083
+ }, _callee24);
869
1084
  })));
870
1085
  return {
871
1086
  client: client
872
1087
  };
873
1088
  };
874
1089
 
875
- it('should create a temporary token', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee20() {
876
- var _setup15, client, konnector, account, _yield$fetchExtraOAut, id_connector, token;
1090
+ it('should create a temporary token', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee25() {
1091
+ var _setup17, client, konnector, account, _yield$fetchExtraOAut, id_connector, token;
877
1092
 
878
- return _regeneratorRuntime.wrap(function _callee20$(_context20) {
1093
+ return _regeneratorRuntime.wrap(function _callee25$(_context25) {
879
1094
  while (1) {
880
- switch (_context20.prev = _context20.next) {
1095
+ switch (_context25.prev = _context25.next) {
881
1096
  case 0:
882
- _setup15 = setup(), client = _setup15.client;
1097
+ _setup17 = setup(), client = _setup17.client;
883
1098
  konnector = {
884
1099
  slug: 'revolut',
885
1100
  parameters: {
@@ -887,7 +1102,7 @@ describe('fetchExtraOAuthUrlParams', function () {
887
1102
  }
888
1103
  };
889
1104
  account = {};
890
- _context20.next = 5;
1105
+ _context25.next = 5;
891
1106
  return fetchExtraOAuthUrlParams({
892
1107
  client: client,
893
1108
  konnector: konnector,
@@ -895,7 +1110,7 @@ describe('fetchExtraOAuthUrlParams', function () {
895
1110
  });
896
1111
 
897
1112
  case 5:
898
- _yield$fetchExtraOAut = _context20.sent;
1113
+ _yield$fetchExtraOAut = _context25.sent;
899
1114
  id_connector = _yield$fetchExtraOAut.id_connector;
900
1115
  token = _yield$fetchExtraOAut.token;
901
1116
  expect(token).toEqual('bi-temporary-access-token-121212');
@@ -903,18 +1118,18 @@ describe('fetchExtraOAuthUrlParams', function () {
903
1118
 
904
1119
  case 10:
905
1120
  case "end":
906
- return _context20.stop();
1121
+ return _context25.stop();
907
1122
  }
908
1123
  }
909
- }, _callee20);
1124
+ }, _callee25);
910
1125
  })));
911
1126
  });
912
1127
  describe('handleOAuthAccount', function () {
913
- it('should handle webauth if any connection is found in the account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee22() {
1128
+ it('should handle webauth if any connection is found in the account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee27() {
914
1129
  var client, flow, account, t;
915
- return _regeneratorRuntime.wrap(function _callee22$(_context22) {
1130
+ return _regeneratorRuntime.wrap(function _callee27$(_context27) {
916
1131
  while (1) {
917
- switch (_context22.prev = _context22.next) {
1132
+ switch (_context27.prev = _context27.next) {
918
1133
  case 0:
919
1134
  client = new CozyClient({
920
1135
  uri: 'http://testcozy.mycozy.cloud'
@@ -924,23 +1139,23 @@ describe('handleOAuthAccount', function () {
924
1139
  flow.handleFormSubmit = jest.fn();
925
1140
 
926
1141
  flow.saveAccount = /*#__PURE__*/function () {
927
- var _ref22 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee21(account) {
928
- return _regeneratorRuntime.wrap(function _callee21$(_context21) {
1142
+ var _ref29 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee26(account) {
1143
+ return _regeneratorRuntime.wrap(function _callee26$(_context26) {
929
1144
  while (1) {
930
- switch (_context21.prev = _context21.next) {
1145
+ switch (_context26.prev = _context26.next) {
931
1146
  case 0:
932
- return _context21.abrupt("return", account);
1147
+ return _context26.abrupt("return", account);
933
1148
 
934
1149
  case 1:
935
1150
  case "end":
936
- return _context21.stop();
1151
+ return _context26.stop();
937
1152
  }
938
1153
  }
939
- }, _callee21);
1154
+ }, _callee26);
940
1155
  }));
941
1156
 
942
- return function (_x2) {
943
- return _ref22.apply(this, arguments);
1157
+ return function (_x4) {
1158
+ return _ref29.apply(this, arguments);
944
1159
  };
945
1160
  }();
946
1161
 
@@ -952,7 +1167,7 @@ describe('handleOAuthAccount', function () {
952
1167
  }
953
1168
  };
954
1169
  t = jest.fn();
955
- _context22.next = 9;
1170
+ _context27.next = 9;
956
1171
  return handleOAuthAccount({
957
1172
  account: account,
958
1173
  flow: flow,
@@ -983,18 +1198,18 @@ describe('handleOAuthAccount', function () {
983
1198
 
984
1199
  case 10:
985
1200
  case "end":
986
- return _context22.stop();
1201
+ return _context27.stop();
987
1202
  }
988
1203
  }
989
- }, _callee22);
1204
+ }, _callee27);
990
1205
  })));
991
1206
  });
992
1207
  describe('setSync', function () {
993
- it('should set synchronization status for a contract', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee24() {
1208
+ it('should set synchronization status for a contract', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee29() {
994
1209
  var client, flow, biConnId, tempToken, biContractId, account, contract, createTemporaryToken, setBIConnectionSyncStatus;
995
- return _regeneratorRuntime.wrap(function _callee24$(_context24) {
1210
+ return _regeneratorRuntime.wrap(function _callee29$(_context29) {
996
1211
  while (1) {
997
- switch (_context24.prev = _context24.next) {
1212
+ switch (_context29.prev = _context29.next) {
998
1213
  case 0:
999
1214
  client = new CozyClient({
1000
1215
  uri: 'http://testcozy.mycozy.cloud'
@@ -1004,23 +1219,23 @@ describe('setSync', function () {
1004
1219
  flow.handleFormSubmit = jest.fn();
1005
1220
 
1006
1221
  flow.saveAccount = /*#__PURE__*/function () {
1007
- var _ref24 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee23(account) {
1008
- return _regeneratorRuntime.wrap(function _callee23$(_context23) {
1222
+ var _ref31 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee28(account) {
1223
+ return _regeneratorRuntime.wrap(function _callee28$(_context28) {
1009
1224
  while (1) {
1010
- switch (_context23.prev = _context23.next) {
1225
+ switch (_context28.prev = _context28.next) {
1011
1226
  case 0:
1012
- return _context23.abrupt("return", account);
1227
+ return _context28.abrupt("return", account);
1013
1228
 
1014
1229
  case 1:
1015
1230
  case "end":
1016
- return _context23.stop();
1231
+ return _context28.stop();
1017
1232
  }
1018
1233
  }
1019
- }, _callee23);
1234
+ }, _callee28);
1020
1235
  }));
1021
1236
 
1022
- return function (_x3) {
1023
- return _ref24.apply(this, arguments);
1237
+ return function (_x5) {
1238
+ return _ref31.apply(this, arguments);
1024
1239
  };
1025
1240
  }();
1026
1241
 
@@ -1043,7 +1258,7 @@ describe('setSync', function () {
1043
1258
  code: tempToken
1044
1259
  });
1045
1260
  setBIConnectionSyncStatus = jest.fn();
1046
- _context24.next = 14;
1261
+ _context29.next = 14;
1047
1262
  return setSync({
1048
1263
  client: client,
1049
1264
  account: account,
@@ -1060,18 +1275,18 @@ describe('setSync', function () {
1060
1275
 
1061
1276
  case 15:
1062
1277
  case "end":
1063
- return _context24.stop();
1278
+ return _context29.stop();
1064
1279
  }
1065
1280
  }
1066
- }, _callee24);
1281
+ }, _callee29);
1067
1282
  })));
1068
1283
  });
1069
1284
  describe('updateBIConnectionFromFlow', function () {
1070
- it('should update a connection with given fields', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee25() {
1285
+ it('should update a connection with given fields', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee30() {
1071
1286
  var client, biConnId, account, flow, connectionData;
1072
- return _regeneratorRuntime.wrap(function _callee25$(_context25) {
1287
+ return _regeneratorRuntime.wrap(function _callee30$(_context30) {
1073
1288
  while (1) {
1074
- switch (_context25.prev = _context25.next) {
1289
+ switch (_context30.prev = _context30.next) {
1075
1290
  case 0:
1076
1291
  client = new CozyClient({
1077
1292
  uri: 'http://testcozy.mycozy.cloud'
@@ -1098,7 +1313,7 @@ describe('updateBIConnectionFromFlow', function () {
1098
1313
  connectionData = {
1099
1314
  login: '1234'
1100
1315
  };
1101
- _context25.next = 10;
1316
+ _context30.next = 10;
1102
1317
  return updateBIConnectionFromFlow(flow, connectionData);
1103
1318
 
1104
1319
  case 10:
@@ -1110,10 +1325,10 @@ describe('updateBIConnectionFromFlow', function () {
1110
1325
 
1111
1326
  case 11:
1112
1327
  case "end":
1113
- return _context25.stop();
1328
+ return _context30.stop();
1114
1329
  }
1115
1330
  }
1116
- }, _callee25);
1331
+ }, _callee30);
1117
1332
  })));
1118
1333
  });
1119
1334
  describe('sendTwoFACode', function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-harvest-lib",
3
- "version": "7.2.4",
3
+ "version": "7.3.0",
4
4
  "description": "Provides logic, modules and components for Cozy's harvest applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -29,8 +29,8 @@
29
29
  "@cozy/minilog": "^1.0.0",
30
30
  "@sentry/browser": "^6.0.1",
31
31
  "cozy-bi-auth": "0.0.24",
32
- "cozy-doctypes": "^1.83.5",
33
- "cozy-logger": "^1.8.0",
32
+ "cozy-doctypes": "^1.83.6",
33
+ "cozy-logger": "^1.8.1",
34
34
  "date-fns": "^1.30.1",
35
35
  "final-form": "^4.18.5",
36
36
  "lodash": "^4.17.19",
@@ -45,37 +45,37 @@
45
45
  "@babel/core": "7.16.12",
46
46
  "@babel/register": "7.16.9",
47
47
  "@cozy/cli-tree": "^0.5.0",
48
- "@material-ui/core": "4",
49
- "@material-ui/lab": "^4.0.0-alpha.57",
50
- "@testing-library/jest-dom": "^4.1.2",
48
+ "@material-ui/core": "4.11.3",
49
+ "@material-ui/lab": "4.0.0-alpha.57",
50
+ "@testing-library/jest-dom": "4.2.4",
51
51
  "@testing-library/react": "10.4.9",
52
52
  "babel-jest": "26.6.3",
53
53
  "babel-plugin-inline-react-svg": "1.1.2",
54
54
  "babel-preset-cozy-app": "^2.0.2",
55
- "cozy-client": "17.6.1",
55
+ "cozy-client": "27.14.3",
56
56
  "cozy-device-helper": "^1.17.0",
57
- "cozy-flags": "^2.8.4",
57
+ "cozy-flags": "^2.8.5",
58
58
  "cozy-keys-lib": "3.8.0",
59
59
  "cozy-realtime": "^4.0.5",
60
- "cozy-ui": "^57.6.0",
60
+ "cozy-ui": "57.6.0",
61
61
  "enzyme": "3.11.0",
62
62
  "enzyme-adapter-react-16": "1.15.6",
63
63
  "form-data": "3.0.0",
64
64
  "identity-obj-proxy": "3.0.0",
65
65
  "jest": "26.6.3",
66
- "jest-environment-jsdom-sixteen": "^1.0.3",
67
- "jest-resolve-cached": "^1.0.0",
68
- "jsdom": "^16.1.0",
69
- "leaflet": "^1.7.1",
66
+ "jest-environment-jsdom-sixteen": "1.0.3",
67
+ "jest-resolve-cached": "1.0.0",
68
+ "jsdom": "16.4.0",
69
+ "leaflet": "1.7.1",
70
70
  "prop-types": "15.7.2",
71
- "react": "^16.12.0",
72
- "react-dom": "^16.12.0",
73
- "react-swipeable-views": "^0.13.9"
71
+ "react": "16.12.0",
72
+ "react-dom": "16.12.0",
73
+ "react-swipeable-views": "0.13.9"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@babel/runtime": ">=7.12.5",
77
77
  "@material-ui/core": ">=4",
78
- "cozy-client": ">=17.6.1",
78
+ "cozy-client": ">=27.14.3",
79
79
  "cozy-device-helper": ">=1.10.2",
80
80
  "cozy-flags": ">=2.3.5",
81
81
  "cozy-keys-lib": ">=3.7.0",
@@ -85,5 +85,5 @@
85
85
  "react-router-dom": "^5.0.1"
86
86
  },
87
87
  "sideEffects": false,
88
- "gitHead": "5f0a839a3dcc01f2aa0106ccdbd67062c8faf44f"
88
+ "gitHead": "3bfcea11a7a0d354b5feabc3d4a2655345eee2ee"
89
89
  }
@@ -22,6 +22,8 @@ const TERMS_VERSION_MISMATCH = 'TERMS_VERSION_MISMATCH'
22
22
  const UNKNOWN_ERROR = 'UNKNOWN_ERROR'
23
23
  const USER_ACTION_NEEDED = 'USER_ACTION_NEEDED'
24
24
  const VENDOR_DOWN = 'VENDOR_DOWN'
25
+ const ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED =
26
+ 'ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED'
25
27
 
26
28
  const KNOWN_ERRORS = [
27
29
  CHALLENGE_ASKED,
@@ -31,7 +33,8 @@ const KNOWN_ERRORS = [
31
33
  NOT_EXISTING_DIRECTORY,
32
34
  TERMS_VERSION_MISMATCH,
33
35
  USER_ACTION_NEEDED,
34
- VENDOR_DOWN
36
+ VENDOR_DOWN,
37
+ ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED
35
38
  ]
36
39
 
37
40
  const USER_ERRORS = [
@@ -204,6 +204,10 @@
204
204
  "VENDOR_DOWN.LINXO_DOWN": {
205
205
  "title": "Unavailable service",
206
206
  "description": "It seems that we are experiencing overload with our bank konnectors at the moment. Please rerun the connector later."
207
+ },
208
+ "ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED": {
209
+ "title": "This account already exists",
210
+ "description": "You already have configured an account with these identifiers."
207
211
  }
208
212
  }
209
213
  },
@@ -204,6 +204,10 @@
204
204
  "VENDOR_DOWN.LINXO_DOWN": {
205
205
  "title": "Service non disponible",
206
206
  "description": "Il semble que le service [%{name}](%{link}) ne nous ait pas répondu dans les temps. Vous pouvez tenter de le relancer manuellement maintenant ou ultérieurement."
207
+ },
208
+ "ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED": {
209
+ "title": "Ce compte est déjà configuré",
210
+ "description": "Vous avez déjà configuré un compte avec ces identifiants."
207
211
  }
208
212
  }
209
213
  },
@@ -9,7 +9,9 @@ import get from 'lodash/get'
9
9
  import omit from 'lodash/omit'
10
10
  import clone from 'lodash/clone'
11
11
  import set from 'lodash/set'
12
+ import keyBy from 'lodash/keyBy'
12
13
  import defaults from 'lodash/defaults'
14
+ import { Q } from 'cozy-client'
13
15
 
14
16
  import { waitForRealtimeEvent } from './jobUtils'
15
17
  import {
@@ -42,7 +44,9 @@ const getBIIdFromContract = bankAccount => bankAccount.vendorId
42
44
  * front-end.
43
45
  */
44
46
  const extraBIErrorMap = {
45
- config: 'LOGIN_FAILED'
47
+ config: 'LOGIN_FAILED',
48
+ ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED:
49
+ 'ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED'
46
50
  }
47
51
 
48
52
  /**
@@ -166,10 +170,26 @@ export const createOrUpdateBIConnection = async ({
166
170
  }
167
171
  }
168
172
  logger.info('Creating or updating connection...')
169
- const connection = await (connId
173
+ const isUpdate = Boolean(connId)
174
+ const connection = await (isUpdate
170
175
  ? updateBIConnection(config, connId, credsToSend, tempToken)
171
176
  : createBIConnection(config, credsToSend, tempToken))
172
177
 
178
+ if (!isUpdate) {
179
+ const sameAccount = await findAccountWithBiConnection({
180
+ client,
181
+ konnector,
182
+ connectionId: connection.id
183
+ })
184
+ if (sameAccount) {
185
+ const err = new KonnectorJobError(
186
+ 'ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED'
187
+ )
188
+ err.accountId = sameAccount._id
189
+ throw err
190
+ }
191
+ }
192
+
173
193
  logger.info(`Created or updated connection ${connection.id}`)
174
194
  return connection
175
195
  } catch (e) {
@@ -505,6 +525,44 @@ const shouldResumeConnection = error => {
505
525
  )
506
526
  }
507
527
 
528
+ /**
529
+ * Tries to find an existing account, associated to an existing trigger
530
+ * with the given bi connection id
531
+ *
532
+ * @param {CozyClient} options.client - Cozy client
533
+ * @param {Object} options.konnector - Konnector manifest
534
+ * @param {Integer} options.connectionId - BI connection id
535
+ * @return {Account|null} An account with a trigger with the same identifier if any
536
+ */
537
+ export const findAccountWithBiConnection = async ({
538
+ client,
539
+ konnector,
540
+ connectionId
541
+ }) => {
542
+ const [accountsResult, triggersResult] = await Promise.all([
543
+ client.query(
544
+ Q('io.cozy.accounts')
545
+ .where({ data: { auth: { bi: { connId: connectionId } } } })
546
+ .indexFields(['data.auth.bi.connId'])
547
+ ),
548
+ client.query(
549
+ Q('io.cozy.triggers')
550
+ .where({
551
+ message: {
552
+ konnector: konnector.slug
553
+ }
554
+ })
555
+ .indexFields(['message.konnector'])
556
+ )
557
+ ])
558
+
559
+ const accountsIndex = keyBy(accountsResult.data, '_id')
560
+ const trigger = triggersResult.data.find(
561
+ t => accountsIndex[get(t, 'message.account')]
562
+ )
563
+ return trigger ? accountsIndex[trigger.message.account] : null
564
+ }
565
+
508
566
  export const konnectorPolicy = {
509
567
  name: 'budget-insight',
510
568
  match: isBudgetInsightConnector,
@@ -515,5 +573,6 @@ export const konnectorPolicy = {
515
573
  fetchExtraOAuthUrlParams: fetchExtraOAuthUrlParams,
516
574
  getAdditionalInformationNeeded,
517
575
  handleOAuthAccount,
518
- setSync
576
+ setSync,
577
+ findAccountWithBiConnection
519
578
  }
@@ -297,6 +297,7 @@ describe('createOrUpdateBIConnection', () => {
297
297
  }
298
298
  }
299
299
  })
300
+ jest.spyOn(client, 'query').mockImplementation(async () => ({ data: [] }))
300
301
 
301
302
  createBIConnection
302
303
  .mockReset()
@@ -604,6 +605,127 @@ describe('createOrUpdateBIConnection', () => {
604
605
  }
605
606
  })
606
607
  })
608
+
609
+ it('should refuse to create an account with a bi connection id which already exists', async () => {
610
+ const { client, flow } = setup()
611
+
612
+ const account = {
613
+ auth: {
614
+ login: '1234',
615
+ password: '4567',
616
+ dob: '20/12/1890',
617
+ bankId: '100000'
618
+ }
619
+ }
620
+
621
+ const konnector = {
622
+ slug: 'bankingconnectortest'
623
+ }
624
+
625
+ jest.spyOn(flow, 'saveAccount').mockImplementation(account => ({
626
+ _id: 'created-account-id',
627
+ ...account
628
+ }))
629
+
630
+ jest.spyOn(client, 'query').mockImplementation(async ({ doctype }) => {
631
+ if (doctype === 'io.cozy.accounts') {
632
+ return {
633
+ data: [
634
+ {
635
+ _id: 'account_id',
636
+ auth: { bi: { connId: 12 } }
637
+ }
638
+ ]
639
+ }
640
+ } else if (doctype === 'io.cozy.triggers') {
641
+ return {
642
+ data: [
643
+ {
644
+ message: { account: 'account_id' }
645
+ }
646
+ ]
647
+ }
648
+ } else {
649
+ throw new Error('unexpected doctype ' + doctype)
650
+ }
651
+ })
652
+
653
+ await expect(
654
+ onBIAccountCreation({
655
+ client,
656
+ flow,
657
+ account,
658
+ konnector
659
+ })
660
+ ).rejects.toEqual(new Error('ACCOUNT_WITH_SAME_IDENTIFIER_ALREADY_DEFINED'))
661
+ })
662
+
663
+ it('should create an account if bi connection is not already used', async () => {
664
+ const { client, flow } = setup()
665
+
666
+ const account = {
667
+ auth: {
668
+ login: '1234',
669
+ password: '4567',
670
+ dob: '20/12/1890',
671
+ bankId: '100000'
672
+ }
673
+ }
674
+
675
+ const konnector = {
676
+ slug: 'bankingconnectortest'
677
+ }
678
+
679
+ jest.spyOn(flow, 'saveAccount').mockImplementation(account => ({
680
+ _id: 'created-account-id',
681
+ ...account
682
+ }))
683
+
684
+ jest.spyOn(client, 'query').mockImplementation(async ({ doctype }) => {
685
+ if (doctype === 'io.cozy.accounts') {
686
+ return {
687
+ data: [
688
+ {
689
+ _id: 'account_id',
690
+ auth: { bi: { connId: 12 } }
691
+ }
692
+ ]
693
+ }
694
+ } else if (doctype === 'io.cozy.triggers') {
695
+ return {
696
+ data: [
697
+ {
698
+ message: { account: 'other_account_id' }
699
+ }
700
+ ]
701
+ }
702
+ } else {
703
+ throw new Error('unexpected doctype ' + doctype)
704
+ }
705
+ })
706
+
707
+ await expect(
708
+ onBIAccountCreation({
709
+ client,
710
+ flow,
711
+ account,
712
+ konnector
713
+ })
714
+ ).resolves.toEqual({
715
+ _id: 'created-account-id',
716
+ auth: {
717
+ bankId: '100000',
718
+ login: '1234'
719
+ },
720
+ data: {
721
+ auth: {
722
+ bi: {
723
+ connId: 'created-bi-connection-id-789'
724
+ }
725
+ }
726
+ }
727
+ })
728
+ })
607
729
  })
608
730
 
609
731
  describe('fetchExtraOAuthUrlParams', () => {