From 1e31444c67f4c67518d8ede75c348c6cb5c05dda Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 27 Aug 2023 18:22:04 +0200 Subject: [PATCH] Update to shlink-web-client 0.3.1 --- package-lock.json | 64 ++++++++++--------------- package.json | 2 +- src/api/services/ShlinkApiClient.ts | 10 ++-- src/common/services/HttpClient.ts | 12 +++-- src/index.tsx | 2 - test/common/services/HttpClient.test.ts | 17 ++++--- 6 files changed, 51 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index ccd46333..19d1025d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@json2csv/plainjs": "^7.0.3", "@reduxjs/toolkit": "^1.9.5", "@shlinkio/shlink-frontend-kit": "^0.2.0", - "@shlinkio/shlink-web-component": "^0.3.0", + "@shlinkio/shlink-web-component": "^0.3.1", "bootstrap": "5.2.3", "bottlejs": "^2.0.1", "classnames": "^2.3.2", @@ -3032,14 +3032,14 @@ } }, "node_modules/@shlinkio/shlink-web-component": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@shlinkio/shlink-web-component/-/shlink-web-component-0.3.0.tgz", - "integrity": "sha512-8zAcSF5j403pQrnScHBwPJpJbZDLTWxbuu9GZLJK77ZHDRQ0xYjc1iKZPAiwVWDYnr7SSHkeGq8DgrKs7n2K8Q==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@shlinkio/shlink-web-component/-/shlink-web-component-0.3.1.tgz", + "integrity": "sha512-9oVbAC/4kNFMWTeDOI5QSVs0SY7lF6o0me8SNcju+cBLIxPrXlsJcYOGUa0OZGcsM+aFbnRKvlCJJAFT462ymw==", "dependencies": { - "@json2csv/plainjs": "^7.0.1", + "@json2csv/plainjs": "^7.0.3", "bottlejs": "^2.0.1", "bowser": "^2.11.0", - "chart.js": "^4.3.3", + "chart.js": "^4.4.0", "classnames": "^2.3.2", "compare-versions": "^6.1.0", "date-fns": "^2.30.0", @@ -3047,13 +3047,12 @@ "leaflet": "^1.9.4", "ramda": "^0.27.2", "react-chartjs-2": "^5.2.0", - "react-colorful": "^5.6.1", "react-copy-to-clipboard": "^5.1.0", "react-datepicker": "^4.16.0", "react-external-link": "^2.2.0", "react-leaflet": "^4.2.1", "react-swipeable": "^7.0.1", - "react-tag-autocomplete": "^7.0.0" + "react-tag-autocomplete": "^7.0.1" }, "peerDependencies": { "@fortawesome/fontawesome-svg-core": "^6.4.2", @@ -4502,9 +4501,9 @@ } }, "node_modules/chart.js": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz", - "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", + "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -8111,14 +8110,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-colorful": { - "version": "5.6.1", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, "node_modules/react-copy-to-clipboard": { "version": "5.1.0", "license": "MIT", @@ -8296,9 +8287,9 @@ } }, "node_modules/react-tag-autocomplete": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-7.0.0.tgz", - "integrity": "sha512-PFxT7fpMB8Au+S9cJYAGRVTnacZpeXybc5SkpTCyuJHmUN1Bt8gHb9vZi3f+aX/eDX44x2WIwYiqfRBi2E5AMg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-7.0.1.tgz", + "integrity": "sha512-Aw+VJpCY+PUX03aFs1MD9QeXmrDcThCZqI8ttVt4RpsHGEfjgtr3Zq8iJ9+PJgTW6RKPoI8dhheYNYGEN7AG/A==", "engines": { "node": ">= 16.12.0" }, @@ -12481,14 +12472,14 @@ } }, "@shlinkio/shlink-web-component": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@shlinkio/shlink-web-component/-/shlink-web-component-0.3.0.tgz", - "integrity": "sha512-8zAcSF5j403pQrnScHBwPJpJbZDLTWxbuu9GZLJK77ZHDRQ0xYjc1iKZPAiwVWDYnr7SSHkeGq8DgrKs7n2K8Q==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@shlinkio/shlink-web-component/-/shlink-web-component-0.3.1.tgz", + "integrity": "sha512-9oVbAC/4kNFMWTeDOI5QSVs0SY7lF6o0me8SNcju+cBLIxPrXlsJcYOGUa0OZGcsM+aFbnRKvlCJJAFT462ymw==", "requires": { - "@json2csv/plainjs": "^7.0.1", + "@json2csv/plainjs": "^7.0.3", "bottlejs": "^2.0.1", "bowser": "^2.11.0", - "chart.js": "^4.3.3", + "chart.js": "^4.4.0", "classnames": "^2.3.2", "compare-versions": "^6.1.0", "date-fns": "^2.30.0", @@ -12496,13 +12487,12 @@ "leaflet": "^1.9.4", "ramda": "^0.27.2", "react-chartjs-2": "^5.2.0", - "react-colorful": "^5.6.1", "react-copy-to-clipboard": "^5.1.0", "react-datepicker": "^4.16.0", "react-external-link": "^2.2.0", "react-leaflet": "^4.2.1", "react-swipeable": "^7.0.1", - "react-tag-autocomplete": "^7.0.0" + "react-tag-autocomplete": "^7.0.1" } }, "@shlinkio/stylelint-config-css-coding-standard": { @@ -13458,9 +13448,9 @@ "dev": true }, "chart.js": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz", - "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", + "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", "requires": { "@kurkle/color": "^0.3.0" } @@ -15882,10 +15872,6 @@ "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", "requires": {} }, - "react-colorful": { - "version": "5.6.1", - "requires": {} - }, "react-copy-to-clipboard": { "version": "5.1.0", "requires": { @@ -15990,9 +15976,9 @@ "requires": {} }, "react-tag-autocomplete": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-7.0.0.tgz", - "integrity": "sha512-PFxT7fpMB8Au+S9cJYAGRVTnacZpeXybc5SkpTCyuJHmUN1Bt8gHb9vZi3f+aX/eDX44x2WIwYiqfRBi2E5AMg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-7.0.1.tgz", + "integrity": "sha512-Aw+VJpCY+PUX03aFs1MD9QeXmrDcThCZqI8ttVt4RpsHGEfjgtr3Zq8iJ9+PJgTW6RKPoI8dhheYNYGEN7AG/A==", "requires": {} }, "react-transition-group": { diff --git a/package.json b/package.json index 6c03e9e4..98784e98 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@json2csv/plainjs": "^7.0.3", "@reduxjs/toolkit": "^1.9.5", "@shlinkio/shlink-frontend-kit": "^0.2.0", - "@shlinkio/shlink-web-component": "^0.3.0", + "@shlinkio/shlink-web-component": "^0.3.1", "bootstrap": "5.2.3", "bottlejs": "^2.0.1", "classnames": "^2.3.2", diff --git a/src/api/services/ShlinkApiClient.ts b/src/api/services/ShlinkApiClient.ts index 21e6a60d..602a5e76 100644 --- a/src/api/services/ShlinkApiClient.ts +++ b/src/api/services/ShlinkApiClient.ts @@ -25,13 +25,13 @@ import { ErrorTypeV3, } from '@shlinkio/shlink-web-component/api-contract'; import { isEmpty, isNil, reject } from 'ramda'; -import type { HttpClient } from '../../common/services/HttpClient'; +import type { HttpClient, RequestOptions } from '../../common/services/HttpClient'; import { replaceAuthorityFromUri } from '../../utils/helpers/uri'; import type { OptionalString } from '../../utils/utils'; type ApiVersion = 2 | 3; -type RequestOptions = { +type ShlinkRequestOptions = { url: string; method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; query?: object; @@ -145,12 +145,12 @@ export class ShlinkApiClient implements BaseShlinkApiClient { ): Promise => this.performRequest({ url: '/domains/redirects', method: 'PATCH', body: domainRedirects }); - private readonly performRequest = async (requestOptions: RequestOptions): Promise => + private readonly performRequest = async (requestOptions: ShlinkRequestOptions): Promise => this.httpClient.fetchJson(...this.toFetchParams(requestOptions)).catch( this.handleFetchError(() => this.httpClient.fetchJson(...this.toFetchParams(requestOptions))), ); - private readonly performEmptyRequest = async (requestOptions: RequestOptions): Promise => + private readonly performEmptyRequest = async (requestOptions: ShlinkRequestOptions): Promise => this.httpClient.fetchEmpty(...this.toFetchParams(requestOptions)).catch( this.handleFetchError(() => this.httpClient.fetchEmpty(...this.toFetchParams(requestOptions))), ); @@ -161,7 +161,7 @@ export class ShlinkApiClient implements BaseShlinkApiClient { query = {}, body, domain, - }: RequestOptions): [string, RequestInit] => { + }: ShlinkRequestOptions): [string, RequestOptions] => { const normalizedQuery = stringifyQuery(rejectNilProps(query)); const stringifiedQuery = isEmpty(normalizedQuery) ? '' : `?${normalizedQuery}`; const baseUrl = domain ? replaceAuthorityFromUri(this.baseUrl, domain) : this.baseUrl; diff --git a/src/common/services/HttpClient.ts b/src/common/services/HttpClient.ts index 3c6e9330..3065c3ca 100644 --- a/src/common/services/HttpClient.ts +++ b/src/common/services/HttpClient.ts @@ -1,7 +1,13 @@ type Fetch = typeof window.fetch; +export type RequestOptions = { + method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; + body?: string; + headers?: Record; +}; + const applicationJsonHeader = { 'Content-Type': 'application/json' }; -const withJsonContentType = (options?: RequestInit): RequestInit | undefined => { +const withJsonContentType = (options?: RequestOptions): RequestInit | undefined => { if (!options?.body) { return options; } @@ -20,7 +26,7 @@ const withJsonContentType = (options?: RequestInit): RequestInit | undefined => export class HttpClient { constructor(private readonly fetch: Fetch) {} - public readonly fetchJson = (url: string, options?: RequestInit): Promise => + public readonly fetchJson = (url: string, options?: RequestOptions): Promise => this.fetch(url, withJsonContentType(options)).then(async (resp) => { const json = await resp.json(); @@ -31,7 +37,7 @@ export class HttpClient { return json as T; }); - public readonly fetchEmpty = (url: string, options?: RequestInit): Promise => + public readonly fetchEmpty = (url: string, options?: RequestOptions): Promise => this.fetch(url, withJsonContentType(options)).then(async (resp) => { if (!resp.ok) { throw await resp.json(); diff --git a/src/index.tsx b/src/index.tsx index 9d4cea77..0684515b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,8 +6,6 @@ import { container } from './container'; import { setUpStore } from './container/store'; import { register as registerServiceWorker } from './serviceWorkerRegistration'; import './index.scss'; -import 'leaflet/dist/leaflet.css'; -import 'react-datepicker/dist/react-datepicker.css'; const store = setUpStore(container); const { App, ScrollToTop, ErrorHandler, appUpdateAvailable } = container; diff --git a/test/common/services/HttpClient.test.ts b/test/common/services/HttpClient.test.ts index 45a38f11..9df724b8 100644 --- a/test/common/services/HttpClient.test.ts +++ b/test/common/services/HttpClient.test.ts @@ -1,8 +1,13 @@ +import type { RequestOptions } from '../../../src/common/services/HttpClient'; import { HttpClient } from '../../../src/common/services/HttpClient'; describe('HttpClient', () => { const fetch = vi.fn(); const httpClient = new HttpClient(fetch); + const requestOptions = (options: Omit): RequestOptions => ({ + method: 'GET', + ...options, + }); describe('fetchJson', () => { it('throws json on success', async () => { @@ -14,9 +19,9 @@ describe('HttpClient', () => { it.each([ [undefined], - [{}], - [{ body: undefined }], - [{ body: '' }], + [requestOptions({})], + [requestOptions({ body: undefined })], + [requestOptions({ body: '' })], ])('return json on failure', async (options) => { const theJson = { foo: 'bar' }; fetch.mockResolvedValue({ json: () => theJson, ok: true }); @@ -28,13 +33,13 @@ describe('HttpClient', () => { }); it.each([ - [{ body: 'the_body' }], - [{ + [requestOptions({ body: 'the_body' })], + [requestOptions({ body: 'the_body', headers: { 'Content-Type': 'text/plain', }, - }], + })], ])('forwards JSON content-type when appropriate', async (options) => { const theJson = { foo: 'bar' }; fetch.mockResolvedValue({ json: () => theJson, ok: true });