@soleil-se/app-util 5.3.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.4.0] - 2023-08-09
9
+
10
+ - Add `fetchJson` function to make requests to app routes or other resources.
11
+
8
12
  ## [5.3.0] - 2023-07-06
9
13
 
10
14
  - Added helper functions for managing parameters in the URL-field.
package/README.md CHANGED
@@ -180,7 +180,7 @@ Stringify an object to a query string compatible with Sitevision.
180
180
  | Param | Type | Default | Description |
181
181
  | --- | --- | --- | --- |
182
182
  | params | `Object` | | Object with parameters to stringify. |
183
- | [options] | `Object` | `{}` | Settings object. |
183
+ | [options] | `Object` | `{}` | Options object. |
184
184
  | [options.addQueryPrefix] | `Boolean` | `false` | If a leading `?` should be added to the string. |
185
185
 
186
186
  ```js
@@ -214,6 +214,79 @@ const params = parseParams('?foo=bar&arr[]=1&arr[]=2');
214
214
 
215
215
  Following API:s are available in a client context.
216
216
 
217
+ ### Fetch
218
+
219
+ Fetch wrapper for calling app routes, rest-api or external resources.
220
+
221
+ #### fetchJson() => `Promise<Object>`
222
+
223
+ | Param | Type | Default | Description |
224
+ | --- | --- | --- | --- |
225
+ | uri | `String` | | URI for resource |
226
+ | [options] | `Object` | `{}` | Options object, two custom options rest is standard [fetch options](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) |
227
+ | [options.params] | `Object` | `{}` | Object with parameters to be appended to the URI |
228
+ | [options.retries] | `Number` | `0` | Number of retries if the request times out. |
229
+
230
+ **Returns**: `Promise<Object>` - Promise containing parsed JSON-data.
231
+ **Throws** `Error` - Extended error object with custom properties for `status`, `aborted` and other JSON-data returned by the request.
232
+
233
+ Most common usage is getting data from a route in the current app.
234
+
235
+ ```js
236
+ import { fetchJson } from '@soleil-se/webapp-util/client';
237
+
238
+ async function getItems() {
239
+ const params = { query: 'foo', start: 0, num: 10 };
240
+ const result = await fetchJson('/items', { params });
241
+ console.log(result);
242
+ }
243
+ ```
244
+
245
+ Posting form data.
246
+
247
+ ```js
248
+ import { fetchJson } from '@soleil-se/webapp-util/client';
249
+
250
+ async function postForm() {
251
+ const body = new FormData();
252
+ body.append('name', 'Foo');
253
+ body.append('mail', 'foo@bar.com');
254
+
255
+ const result = await fetchJson('/create', { method: 'POST', body }));
256
+ console.log(result);
257
+ }
258
+ ```
259
+
260
+ Specify number of retries if a request times out.
261
+
262
+ ```js
263
+ import { fetchJson } from '@soleil-se/webapp-util/client';
264
+
265
+ async function getItems() {
266
+ const params = { query: 'foo', start: 0, num: 10 };
267
+ const result = await fetchJson('/items', { params, retries: 5 });
268
+ console.log(result);
269
+ }
270
+ ```
271
+
272
+ Handle aborted requests called in rapid succession, for example searching when typing.
273
+
274
+ ```js
275
+ import { fetchJson } from '@soleil-se/webapp-util/client';
276
+
277
+ async function onInput() {
278
+ const params = { query: 'foo' };
279
+ try {
280
+ const result = await fetchJson('/search', { params });
281
+ console.log(result);
282
+ } catch(e) {
283
+ // Ignore aborts due to new search.
284
+ if(e.aborted) return;
285
+ // Handle error as usual.
286
+ }
287
+ }
288
+ ```
289
+
217
290
  ### URL Parameters
218
291
 
219
292
  Helper functions setting, getting, updating and clearing query parameters in the URL-field.
@@ -0,0 +1,48 @@
1
+ import { getRouteUri, stringifyParams } from '../../common';
2
+
3
+ function getUrl(uri, params) {
4
+ if (uri.startsWith('/rest-api') || uri.startsWith('/appresource') || !uri.startsWith('/')) {
5
+ return uri + stringifyParams(params, { addQueryPrefix: true });
6
+ }
7
+ return getRouteUri(uri, params);
8
+ }
9
+
10
+ function toJson(response) {
11
+ try {
12
+ return response.json();
13
+ } catch (e) {
14
+ return {};
15
+ }
16
+ }
17
+
18
+ async function handleResponse(response) {
19
+ const json = await toJson(response);
20
+ if (!response.ok) {
21
+ const error = new Error(json?.message || response?.statusText);
22
+ Object.entries(json).forEach(([key, value]) => {
23
+ error[key] = value;
24
+ });
25
+ error.status = response.status;
26
+ throw error;
27
+ }
28
+
29
+ return json;
30
+ }
31
+
32
+ const controllers = {};
33
+
34
+ export default function fetchJson(uri, { params = {}, retries = 0, ...options } = {}) {
35
+ if (controllers[uri]) controllers[uri].abort();
36
+ controllers[uri] = new AbortController();
37
+ return fetch(getUrl(uri, params), { signal: controllers[uri].signal, ...options })
38
+ .then(handleResponse)
39
+ .catch((error) => {
40
+ const isTimeout = error.status === 504 || error.status === 408 || error.message.includes('SocketTimeoutException');
41
+ if (isTimeout && retries > 0) {
42
+ return fetchJson(uri, { ...options, params, retries: retries - 1 });
43
+ }
44
+ // eslint-disable-next-line no-param-reassign
45
+ error.aborted = error.name === 'AbortError';
46
+ return Promise.reject(error);
47
+ });
48
+ }
package/client/index.js CHANGED
@@ -5,3 +5,5 @@ export {
5
5
  updateUrlParams,
6
6
  clearUrlParams,
7
7
  } from './url-params';
8
+
9
+ export { default as fetchJson } from './fetch-json';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soleil-se/app-util",
3
- "version": "5.3.0",
3
+ "version": "5.4.0",
4
4
  "description": "Utility and rendering functions for WebApps.",
5
5
  "main": "./common/index.js",
6
6
  "author": "Soleil AB",
@@ -14,6 +14,6 @@
14
14
  "peerDependencies": {
15
15
  "@sitevision/api": "*"
16
16
  },
17
- "gitHead": "1928f2e710e25b16b0e3a792bb8d6b9254d72084",
17
+ "gitHead": "b590af917fa521db8bdb7e6f97bd35dffa50a0de",
18
18
  "dependencies": {}
19
19
  }
package/docs/2.vue.md DELETED
@@ -1,57 +0,0 @@
1
- # Vue (DEPRECATED)
2
-
3
- Sitevision only supports client side rendering with Vue.
4
-
5
- ## index.js
6
-
7
- Render an app with only client code.
8
- `index.js`
9
-
10
- ```javascript
11
- import router from '@sitevision/api/common/router';
12
-
13
- router.get('/', (req, res) => {
14
- const props = { foo: 'bar' };
15
- res.agnosticRender('', props)
16
- });
17
- ```
18
-
19
- ## main.js
20
-
21
- ### `render(App, { target, props })`
22
-
23
- `@soleil-api/webapp-util/client/vue`
24
-
25
- Renders a client side Vue application.
26
-
27
- **Returns**: <code>\*</code> - Initialized Vue app.
28
-
29
- | Param | Type | Default | Description |
30
- | --- | --- | --- | --- |
31
- | App | <code>\*</code> | | Svelte app root component. |
32
- | [settings] | <code>Object</code> | <code>{}</code> | Settings object. |
33
- | [settings.target] | <code>Element</code> | | Target where app should be mounted. |
34
- | [settings.props] | <code>Object</code> | | Root component props. |
35
-
36
- `main.js`
37
-
38
- ```javascript
39
- import { render } from '@soleil-api/webapp-util/client/vue';
40
- import App from './App.vue';
41
-
42
- export default (props, target) => {
43
- render(App, { props, target });
44
- };
45
- ```
46
-
47
- Mount the app in another element:
48
- `main.js`
49
-
50
- ```javascript
51
- import { render } from '@soleil-api/webapp-util/client/vue';
52
- import App from './App.vue';
53
-
54
- export default (props) => {
55
- render(App, { props, target: document.querySelector('#mount_app_here') });
56
- };
57
- ```