cozy-harvest-lib 9.22.2 → 9.23.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,37 @@
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
+ ## [9.23.1](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@9.23.0...cozy-harvest-lib@9.23.1) (2022-07-26)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Google connector in web mode ([1777fcc](https://github.com/cozy/cozy-libs/commit/1777fccc6b9e1e581189ca4563f851b341a60644))
12
+ * OauthWindowInAppBrowser re-render ([334e7d8](https://github.com/cozy/cozy-libs/commit/334e7d8b7d777bf265803f81583a65f39fb0cabb))
13
+
14
+
15
+
16
+
17
+
18
+ # [9.23.0](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@9.22.3...cozy-harvest-lib@9.23.0) (2022-07-26)
19
+
20
+
21
+ ### Features
22
+
23
+ * Add BI aggregator releationship to BI accounts ([cb7a79c](https://github.com/cozy/cozy-libs/commit/cb7a79c6cd72a9f8e95ae71307f27f04b68f0e94))
24
+
25
+
26
+
27
+
28
+
29
+ ## [9.22.3](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@9.22.2...cozy-harvest-lib@9.22.3) (2022-07-26)
30
+
31
+ **Note:** Version bump only for package cozy-harvest-lib
32
+
33
+
34
+
35
+
36
+
6
37
  ## [9.22.2](https://github.com/cozy/cozy-libs/compare/cozy-harvest-lib@9.22.1...cozy-harvest-lib@9.22.2) (2022-07-21)
7
38
 
8
39
  **Note:** Version bump only for package cozy-harvest-lib
@@ -1,6 +1,6 @@
1
1
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
2
  import _regeneratorRuntime from "@babel/runtime/regenerator";
3
- import { useEffect } from 'react';
3
+ import React, { useEffect } from 'react';
4
4
  import PropTypes from 'prop-types';
5
5
  import { useWebviewIntent } from 'cozy-intent';
6
6
  import logger from '../logger';
@@ -9,22 +9,27 @@ import { intentsApiProptype } from '../helpers/proptypes';
9
9
  var InAppBrowser = function InAppBrowser(_ref) {
10
10
  var url = _ref.url,
11
11
  onClose = _ref.onClose,
12
- _ref$intentsApi = _ref.intentsApi,
13
- intentsApi = _ref$intentsApi === void 0 ? {} : _ref$intentsApi;
14
- var webviewIntent = useWebviewIntent();
15
- var fetchSessionCode = intentsApi !== null && intentsApi !== void 0 && intentsApi.fetchSessionCode ? intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.fetchSessionCode : function () {
16
- return webviewIntent.call('fetchSessionCode');
17
- };
18
- var showInAppBrowser = intentsApi !== null && intentsApi !== void 0 && intentsApi.showInAppBrowser ? intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.showInAppBrowser : function (url) {
19
- return webviewIntent.call('showInAppBrowser', {
20
- url: url
12
+ intentsApi = _ref.intentsApi;
13
+
14
+ if (intentsApi) {
15
+ return /*#__PURE__*/React.createElement(InAppBrowserWithIntentsApi, {
16
+ url: url,
17
+ onClose: onClose,
18
+ intentsApi: intentsApi
21
19
  });
22
- };
23
- var closeInAppBrowser = intentsApi !== null && intentsApi !== void 0 && intentsApi.closeInAppBrowser ? intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.closeInAppBrowser : function () {
24
- return webviewIntent.call('closeInAppBrowser');
25
- };
26
- var tokenParamName = intentsApi !== null && intentsApi !== void 0 && intentsApi.tokenParamName ? intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.tokenParamName : 'session_code';
27
- var isReady = Boolean(webviewIntent || (intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.fetchSessionCode) && (intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.showInAppBrowser) && (intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.closeInAppBrowser));
20
+ } else {
21
+ return /*#__PURE__*/React.createElement(InAppBrowserWithWebviewIntent, {
22
+ url: url,
23
+ onClose: onClose
24
+ });
25
+ }
26
+ };
27
+
28
+ var InAppBrowserWithWebviewIntent = function InAppBrowserWithWebviewIntent(_ref2) {
29
+ var url = _ref2.url,
30
+ onClose = _ref2.onClose;
31
+ var webviewIntent = useWebviewIntent();
32
+ var isReady = Boolean(webviewIntent);
28
33
  useEffect(function () {
29
34
  function insideEffect() {
30
35
  return _insideEffect.apply(this, arguments);
@@ -45,19 +50,21 @@ var InAppBrowser = function InAppBrowser(_ref) {
45
50
  _context.prev = 1;
46
51
  logger.debug('url at the beginning: ', url);
47
52
  _context.next = 5;
48
- return fetchSessionCode();
53
+ return webviewIntent.call('fetchSessionCode');
49
54
 
50
55
  case 5:
51
56
  sessionCode = _context.sent;
52
57
  logger.debug('got session code', sessionCode);
53
58
  iabUrl = new URL(url);
54
- iabUrl.searchParams.append(tokenParamName, sessionCode); // we need to decodeURIComponent since toString() encodes URL
59
+ iabUrl.searchParams.append('session_code', sessionCode); // we need to decodeURIComponent since toString() encodes URL
55
60
  // but native browser will also encode them.
56
61
 
57
62
  urlToOpen = decodeURIComponent(iabUrl.toString());
58
63
  logger.debug('url to open: ', urlToOpen);
59
64
  _context.next = 13;
60
- return showInAppBrowser(urlToOpen);
65
+ return webviewIntent.call('showInAppBrowser', {
66
+ url: urlToOpen
67
+ });
61
68
 
62
69
  case 13:
63
70
  result = _context.sent;
@@ -89,6 +96,89 @@ var InAppBrowser = function InAppBrowser(_ref) {
89
96
  return _insideEffect.apply(this, arguments);
90
97
  }
91
98
 
99
+ insideEffect();
100
+ return function cleanup() {
101
+ webviewIntent.call('closeInAppBrowser');
102
+ };
103
+ }, [isReady, url, onClose, webviewIntent]);
104
+ return null;
105
+ };
106
+
107
+ var InAppBrowserWithIntentsApi = function InAppBrowserWithIntentsApi(_ref3) {
108
+ var url = _ref3.url,
109
+ onClose = _ref3.onClose,
110
+ _ref3$intentsApi = _ref3.intentsApi,
111
+ intentsApi = _ref3$intentsApi === void 0 ? {} : _ref3$intentsApi;
112
+ var fetchSessionCode = intentsApi.fetchSessionCode,
113
+ showInAppBrowser = intentsApi.showInAppBrowser,
114
+ closeInAppBrowser = intentsApi.closeInAppBrowser,
115
+ _intentsApi$tokenPara = intentsApi.tokenParamName,
116
+ tokenParamName = _intentsApi$tokenPara === void 0 ? 'session_code' : _intentsApi$tokenPara;
117
+ var isReady = Boolean((intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.fetchSessionCode) && (intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.showInAppBrowser) && (intentsApi === null || intentsApi === void 0 ? void 0 : intentsApi.closeInAppBrowser));
118
+ useEffect(function () {
119
+ function insideEffect() {
120
+ return _insideEffect2.apply(this, arguments);
121
+ }
122
+
123
+ function _insideEffect2() {
124
+ _insideEffect2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
125
+ var sessionCode, iabUrl, urlToOpen, result;
126
+ return _regeneratorRuntime.wrap(function _callee2$(_context2) {
127
+ while (1) {
128
+ switch (_context2.prev = _context2.next) {
129
+ case 0:
130
+ if (!isReady) {
131
+ _context2.next = 21;
132
+ break;
133
+ }
134
+
135
+ _context2.prev = 1;
136
+ logger.debug('url at the beginning: ', url);
137
+ _context2.next = 5;
138
+ return fetchSessionCode();
139
+
140
+ case 5:
141
+ sessionCode = _context2.sent;
142
+ logger.debug('got session code', sessionCode);
143
+ iabUrl = new URL(url);
144
+ iabUrl.searchParams.append(tokenParamName, sessionCode); // we need to decodeURIComponent since toString() encodes URL
145
+ // but native browser will also encode them.
146
+
147
+ urlToOpen = decodeURIComponent(iabUrl.toString());
148
+ logger.debug('url to open: ', urlToOpen);
149
+ _context2.next = 13;
150
+ return showInAppBrowser(urlToOpen);
151
+
152
+ case 13:
153
+ result = _context2.sent;
154
+
155
+ if ((result === null || result === void 0 ? void 0 : result.type) !== 'dismiss' && (result === null || result === void 0 ? void 0 : result.type) !== 'cancel') {
156
+ logger.error('Unexpected InAppBrowser result', result);
157
+ }
158
+
159
+ _context2.next = 20;
160
+ break;
161
+
162
+ case 17:
163
+ _context2.prev = 17;
164
+ _context2.t0 = _context2["catch"](1);
165
+ logger.error('unexpected fetchSessionCode result', _context2.t0);
166
+
167
+ case 20:
168
+ if (onClose) {
169
+ onClose();
170
+ }
171
+
172
+ case 21:
173
+ case "end":
174
+ return _context2.stop();
175
+ }
176
+ }
177
+ }, _callee2, null, [[1, 17]]);
178
+ }));
179
+ return _insideEffect2.apply(this, arguments);
180
+ }
181
+
92
182
  insideEffect();
93
183
  return function cleanup() {
94
184
  closeInAppBrowser();
@@ -118,7 +118,7 @@ export var getOAuthUrl = function getOAuthUrl(_ref) {
118
118
  oAuthUrl.searchParams.set('nonce', nonce);
119
119
 
120
120
  if (oAuthConf.scope !== undefined && oAuthConf.scope !== null && oAuthConf.scope !== false) {
121
- var urlScope = Array.isArray(oAuthConf.scope) ? oAuthConf.scope.join('+') : oAuthConf.scope;
121
+ var urlScope = Array.isArray(oAuthConf.scope) ? oAuthConf.scope.join('%2B') : oAuthConf.scope;
122
122
  oAuthUrl.searchParams.set('scope', urlScope);
123
123
  }
124
124
 
@@ -53,7 +53,7 @@ describe('Oauth helper', function () {
53
53
  scope: ['thescope', 'thescope2']
54
54
  }
55
55
  }));
56
- expect(url).toEqual('https://cozyurl/accounts/testslug/start?state=statekey&nonce=1234&scope=thescope+thescope2');
56
+ expect(url).toEqual('https://cozyurl/accounts/testslug/start?state=statekey&nonce=1234&scope=thescope%2Bthescope2');
57
57
  });
58
58
  it('should use redirectSlug if present', function () {
59
59
  var url = getOAuthUrl(_objectSpread(_objectSpread({}, defaultConf), {}, {
@@ -33,6 +33,7 @@ import { waitForRealtimeEvent } from './jobUtils';
33
33
  import '../types';
34
34
  import { LOGIN_SUCCESS_EVENT } from '../models/flowEvents';
35
35
  var TEMP_TOKEN_TIMOUT_S = 60;
36
+ export var ACCOUNTS_DOCTYPE = 'io.cozy.accounts';
36
37
  export var isBiWebViewConnector = function isBiWebViewConnector(konnector) {
37
38
  return flag('harvest.bi.webview') && isBudgetInsightConnector(konnector);
38
39
  };
@@ -201,7 +202,7 @@ export var handleOAuthAccount = /*#__PURE__*/function () {
201
202
  logger.info("Found a BI webview connection id: ".concat(connectionId));
202
203
  flow.konnector = konnector;
203
204
  _context3.next = 12;
204
- return flow.saveAccount(setBIConnectionId(biWebviewAccount, connectionId));
205
+ return flow.saveAccount(_objectSpread(_objectSpread({}, setBIConnectionId(biWebviewAccount, connectionId)), getBiAggregatorParentRelationship(konnector)));
205
206
 
206
207
  case 12:
207
208
  biWebviewAccount = _context3.sent;
@@ -228,6 +229,34 @@ export var handleOAuthAccount = /*#__PURE__*/function () {
228
229
  return _ref6.apply(this, arguments);
229
230
  };
230
231
  }();
232
+ /**
233
+ * Return the bi aggregator parent relationship configuration for a given konnector
234
+ *
235
+ * @param {io.cozy.konnectors} konnector connector manifest content
236
+ *
237
+ * @return {Object}
238
+ */
239
+
240
+ var getBiAggregatorParentRelationship = function getBiAggregatorParentRelationship(konnector) {
241
+ var _konnector$aggregator;
242
+
243
+ var biAggregatorId = konnector === null || konnector === void 0 ? void 0 : (_konnector$aggregator = konnector.aggregator) === null || _konnector$aggregator === void 0 ? void 0 : _konnector$aggregator.accountId;
244
+
245
+ if (!biAggregatorId) {
246
+ return {};
247
+ }
248
+
249
+ return {
250
+ relationships: {
251
+ parent: {
252
+ data: {
253
+ _id: biAggregatorId,
254
+ _type: ACCOUNTS_DOCTYPE
255
+ }
256
+ }
257
+ }
258
+ };
259
+ };
231
260
  /**
232
261
  * Gets BI webview connection id which is returned in the account by the stack
233
262
  * via oauth callback url
@@ -237,6 +266,7 @@ export var handleOAuthAccount = /*#__PURE__*/function () {
237
266
  * @return {Integer} Connection Id
238
267
  */
239
268
 
269
+
240
270
  var getWebviewBIConnectionId = function getWebviewBIConnectionId(account) {
241
271
  var _account$oauth, _account$oauth$query, _account$oauth$query$;
242
272
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-harvest-lib",
3
- "version": "9.22.2",
3
+ "version": "9.23.1",
4
4
  "description": "Provides logic, modules and components for Cozy's harvest applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -29,7 +29,7 @@
29
29
  "@cozy/minilog": "^1.0.0",
30
30
  "@sentry/browser": "^6.0.1",
31
31
  "cozy-bi-auth": "0.0.25",
32
- "cozy-doctypes": "^1.84.0",
32
+ "cozy-doctypes": "^1.85.0",
33
33
  "cozy-logger": "^1.9.0",
34
34
  "date-fns": "^1.30.1",
35
35
  "final-form": "^4.18.5",
@@ -77,6 +77,7 @@
77
77
  "peerDependencies": {
78
78
  "@babel/runtime": ">=7.12.5",
79
79
  "@material-ui/core": ">=4",
80
+ "@material-ui/lab": "4.0.0-alpha.60",
80
81
  "cozy-client": ">=27.17.0",
81
82
  "cozy-device-helper": ">=1.10.2",
82
83
  "cozy-flags": ">=2.3.5",
@@ -89,5 +90,5 @@
89
90
  "react-router-dom": "^5.0.1"
90
91
  },
91
92
  "sideEffects": false,
92
- "gitHead": "10d835d98bc13d984796b18343cddce90b11ef37"
93
+ "gitHead": "3e2319da1add48da77bf8dd3f9c8113e831ea752"
93
94
  }
@@ -1,30 +1,73 @@
1
- import { useEffect } from 'react'
1
+ import React, { useEffect } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { useWebviewIntent } from 'cozy-intent'
4
4
  import logger from '../logger'
5
5
  import { intentsApiProptype } from '../helpers/proptypes'
6
6
 
7
- const InAppBrowser = ({ url, onClose, intentsApi = {} }) => {
7
+ const InAppBrowser = ({ url, onClose, intentsApi }) => {
8
+ if (intentsApi) {
9
+ return (
10
+ <InAppBrowserWithIntentsApi
11
+ url={url}
12
+ onClose={onClose}
13
+ intentsApi={intentsApi}
14
+ />
15
+ )
16
+ } else {
17
+ return <InAppBrowserWithWebviewIntent url={url} onClose={onClose} />
18
+ }
19
+ }
20
+
21
+ const InAppBrowserWithWebviewIntent = ({ url, onClose }) => {
8
22
  const webviewIntent = useWebviewIntent()
9
- const fetchSessionCode = intentsApi?.fetchSessionCode
10
- ? intentsApi?.fetchSessionCode
11
- : () => webviewIntent.call('fetchSessionCode')
12
- const showInAppBrowser = intentsApi?.showInAppBrowser
13
- ? intentsApi?.showInAppBrowser
14
- : url => webviewIntent.call('showInAppBrowser', { url })
15
- const closeInAppBrowser = intentsApi?.closeInAppBrowser
16
- ? intentsApi?.closeInAppBrowser
17
- : () => webviewIntent.call('closeInAppBrowser')
23
+ const isReady = Boolean(webviewIntent)
24
+ useEffect(() => {
25
+ async function insideEffect() {
26
+ if (isReady) {
27
+ try {
28
+ logger.debug('url at the beginning: ', url)
29
+ const sessionCode = await webviewIntent.call('fetchSessionCode')
30
+ logger.debug('got session code', sessionCode)
31
+ const iabUrl = new URL(url)
32
+ iabUrl.searchParams.append('session_code', sessionCode)
33
+ // we need to decodeURIComponent since toString() encodes URL
34
+ // but native browser will also encode them.
35
+ const urlToOpen = decodeURIComponent(iabUrl.toString())
36
+ logger.debug('url to open: ', urlToOpen)
37
+ const result = await webviewIntent.call('showInAppBrowser', {
38
+ url: urlToOpen
39
+ })
40
+ if (result?.type !== 'dismiss' && result?.type !== 'cancel') {
41
+ logger.error('Unexpected InAppBrowser result', result)
42
+ }
43
+ } catch (err) {
44
+ logger.error('unexpected fetchSessionCode result', err)
45
+ }
46
+ if (onClose) {
47
+ onClose()
48
+ }
49
+ }
50
+ }
51
+ insideEffect()
52
+ return function cleanup() {
53
+ webviewIntent.call('closeInAppBrowser')
54
+ }
55
+ }, [isReady, url, onClose, webviewIntent])
56
+ return null
57
+ }
18
58
 
19
- const tokenParamName = intentsApi?.tokenParamName
20
- ? intentsApi?.tokenParamName
21
- : 'session_code'
59
+ const InAppBrowserWithIntentsApi = ({ url, onClose, intentsApi = {} }) => {
60
+ const {
61
+ fetchSessionCode,
62
+ showInAppBrowser,
63
+ closeInAppBrowser,
64
+ tokenParamName = 'session_code'
65
+ } = intentsApi
22
66
 
23
67
  const isReady = Boolean(
24
- webviewIntent ||
25
- (intentsApi?.fetchSessionCode &&
26
- intentsApi?.showInAppBrowser &&
27
- intentsApi?.closeInAppBrowser)
68
+ intentsApi?.fetchSessionCode &&
69
+ intentsApi?.showInAppBrowser &&
70
+ intentsApi?.closeInAppBrowser
28
71
  )
29
72
 
30
73
  useEffect(() => {
@@ -52,6 +95,7 @@ const InAppBrowser = ({ url, onClose, intentsApi = {} }) => {
52
95
  }
53
96
  }
54
97
  }
98
+
55
99
  insideEffect()
56
100
  return function cleanup() {
57
101
  closeInAppBrowser()
@@ -130,7 +130,7 @@ export const getOAuthUrl = ({
130
130
  oAuthConf.scope !== false
131
131
  ) {
132
132
  const urlScope = Array.isArray(oAuthConf.scope)
133
- ? oAuthConf.scope.join('+')
133
+ ? oAuthConf.scope.join('%2B')
134
134
  : oAuthConf.scope
135
135
  oAuthUrl.searchParams.set('scope', urlScope)
136
136
  }
@@ -50,7 +50,7 @@ describe('Oauth helper', () => {
50
50
  oAuthConf: { scope: ['thescope', 'thescope2'] }
51
51
  })
52
52
  expect(url).toEqual(
53
- 'https://cozyurl/accounts/testslug/start?state=statekey&nonce=1234&scope=thescope+thescope2'
53
+ 'https://cozyurl/accounts/testslug/start?state=statekey&nonce=1234&scope=thescope%2Bthescope2'
54
54
  )
55
55
  })
56
56
  it('should use redirectSlug if present', () => {
@@ -23,6 +23,7 @@ import '../types'
23
23
  import { LOGIN_SUCCESS_EVENT } from '../models/flowEvents'
24
24
 
25
25
  const TEMP_TOKEN_TIMOUT_S = 60
26
+ export const ACCOUNTS_DOCTYPE = 'io.cozy.accounts'
26
27
 
27
28
  export const isBiWebViewConnector = konnector =>
28
29
  flag('harvest.bi.webview') && isBudgetInsightConnector(konnector)
@@ -130,9 +131,11 @@ export const handleOAuthAccount = async ({
130
131
  if (connectionId) {
131
132
  logger.info(`Found a BI webview connection id: ${connectionId}`)
132
133
  flow.konnector = konnector
133
- biWebviewAccount = await flow.saveAccount(
134
- setBIConnectionId(biWebviewAccount, connectionId)
135
- )
134
+
135
+ biWebviewAccount = await flow.saveAccount({
136
+ ...setBIConnectionId(biWebviewAccount, connectionId),
137
+ ...getBiAggregatorParentRelationship(konnector)
138
+ })
136
139
 
137
140
  await flow.handleFormSubmit({
138
141
  client,
@@ -145,6 +148,30 @@ export const handleOAuthAccount = async ({
145
148
  return connectionId
146
149
  }
147
150
 
151
+ /**
152
+ * Return the bi aggregator parent relationship configuration for a given konnector
153
+ *
154
+ * @param {io.cozy.konnectors} konnector connector manifest content
155
+ *
156
+ * @return {Object}
157
+ */
158
+ const getBiAggregatorParentRelationship = konnector => {
159
+ const biAggregatorId = konnector?.aggregator?.accountId
160
+ if (!biAggregatorId) {
161
+ return {}
162
+ }
163
+ return {
164
+ relationships: {
165
+ parent: {
166
+ data: {
167
+ _id: biAggregatorId,
168
+ _type: ACCOUNTS_DOCTYPE
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+
148
175
  /**
149
176
  * Gets BI webview connection id which is returned in the account by the stack
150
177
  * via oauth callback url