cozy-client 27.12.0 → 27.14.2

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,44 @@
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
+ ## [27.14.2](https://github.com/cozy/cozy-client/compare/v27.14.1...v27.14.2) (2022-01-28)
7
+
8
+ **Note:** Version bump only for package cozy-client
9
+
10
+
11
+
12
+
13
+
14
+ ## [27.14.1](https://github.com/cozy/cozy-client/compare/v27.14.0...v27.14.1) (2022-01-28)
15
+
16
+ **Note:** Version bump only for package cozy-client
17
+
18
+
19
+
20
+
21
+
22
+ # [27.14.0](https://github.com/cozy/cozy-client/compare/v27.13.0...v27.14.0) (2022-01-25)
23
+
24
+
25
+ ### Features
26
+
27
+ * Add rootCozyUrl helper ([ca09e71](https://github.com/cozy/cozy-client/commit/ca09e71a7b687bf28055ed0764d08c4dbd7e70cc))
28
+
29
+
30
+
31
+
32
+
33
+ # [27.13.0](https://github.com/cozy/cozy-client/compare/v27.12.0...v27.13.0) (2022-01-24)
34
+
35
+
36
+ ### Features
37
+
38
+ * Add fetchBlobFileById function ([eb37b1a](https://github.com/cozy/cozy-client/commit/eb37b1af091d3e95372a0540ad0f14ba59e74a4c))
39
+
40
+
41
+
42
+
43
+
6
44
  # [27.12.0](https://github.com/cozy/cozy-client/compare/v27.11.1...v27.12.0) (2022-01-14)
7
45
 
8
46
 
@@ -2293,6 +2293,7 @@ var CozyClient = /*#__PURE__*/function () {
2293
2293
  * At any time put an error function
2294
2294
  *
2295
2295
  * @param {Function} [onError] - Set a callback for queries which are errored
2296
+ * @throws {Error} onError should not have been defined yet
2296
2297
  */
2297
2298
 
2298
2299
  }, {
@@ -2463,7 +2464,7 @@ var CozyClient = /*#__PURE__*/function () {
2463
2464
  CozyClient.hooks = CozyClient.hooks || {};
2464
2465
  CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
2465
2466
 
2466
- CozyClient.version = "27.12.0";
2467
+ CozyClient.version = "27.14.2";
2467
2468
 
2468
2469
  _microee.default.mixin(CozyClient);
2469
2470
 
@@ -1140,7 +1140,7 @@ describe('CozyClient', () => {
1140
1140
  })
1141
1141
 
1142
1142
  it('should first dispatch a INIT_QUERY action', async () => {
1143
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1143
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1144
1144
  await client.query(query, { as: 'allTodos' })
1145
1145
  expect(client.store.dispatch.mock.calls[0][0]).toEqual(
1146
1146
  initQuery('allTodos', { doctype: 'io.cozy.todos' }, { as: 'allTodos' })
@@ -1148,7 +1148,7 @@ describe('CozyClient', () => {
1148
1148
  })
1149
1149
 
1150
1150
  it('should then dispatch a RECEIVE_QUERY_RESULT action', async () => {
1151
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1151
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1152
1152
  await client.query(query, { as: 'allTodos' })
1153
1153
  const dispatchCalls = client.store.dispatch.mock.calls
1154
1154
  const lastDispatchCall = dispatchCalls[dispatchCalls.length - 1]
@@ -1159,7 +1159,7 @@ describe('CozyClient', () => {
1159
1159
 
1160
1160
  it('should dispatch a RECEIVE_QUERY_ERROR action if an error occurs', async () => {
1161
1161
  const error = new Error('Fake error')
1162
- requestHandler.mockReturnValueOnce(Promise.reject(error))
1162
+ requestHandler.mockRejectedValueOnce(error)
1163
1163
  try {
1164
1164
  await client.query(query, { as: 'allTodos' })
1165
1165
  } catch (e) {} // eslint-disable-line no-empty
@@ -1169,13 +1169,13 @@ describe('CozyClient', () => {
1169
1169
  })
1170
1170
 
1171
1171
  it('should resolve to the query response', async () => {
1172
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1172
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1173
1173
  const resp = await client.query(query)
1174
1174
  expect(resp).toEqual(fakeResponse)
1175
1175
  })
1176
1176
 
1177
1177
  it('should call the link with the query', async () => {
1178
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1178
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1179
1179
  await client.query(query)
1180
1180
  expect(requestHandler).toHaveBeenCalledTimes(1)
1181
1181
  expect(requestHandler.mock.calls[0][0]).toBe(query)
@@ -1200,8 +1200,8 @@ describe('CozyClient', () => {
1200
1200
  ]
1201
1201
  })
1202
1202
  )
1203
- .mockReturnValueOnce(Promise.resolve({ data: [], included: [] }))
1204
- .mockReturnValueOnce(Promise.resolve({ data: [], included: [] }))
1203
+ .mockResolvedValueOnce({ data: [], included: [] })
1204
+ .mockResolvedValueOnce({ data: [], included: [] })
1205
1205
 
1206
1206
  const resp = await client.query(
1207
1207
  Q('io.cozy.todos').include(['attachments'])
@@ -1246,7 +1246,7 @@ describe('CozyClient', () => {
1246
1246
  })
1247
1247
 
1248
1248
  it('should set the correct name to the query', async () => {
1249
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1249
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1250
1250
  const getById = Q('io.cozy.files').getById('1')
1251
1251
  await client.query(getById)
1252
1252
  expect(client.store.dispatch.mock.calls[0][0]).toEqual(
@@ -1256,7 +1256,7 @@ describe('CozyClient', () => {
1256
1256
  )
1257
1257
  })
1258
1258
  it('should use the name passed as an argument', async () => {
1259
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1259
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1260
1260
  const getById = Q('io.cozy.files').getById('1')
1261
1261
  await client.query(getById, { as: 'toto' })
1262
1262
  expect(client.store.dispatch.mock.calls[0][0]).toEqual(
@@ -1266,7 +1266,7 @@ describe('CozyClient', () => {
1266
1266
  )
1267
1267
  })
1268
1268
  it('should name the query correctly', async () => {
1269
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1269
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1270
1270
  await client.query(query, { as: 'allTodos' })
1271
1271
  expect(client.store.dispatch.mock.calls[0][0]).toEqual(
1272
1272
  initQuery('allTodos', { doctype: 'io.cozy.todos' }, { as: 'allTodos' })
@@ -1353,7 +1353,7 @@ describe('CozyClient', () => {
1353
1353
  })
1354
1354
 
1355
1355
  it('should dispatch a INIT_QUERY action if status is not loaded', async () => {
1356
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1356
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1357
1357
  getQueryFromState.mockReturnValueOnce({
1358
1358
  fetchStatus: 'pending'
1359
1359
  })
@@ -1364,7 +1364,7 @@ describe('CozyClient', () => {
1364
1364
  })
1365
1365
 
1366
1366
  it('should dispatch a INIT_QUERY action if no skip and no bookmark', async () => {
1367
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1367
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1368
1368
  getQueryFromState.mockReturnValueOnce({
1369
1369
  fetchStatus: 'loaded'
1370
1370
  })
@@ -1375,7 +1375,7 @@ describe('CozyClient', () => {
1375
1375
  })
1376
1376
 
1377
1377
  it('should dispatch a RECEIVE_QUERY_RESULT action if query has skip', async () => {
1378
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1378
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1379
1379
  getQueryFromState.mockReturnValueOnce({
1380
1380
  fetchStatus: 'loaded'
1381
1381
  })
@@ -1389,7 +1389,7 @@ describe('CozyClient', () => {
1389
1389
  })
1390
1390
 
1391
1391
  it('should dispatch a RECEIVE_QUERY_RESULT action if query has bookmark', async () => {
1392
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1392
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1393
1393
  getQueryFromState.mockReturnValueOnce({
1394
1394
  fetchStatus: 'loaded'
1395
1395
  })
@@ -1402,52 +1402,54 @@ describe('CozyClient', () => {
1402
1402
  )
1403
1403
  })
1404
1404
 
1405
- const setupOnError = () => {
1406
- const error = new TypeError('Failed to fetch')
1407
- requestHandler.mockReturnValueOnce(Promise.reject(error))
1408
- const onError = jest.fn().mockReturnValueOnce(() => true)
1409
- return onError
1410
- }
1405
+ describe('setupOnError', () => {
1406
+ const setupOnError = () => {
1407
+ const error = new TypeError('Failed to fetch')
1408
+ requestHandler.mockRejectedValueOnce(error)
1409
+ return jest.fn().mockReturnValueOnce(() => true)
1410
+ }
1411
1411
 
1412
- it('should call onError callback when an error is thrown. Callback passed as argument', async () => {
1413
- const onError = setupOnError()
1412
+ it('should call onError callback when an error is thrown. Callback passed as argument', async () => {
1413
+ const onError = setupOnError()
1414
1414
 
1415
- await client.query(query, { as: 'allTodos', onError })
1416
- expect(onError).toBeCalled()
1417
- })
1415
+ await client.query(query, { as: 'allTodos', onError })
1416
+ expect(onError).toBeCalled()
1417
+ })
1418
1418
 
1419
- it('should call onError callback when an error is catched. It is passed during instantiation', async () => {
1420
- const onError = setupOnError()
1421
- const client = new CozyClient({ onError })
1419
+ it('should call onError callback when an error is caught. It is passed during instantiation', async () => {
1420
+ const setupOnError = () => jest.fn().mockReturnValueOnce(() => true)
1421
+ const onError = setupOnError()
1422
+ const client = new CozyClient({ onError })
1422
1423
 
1423
- await client.query(query, { as: 'allTodos' })
1424
- expect(onError).toBeCalled()
1425
- })
1424
+ await client.query(query, { as: 'allTodos' })
1425
+ expect(onError).toBeCalled()
1426
+ })
1426
1427
 
1427
- it('should call onError callback when an error is catched. It is passed with setOnError', async () => {
1428
- const onError = setupOnError()
1429
- client.setOnError(onError)
1428
+ it('should call onError callback when an error is catched. It is passed with setOnError', async () => {
1429
+ const onError = setupOnError()
1430
+ client.setOnError(onError)
1430
1431
 
1431
- await client.query(query, { as: 'allTodos' })
1432
- expect(onError).toBeCalled()
1433
- })
1432
+ await client.query(query, { as: 'allTodos' })
1433
+ expect(onError).toBeCalled()
1434
+ })
1434
1435
 
1435
- it('should throw an error when we call setOnError twice (or several times)', async () => {
1436
- const client = new CozyClient({})
1437
- const onError = () => true
1436
+ it('should throw an error when we call setOnError twice (or several times)', async () => {
1437
+ const client = new CozyClient({})
1438
+ const onError = () => true
1438
1439
 
1439
- const funcShouldThrow = () => client.setOnError(onError)
1440
- expect(funcShouldThrow).not.toThrow()
1441
- expect(funcShouldThrow).toThrow('On Error is already defined')
1442
- })
1440
+ const funcShouldThrow = () => client.setOnError(onError)
1441
+ expect(funcShouldThrow).not.toThrow()
1442
+ expect(funcShouldThrow).toThrow('On Error is already defined')
1443
+ })
1443
1444
 
1444
- it('should throw an error when there is no onError callback', async () => {
1445
- const error = new TypeError('Failed to fetch')
1446
- requestHandler.mockReturnValueOnce(Promise.reject(error))
1445
+ it('should throw an error when there is no onError callback', async () => {
1446
+ const error = new TypeError('Failed to fetch')
1447
+ requestHandler.mockRejectedValueOnce(error)
1447
1448
 
1448
- await expect(client.query(query, { as: 'allTodos' })).rejects.toThrow(
1449
- 'Failed to fetch'
1450
- )
1449
+ await expect(client.query(query, { as: 'allTodos' })).rejects.toThrow(
1450
+ 'Failed to fetch'
1451
+ )
1452
+ })
1451
1453
  })
1452
1454
  })
1453
1455
 
@@ -1546,7 +1548,7 @@ describe('CozyClient', () => {
1546
1548
  })
1547
1549
 
1548
1550
  it('should then dispatch a RECEIVE_MUTATION_RESULT action', async () => {
1549
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1551
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1550
1552
  await client.mutate(mutation, { as: 'updateTodo' })
1551
1553
  expect(client.store.dispatch.mock.calls[1][0]).toEqual(
1552
1554
  receiveMutationResult('updateTodo', fakeResponse, {}, mutation)
@@ -1554,14 +1556,14 @@ describe('CozyClient', () => {
1554
1556
  })
1555
1557
 
1556
1558
  it('should resolve to the mutation response', async () => {
1557
- requestHandler.mockReturnValueOnce(Promise.resolve(fakeResponse))
1559
+ requestHandler.mockResolvedValueOnce(fakeResponse)
1558
1560
  const resp = await client.mutate(mutation)
1559
1561
  expect(resp).toEqual(fakeResponse)
1560
1562
  })
1561
1563
 
1562
1564
  it('should dispatch a RECEIVE_MUTATION_ERROR action if an error occurs', async () => {
1563
1565
  const error = new Error('Fake error')
1564
- requestHandler.mockReturnValueOnce(Promise.reject(error))
1566
+ requestHandler.mockRejectedValueOnce(error)
1565
1567
  try {
1566
1568
  await client.mutate(mutation, { as: 'updateTodo' })
1567
1569
  } catch (e) {} // eslint-disable-line no-empty
@@ -1573,7 +1575,7 @@ describe('CozyClient', () => {
1573
1575
  it('should handle an array of mutations (including mutation creators)', async () => {
1574
1576
  const FAKE_MUTATION_1 = { mutationType: 'FAKE_1' }
1575
1577
  const FAKE_MUTATION_2 = resp => ({ mutationType: 'FAKE_2', resp })
1576
- requestHandler.mockReturnValue(Promise.resolve(fakeResponse))
1578
+ requestHandler.mockResolvedValue(fakeResponse)
1577
1579
  await client.mutate([mutation, FAKE_MUTATION_1, FAKE_MUTATION_2])
1578
1580
  expect(requestHandler).toHaveBeenCalledTimes(3)
1579
1581
  expect(requestHandler.mock.calls[0][0]).toBe(mutation)
@@ -119,7 +119,7 @@ describe('Associations', () => {
119
119
  ],
120
120
  next: false
121
121
  }
122
- requestHandler.mockReturnValueOnce(Promise.resolve(FAKE_RESPONSE))
122
+ requestHandler.mockResolvedValueOnce(FAKE_RESPONSE)
123
123
 
124
124
  const queryBefore = getQueryFromStore(client.store, 'allTodos')
125
125
  await getTodo(TODO_1._id).attachments.fetchMore()
package/dist/helpers.js CHANGED
@@ -5,12 +5,34 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.generateWebLink = exports.dehydrate = void 0;
8
+ exports.rootCozyUrl = exports.InvalidCozyUrlError = exports.InvalidProtocolError = exports.generateWebLink = exports.dehydrate = void 0;
9
+
10
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
+
12
+ var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray"));
13
+
14
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
15
+
16
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
17
+
18
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
19
+
20
+ var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
21
+
22
+ var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
23
+
24
+ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
25
+
26
+ var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));
9
27
 
10
28
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
29
 
12
30
  var _associations = require("./associations");
13
31
 
32
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
33
+
34
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
35
+
14
36
  function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
15
37
 
16
38
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
@@ -103,4 +125,275 @@ var generateWebLink = function generateWebLink(_ref3) {
103
125
  return url.toString();
104
126
  };
105
127
 
106
- exports.generateWebLink = generateWebLink;
128
+ exports.generateWebLink = generateWebLink;
129
+
130
+ var InvalidProtocolError = /*#__PURE__*/function (_Error) {
131
+ (0, _inherits2.default)(InvalidProtocolError, _Error);
132
+
133
+ var _super = _createSuper(InvalidProtocolError);
134
+
135
+ function InvalidProtocolError(url) {
136
+ var _this;
137
+
138
+ (0, _classCallCheck2.default)(this, InvalidProtocolError);
139
+ _this = _super.call(this, "Invalid URL protocol ".concat(url.protocol));
140
+ _this.url = url;
141
+ return _this;
142
+ }
143
+
144
+ return InvalidProtocolError;
145
+ }( /*#__PURE__*/(0, _wrapNativeSuper2.default)(Error));
146
+
147
+ exports.InvalidProtocolError = InvalidProtocolError;
148
+
149
+ var InvalidCozyUrlError = /*#__PURE__*/function (_Error2) {
150
+ (0, _inherits2.default)(InvalidCozyUrlError, _Error2);
151
+
152
+ var _super2 = _createSuper(InvalidCozyUrlError);
153
+
154
+ function InvalidCozyUrlError(url) {
155
+ var _this2;
156
+
157
+ (0, _classCallCheck2.default)(this, InvalidCozyUrlError);
158
+ _this2 = _super2.call(this, "URL ".concat(url.toString(), " does not seem to be a valid Cozy URL"));
159
+ _this2.url = url;
160
+ return _this2;
161
+ }
162
+
163
+ return InvalidCozyUrlError;
164
+ }( /*#__PURE__*/(0, _wrapNativeSuper2.default)(Error));
165
+ /* uri - Returns a well formed URL origin from a protocol, a hostname and a port
166
+ *
167
+ * If the protocol and/or port are omitted from the argument, the function will
168
+ * default to HTTPS and omit the port in the returned origin.
169
+ *
170
+ * @param {object} url Object of URL elements
171
+ * @param {string} url.protocol Protocol to use in the origin (e.g. http)
172
+ * @param {string} url.hostname Hostname to use in the origin (e.g. claude.mycozy.cloud)
173
+ * @param {string} url.port Port to use in the origin (e.g. 8080)
174
+ *
175
+ * @returns {string} Generated URL origin
176
+ */
177
+
178
+
179
+ exports.InvalidCozyUrlError = InvalidCozyUrlError;
180
+
181
+ var uri = function uri(_ref4) {
182
+ var protocol = _ref4.protocol,
183
+ hostname = _ref4.hostname,
184
+ port = _ref4.port;
185
+ return (protocol !== '' ? "".concat(protocol, "//") : 'https://') + hostname + (port !== '' ? ":".concat(port) : '');
186
+ };
187
+ /* wellKnownUrl - Returns a valid URL string to a Well Known password change page
188
+ *
189
+ * The built URL will point to the origin generated from the given protocol,
190
+ * hostname and port.
191
+ *
192
+ * @param {object} url Object of URL elements
193
+ * @param {string} url.protocol Protocol to use in the origin (e.g. http)
194
+ * @param {string} url.hostname Hostname to use in the origin (e.g. claude.mycozy.cloud)
195
+ * @param {string} url.port Port to use in the origin (e.g. 8080)
196
+ *
197
+ * @returns {string} Generated Well Known password change URL string
198
+ */
199
+
200
+
201
+ var wellKnownUrl = function wellKnownUrl(url) {
202
+ return uri(url) + '/.well-known/change-password';
203
+ };
204
+ /* isValidOrigin - Checks whether a given URL is a valid Cozy origin
205
+ *
206
+ * This method tries to fetch the Well Known change password page of the Cozy
207
+ * supposedly at the given origin. This allows us to determine whether the given
208
+ * origin is the root URL of a Cozy or not via the status of the response:
209
+ * - a 200 response status means there's an actual Well Known password change
210
+ * page accessible from the given origin so we suppose it's a valid Cozy
211
+ * origin (i.e. it could be another site altogether though)
212
+ * - a 401 response status means the pointed page requires authentication so the
213
+ * origin is probably pointing to a Cozy app
214
+ * - another status means there aren't any Cozy behind to the given origin
215
+ *
216
+ * @param {object} url Object of URL elements
217
+ * @param {string} url.protocol Protocol to use in the origin (e.g. http)
218
+ * @param {string} url.hostname Hostname to use in the origin (e.g. claude.mycozy.cloud)
219
+ * @param {string} url.port Port to use in the origin (e.g. 8080)
220
+ *
221
+ * @returns {Promise<boolean>} True if we believe there's a Cozy behind the given origin
222
+ * @throws {InvalidCozyUrlError} Thrown when we know for sure there aren't any Cozy behind the given origin
223
+ */
224
+
225
+
226
+ var isValidOrigin = /*#__PURE__*/function () {
227
+ var _ref5 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(url) {
228
+ var _yield$fetch, status;
229
+
230
+ return _regenerator.default.wrap(function _callee$(_context) {
231
+ while (1) {
232
+ switch (_context.prev = _context.next) {
233
+ case 0:
234
+ _context.next = 2;
235
+ return fetch(wellKnownUrl(url));
236
+
237
+ case 2:
238
+ _yield$fetch = _context.sent;
239
+ status = _yield$fetch.status;
240
+
241
+ if (!(status === 404)) {
242
+ _context.next = 6;
243
+ break;
244
+ }
245
+
246
+ throw new InvalidCozyUrlError(url);
247
+
248
+ case 6:
249
+ return _context.abrupt("return", status === 200);
250
+
251
+ case 7:
252
+ case "end":
253
+ return _context.stop();
254
+ }
255
+ }
256
+ }, _callee);
257
+ }));
258
+
259
+ return function isValidOrigin(_x) {
260
+ return _ref5.apply(this, arguments);
261
+ };
262
+ }();
263
+ /**
264
+ * rootCozyUrl - Get the root URL of a Cozy from more precise ones
265
+ *
266
+ * The goal is to allow users to use any URL copied from their browser as their
267
+ * Cozy URL rather than trying to explain to them what we expect (e.g. when
268
+ * requesting the Cozy URL to connect an app).
269
+ * If we can't get the root URL either because there's no Cozy or the domain
270
+ * does not exist or anything else, we'll throw an InvalidCozyUrlError.
271
+ * Also, since we communicate only via HTTP or HTTPS, we'll throw an
272
+ * InvalidProtocolError if any other protocol is used.
273
+ *
274
+ * This function expects a fully qualified URL thus with a protocol and a valid
275
+ * hostname. If your application accepts Cozy intances as input (e.g. `claude`
276
+ * when the Cozy can be found at `https://claude.mycozy.cloud`), it is your
277
+ * responsibility to add the appropriate domain to the hostname before calling
278
+ * this function.
279
+ *
280
+ * Examples:
281
+ *
282
+ * 1. getting the root URL when your user gives you its instance name
283
+ *
284
+ * const userInput = 'claude'
285
+ * const rootUrl = await rootCozyUrl(new URL(`https://${userInput}.mycozy.cloud`))
286
+ * // → returns new URL('https://claude.mycozy.cloud')
287
+ *
288
+ * 2. getting the root URL when your user gives you a Cozy Drive URL
289
+ *
290
+ * const userInput = 'https://claude-drive.mycozy.cloud/#/folder/io.cozy.files.root-dir'
291
+ * const rootUrl = await rootCozyUrl(new URL(userInput))
292
+ * // → returns new URL('https://claude.mycozy.cloud')
293
+ *
294
+ * 3. getting the root URL when the Cozy uses nested sub-domains
295
+ *
296
+ * const userInput = 'http://photos.camille.nimbus.com:8080/#/album/1234567890'
297
+ * const rootCozyUrl = await rootCozyUrl(new URL(userInput))
298
+ * // → returns new URL('http://camille.nimbus.com:8080')
299
+ *
300
+ * @param {URL} url The URL from which we'll try to get the root Cozy URL
301
+ *
302
+ * @returns {Promise<URL>} The root Cozy URL
303
+ */
304
+
305
+
306
+ var rootCozyUrl = /*#__PURE__*/function () {
307
+ var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(url) {
308
+ var _url$hostname$split, _url$hostname$split2, subDomain, domain, _hostname, hostname;
309
+
310
+ return _regenerator.default.wrap(function _callee2$(_context2) {
311
+ while (1) {
312
+ switch (_context2.prev = _context2.next) {
313
+ case 0:
314
+ if (['http:', 'https:'].includes(url.protocol)) {
315
+ _context2.next = 2;
316
+ break;
317
+ }
318
+
319
+ throw new InvalidProtocolError(url);
320
+
321
+ case 2:
322
+ _context2.next = 4;
323
+ return isValidOrigin(url);
324
+
325
+ case 4:
326
+ if (!_context2.sent) {
327
+ _context2.next = 6;
328
+ break;
329
+ }
330
+
331
+ return _context2.abrupt("return", url);
332
+
333
+ case 6:
334
+ if (!/^[^.-][^.]+-[^.-]+\./.test(url.hostname)) {
335
+ _context2.next = 13;
336
+ break;
337
+ }
338
+
339
+ _url$hostname$split = url.hostname.split('.'), _url$hostname$split2 = (0, _toArray2.default)(_url$hostname$split), subDomain = _url$hostname$split2[0], domain = _url$hostname$split2.slice(1);
340
+ _hostname = [subDomain.replace(/-.+/, '')].concat((0, _toConsumableArray2.default)(domain)).join('.');
341
+ _context2.next = 11;
342
+ return isValidOrigin({
343
+ protocol: url.protocol,
344
+ hostname: _hostname,
345
+ port: url.port
346
+ });
347
+
348
+ case 11:
349
+ if (!_context2.sent) {
350
+ _context2.next = 13;
351
+ break;
352
+ }
353
+
354
+ return _context2.abrupt("return", new URL(uri({
355
+ protocol: url.protocol,
356
+ hostname: _hostname,
357
+ port: url.port
358
+ })));
359
+
360
+ case 13:
361
+ // Try to remove the first sub-domain in case its a nested app name
362
+ // eslint-disable-next-line no-unused-vars
363
+ hostname = url.hostname.split('.').splice(1).join('.');
364
+ _context2.next = 16;
365
+ return isValidOrigin({
366
+ protocol: url.protocol,
367
+ hostname: hostname,
368
+ port: url.port
369
+ });
370
+
371
+ case 16:
372
+ if (!_context2.sent) {
373
+ _context2.next = 18;
374
+ break;
375
+ }
376
+
377
+ return _context2.abrupt("return", new URL(uri({
378
+ protocol: url.protocol,
379
+ hostname: hostname,
380
+ port: url.port
381
+ })));
382
+
383
+ case 18:
384
+ throw new InvalidCozyUrlError(url);
385
+
386
+ case 19:
387
+ case "end":
388
+ return _context2.stop();
389
+ }
390
+ }
391
+ }, _callee2);
392
+ }));
393
+
394
+ return function rootCozyUrl(_x2) {
395
+ return _ref6.apply(this, arguments);
396
+ };
397
+ }();
398
+
399
+ exports.rootCozyUrl = rootCozyUrl;
@@ -1,4 +1,12 @@
1
- import { dehydrate, generateWebLink } from './helpers'
1
+ import { enableFetchMocks, disableFetchMocks } from 'jest-fetch-mock'
2
+
3
+ import {
4
+ dehydrate,
5
+ generateWebLink,
6
+ rootCozyUrl,
7
+ InvalidCozyUrlError,
8
+ InvalidProtocolError
9
+ } from './helpers'
2
10
 
3
11
  import {
4
12
  HasManyInPlace,
@@ -141,3 +149,234 @@ describe('generateWebLink', () => {
141
149
  ).toEqual(`https://drive.alice.cozy.tools/public/#/files/432432`)
142
150
  })
143
151
  })
152
+
153
+ describe('rootCozyUrl', () => {
154
+ beforeAll(() => {
155
+ enableFetchMocks()
156
+ })
157
+
158
+ afterAll(() => {
159
+ disableFetchMocks()
160
+ })
161
+
162
+ it('should handle cozy-hosted https', async () => {
163
+ fetch.mockOnceIf(
164
+ 'https://camillenimbus.mycozy.cloud/.well-known/change-password',
165
+ {},
166
+ { status: 200 }
167
+ )
168
+
169
+ await expect(
170
+ rootCozyUrl(new URL('https://camillenimbus.mycozy.cloud'))
171
+ ).resolves.toEqual(new URL('https://camillenimbus.mycozy.cloud'))
172
+ })
173
+
174
+ it('should handle cozy-hosted https trailing slash', async () => {
175
+ fetch.mockOnceIf(
176
+ 'https://camillenimbus.mycozy.cloud/.well-known/change-password',
177
+ {},
178
+ { status: 200 }
179
+ )
180
+
181
+ await expect(
182
+ rootCozyUrl(new URL('https://camillenimbus.mycozy.cloud/'))
183
+ ).resolves.toEqual(new URL('https://camillenimbus.mycozy.cloud'))
184
+ })
185
+
186
+ it('should handle cozy-hosted https trailing path', async () => {
187
+ fetch.mockOnceIf(
188
+ 'https://camillenimbus.mycozy.cloud/.well-known/change-password',
189
+ {},
190
+ { status: 200 }
191
+ )
192
+
193
+ await expect(
194
+ rootCozyUrl(new URL('https://camillenimbus.mycozy.cloud/some-path'))
195
+ ).resolves.toEqual(new URL('https://camillenimbus.mycozy.cloud'))
196
+ })
197
+
198
+ it('should handle cozy-hosted drive web app url', async () => {
199
+ fetch.mockOnceIf(
200
+ 'https://camillenimbus-drive.mycozy.cloud/.well-known/change-password',
201
+ {},
202
+ { status: 401 }
203
+ )
204
+ fetch.mockOnceIf(
205
+ 'https://camillenimbus.mycozy.cloud/.well-known/change-password',
206
+ {},
207
+ { status: 200 }
208
+ )
209
+
210
+ await expect(
211
+ rootCozyUrl(new URL('https://camillenimbus-drive.mycozy.cloud/#/folder'))
212
+ ).resolves.toEqual(new URL('https://camillenimbus.mycozy.cloud'))
213
+ })
214
+
215
+ it('should handle cozy-hosted photos album url', async () => {
216
+ fetch.mockOnceIf(
217
+ 'https://camillenimbus-photos.mycozy.cloud/.well-known/change-password',
218
+ {},
219
+ { status: 401 }
220
+ )
221
+ fetch.mockOnceIf(
222
+ 'https://camillenimbus.mycozy.cloud/.well-known/change-password',
223
+ {},
224
+ { status: 200 }
225
+ )
226
+
227
+ await expect(
228
+ rootCozyUrl(
229
+ new URL(
230
+ 'https://camillenimbus-photos.mycozy.cloud/#/albums/68b5cda502ae29f5fa73fd89f1be4f92'
231
+ )
232
+ )
233
+ ).resolves.toEqual(new URL('https://camillenimbus.mycozy.cloud'))
234
+ })
235
+
236
+ it('should handle cozy-hosted app name', async () => {
237
+ fetch.mockOnceIf(
238
+ 'https://camillenimbus-drive.mycozy.cloud/.well-known/change-password',
239
+ {},
240
+ { status: 401 }
241
+ )
242
+ fetch.mockOnceIf(
243
+ 'https://camillenimbus.mycozy.cloud/.well-known/change-password',
244
+ {},
245
+ { status: 200 }
246
+ )
247
+
248
+ await expect(
249
+ rootCozyUrl(new URL('https://camillenimbus-drive.mycozy.cloud'))
250
+ ).resolves.toEqual(new URL('https://camillenimbus.mycozy.cloud'))
251
+ })
252
+
253
+ it('should handle self-hosted https', async () => {
254
+ fetch.mockOnceIf(
255
+ 'https://camillenimbus.com/.well-known/change-password',
256
+ {},
257
+ { status: 200 }
258
+ )
259
+
260
+ await expect(
261
+ rootCozyUrl(new URL('https://camillenimbus.com'))
262
+ ).resolves.toEqual(new URL('https://camillenimbus.com'))
263
+ })
264
+
265
+ it('should handle self-hosted with dash', async () => {
266
+ fetch.mockOnceIf(
267
+ 'https://camille-nimbus.com/.well-known/change-password',
268
+ {},
269
+ {
270
+ status: 200
271
+ }
272
+ )
273
+
274
+ await expect(
275
+ rootCozyUrl(new URL('https://camille-nimbus.com'))
276
+ ).resolves.toEqual(new URL('https://camille-nimbus.com'))
277
+ })
278
+
279
+ it('should handle self-hosted http', async () => {
280
+ fetch.mockOnceIf(
281
+ 'http://camille-nimbus.com/.well-known/change-password',
282
+ {},
283
+ {
284
+ status: 200
285
+ }
286
+ )
287
+
288
+ await expect(
289
+ rootCozyUrl(new URL('http://camille-nimbus.com'))
290
+ ).resolves.toEqual(new URL('http://camille-nimbus.com'))
291
+ })
292
+
293
+ it('should handle self-hosted https with port', async () => {
294
+ fetch.mockOnceIf(
295
+ 'https://camillenimbus.com:666/.well-known/change-password',
296
+ {},
297
+ {
298
+ status: 200
299
+ }
300
+ )
301
+
302
+ await expect(
303
+ rootCozyUrl(new URL('https://camillenimbus.com:666'))
304
+ ).resolves.toEqual(new URL('https://camillenimbus.com:666'))
305
+ })
306
+
307
+ it('should handle self-hosted nested drive web app url', async () => {
308
+ fetch.mockOnceIf(
309
+ 'https://drive.camillenimbus.com/.well-known/change-password',
310
+ {},
311
+ {
312
+ status: 401
313
+ }
314
+ )
315
+ fetch.mockOnceIf(
316
+ 'https://camillenimbus.com/.well-known/change-password',
317
+ {},
318
+ {
319
+ status: 200
320
+ }
321
+ )
322
+
323
+ await expect(
324
+ rootCozyUrl(new URL('https://drive.camillenimbus.com/#/folder'))
325
+ ).resolves.toEqual(new URL('https://camillenimbus.com'))
326
+ })
327
+
328
+ it('should handle self-hosted flat drive web app url', async () => {
329
+ fetch.mockOnceIf(
330
+ 'https://camille-drive.nimbus.com/.well-known/change-password',
331
+ {},
332
+ {
333
+ status: 401
334
+ }
335
+ )
336
+ fetch.mockOnceIf(
337
+ 'https://camille.nimbus.com/.well-known/change-password',
338
+ {},
339
+ {
340
+ status: 200
341
+ }
342
+ )
343
+
344
+ await expect(
345
+ rootCozyUrl(new URL('https://camille-drive.nimbus.com/#/folder'))
346
+ ).resolves.toEqual(new URL('https://camille.nimbus.com'))
347
+ })
348
+
349
+ it('should handle cozy.localhost', async () => {
350
+ await expect(
351
+ rootCozyUrl(new URL('http://cozy.localhost:8080'))
352
+ ).resolves.toEqual(new URL('http://cozy.localhost:8080'))
353
+ })
354
+
355
+ it('expects an http or https protocol', async () => {
356
+ fetch.mockOnceIf(
357
+ 'ftp://camillenimbus.com',
358
+ {},
359
+ {
360
+ status: 200
361
+ }
362
+ )
363
+
364
+ await expect(
365
+ rootCozyUrl(new URL('ftp://camillenimbus.com'))
366
+ ).rejects.toBeInstanceOf(InvalidProtocolError)
367
+ })
368
+
369
+ it('rejects if URL points to a valid cozy-stack but no valid Cozy instance', async () => {
370
+ fetch.mockOnceIf(
371
+ 'https://missing.mycozy.cloud',
372
+ {},
373
+ {
374
+ status: 404
375
+ }
376
+ )
377
+
378
+ await expect(
379
+ rootCozyUrl(new URL('https://missing.mycozy.cloud'))
380
+ ).rejects.toBeInstanceOf(InvalidCozyUrlError)
381
+ })
382
+ })
@@ -214,7 +214,7 @@ describe('use query', () => {
214
214
  links: [new CozyLink(requestHandler)]
215
215
  })
216
216
  const error = new TypeError('Failed to fetch')
217
- requestHandler.mockReturnValue(Promise.reject(error))
217
+ requestHandler.mockRejectedValue(error)
218
218
  const onError = jest.fn().mockReturnValue(() => true)
219
219
  setupQuery({
220
220
  customClient: client,
package/dist/index.js CHANGED
@@ -28,6 +28,9 @@ var _exportNames = {
28
28
  getReferencedById: true,
29
29
  dehydrate: true,
30
30
  generateWebLink: true,
31
+ rootCozyUrl: true,
32
+ InvalidCozyUrlError: true,
33
+ InvalidProtocolError: true,
31
34
  cancelable: true,
32
35
  isQueryLoading: true,
33
36
  hasQueryBeenLoaded: true,
@@ -172,6 +175,24 @@ Object.defineProperty(exports, "generateWebLink", {
172
175
  return _helpers2.generateWebLink;
173
176
  }
174
177
  });
178
+ Object.defineProperty(exports, "rootCozyUrl", {
179
+ enumerable: true,
180
+ get: function get() {
181
+ return _helpers2.rootCozyUrl;
182
+ }
183
+ });
184
+ Object.defineProperty(exports, "InvalidCozyUrlError", {
185
+ enumerable: true,
186
+ get: function get() {
187
+ return _helpers2.InvalidCozyUrlError;
188
+ }
189
+ });
190
+ Object.defineProperty(exports, "InvalidProtocolError", {
191
+ enumerable: true,
192
+ get: function get() {
193
+ return _helpers2.InvalidProtocolError;
194
+ }
195
+ });
175
196
  Object.defineProperty(exports, "cancelable", {
176
197
  enumerable: true,
177
198
  get: function get() {
@@ -24,6 +24,9 @@ var _exportNames = {
24
24
  HasManyTriggers: true,
25
25
  dehydrate: true,
26
26
  generateWebLink: true,
27
+ rootCozyUrl: true,
28
+ InvalidCozyUrlError: true,
29
+ InvalidProtocolError: true,
27
30
  cancelable: true,
28
31
  getQueryFromState: true,
29
32
  Registry: true,
@@ -133,6 +136,24 @@ Object.defineProperty(exports, "generateWebLink", {
133
136
  return _helpers.generateWebLink;
134
137
  }
135
138
  });
139
+ Object.defineProperty(exports, "rootCozyUrl", {
140
+ enumerable: true,
141
+ get: function get() {
142
+ return _helpers.rootCozyUrl;
143
+ }
144
+ });
145
+ Object.defineProperty(exports, "InvalidCozyUrlError", {
146
+ enumerable: true,
147
+ get: function get() {
148
+ return _helpers.InvalidCozyUrlError;
149
+ }
150
+ });
151
+ Object.defineProperty(exports, "InvalidProtocolError", {
152
+ enumerable: true,
153
+ get: function get() {
154
+ return _helpers.InvalidProtocolError;
155
+ }
156
+ });
136
157
  Object.defineProperty(exports, "cancelable", {
137
158
  enumerable: true,
138
159
  get: function get() {
@@ -8,7 +8,7 @@ Object.defineProperty(exports, "__esModule", {
8
8
  exports.normalize = normalize;
9
9
  exports.ensureFilePath = ensureFilePath;
10
10
  exports.getParentFolderId = getParentFolderId;
11
- exports.isFromKonnector = exports.hasCertifications = exports.hasQualifications = exports.isPlainText = exports.doMobileUpload = exports.readMobileFile = exports.uploadFileWithConflictStrategy = exports.generateFileNameForRevision = exports.generateNewFileNameOnConflict = exports.overrideFileForPath = exports.move = exports.getFullpath = exports.hasMetadataAttribute = exports.isReferencedByAlbum = exports.fetchFilesByQualificationRules = exports.saveFileQualification = exports.isSharingShorcutNew = exports.isSharingShortcutNew = exports.isSharingShorcut = exports.isSharingShortcut = exports.getSharingShortcutTargetDoctype = exports.getSharingShortcutTargetMime = exports.getSharingShortcutStatus = exports.isShortcut = exports.shouldBeOpenedByOnlyOffice = exports.isOnlyOfficeFile = exports.isNote = exports.isDirectory = exports.isFile = exports.splitFilename = exports.ALBUMS_DOCTYPE = void 0;
11
+ exports.fetchBlobFileById = exports.isFromKonnector = exports.hasCertifications = exports.hasQualifications = exports.isPlainText = exports.doMobileUpload = exports.readMobileFile = exports.uploadFileWithConflictStrategy = exports.generateFileNameForRevision = exports.generateNewFileNameOnConflict = exports.overrideFileForPath = exports.move = exports.getFullpath = exports.hasMetadataAttribute = exports.isReferencedByAlbum = exports.fetchFilesByQualificationRules = exports.saveFileQualification = exports.isSharingShorcutNew = exports.isSharingShortcutNew = exports.isSharingShorcut = exports.isSharingShortcut = exports.getSharingShortcutTargetDoctype = exports.getSharingShortcutTargetMime = exports.getSharingShortcutStatus = exports.isShortcut = exports.shouldBeOpenedByOnlyOffice = exports.isOnlyOfficeFile = exports.isNote = exports.isDirectory = exports.isFile = exports.splitFilename = exports.ALBUMS_DOCTYPE = void 0;
12
12
 
13
13
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
14
14
 
@@ -1023,5 +1023,46 @@ exports.hasCertifications = hasCertifications;
1023
1023
  var isFromKonnector = function isFromKonnector(file) {
1024
1024
  return (0, _has.default)(file, 'cozyMetadata.sourceAccount');
1025
1025
  };
1026
+ /**
1027
+ * @param {CozyClient} client - Instance of CozyClient
1028
+ * @param {string} fileId - Id of io.cozy.files document
1029
+ * @returns {Promise<Blob>}
1030
+ */
1031
+
1032
+
1033
+ exports.isFromKonnector = isFromKonnector;
1034
+
1035
+ var fetchBlobFileById = /*#__PURE__*/function () {
1036
+ var _ref13 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee12(client, fileId) {
1037
+ var fileColl, fileBin, fileBlob;
1038
+ return _regenerator.default.wrap(function _callee12$(_context12) {
1039
+ while (1) {
1040
+ switch (_context12.prev = _context12.next) {
1041
+ case 0:
1042
+ fileColl = client.collection(_const.DOCTYPE_FILES);
1043
+ _context12.next = 3;
1044
+ return fileColl.fetchFileContentById(fileId);
1045
+
1046
+ case 3:
1047
+ fileBin = _context12.sent;
1048
+ _context12.next = 6;
1049
+ return fileBin.blob();
1050
+
1051
+ case 6:
1052
+ fileBlob = _context12.sent;
1053
+ return _context12.abrupt("return", fileBlob);
1054
+
1055
+ case 8:
1056
+ case "end":
1057
+ return _context12.stop();
1058
+ }
1059
+ }
1060
+ }, _callee12);
1061
+ }));
1062
+
1063
+ return function fetchBlobFileById(_x25, _x26) {
1064
+ return _ref13.apply(this, arguments);
1065
+ };
1066
+ }();
1026
1067
 
1027
- exports.isFromKonnector = isFromKonnector;
1068
+ exports.fetchBlobFileById = fetchBlobFileById;
@@ -15,6 +15,7 @@ const statByPathSpy = jest.fn().mockName('statByPath')
15
15
  const updateFileMetadataSpy = jest.fn().mockName('updateFileMetadata')
16
16
  const createFileSpy = jest.fn().mockName('createFileSpy')
17
17
  const updateFileSpy = jest.fn().mockName('updateFileSpy')
18
+ const fetchFileContentByIdSpy = jest.fn().mockName('fetchFileContentById')
18
19
 
19
20
  beforeAll(() => {
20
21
  cozyClient.stackClient.collection.mockReturnValue({
@@ -23,7 +24,8 @@ beforeAll(() => {
23
24
  statByPath: statByPathSpy,
24
25
  updateFileMetadata: updateFileMetadataSpy,
25
26
  createFile: createFileSpy,
26
- updateFile: updateFileSpy
27
+ updateFile: updateFileSpy,
28
+ fetchFileContentById: fetchFileContentByIdSpy
27
29
  })
28
30
  })
29
31
 
@@ -702,6 +704,21 @@ describe('File Model', () => {
702
704
  })
703
705
  })
704
706
  })
707
+
708
+ describe('fetchBlobFileById', () => {
709
+ const mockData = { _id: '001', name: 'file01' }
710
+
711
+ it('should return Blob data', async () => {
712
+ fetchFileContentByIdSpy.mockImplementation(() => ({
713
+ blob: jest.fn().mockReturnValue(new Blob([mockData]))
714
+ }))
715
+ const res = await fileModel.fetchBlobFileById(cozyClient, mockData._id)
716
+
717
+ expect(fetchFileContentByIdSpy).toHaveBeenCalledWith('001')
718
+ expect(res instanceof Blob).toEqual(true)
719
+ expect(res.size).toBeGreaterThan(0)
720
+ })
721
+ })
705
722
  })
706
723
 
707
724
  describe('File qualification', () => {
@@ -22,6 +22,7 @@ describe('cancelable', () => {
22
22
  })
23
23
 
24
24
  it('should reject with the rejection of the promise', done => {
25
+ jest.spyOn(console, 'log').mockImplementation()
25
26
  const error = 'normal-rejection-from-cancelable-test'
26
27
  const { reject, wrapped } = setup()
27
28
  expect.assertions(1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-client",
3
- "version": "27.12.0",
3
+ "version": "27.14.2",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.node.js",
6
6
  "browser": "dist/index.js",
@@ -21,7 +21,7 @@
21
21
  "cozy-device-helper": "^1.12.0",
22
22
  "cozy-flags": "2.7.1",
23
23
  "cozy-logger": "^1.6.0",
24
- "cozy-stack-client": "^27.9.0",
24
+ "cozy-stack-client": "^27.14.1",
25
25
  "json-stable-stringify": "^1.0.1",
26
26
  "lodash": "^4.17.13",
27
27
  "microee": "^0.0.6",
@@ -71,5 +71,5 @@
71
71
  "react-native-ios11-devicecheck": "https://github.com/cozy/react-native-devicecheck#app-attest-v0.1"
72
72
  },
73
73
  "sideEffects": false,
74
- "gitHead": "9ea681793d6212a6b699a871a0fa700e6d1576b9"
74
+ "gitHead": "c4f8d6121a6e97429fbfda1a2a1973100418afee"
75
75
  }
@@ -652,6 +652,7 @@ declare class CozyClient {
652
652
  * At any time put an error function
653
653
  *
654
654
  * @param {Function} [onError] - Set a callback for queries which are errored
655
+ * @throws {Error} onError should not have been defined yet
655
656
  */
656
657
  setOnError(onError?: Function): void;
657
658
  toJSON(): SnapshotClient;
@@ -7,3 +7,12 @@ export function generateWebLink({ cozyUrl, searchParams: searchParamsOption, pat
7
7
  slug: string;
8
8
  subDomainType: string;
9
9
  }): string;
10
+ export class InvalidProtocolError extends Error {
11
+ constructor(url: any);
12
+ url: any;
13
+ }
14
+ export class InvalidCozyUrlError extends Error {
15
+ constructor(url: any);
16
+ url: any;
17
+ }
18
+ export function rootCozyUrl(url: URL): Promise<URL>;
package/types/index.d.ts CHANGED
@@ -19,6 +19,6 @@ export { manifest, models };
19
19
  export { QueryDefinition, Q, Mutations, MutationTypes, getDoctypeFromOperation } from "./queries/dsl";
20
20
  export { Association, HasMany, HasOne, HasOneInPlace, HasManyInPlace, HasManyTriggers } from "./associations";
21
21
  export { isReferencedBy, isReferencedById, getReferencedBy, getReferencedById } from "./associations/helpers";
22
- export { dehydrate, generateWebLink } from "./helpers";
22
+ export { dehydrate, generateWebLink, rootCozyUrl, InvalidCozyUrlError, InvalidProtocolError } from "./helpers";
23
23
  export { cancelable, isQueryLoading, hasQueryBeenLoaded } from "./utils";
24
24
  export { queryConnect, queryConnectFlat, withClient } from "./hoc";
@@ -13,4 +13,4 @@ import * as models from "./models";
13
13
  export { manifest, models };
14
14
  export { QueryDefinition, Mutations, MutationTypes, getDoctypeFromOperation, Q } from "./queries/dsl";
15
15
  export { Association, HasMany, HasOne, HasOneInPlace, HasManyInPlace, HasManyTriggers } from "./associations";
16
- export { dehydrate, generateWebLink } from "./helpers";
16
+ export { dehydrate, generateWebLink, rootCozyUrl, InvalidCozyUrlError, InvalidProtocolError } from "./helpers";
@@ -61,6 +61,7 @@ export function isPlainText(mimeType?: string, fileName?: string): boolean;
61
61
  export function hasQualifications(file: IOCozyFile): boolean;
62
62
  export function hasCertifications(file: IOCozyFile): boolean;
63
63
  export function isFromKonnector(file: IOCozyFile): boolean;
64
+ export function fetchBlobFileById(client: CozyClient, fileId: string): Promise<Blob>;
64
65
  export type FileUploadOptions = {
65
66
  /**
66
67
  * - The file name to upload