mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Tweaked server types and data
This commit is contained in:
parent
dc78138066
commit
1b7e1e2b5b
8 changed files with 64 additions and 38 deletions
|
@ -1,11 +1,12 @@
|
||||||
import { MercureInfo } from '../mercure/reducers/mercureInfo';
|
import { MercureInfo } from '../mercure/reducers/mercureInfo';
|
||||||
import { ServersMap } from '../servers/reducers/servers';
|
import { ServersMap } from '../servers/reducers/servers';
|
||||||
|
import { SelectedServer } from '../servers/data';
|
||||||
|
|
||||||
export type ConnectDecorator = (props: string[], actions?: string[]) => any;
|
export type ConnectDecorator = (props: string[], actions?: string[]) => any;
|
||||||
|
|
||||||
export interface ShlinkState {
|
export interface ShlinkState {
|
||||||
servers: ServersMap;
|
servers: ServersMap;
|
||||||
selectedServer: any;
|
selectedServer: SelectedServer;
|
||||||
shortUrlsList: any;
|
shortUrlsList: any;
|
||||||
shortUrlsListParams: any;
|
shortUrlsListParams: any;
|
||||||
shortUrlCreationResult: any;
|
shortUrlCreationResult: any;
|
||||||
|
|
|
@ -6,13 +6,13 @@ import NoMenuLayout from '../common/NoMenuLayout';
|
||||||
import { StateFlagTimeout } from '../utils/helpers/hooks';
|
import { StateFlagTimeout } from '../utils/helpers/hooks';
|
||||||
import { ServerForm } from './helpers/ServerForm';
|
import { ServerForm } from './helpers/ServerForm';
|
||||||
import { ImportServersBtnProps } from './helpers/ImportServersBtn';
|
import { ImportServersBtnProps } from './helpers/ImportServersBtn';
|
||||||
import { NewServerData, RegularServer } from './data';
|
import { NewServerData, ServerWithId } from './data';
|
||||||
import './CreateServer.scss';
|
import './CreateServer.scss';
|
||||||
|
|
||||||
const SHOW_IMPORT_MSG_TIME = 4000;
|
const SHOW_IMPORT_MSG_TIME = 4000;
|
||||||
|
|
||||||
interface CreateServerProps extends RouterProps {
|
interface CreateServerProps extends RouterProps {
|
||||||
createServer: (server: RegularServer) => void;
|
createServer: (server: ServerWithId) => void;
|
||||||
resetSelectedServer: Function;
|
resetSelectedServer: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,23 @@ export interface NewServerData {
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RegularServer extends NewServerData {
|
export interface ServerWithId {
|
||||||
id: string;
|
id: string;
|
||||||
version?: string;
|
|
||||||
printableVersion?: string;
|
|
||||||
serverNotReachable?: true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NotFoundServer {
|
export interface ReachableServer extends ServerWithId {
|
||||||
|
version: string;
|
||||||
|
printableVersion: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NonReachableServer extends ServerWithId {
|
||||||
|
serverNotReachable: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NotFoundServer {
|
||||||
serverNotFound: true;
|
serverNotFound: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Server = RegularServer | NotFoundServer;
|
export type RegularServer = ReachableServer | NonReachableServer;
|
||||||
|
|
||||||
|
export type SelectedServer = RegularServer | NotFoundServer | null;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useRef, RefObject, ChangeEvent, MutableRefObject } from 'react';
|
import React, { useRef, RefObject, ChangeEvent, MutableRefObject } from 'react';
|
||||||
import { UncontrolledTooltip } from 'reactstrap';
|
import { UncontrolledTooltip } from 'reactstrap';
|
||||||
import ServersImporter from '../services/ServersImporter';
|
import ServersImporter from '../services/ServersImporter';
|
||||||
import { Server } from '../data';
|
import { NewServerData } from '../data';
|
||||||
|
|
||||||
type Ref<T> = RefObject<T> | MutableRefObject<T>;
|
type Ref<T> = RefObject<T> | MutableRefObject<T>;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ export interface ImportServersBtnProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportServersBtnConnectProps extends ImportServersBtnProps {
|
interface ImportServersBtnConnectProps extends ImportServersBtnProps {
|
||||||
createServers: (servers: Server[]) => void;
|
createServers: (servers: NewServerData[]) => void;
|
||||||
fileRef: Ref<HTMLInputElement>;
|
fileRef: Ref<HTMLInputElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import { createAction, handleActions } from 'redux-actions';
|
import { createAction, handleActions } from 'redux-actions';
|
||||||
import { identity, memoizeWith, pipe } from 'ramda';
|
import { identity, memoizeWith, pipe } from 'ramda';
|
||||||
|
import { Action, Dispatch } from 'redux';
|
||||||
import { resetShortUrlParams } from '../../short-urls/reducers/shortUrlsListParams';
|
import { resetShortUrlParams } from '../../short-urls/reducers/shortUrlsListParams';
|
||||||
import { versionToPrintable, versionToSemVer as toSemVer } from '../../utils/helpers/version';
|
import { versionToPrintable, versionToSemVer as toSemVer } from '../../utils/helpers/version';
|
||||||
|
import { NonReachableServer, NotFoundServer, ReachableServer, SelectedServer } from '../data';
|
||||||
|
import { GetState } from '../../container/types';
|
||||||
|
import { ShlinkApiClientBuilder, ShlinkHealth } from '../../utils/services/types';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const SELECT_SERVER = 'shlink/selectedServer/SELECT_SERVER';
|
export const SELECT_SERVER = 'shlink/selectedServer/SELECT_SERVER';
|
||||||
|
@ -12,22 +16,30 @@ export const MAX_FALLBACK_VERSION = '999.999.999';
|
||||||
export const LATEST_VERSION_CONSTRAINT = 'latest';
|
export const LATEST_VERSION_CONSTRAINT = 'latest';
|
||||||
/* eslint-enable padding-line-between-statements */
|
/* eslint-enable padding-line-between-statements */
|
||||||
|
|
||||||
const initialState = null;
|
const initialState: SelectedServer = null;
|
||||||
const versionToSemVer = pipe(
|
const versionToSemVer = pipe(
|
||||||
(version) => version === LATEST_VERSION_CONSTRAINT ? MAX_FALLBACK_VERSION : version,
|
(version: string) => version === LATEST_VERSION_CONSTRAINT ? MAX_FALLBACK_VERSION : version,
|
||||||
toSemVer(MIN_FALLBACK_VERSION),
|
toSemVer(MIN_FALLBACK_VERSION),
|
||||||
);
|
);
|
||||||
|
|
||||||
const getServerVersion = memoizeWith(identity, (serverId, health) => health().then(({ version }) => ({
|
const getServerVersion = memoizeWith(
|
||||||
|
identity,
|
||||||
|
async (_serverId: string, health: () => Promise<ShlinkHealth>) => health().then(({ version }) => ({
|
||||||
version: versionToSemVer(version),
|
version: versionToSemVer(version),
|
||||||
printableVersion: versionToPrintable(version),
|
printableVersion: versionToPrintable(version),
|
||||||
})));
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
export const resetSelectedServer = createAction(RESET_SELECTED_SERVER);
|
export const resetSelectedServer = createAction(RESET_SELECTED_SERVER);
|
||||||
|
|
||||||
export const selectServer = (buildShlinkApiClient, loadMercureInfo) => (serverId) => async (
|
export const selectServer = (
|
||||||
dispatch,
|
buildShlinkApiClient: ShlinkApiClientBuilder,
|
||||||
getState,
|
loadMercureInfo: () => Action,
|
||||||
|
) => (
|
||||||
|
serverId: string,
|
||||||
|
) => async (
|
||||||
|
dispatch: Dispatch,
|
||||||
|
getState: GetState,
|
||||||
) => {
|
) => {
|
||||||
dispatch(resetSelectedServer());
|
dispatch(resetSelectedServer());
|
||||||
dispatch(resetShortUrlParams());
|
dispatch(resetShortUrlParams());
|
||||||
|
@ -36,7 +48,7 @@ export const selectServer = (buildShlinkApiClient, loadMercureInfo) => (serverId
|
||||||
const selectedServer = servers[serverId];
|
const selectedServer = servers[serverId];
|
||||||
|
|
||||||
if (!selectedServer) {
|
if (!selectedServer) {
|
||||||
dispatch({
|
dispatch<Action & { selectedServer: NotFoundServer }>({
|
||||||
type: SELECT_SERVER,
|
type: SELECT_SERVER,
|
||||||
selectedServer: { serverNotFound: true },
|
selectedServer: { serverNotFound: true },
|
||||||
});
|
});
|
||||||
|
@ -48,7 +60,7 @@ export const selectServer = (buildShlinkApiClient, loadMercureInfo) => (serverId
|
||||||
const { health } = buildShlinkApiClient(selectedServer);
|
const { health } = buildShlinkApiClient(selectedServer);
|
||||||
const { version, printableVersion } = await getServerVersion(serverId, health);
|
const { version, printableVersion } = await getServerVersion(serverId, health);
|
||||||
|
|
||||||
dispatch({
|
dispatch<Action & { selectedServer: ReachableServer }>({
|
||||||
type: SELECT_SERVER,
|
type: SELECT_SERVER,
|
||||||
selectedServer: {
|
selectedServer: {
|
||||||
...selectedServer,
|
...selectedServer,
|
||||||
|
@ -58,14 +70,14 @@ export const selectServer = (buildShlinkApiClient, loadMercureInfo) => (serverId
|
||||||
});
|
});
|
||||||
dispatch(loadMercureInfo());
|
dispatch(loadMercureInfo());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dispatch({
|
dispatch<Action & { selectedServer: NonReachableServer }>({
|
||||||
type: SELECT_SERVER,
|
type: SELECT_SERVER,
|
||||||
selectedServer: { ...selectedServer, serverNotReachable: true },
|
selectedServer: { ...selectedServer, serverNotReachable: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions({
|
export default handleActions<SelectedServer, any>({
|
||||||
[RESET_SELECTED_SERVER]: () => initialState,
|
[RESET_SELECTED_SERVER]: () => initialState,
|
||||||
[SELECT_SERVER]: (state, { selectedServer }) => selectedServer,
|
[SELECT_SERVER]: (_, { selectedServer }: any) => selectedServer,
|
||||||
}, initialState);
|
}, initialState);
|
|
@ -1,7 +1,7 @@
|
||||||
import { handleActions } from 'redux-actions';
|
import { handleActions } from 'redux-actions';
|
||||||
import { pipe, assoc, map, reduce, dissoc } from 'ramda';
|
import { pipe, assoc, map, reduce, dissoc } from 'ramda';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { RegularServer, NewServerData } from '../data';
|
import { NewServerData, ServerWithId } from '../data';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const EDIT_SERVER = 'shlink/servers/EDIT_SERVER';
|
export const EDIT_SERVER = 'shlink/servers/EDIT_SERVER';
|
||||||
|
@ -9,13 +9,13 @@ export const DELETE_SERVER = 'shlink/servers/DELETE_SERVER';
|
||||||
export const CREATE_SERVERS = 'shlink/servers/CREATE_SERVERS';
|
export const CREATE_SERVERS = 'shlink/servers/CREATE_SERVERS';
|
||||||
/* eslint-enable padding-line-between-statements */
|
/* eslint-enable padding-line-between-statements */
|
||||||
|
|
||||||
export type ServersMap = Record<string, RegularServer>;
|
export type ServersMap = Record<string, ServerWithId>;
|
||||||
|
|
||||||
const initialState: ServersMap = {};
|
const initialState: ServersMap = {};
|
||||||
|
|
||||||
const serverWithId = (server: RegularServer | NewServerData): RegularServer => {
|
const serverWithId = (server: ServerWithId | NewServerData): ServerWithId => {
|
||||||
if ((server as RegularServer).id) {
|
if ((server as ServerWithId).id) {
|
||||||
return server as RegularServer;
|
return server as ServerWithId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return assoc('id', uuid(), server);
|
return assoc('id', uuid(), server);
|
||||||
|
@ -29,7 +29,7 @@ export default handleActions<ServersMap, any>({
|
||||||
: assoc(serverId, { ...state[serverId], ...serverData }, state),
|
: assoc(serverId, { ...state[serverId], ...serverData }, state),
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
|
||||||
const serversListToMap = reduce<RegularServer, ServersMap>((acc, server) => assoc(server.id, server, acc), {});
|
const serversListToMap = reduce<ServerWithId, ServersMap>((acc, server) => assoc(server.id, server, acc), {});
|
||||||
|
|
||||||
export const createServers = pipe(
|
export const createServers = pipe(
|
||||||
map(serverWithId),
|
map(serverWithId),
|
||||||
|
@ -37,7 +37,7 @@ export const createServers = pipe(
|
||||||
(newServers: ServersMap) => ({ type: CREATE_SERVERS, newServers }),
|
(newServers: ServersMap) => ({ type: CREATE_SERVERS, newServers }),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const createServer = (server: RegularServer) => createServers([ server ]);
|
export const createServer = (server: ServerWithId) => createServers([ server ]);
|
||||||
|
|
||||||
export const editServer = (serverId: string, serverData: Partial<NewServerData>) => ({
|
export const editServer = (serverId: string, serverData: Partial<NewServerData>) => ({
|
||||||
type: EDIT_SERVER,
|
type: EDIT_SERVER,
|
||||||
|
@ -45,4 +45,4 @@ export const editServer = (serverId: string, serverData: Partial<NewServerData>)
|
||||||
serverData,
|
serverData,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const deleteServer = ({ id }: RegularServer) => ({ type: DELETE_SERVER, serverId: id });
|
export const deleteServer = ({ id }: ServerWithId) => ({ type: DELETE_SERVER, serverId: id });
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { CsvJson } from 'csvjson';
|
import { CsvJson } from 'csvjson';
|
||||||
import { RegularServer } from '../data';
|
import { NewServerData } from '../data';
|
||||||
|
|
||||||
const CSV_MIME_TYPE = 'text/csv';
|
const CSV_MIME_TYPE = 'text/csv';
|
||||||
|
|
||||||
export default class ServersImporter {
|
export default class ServersImporter {
|
||||||
public constructor(private readonly csvjson: CsvJson, private readonly fileReaderFactory: () => FileReader) {}
|
public constructor(private readonly csvjson: CsvJson, private readonly fileReaderFactory: () => FileReader) {}
|
||||||
|
|
||||||
public importServersFromFile = async (file?: File | null): Promise<RegularServer[]> => {
|
public importServersFromFile = async (file?: File | null): Promise<NewServerData[]> => {
|
||||||
if (!file || file.type !== CSV_MIME_TYPE) {
|
if (!file || file.type !== CSV_MIME_TYPE) {
|
||||||
throw new Error('No file provided or file is not a CSV');
|
throw new Error('No file provided or file is not a CSV');
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export default class ServersImporter {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
reader.addEventListener('loadend', (e: ProgressEvent<FileReader>) => {
|
reader.addEventListener('loadend', (e: ProgressEvent<FileReader>) => {
|
||||||
const content = e.target?.result?.toString() ?? '';
|
const content = e.target?.result?.toString() ?? '';
|
||||||
const servers = this.csvjson.toObject<RegularServer>(content);
|
const servers = this.csvjson.toObject<NewServerData>(content);
|
||||||
|
|
||||||
resolve(servers);
|
resolve(servers);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { RegularServer } from '../../servers/data';
|
import { ServerWithId } from '../../servers/data';
|
||||||
import { GetState } from '../../container/types';
|
import { GetState } from '../../container/types';
|
||||||
import ShlinkApiClient from './ShlinkApiClient';
|
import ShlinkApiClient from './ShlinkApiClient';
|
||||||
|
|
||||||
// FIXME Move to ShlinkApiClientBuilder
|
// FIXME Move to ShlinkApiClientBuilder
|
||||||
export type ShlinkApiClientBuilder = (getStateOrSelectedServer: RegularServer | GetState) => ShlinkApiClient;
|
export type ShlinkApiClientBuilder = (getStateOrSelectedServer: ServerWithId | GetState) => ShlinkApiClient;
|
||||||
|
|
||||||
export interface ShlinkMercureInfo {
|
export interface ShlinkMercureInfo {
|
||||||
token: string;
|
token: string;
|
||||||
mercureHubUrl: string;
|
mercureHubUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ShlinkHealth {
|
||||||
|
status: 'pass' | 'fail';
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue