cozy-harvest-lib 7.3.7 → 8.1.1

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,52 @@
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.1](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@8.1.0...cozy-harvest-lib@8.1.1) (2022-04-11)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **cozy-harvest-lib:** Correctly handle onClick on AppLinkCard ([c9068c5](https://github.com/cozy/cozy-libs/commit/c9068c53a1ec2e1f6b9b7636f6b25aadec69900f))
12
+ * **cozy-harvest-lib:** Disable AppLink button until fallback is resolved ([bedc90d](https://github.com/cozy/cozy-libs/commit/bedc90d80c899bb93a2be70d6963d62742fb600b))
13
+
14
+
15
+
16
+
17
+
18
+ # [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)
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * Remove unused restoreAllMocks ([6dd376a](https://github.com/cozy/cozy-libs/commit/6dd376a2a7f4393a9446375383d6b77510f87d95))
24
+
25
+
26
+ ### Features
27
+
28
+ * Do not mark a job as succeeded on login success ([39e6d01](https://github.com/cozy/cozy-libs/commit/39e6d0161839a6d00ef0dcf24eb6aa0ddeff5f78))
29
+ * Watch job changes when the trigger is already running ([ca01663](https://github.com/cozy/cozy-libs/commit/ca01663eb41bbbcfed5d1f146c9cd68956faa9df))
30
+
31
+
32
+
33
+
34
+
35
+ # [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)
36
+
37
+
38
+ ### Bug Fixes
39
+
40
+ * **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)
41
+ * **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)
42
+
43
+
44
+ ### BREAKING CHANGES
45
+
46
+ * **cozy-harvest-lib:** `cozy-harvest-lib` now requires `cozy-ui` version `60.6.0` or superior
47
+
48
+
49
+
50
+
51
+
6
52
  ## [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)
7
53
 
8
54
 
@@ -18,17 +18,21 @@ var BanksLinkRedirectStore = function BanksLinkRedirectStore(_ref) {
18
18
 
19
19
  if (fetchStatus === 'loaded') {
20
20
  return /*#__PURE__*/React.createElement(AppLinker, {
21
- slug: slug,
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
- slug: slug,
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
- slug: storeAppName,
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
- slug: "store",
40
+ app: {
41
+ slug: 'store'
42
+ },
39
43
  href: konnectorUpdateUrl
40
44
  }, function (_ref3) {
41
- var href = _ref3.href;
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,14 +32,17 @@ export var AppLinkButton = function AppLinkButton(_ref) {
32
32
  isInstalled = _useAppLinkWithStoreF.isInstalled;
33
33
 
34
34
  return /*#__PURE__*/React.createElement(AppLinker, {
35
- slug: slug,
35
+ app: {
36
+ slug: slug
37
+ },
36
38
  nativePath: path,
37
39
  href: url || '#'
38
40
  }, function (_ref2) {
39
41
  var onClick = _ref2.onClick,
40
42
  href = _ref2.href;
41
43
  return /*#__PURE__*/React.createElement(ButtonLink, {
42
- onClick: fetchStatus !== 'loaded' ? onClick : null,
44
+ disabled: fetchStatus !== 'loaded',
45
+ onClick: fetchStatus === 'loaded' ? onClick : null,
43
46
  href: href,
44
47
  icon: isInstalled ? /*#__PURE__*/React.createElement(AppIcon, {
45
48
  app: slug,
@@ -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.handleSuccess();
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.realtime.subscribe('updated', JOBS_DOCTYPE, this.job._id, this.handleJobUpdated.bind(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 19:
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": "7.3.7",
3
+ "version": "8.1.1",
4
4
  "description": "Provides logic, modules and components for Cozy's harvest applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -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": "57.6.0",
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": ">=57.6.0",
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": "f0cc87a62171e60b8ec8e15aa92fd17db81c9921"
88
+ "gitHead": "33faa014567ecbc026970a630e43d4425b3ccec3"
89
89
  }
@@ -15,14 +15,15 @@ const BanksLinkRedirectStore = ({ client, t }) => {
15
15
 
16
16
  if (fetchStatus === 'loaded') {
17
17
  return (
18
- <AppLinker slug={slug} href={url}>
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 slug={slug} href={url} nativePath={path}>
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
  )}
@@ -85,7 +85,7 @@ const KonnectorSuggestionModal = ({
85
85
  </Typography>
86
86
  )}
87
87
  <AppLinker
88
- slug={storeAppName}
88
+ app={{ slug: storeAppName }}
89
89
  nativePath={nativePath}
90
90
  href={generateWebLink({
91
91
  cozyUrl: cozyURL.origin,
@@ -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 = ({ disabled, isBlocking, href, label }) => (
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="store" href={konnectorUpdateUrl}>
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,10 +25,11 @@ export const AppLinkButton = ({ slug, path }) => {
25
25
  path
26
26
  )
27
27
  return (
28
- <AppLinker slug={slug} nativePath={path} href={url || '#'}>
28
+ <AppLinker app={{ slug }} nativePath={path} href={url || '#'}>
29
29
  {({ onClick, href }) => (
30
30
  <ButtonLink
31
- onClick={fetchStatus !== 'loaded' ? onClick : null}
31
+ disabled={fetchStatus !== 'loaded'}
32
+ onClick={fetchStatus === 'loaded' ? onClick : null}
32
33
  href={href}
33
34
  icon={
34
35
  isInstalled ? (
@@ -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.handleSuccess()
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: ![ERRORED, IDLE, SUCCESS].includes(status),
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',