Loaded version of selected server and created component to filter content based on that version

This commit is contained in:
Alejandro Celaya 2019-10-05 10:20:33 +02:00
parent 67a23bfe33
commit 4120d09220
11 changed files with 71 additions and 21 deletions

5
package-lock.json generated
View file

@ -4951,6 +4951,11 @@
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
"dev": true
},
"compare-versions": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.5.1.tgz",
"integrity": "sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg=="
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz",

View file

@ -35,6 +35,7 @@
"bottlejs": "^1.7.1",
"chart.js": "^2.7.2",
"classnames": "^2.2.6",
"compare-versions": "^3.5.1",
"csvjson": "^5.1.0",
"leaflet": "^1.4.0",
"moment": "^2.22.2",

View file

@ -10,14 +10,19 @@ const initialState = null;
export const resetSelectedServer = createAction(RESET_SELECTED_SERVER);
export const selectServer = ({ findServerById }) => (serverId) => (dispatch) => {
export const selectServer = ({ findServerById }, buildShlinkApiClient) => (serverId) => async (dispatch) => {
dispatch(resetShortUrlParams());
const selectedServer = findServerById(serverId);
const { health } = await buildShlinkApiClient(selectedServer);
const { version } = await health();
dispatch({
type: SELECT_SERVER,
selectedServer,
selectedServer: {
...selectedServer,
version,
},
});
};

View file

@ -34,7 +34,7 @@ const provideServices = (bottle, connect, withRouter) => {
bottle.service('ServersExporter', ServersExporter, 'ServersService', 'window', 'csvjson');
// Actions
bottle.serviceFactory('selectServer', selectServer, 'ServersService');
bottle.serviceFactory('selectServer', selectServer, 'ServersService', 'buildShlinkApiClient');
bottle.serviceFactory('createServer', createServer, 'ServersService', 'listServers');
bottle.serviceFactory('createServers', createServers, 'ServersService', 'listServers');
bottle.serviceFactory('deleteServer', deleteServer, 'ServersService', 'listServers');

View file

@ -6,6 +6,8 @@ import { Collapse } from 'reactstrap';
import * as PropTypes from 'prop-types';
import DateInput from '../utils/DateInput';
import Checkbox from '../utils/Checkbox';
import ForVersion from '../utils/ForVersion';
import { serverType } from '../servers/prop-types';
import { createShortUrlResultType } from './reducers/shortUrlCreation';
import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon';
@ -17,6 +19,7 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort
createShortUrl: PropTypes.func,
shortUrlCreationResult: createShortUrlResultType,
resetCreateShortUrl: PropTypes.func,
selectedServer: serverType,
};
state = {
@ -108,6 +111,10 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort
</div>
</div>
<ForVersion
minVersion="1.16.0"
currentServerVersion={this.props.selectedServer ? this.props.selectedServer.version : ''}
>
<div className="mb-4 text-right">
<Checkbox
className="mr-2"
@ -118,6 +125,7 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort
</Checkbox>
<UseExistingIfFoundInfoIcon />
</div>
</ForVersion>
</Collapse>
<div>

View file

@ -23,8 +23,8 @@ const renderInfoModal = (isOpen, toggle) => (
if none is found.
</li>
<li>
When long URL and custom slug are provided: Same as in previous case, but it will try to match the short URL
using both the long URL and the slug.
When long URL and custom slug and/or domain are provided: Same as in previous case, but it will try to match
the short URL using both the long URL and the slug, the long URL and the domain, or the three of them.
<br />
If the slug is being used by another long URL, an error will be returned.
</li>

View file

@ -39,7 +39,7 @@ const provideServices = (bottle, connect) => {
bottle.serviceFactory('CreateShortUrl', CreateShortUrl, 'TagsSelector', 'CreateShortUrlResult');
bottle.decorator(
'CreateShortUrl',
connect([ 'shortUrlCreationResult' ], [ 'createShortUrl', 'resetCreateShortUrl' ])
connect([ 'shortUrlCreationResult', 'selectedServer' ], [ 'createShortUrl', 'resetCreateShortUrl' ])
);
bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal);

19
src/utils/ForVersion.js Normal file
View file

@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import { compare } from 'compare-versions';
import { isEmpty } from 'ramda';
const propTypes = {
minVersion: PropTypes.string.isRequired,
currentServerVersion: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
};
const ForVersion = ({ minVersion, currentServerVersion, children }) =>
isEmpty(currentServerVersion) || compare(minVersion, currentServerVersion, '>')
? null
: <React.Fragment>{children}</React.Fragment>;
ForVersion.propTypes = propTypes;
export default ForVersion;

View file

@ -50,6 +50,8 @@ export default class ShlinkApiClient {
this._performRequest('/tags', 'PUT', {}, { oldName, newName })
.then(() => ({ oldName, newName }));
health = () => this._performRequest('/health', 'GET').then((resp) => resp.data);
_performRequest = async (url, method = 'GET', query = {}, body = {}) =>
await this.axios({
method,

View file

@ -13,8 +13,10 @@ const getSelectedServerFromState = async (getState) => {
return selectedServer;
};
const buildShlinkApiClient = (axios) => async (getState) => {
const { url, apiKey } = await getSelectedServerFromState(getState);
const buildShlinkApiClient = (axios) => async (getStateOrSelectedServer) => {
const { url, apiKey } = typeof getStateOrSelectedServer === 'function'
? await getSelectedServerFromState(getStateOrSelectedServer)
: getStateOrSelectedServer;
const clientKey = `${url}_${apiKey}`;
if (!apiClients[clientKey]) {

View file

@ -29,22 +29,30 @@ describe('selectedServerReducer', () => {
const selectedServer = {
id: serverId,
};
const version = '1.19.0';
const ServersServiceMock = {
findServerById: jest.fn(() => selectedServer),
};
const apiClientMock = {
health: jest.fn().mockResolvedValue({ version }),
};
afterEach(() => {
ServersServiceMock.findServerById.mockClear();
});
it('dispatches proper actions', () => {
it('dispatches proper actions', async () => {
const dispatch = jest.fn();
const expectedSelectedServer = {
...selectedServer,
version,
};
selectServer(ServersServiceMock)(serverId)(dispatch);
await selectServer(ServersServiceMock, async () => apiClientMock)(serverId)(dispatch);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenNthCalledWith(1, { type: RESET_SHORT_URL_PARAMS });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SELECT_SERVER, selectedServer });
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SELECT_SERVER, selectedServer: expectedSelectedServer });
});
it('invokes dependencies', () => {