Added missing application/json content-type when calling Shlink with payload

This commit is contained in:
Alejandro Celaya 2022-12-17 09:57:40 +01:00
parent b106b3cd0a
commit 2603f2f987
3 changed files with 48 additions and 4 deletions

View file

@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Fixed ### Fixed
* [#766](https://github.com/shlinkio/shlink-web-client/issues/766) Fixed visits query being lost when switching between sub-sections. * [#766](https://github.com/shlinkio/shlink-web-client/issues/766) Fixed visits query being lost when switching between sub-sections.
* [#765](https://github.com/shlinkio/shlink-web-client/issues/765) Added missing `"Content-Type": "application/json"` to requests with payload, making older Shlink versions fail.
## [3.8.1] - 2022-12-06 ## [3.8.1] - 2022-12-06

View file

@ -1,10 +1,27 @@
import { Fetch } from '../../utils/types'; import { Fetch } from '../../utils/types';
const applicationJsonHeader = { 'Content-Type': 'application/json' };
const withJsonContentType = (options?: RequestInit): RequestInit | undefined => {
if (!options?.body) {
return options;
}
return options ? {
...options,
headers: {
...(options.headers ?? {}),
...applicationJsonHeader,
},
} : {
headers: applicationJsonHeader,
};
};
export class HttpClient { export class HttpClient {
constructor(private readonly fetch: Fetch) {} constructor(private readonly fetch: Fetch) {}
public readonly fetchJson = <T>(url: string, options?: RequestInit): Promise<T> => public readonly fetchJson = <T>(url: string, options?: RequestInit): Promise<T> =>
this.fetch(url, options).then(async (resp) => { this.fetch(url, withJsonContentType(options)).then(async (resp) => {
const json = await resp.json(); const json = await resp.json();
if (!resp.ok) { if (!resp.ok) {
@ -15,7 +32,7 @@ export class HttpClient {
}); });
public readonly fetchEmpty = (url: string, options?: RequestInit): Promise<void> => public readonly fetchEmpty = (url: string, options?: RequestInit): Promise<void> =>
this.fetch(url, options).then(async (resp) => { this.fetch(url, withJsonContentType(options)).then(async (resp) => {
if (!resp.ok) { if (!resp.ok) {
throw await resp.json(); throw await resp.json();
} }

View file

@ -14,13 +14,39 @@ describe('HttpClient', () => {
await expect(httpClient.fetchJson('')).rejects.toEqual(theError); await expect(httpClient.fetchJson('')).rejects.toEqual(theError);
}); });
it('return json on failure', async () => { it.each([
[undefined],
[{}],
[{ body: undefined }],
[{ body: '' }],
])('return json on failure', async (options) => {
const theJson = { foo: 'bar' }; const theJson = { foo: 'bar' };
fetch.mockResolvedValue({ json: () => theJson, ok: true }); fetch.mockResolvedValue({ json: () => theJson, ok: true });
const result = await httpClient.fetchJson(''); const result = await httpClient.fetchJson('the_url', options);
expect(result).toEqual(theJson); expect(result).toEqual(theJson);
expect(fetch).toHaveBeenCalledWith('the_url', options);
});
it.each([
[{ body: 'the_body' }],
[{
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 });
const result = await httpClient.fetchJson('the_url', options);
expect(result).toEqual(theJson);
expect(fetch).toHaveBeenCalledWith('the_url', expect.objectContaining({
headers: { 'Content-Type': 'application/json' },
}));
}); });
}); });