cozy-harvest-lib 7.3.6 → 8.1.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 +45 -0
- package/dist/components/KonnectorConfiguration/Success/BanksLink.js +6 -2
- package/dist/components/KonnectorConfiguration/Success/DriveLink.js +6 -2
- package/dist/components/KonnectorSuggestionModal/index.js +3 -1
- package/dist/components/KonnectorUpdateLinker.js +9 -3
- package/dist/components/cards/AppLinkCard.js +3 -1
- package/dist/models/ConnectionFlow.js +63 -40
- package/dist/models/ConnectionFlow.spec.js +43 -4
- package/dist/models/konnector/KonnectorJobWatcher.js +6 -0
- package/package.json +5 -5
- package/src/components/KonnectorConfiguration/Success/BanksLink.jsx +3 -2
- package/src/components/KonnectorConfiguration/Success/DriveLink.jsx +3 -2
- package/src/components/KonnectorSuggestionModal/index.jsx +1 -1
- package/src/components/KonnectorUpdateLinker.jsx +11 -3
- package/src/components/cards/AppLinkCard.jsx +1 -1
- package/src/models/ConnectionFlow.js +23 -5
- package/src/models/ConnectionFlow.spec.js +30 -5
- package/src/models/konnector/KonnectorJobWatcher.js +5 -0
- package/test/fixtures.js +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,51 @@
|
|
|
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
|
+
# [8.1.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@8.0.0...cozy-harvest-lib@8.1.0) (2022-04-08)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Remove unused restoreAllMocks ([6dd376a](https://github.com/cozy/cozy-libs/commit/6dd376a2a7f4393a9446375383d6b77510f87d95))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* Do not mark a job as succeeded on login success ([39e6d01](https://github.com/cozy/cozy-libs/commit/39e6d0161839a6d00ef0dcf24eb6aa0ddeff5f78))
|
|
17
|
+
* Watch job changes when the trigger is already running ([ca01663](https://github.com/cozy/cozy-libs/commit/ca01663eb41bbbcfed5d1f146c9cd68956faa9df))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# [8.0.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@7.3.7...cozy-harvest-lib@8.0.0) (2022-04-04)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* **cozy-harvest-lib:** Correctly handle AppLinker's onClick ([b45b2e9](https://github.com/cozy/cozy-libs/commit/b45b2e9942635bf36a78bcbf9ac3f8eacdd1fde2)), closes [cozy/cozy-ui#2015](https://github.com/cozy/cozy-ui/issues/2015)
|
|
29
|
+
* **cozy-harvest-lib:** Replace deprecated usage of slug in AppLinker ([24b33d7](https://github.com/cozy/cozy-libs/commit/24b33d7cf3fe26339a00d6f5279d5e92adc71bc5)), closes [cozy/cozy-ui#2015](https://github.com/cozy/cozy-ui/issues/2015)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### BREAKING CHANGES
|
|
33
|
+
|
|
34
|
+
* **cozy-harvest-lib:** `cozy-harvest-lib` now requires `cozy-ui` version `60.6.0` or superior
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## [7.3.7](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@7.3.6...cozy-harvest-lib@7.3.7) (2022-04-01)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Bug Fixes
|
|
44
|
+
|
|
45
|
+
* bump @material-ui/lab from 4.0.0-alpha.57 to 4.0.0-alpha.60 ([660bc7c](https://github.com/cozy/cozy-libs/commit/660bc7ca64223028894eaa4639460cf5d1e6a8e9))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
6
51
|
## [7.3.6](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@7.3.5...cozy-harvest-lib@7.3.6) (2022-03-21)
|
|
7
52
|
|
|
8
53
|
**Note:** Version bump only for package cozy-harvest-lib
|
|
@@ -18,17 +18,21 @@ var BanksLinkRedirectStore = function BanksLinkRedirectStore(_ref) {
|
|
|
18
18
|
|
|
19
19
|
if (fetchStatus === 'loaded') {
|
|
20
20
|
return /*#__PURE__*/React.createElement(AppLinker, {
|
|
21
|
-
|
|
21
|
+
app: {
|
|
22
|
+
slug: slug
|
|
23
|
+
},
|
|
22
24
|
href: url
|
|
23
25
|
}, function (_ref2) {
|
|
24
26
|
var href = _ref2.href,
|
|
25
|
-
name = _ref2.name
|
|
27
|
+
name = _ref2.name,
|
|
28
|
+
onClick = _ref2.onClick;
|
|
26
29
|
return /*#__PURE__*/React.createElement(ButtonLink, {
|
|
27
30
|
icon: OpenwithIcon,
|
|
28
31
|
href: href,
|
|
29
32
|
label: t('account.success.banksLinkText', {
|
|
30
33
|
appName: name
|
|
31
34
|
}),
|
|
35
|
+
onClick: onClick,
|
|
32
36
|
subtle: true
|
|
33
37
|
});
|
|
34
38
|
});
|
|
@@ -19,18 +19,22 @@ var DriveLink = /*#__PURE__*/memo(function (_ref) {
|
|
|
19
19
|
|
|
20
20
|
if (fetchStatus === 'loaded') {
|
|
21
21
|
return /*#__PURE__*/React.createElement(AppLinker, {
|
|
22
|
-
|
|
22
|
+
app: {
|
|
23
|
+
slug: slug
|
|
24
|
+
},
|
|
23
25
|
href: url,
|
|
24
26
|
nativePath: path
|
|
25
27
|
}, function (_ref2) {
|
|
26
28
|
var href = _ref2.href,
|
|
27
|
-
name = _ref2.name
|
|
29
|
+
name = _ref2.name,
|
|
30
|
+
onClick = _ref2.onClick;
|
|
28
31
|
return /*#__PURE__*/React.createElement(ButtonLink, {
|
|
29
32
|
icon: OpenwithIcon,
|
|
30
33
|
href: href,
|
|
31
34
|
label: t('account.success.driveLinkText', {
|
|
32
35
|
appName: name
|
|
33
36
|
}),
|
|
37
|
+
onClick: onClick,
|
|
34
38
|
subtle: true
|
|
35
39
|
});
|
|
36
40
|
});
|
|
@@ -117,7 +117,9 @@ var KonnectorSuggestionModal = function KonnectorSuggestionModal(_ref) {
|
|
|
117
117
|
}), /*#__PURE__*/React.createElement("br", null), t('suggestions.reason_bank', {
|
|
118
118
|
name: name
|
|
119
119
|
})), /*#__PURE__*/React.createElement(AppLinker, {
|
|
120
|
-
|
|
120
|
+
app: {
|
|
121
|
+
slug: storeAppName
|
|
122
|
+
},
|
|
121
123
|
nativePath: nativePath,
|
|
122
124
|
href: generateWebLink({
|
|
123
125
|
cozyUrl: cozyURL.origin,
|
|
@@ -11,11 +11,13 @@ var KonnectorUpdateButton = function KonnectorUpdateButton(_ref) {
|
|
|
11
11
|
var disabled = _ref.disabled,
|
|
12
12
|
isBlocking = _ref.isBlocking,
|
|
13
13
|
href = _ref.href,
|
|
14
|
+
onClick = _ref.onClick,
|
|
14
15
|
label = _ref.label;
|
|
15
16
|
return /*#__PURE__*/React.createElement(ButtonLink, {
|
|
16
17
|
disabled: disabled,
|
|
17
18
|
className: "u-m-0",
|
|
18
19
|
href: href,
|
|
20
|
+
onClick: onClick,
|
|
19
21
|
icon: EyeIcon,
|
|
20
22
|
label: label,
|
|
21
23
|
theme: isBlocking ? 'danger' : 'secondary'
|
|
@@ -35,14 +37,18 @@ var KonnectorUpdateLinker = function KonnectorUpdateLinker(_ref2) {
|
|
|
35
37
|
var konnectorUpdateUrl = data ? Application.getStoreInstallationURL(data, konnector) : null;
|
|
36
38
|
var isReady = isLoaded && konnectorUpdateUrl;
|
|
37
39
|
return isReady ? /*#__PURE__*/React.createElement(AppLinker, {
|
|
38
|
-
|
|
40
|
+
app: {
|
|
41
|
+
slug: 'store'
|
|
42
|
+
},
|
|
39
43
|
href: konnectorUpdateUrl
|
|
40
44
|
}, function (_ref3) {
|
|
41
|
-
var
|
|
45
|
+
var onClick = _ref3.onClick,
|
|
46
|
+
href = _ref3.href;
|
|
42
47
|
return /*#__PURE__*/React.createElement(KonnectorUpdateButton, {
|
|
43
48
|
href: href,
|
|
44
49
|
isBlocking: isBlocking,
|
|
45
|
-
label: label
|
|
50
|
+
label: label,
|
|
51
|
+
onClick: onClick
|
|
46
52
|
});
|
|
47
53
|
}) : /*#__PURE__*/React.createElement(KonnectorUpdateButton, {
|
|
48
54
|
disabled: true,
|
|
@@ -32,7 +32,9 @@ export var AppLinkButton = function AppLinkButton(_ref) {
|
|
|
32
32
|
isInstalled = _useAppLinkWithStoreF.isInstalled;
|
|
33
33
|
|
|
34
34
|
return /*#__PURE__*/React.createElement(AppLinker, {
|
|
35
|
-
|
|
35
|
+
app: {
|
|
36
|
+
slug: slug
|
|
37
|
+
},
|
|
36
38
|
nativePath: path,
|
|
37
39
|
href: url || '#'
|
|
38
40
|
}, function (_ref2) {
|
|
@@ -184,6 +184,7 @@ export var ConnectionFlow = /*#__PURE__*/function () {
|
|
|
184
184
|
this.realtime = new Realtime({
|
|
185
185
|
client: client
|
|
186
186
|
});
|
|
187
|
+
this.watchCurrentJobIfTriggerIsAlreadyRunning();
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
_createClass(ConnectionFlow, [{
|
|
@@ -500,7 +501,8 @@ export var ConnectionFlow = /*#__PURE__*/function () {
|
|
|
500
501
|
}, {
|
|
501
502
|
key: "handleLoginSuccess",
|
|
502
503
|
value: function handleLoginSuccess() {
|
|
503
|
-
this.jobWatcher.
|
|
504
|
+
this.jobWatcher.handleLoginSuccess();
|
|
505
|
+
this.triggerEvent(LOGIN_SUCCESS_EVENT);
|
|
504
506
|
}
|
|
505
507
|
}, {
|
|
506
508
|
key: "handleLoginSuccessHandled",
|
|
@@ -848,16 +850,11 @@ export var ConnectionFlow = /*#__PURE__*/function () {
|
|
|
848
850
|
key: "launch",
|
|
849
851
|
value: function () {
|
|
850
852
|
var _launch = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11() {
|
|
851
|
-
var _this2 = this;
|
|
852
|
-
|
|
853
853
|
var _ref5,
|
|
854
854
|
_ref5$autoSuccessTime,
|
|
855
855
|
autoSuccessTimer,
|
|
856
856
|
computedAutoSuccessTimer,
|
|
857
857
|
launcher,
|
|
858
|
-
_iterator2,
|
|
859
|
-
_step2,
|
|
860
|
-
_loop,
|
|
861
858
|
_args11 = arguments;
|
|
862
859
|
|
|
863
860
|
return _regeneratorRuntime.wrap(function _callee11$(_context11) {
|
|
@@ -908,42 +905,11 @@ export var ConnectionFlow = /*#__PURE__*/function () {
|
|
|
908
905
|
}
|
|
909
906
|
}
|
|
910
907
|
|
|
911
|
-
this.
|
|
912
|
-
this.jobWatcher = watchKonnectorJob(this.client, this.job, {
|
|
908
|
+
this.watchJob({
|
|
913
909
|
autoSuccessTimer: computedAutoSuccessTimer
|
|
914
910
|
});
|
|
915
|
-
logger.info("ConnectionFlow: Subscribed to ".concat(JOBS_DOCTYPE, ":").concat(this.job._id));
|
|
916
|
-
_iterator2 = _createForOfIteratorHelper(JOB_EVENTS);
|
|
917
|
-
|
|
918
|
-
try {
|
|
919
|
-
_loop = function _loop() {
|
|
920
|
-
var ev = _step2.value;
|
|
921
|
-
|
|
922
|
-
_this2.jobWatcher.on(ev, function () {
|
|
923
|
-
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
924
|
-
args[_key2] = arguments[_key2];
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
return _this2.triggerEvent.apply(_this2, [ev].concat(args));
|
|
928
|
-
});
|
|
929
|
-
};
|
|
930
|
-
|
|
931
|
-
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
932
|
-
_loop();
|
|
933
|
-
}
|
|
934
|
-
} catch (err) {
|
|
935
|
-
_iterator2.e(err);
|
|
936
|
-
} finally {
|
|
937
|
-
_iterator2.f();
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
this.unsubscribeAllRealtime = function () {
|
|
941
|
-
_this2.jobWatcher.unsubscribeAll();
|
|
942
|
-
|
|
943
|
-
_this2.realtime.unsubscribeAll();
|
|
944
|
-
};
|
|
945
911
|
|
|
946
|
-
case
|
|
912
|
+
case 14:
|
|
947
913
|
case "end":
|
|
948
914
|
return _context11.stop();
|
|
949
915
|
}
|
|
@@ -957,6 +923,63 @@ export var ConnectionFlow = /*#__PURE__*/function () {
|
|
|
957
923
|
|
|
958
924
|
return launch;
|
|
959
925
|
}()
|
|
926
|
+
/**
|
|
927
|
+
* If the trigger we display is already running, subscribe to the associated job
|
|
928
|
+
*/
|
|
929
|
+
|
|
930
|
+
}, {
|
|
931
|
+
key: "watchCurrentJobIfTriggerIsAlreadyRunning",
|
|
932
|
+
value: function watchCurrentJobIfTriggerIsAlreadyRunning() {
|
|
933
|
+
if (get(this, 'trigger.current_state.status') === 'running') {
|
|
934
|
+
this.job = {
|
|
935
|
+
_id: get(this, 'trigger.current_state.last_executed_job_id')
|
|
936
|
+
};
|
|
937
|
+
this.watchJob();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}, {
|
|
941
|
+
key: "watchJob",
|
|
942
|
+
value: function watchJob() {
|
|
943
|
+
var _this2 = this;
|
|
944
|
+
|
|
945
|
+
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
|
|
946
|
+
autoSuccessTimer: false
|
|
947
|
+
};
|
|
948
|
+
this.realtime.subscribe('updated', JOBS_DOCTYPE, this.job._id, this.handleJobUpdated.bind(this));
|
|
949
|
+
this.jobWatcher = watchKonnectorJob(this.client, this.job, options);
|
|
950
|
+
logger.info("ConnectionFlow: Subscribed to ".concat(JOBS_DOCTYPE, ":").concat(this.job._id));
|
|
951
|
+
|
|
952
|
+
var _iterator2 = _createForOfIteratorHelper(JOB_EVENTS),
|
|
953
|
+
_step2;
|
|
954
|
+
|
|
955
|
+
try {
|
|
956
|
+
var _loop = function _loop() {
|
|
957
|
+
var ev = _step2.value;
|
|
958
|
+
|
|
959
|
+
_this2.jobWatcher.on(ev, function () {
|
|
960
|
+
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
961
|
+
args[_key2] = arguments[_key2];
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
return _this2.triggerEvent.apply(_this2, [ev].concat(args));
|
|
965
|
+
});
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
969
|
+
_loop();
|
|
970
|
+
}
|
|
971
|
+
} catch (err) {
|
|
972
|
+
_iterator2.e(err);
|
|
973
|
+
} finally {
|
|
974
|
+
_iterator2.f();
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
this.unsubscribeAllRealtime = function () {
|
|
978
|
+
_this2.jobWatcher.unsubscribeAll();
|
|
979
|
+
|
|
980
|
+
_this2.realtime.unsubscribeAll();
|
|
981
|
+
};
|
|
982
|
+
}
|
|
960
983
|
}, {
|
|
961
984
|
key: "unwatch",
|
|
962
985
|
value: function unwatch() {
|
|
@@ -985,7 +1008,7 @@ export var ConnectionFlow = /*#__PURE__*/function () {
|
|
|
985
1008
|
accountError = _this$state.accountError;
|
|
986
1009
|
var triggerError = triggersModel.getKonnectorJobError(trigger);
|
|
987
1010
|
return {
|
|
988
|
-
running: ![ERRORED, IDLE, SUCCESS].includes(status),
|
|
1011
|
+
running: get(trigger, 'current_state.status') === 'running' || ![ERRORED, IDLE, SUCCESS].includes(status),
|
|
989
1012
|
twoFARunning: status === RUNNING_TWOFA,
|
|
990
1013
|
twoFARetry: status == TWO_FA_MISMATCH,
|
|
991
1014
|
triggerError: triggerError,
|
|
@@ -11,11 +11,12 @@ import cronHelpers from 'helpers/cron';
|
|
|
11
11
|
import { saveAccount } from '../connections/accounts';
|
|
12
12
|
import { createTrigger, ensureTrigger, prepareTriggerAccount, launchTrigger } from '../connections/triggers';
|
|
13
13
|
import CozyRealtime from 'cozy-realtime';
|
|
14
|
-
import KonnectorJobWatcher from './konnector/KonnectorJobWatcher';
|
|
14
|
+
import KonnectorJobWatcher, { watchKonnectorJob } from './konnector/KonnectorJobWatcher';
|
|
15
15
|
import { konnectorPolicy as biKonnectorPolicy } from '../services/budget-insight';
|
|
16
16
|
import fixtures from '../../test/fixtures';
|
|
17
17
|
import sentryHub from '../sentry';
|
|
18
18
|
import { Q } from 'cozy-client';
|
|
19
|
+
jest.mock('./konnector/KonnectorJobWatcher');
|
|
19
20
|
jest.mock('../sentry', function () {
|
|
20
21
|
var mockScope = {
|
|
21
22
|
setTag: jest.fn()
|
|
@@ -190,6 +191,16 @@ describe('ConnectionFlow', function () {
|
|
|
190
191
|
return flow.getState().running === true;
|
|
191
192
|
};
|
|
192
193
|
|
|
194
|
+
beforeAll(function () {
|
|
195
|
+
watchKonnectorJob.mockReturnValue({
|
|
196
|
+
on: function on() {
|
|
197
|
+
return {};
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
afterEach(function () {
|
|
202
|
+
jest.clearAllMocks();
|
|
203
|
+
});
|
|
193
204
|
it('should render as submitting when there is no account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6() {
|
|
194
205
|
var _setup, flow, submitPromise;
|
|
195
206
|
|
|
@@ -371,6 +382,10 @@ describe('ConnectionFlow', function () {
|
|
|
371
382
|
userCredentials: {
|
|
372
383
|
login: 'foo',
|
|
373
384
|
password: 'bar'
|
|
385
|
+
},
|
|
386
|
+
account: {
|
|
387
|
+
login: 'old',
|
|
388
|
+
password: 'old'
|
|
374
389
|
}
|
|
375
390
|
});
|
|
376
391
|
|
|
@@ -495,13 +510,15 @@ describe('ConnectionFlow', function () {
|
|
|
495
510
|
describe('ensureTriggerAndLaunch', function () {
|
|
496
511
|
beforeAll(function () {
|
|
497
512
|
jest.spyOn(cronHelpers, 'fromFrequency').mockReturnValue('0 0 0 * * 0');
|
|
513
|
+
watchKonnectorJob.mockReturnValue({
|
|
514
|
+
on: function on() {
|
|
515
|
+
return {};
|
|
516
|
+
}
|
|
517
|
+
});
|
|
498
518
|
});
|
|
499
519
|
afterEach(function () {
|
|
500
520
|
jest.clearAllMocks();
|
|
501
521
|
});
|
|
502
|
-
afterAll(function () {
|
|
503
|
-
jest.restoreAllMocks();
|
|
504
|
-
});
|
|
505
522
|
it('should launch trigger without account', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16() {
|
|
506
523
|
var _setup11, flow, client;
|
|
507
524
|
|
|
@@ -782,5 +799,27 @@ describe('ConnectionFlow', function () {
|
|
|
782
799
|
delete window.cozy;
|
|
783
800
|
delete window.ReactNativeWebView;
|
|
784
801
|
});
|
|
802
|
+
describe('constructor', function () {
|
|
803
|
+
beforeAll(function () {
|
|
804
|
+
watchKonnectorJob.mockReturnValue({
|
|
805
|
+
on: function on() {
|
|
806
|
+
return {};
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
afterEach(function () {
|
|
811
|
+
jest.clearAllMocks();
|
|
812
|
+
});
|
|
813
|
+
it('should watch a running trigger', function () {
|
|
814
|
+
setup({
|
|
815
|
+
trigger: fixtures.runningTrigger
|
|
816
|
+
});
|
|
817
|
+
expect(watchKonnectorJob).toHaveBeenCalledWith(expect.any(Object), {
|
|
818
|
+
_id: 'runningjobid'
|
|
819
|
+
}, {
|
|
820
|
+
autoSuccessTimer: false
|
|
821
|
+
});
|
|
822
|
+
});
|
|
823
|
+
});
|
|
785
824
|
}); // it should have running false on trigger updates
|
|
786
825
|
// it should set error on trigger updates
|
|
@@ -76,6 +76,12 @@ export var KonnectorJobWatcher = /*#__PURE__*/function () {
|
|
|
76
76
|
this._error = error;
|
|
77
77
|
this.emit('error', new KonnectorJobError(error));
|
|
78
78
|
}
|
|
79
|
+
}, {
|
|
80
|
+
key: "handleLoginSuccess",
|
|
81
|
+
value: function handleLoginSuccess() {
|
|
82
|
+
logger.info("KonnectorJobWatcher: login success");
|
|
83
|
+
this.disableSuccessTimer();
|
|
84
|
+
}
|
|
79
85
|
}, {
|
|
80
86
|
key: "handleSuccess",
|
|
81
87
|
value: function handleSuccess() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-harvest-lib",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Provides logic, modules and components for Cozy's harvest applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Cozy",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@babel/register": "7.16.9",
|
|
47
47
|
"@cozy/cli-tree": "^0.5.0",
|
|
48
48
|
"@material-ui/core": "4.11.3",
|
|
49
|
-
"@material-ui/lab": "4.0.0-alpha.
|
|
49
|
+
"@material-ui/lab": "4.0.0-alpha.60",
|
|
50
50
|
"@testing-library/jest-dom": "5.16.2",
|
|
51
51
|
"@testing-library/react": "10.4.9",
|
|
52
52
|
"babel-jest": "26.6.3",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"cozy-flags": "^2.8.7",
|
|
58
58
|
"cozy-keys-lib": "3.8.0",
|
|
59
59
|
"cozy-realtime": "^4.0.5",
|
|
60
|
-
"cozy-ui": "
|
|
60
|
+
"cozy-ui": "60.6.0",
|
|
61
61
|
"enzyme": "3.11.0",
|
|
62
62
|
"enzyme-adapter-react-16": "1.15.6",
|
|
63
63
|
"form-data": "3.0.0",
|
|
@@ -80,10 +80,10 @@
|
|
|
80
80
|
"cozy-flags": ">=2.3.5",
|
|
81
81
|
"cozy-keys-lib": ">=3.7.0",
|
|
82
82
|
"cozy-realtime": ">=3.12.2",
|
|
83
|
-
"cozy-ui": ">=
|
|
83
|
+
"cozy-ui": ">=60.6.0",
|
|
84
84
|
"leaflet": "^1.7.1",
|
|
85
85
|
"react-router-dom": "^5.0.1"
|
|
86
86
|
},
|
|
87
87
|
"sideEffects": false,
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "7d830a3634199b430803344cad471783baac1bd5"
|
|
89
89
|
}
|
|
@@ -15,14 +15,15 @@ const BanksLinkRedirectStore = ({ client, t }) => {
|
|
|
15
15
|
|
|
16
16
|
if (fetchStatus === 'loaded') {
|
|
17
17
|
return (
|
|
18
|
-
<AppLinker
|
|
19
|
-
{({ href, name }) => (
|
|
18
|
+
<AppLinker app={{ slug }} href={url}>
|
|
19
|
+
{({ href, name, onClick }) => (
|
|
20
20
|
<ButtonLink
|
|
21
21
|
icon={OpenwithIcon}
|
|
22
22
|
href={href}
|
|
23
23
|
label={t('account.success.banksLinkText', {
|
|
24
24
|
appName: name
|
|
25
25
|
})}
|
|
26
|
+
onClick={onClick}
|
|
26
27
|
subtle
|
|
27
28
|
/>
|
|
28
29
|
)}
|
|
@@ -15,14 +15,15 @@ const DriveLink = memo(({ folderId, client, t }) => {
|
|
|
15
15
|
|
|
16
16
|
if (fetchStatus === 'loaded') {
|
|
17
17
|
return (
|
|
18
|
-
<AppLinker
|
|
19
|
-
{({ href, name }) => (
|
|
18
|
+
<AppLinker app={{ slug }} href={url} nativePath={path}>
|
|
19
|
+
{({ href, name, onClick }) => (
|
|
20
20
|
<ButtonLink
|
|
21
21
|
icon={OpenwithIcon}
|
|
22
22
|
href={href}
|
|
23
23
|
label={t('account.success.driveLinkText', {
|
|
24
24
|
appName: name
|
|
25
25
|
})}
|
|
26
|
+
onClick={onClick}
|
|
26
27
|
subtle
|
|
27
28
|
/>
|
|
28
29
|
)}
|
|
@@ -9,11 +9,18 @@ import EyeIcon from 'cozy-ui/transpiled/react/Icons/Eye'
|
|
|
9
9
|
import { Application } from 'cozy-doctypes'
|
|
10
10
|
import { appsConn } from '../connections/apps'
|
|
11
11
|
|
|
12
|
-
const KonnectorUpdateButton = ({
|
|
12
|
+
const KonnectorUpdateButton = ({
|
|
13
|
+
disabled,
|
|
14
|
+
isBlocking,
|
|
15
|
+
href,
|
|
16
|
+
onClick,
|
|
17
|
+
label
|
|
18
|
+
}) => (
|
|
13
19
|
<ButtonLink
|
|
14
20
|
disabled={disabled}
|
|
15
21
|
className="u-m-0"
|
|
16
22
|
href={href}
|
|
23
|
+
onClick={onClick}
|
|
17
24
|
icon={EyeIcon}
|
|
18
25
|
label={label}
|
|
19
26
|
theme={isBlocking ? 'danger' : 'secondary'}
|
|
@@ -29,13 +36,14 @@ const KonnectorUpdateLinker = ({ label, isBlocking, konnector }) => {
|
|
|
29
36
|
const isReady = isLoaded && konnectorUpdateUrl
|
|
30
37
|
|
|
31
38
|
return isReady ? (
|
|
32
|
-
<AppLinker slug
|
|
33
|
-
{({ href }) => {
|
|
39
|
+
<AppLinker app={{ slug: 'store' }} href={konnectorUpdateUrl}>
|
|
40
|
+
{({ onClick, href }) => {
|
|
34
41
|
return (
|
|
35
42
|
<KonnectorUpdateButton
|
|
36
43
|
href={href}
|
|
37
44
|
isBlocking={isBlocking}
|
|
38
45
|
label={label}
|
|
46
|
+
onClick={onClick}
|
|
39
47
|
/>
|
|
40
48
|
)
|
|
41
49
|
}}
|
|
@@ -25,7 +25,7 @@ export const AppLinkButton = ({ slug, path }) => {
|
|
|
25
25
|
path
|
|
26
26
|
)
|
|
27
27
|
return (
|
|
28
|
-
<AppLinker
|
|
28
|
+
<AppLinker app={{ slug }} nativePath={path} href={url || '#'}>
|
|
29
29
|
{({ onClick, href }) => (
|
|
30
30
|
<ButtonLink
|
|
31
31
|
onClick={fetchStatus !== 'loaded' ? onClick : null}
|
|
@@ -175,6 +175,8 @@ export class ConnectionFlow {
|
|
|
175
175
|
this.twoFAWaiters = this.twoFAWaiters || []
|
|
176
176
|
|
|
177
177
|
this.realtime = new Realtime({ client })
|
|
178
|
+
|
|
179
|
+
this.watchCurrentJobIfTriggerIsAlreadyRunning()
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
getTwoFACodeProvider() {
|
|
@@ -344,7 +346,8 @@ export class ConnectionFlow {
|
|
|
344
346
|
}
|
|
345
347
|
|
|
346
348
|
handleLoginSuccess() {
|
|
347
|
-
this.jobWatcher.
|
|
349
|
+
this.jobWatcher.handleLoginSuccess()
|
|
350
|
+
this.triggerEvent(LOGIN_SUCCESS_EVENT)
|
|
348
351
|
}
|
|
349
352
|
|
|
350
353
|
handleLoginSuccessHandled() {
|
|
@@ -576,16 +579,29 @@ export class ConnectionFlow {
|
|
|
576
579
|
logger.info('Found no client connector launcher')
|
|
577
580
|
}
|
|
578
581
|
}
|
|
582
|
+
this.watchJob({ autoSuccessTimer: computedAutoSuccessTimer })
|
|
583
|
+
}
|
|
579
584
|
|
|
585
|
+
/**
|
|
586
|
+
* If the trigger we display is already running, subscribe to the associated job
|
|
587
|
+
*/
|
|
588
|
+
watchCurrentJobIfTriggerIsAlreadyRunning() {
|
|
589
|
+
if (get(this, 'trigger.current_state.status') === 'running') {
|
|
590
|
+
this.job = {
|
|
591
|
+
_id: get(this, 'trigger.current_state.last_executed_job_id')
|
|
592
|
+
}
|
|
593
|
+
this.watchJob()
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
watchJob(options = {autoSuccessTimer: false}) {
|
|
580
598
|
this.realtime.subscribe(
|
|
581
599
|
'updated',
|
|
582
600
|
JOBS_DOCTYPE,
|
|
583
601
|
this.job._id,
|
|
584
602
|
this.handleJobUpdated.bind(this)
|
|
585
603
|
)
|
|
586
|
-
this.jobWatcher = watchKonnectorJob(this.client, this.job,
|
|
587
|
-
autoSuccessTimer: computedAutoSuccessTimer
|
|
588
|
-
})
|
|
604
|
+
this.jobWatcher = watchKonnectorJob(this.client, this.job, options)
|
|
589
605
|
logger.info(`ConnectionFlow: Subscribed to ${JOBS_DOCTYPE}:${this.job._id}`)
|
|
590
606
|
|
|
591
607
|
for (const ev of JOB_EVENTS) {
|
|
@@ -621,7 +637,9 @@ export class ConnectionFlow {
|
|
|
621
637
|
const { status, accountError } = this.state
|
|
622
638
|
const triggerError = triggersModel.getKonnectorJobError(trigger)
|
|
623
639
|
return {
|
|
624
|
-
running:
|
|
640
|
+
running:
|
|
641
|
+
get(trigger, 'current_state.status') === 'running' ||
|
|
642
|
+
![ERRORED, IDLE, SUCCESS].includes(status),
|
|
625
643
|
twoFARunning: status === RUNNING_TWOFA,
|
|
626
644
|
twoFARetry: status == TWO_FA_MISMATCH,
|
|
627
645
|
triggerError: triggerError,
|
|
@@ -8,12 +8,13 @@ import {
|
|
|
8
8
|
launchTrigger
|
|
9
9
|
} from '../connections/triggers'
|
|
10
10
|
import CozyRealtime from 'cozy-realtime'
|
|
11
|
-
import KonnectorJobWatcher from './konnector/KonnectorJobWatcher'
|
|
11
|
+
import KonnectorJobWatcher, { watchKonnectorJob } from './konnector/KonnectorJobWatcher'
|
|
12
12
|
import { konnectorPolicy as biKonnectorPolicy } from '../services/budget-insight'
|
|
13
13
|
import fixtures from '../../test/fixtures'
|
|
14
14
|
import sentryHub from '../sentry'
|
|
15
15
|
import { Q } from 'cozy-client'
|
|
16
16
|
|
|
17
|
+
jest.mock('./konnector/KonnectorJobWatcher')
|
|
17
18
|
jest.mock('../sentry', () => {
|
|
18
19
|
const mockScope = {
|
|
19
20
|
setTag: jest.fn()
|
|
@@ -116,6 +117,14 @@ describe('ConnectionFlow', () => {
|
|
|
116
117
|
const isSubmitting = flow => {
|
|
117
118
|
return flow.getState().running === true
|
|
118
119
|
}
|
|
120
|
+
beforeAll(() => {
|
|
121
|
+
watchKonnectorJob.mockReturnValue({on: () => ({})})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
afterEach(() => {
|
|
125
|
+
jest.clearAllMocks()
|
|
126
|
+
})
|
|
127
|
+
|
|
119
128
|
|
|
120
129
|
it('should render as submitting when there is no account', async () => {
|
|
121
130
|
const { flow } = setup()
|
|
@@ -213,6 +222,10 @@ describe('ConnectionFlow', () => {
|
|
|
213
222
|
userCredentials: {
|
|
214
223
|
login: 'foo',
|
|
215
224
|
password: 'bar'
|
|
225
|
+
},
|
|
226
|
+
account: {
|
|
227
|
+
login: 'old',
|
|
228
|
+
password: 'old'
|
|
216
229
|
}
|
|
217
230
|
})
|
|
218
231
|
|
|
@@ -314,16 +327,13 @@ describe('ConnectionFlow', () => {
|
|
|
314
327
|
describe('ensureTriggerAndLaunch', () => {
|
|
315
328
|
beforeAll(() => {
|
|
316
329
|
jest.spyOn(cronHelpers, 'fromFrequency').mockReturnValue('0 0 0 * * 0')
|
|
330
|
+
watchKonnectorJob.mockReturnValue({on: () => ({})})
|
|
317
331
|
})
|
|
318
332
|
|
|
319
333
|
afterEach(() => {
|
|
320
334
|
jest.clearAllMocks()
|
|
321
335
|
})
|
|
322
336
|
|
|
323
|
-
afterAll(() => {
|
|
324
|
-
jest.restoreAllMocks()
|
|
325
|
-
})
|
|
326
|
-
|
|
327
337
|
it('should launch trigger without account', async () => {
|
|
328
338
|
const { flow, client } = setup()
|
|
329
339
|
await flow.ensureTriggerAndLaunch(client, {
|
|
@@ -481,6 +491,21 @@ describe('ConnectionFlow', () => {
|
|
|
481
491
|
delete window.cozy
|
|
482
492
|
delete window.ReactNativeWebView
|
|
483
493
|
})
|
|
494
|
+
|
|
495
|
+
describe('constructor', () => {
|
|
496
|
+
beforeAll(() => {
|
|
497
|
+
watchKonnectorJob.mockReturnValue({on: () => ({})})
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
afterEach(() => {
|
|
501
|
+
jest.clearAllMocks()
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
it('should watch a running trigger', () => {
|
|
505
|
+
setup({trigger: fixtures.runningTrigger})
|
|
506
|
+
expect(watchKonnectorJob).toHaveBeenCalledWith(expect.any(Object), {_id: 'runningjobid'}, {autoSuccessTimer: false})
|
|
507
|
+
})
|
|
508
|
+
})
|
|
484
509
|
})
|
|
485
510
|
|
|
486
511
|
// it should have running false on trigger updates
|
|
@@ -66,6 +66,11 @@ export class KonnectorJobWatcher {
|
|
|
66
66
|
this.emit('error', new KonnectorJobError(error))
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
handleLoginSuccess() {
|
|
70
|
+
logger.info(`KonnectorJobWatcher: login success`)
|
|
71
|
+
this.disableSuccessTimer()
|
|
72
|
+
}
|
|
73
|
+
|
|
69
74
|
handleSuccess() {
|
|
70
75
|
logger.info(`KonnectorJobWatcher: Job has succeeded`)
|
|
71
76
|
this.disableSuccessTimer()
|
package/test/fixtures.js
CHANGED
|
@@ -125,6 +125,23 @@ const fixtures = {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
},
|
|
128
|
+
runningTrigger: {
|
|
129
|
+
id: 'running-trigger-id',
|
|
130
|
+
_type: 'io.cozy.triggers',
|
|
131
|
+
current_state: {
|
|
132
|
+
status: 'running',
|
|
133
|
+
last_executed_job_id: 'runningjobid'
|
|
134
|
+
},
|
|
135
|
+
attributes: {
|
|
136
|
+
arguments: '0 0 0 * * 0',
|
|
137
|
+
type: '@cron',
|
|
138
|
+
worker: 'konnector',
|
|
139
|
+
message: {
|
|
140
|
+
account: 'updated-account-id',
|
|
141
|
+
konnector: 'konnectest'
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
128
145
|
createdTriggerWithFolder: {
|
|
129
146
|
id: 'created-trigger-id',
|
|
130
147
|
_type: 'io.cozy.triggers',
|