Enhanced edit tags action so that it calls PATCH endpoint

This commit is contained in:
Alejandro Celaya 2021-02-27 09:49:56 +01:00
parent f653739d50
commit d0825089d0
7 changed files with 56 additions and 15 deletions

View file

@ -63,6 +63,7 @@ export default class ShlinkApiClient {
this.performRequest(`/short-urls/${shortCode}`, 'DELETE', { domain })
.then(() => {});
/* @deprecated. If using Shlink 2.6.0 or greater, use updateShortUrlMeta instead */
public readonly updateShortUrlTags = async (
shortCode: string,
domain: OptionalString,
@ -75,9 +76,9 @@ export default class ShlinkApiClient {
shortCode: string,
domain: OptionalString,
meta: ShlinkShortUrlMeta,
): Promise<ShlinkShortUrlMeta> =>
this.performRequest(`/short-urls/${shortCode}`, 'PATCH', { domain }, meta)
.then(() => meta);
): Promise<ShortUrl> =>
this.performRequest<ShortUrl>(`/short-urls/${shortCode}`, 'PATCH', { domain }, meta)
.then(({ data }) => data);
public readonly listTags = async (): Promise<ShlinkTags> =>
this.performRequest<{ tags: ShlinkTagsResponse }>('/tags', 'GET', { withStats: 'true' })

View file

@ -59,6 +59,7 @@ export interface ShlinkVisitsParams {
export interface ShlinkShortUrlMeta extends ShortUrlMeta {
longUrl?: string;
tags?: string[];
}
export interface ShlinkDomain {

View file

@ -1,4 +1,5 @@
import { Action, Dispatch } from 'redux';
import { prop } from 'ramda';
import { buildActionCreator, buildReducer } from '../../utils/helpers/redux';
import { GetState } from '../../container/types';
import { OptionalString } from '../../utils/utils';
@ -6,6 +7,8 @@ import { ShortUrlIdentifier } from '../data';
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
import { ProblemDetailsError } from '../../api/types';
import { parseApiError } from '../../api/utils';
import { isReachableServer } from '../../servers/data';
import { versionMatch } from '../../utils/helpers/version';
/* eslint-disable padding-line-between-statements */
export const EDIT_SHORT_URL_TAGS_START = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_START';
@ -50,10 +53,19 @@ export const editShortUrlTags = (buildShlinkApiClient: ShlinkApiClientBuilder) =
tags: string[],
) => async (dispatch: Dispatch, getState: GetState) => {
dispatch({ type: EDIT_SHORT_URL_TAGS_START });
const { updateShortUrlTags } = buildShlinkApiClient(getState);
const { selectedServer } = getState();
const supportsTagsInPatch = isReachableServer(selectedServer) && versionMatch(
selectedServer.version,
{ minVersion: '2.6.0' },
);
const { updateShortUrlTags, updateShortUrlMeta } = buildShlinkApiClient(getState);
try {
const normalizedTags = await updateShortUrlTags(shortCode, domain, tags);
const normalizedTags = await (
supportsTagsInPatch
? updateShortUrlMeta(shortCode, domain, { tags }).then(prop('tags'))
: updateShortUrlTags(shortCode, domain, tags)
);
dispatch<EditShortUrlTagsAction>({ tags: normalizedTags, shortCode, domain, type: SHORT_URL_TAGS_EDITED });
} catch (e) {

View file

@ -3,6 +3,7 @@ import ShlinkApiClient from '../../../src/api/services/ShlinkApiClient';
import { OptionalString } from '../../../src/utils/utils';
import { Mock } from 'ts-mockery';
import { ShlinkDomain, ShlinkVisitsOverview } from '../../../src/api/types';
import { ShortUrl } from '../../../src/short-urls/data';
describe('ShlinkApiClient', () => {
const createAxios = (data: AxiosRequestConfig) => (async () => Promise.resolve(data)) as unknown as AxiosInstance;
@ -139,16 +140,17 @@ describe('ShlinkApiClient', () => {
describe('updateShortUrlMeta', () => {
it.each(shortCodesWithDomainCombinations)('properly updates short URL meta', async (shortCode, domain) => {
const expectedMeta = {
const meta = {
maxVisits: 50,
validSince: '2025-01-01T10:00:00+01:00',
};
const axiosSpy = createAxiosMock();
const expectedResp = Mock.of<ShortUrl>()
const axiosSpy = createAxiosMock({ data: expectedResp });
const { updateShortUrlMeta } = new ShlinkApiClient(axiosSpy, '', '');
const result = await updateShortUrlMeta(shortCode, domain, expectedMeta);
const result = await updateShortUrlMeta(shortCode, domain, meta);
expect(expectedMeta).toEqual(result);
expect(expectedResp).toEqual(result);
expect(axiosSpy).toHaveBeenCalledWith(expect.objectContaining({
url: `/short-urls/${shortCode}`,
method: 'PATCH',

View file

@ -1,10 +1,10 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { Mock } from 'ts-mockery';
import { Modal } from 'reactstrap';
import createEditTagsModal from '../../../src/short-urls/helpers/EditTagsModal';
import { ShortUrl } from '../../../src/short-urls/data';
import { ShortUrlTags } from '../../../src/short-urls/reducers/shortUrlTags';
import { OptionalString } from '../../../src/utils/utils';
import { Modal } from 'reactstrap';
describe('<EditTagsModal />', () => {
let wrapper: ShallowWrapper;

View file

@ -9,6 +9,7 @@ import reducer, {
EditShortUrlTagsAction,
} from '../../../src/short-urls/reducers/shortUrlTags';
import { ShlinkState } from '../../../src/container/types';
import { ReachableServer, SelectedServer } from '../../../src/servers/data';
describe('shortUrlTagsReducer', () => {
const tags = [ 'foo', 'bar', 'baz' ];
@ -60,9 +61,10 @@ describe('shortUrlTagsReducer', () => {
describe('editShortUrlTags', () => {
const updateShortUrlTags = jest.fn();
const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrlTags });
const updateShortUrlMeta = jest.fn();
const buildShlinkApiClient = jest.fn().mockReturnValue({ updateShortUrlTags, updateShortUrlMeta });
const dispatch = jest.fn();
const getState = () => Mock.all<ShlinkState>();
const buildGetState = (selectedServer?: SelectedServer) => () => Mock.of<ShlinkState>({ selectedServer });
afterEach(jest.clearAllMocks);
@ -71,11 +73,12 @@ describe('shortUrlTagsReducer', () => {
updateShortUrlTags.mockResolvedValue(normalizedTags);
await editShortUrlTags(buildShlinkApiClient)(shortCode, domain, tags)(dispatch, getState);
await editShortUrlTags(buildShlinkApiClient)(shortCode, domain, tags)(dispatch, buildGetState());
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlTags).toHaveBeenCalledTimes(1);
expect(updateShortUrlTags).toHaveBeenCalledWith(shortCode, domain, tags);
expect(updateShortUrlMeta).not.toHaveBeenCalled();
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });
expect(dispatch).toHaveBeenNthCalledWith(
@ -84,13 +87,35 @@ describe('shortUrlTagsReducer', () => {
);
});
it('calls updateShortUrlMeta when server is version 2.6.0 or above', async () => {
const normalizedTags = [ 'bar', 'foo' ];
updateShortUrlMeta.mockResolvedValue({ tags: normalizedTags });
await editShortUrlTags(buildShlinkApiClient)(shortCode, undefined, tags)(
dispatch,
buildGetState(Mock.of<ReachableServer>({ printableVersion: '', version: '2.6.0' })),
);
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledTimes(1);
expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, undefined, { tags });
expect(updateShortUrlTags).not.toHaveBeenCalled();
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_TAGS_START });
expect(dispatch).toHaveBeenNthCalledWith(
2,
{ type: SHORT_URL_TAGS_EDITED, tags: normalizedTags, shortCode },
);
});
it('dispatches error on failure', async () => {
const error = new Error();
updateShortUrlTags.mockRejectedValue(error);
try {
await editShortUrlTags(buildShlinkApiClient)(shortCode, undefined, tags)(dispatch, getState);
await editShortUrlTags(buildShlinkApiClient)(shortCode, undefined, tags)(dispatch, buildGetState());
} catch (e) {
expect(e).toBe(error);
}

View file

@ -1,8 +1,8 @@
import { shallow, ShallowWrapper } from 'enzyme';
import { Marker, Popup } from 'react-leaflet';
import { Modal } from 'reactstrap';
import MapModal from '../../../src/visits/helpers/MapModal';
import { CityStats } from '../../../src/visits/types';
import { Modal } from 'reactstrap';
describe('<MapModal />', () => {
let wrapper: ShallowWrapper;