From dc7813806697949263586ba195358fd6998a380c Mon Sep 17 00:00:00 2001
From: Alejandro Celaya <alejandro@alejandrocelaya.com>
Date: Sun, 23 Aug 2020 10:20:31 +0200
Subject: [PATCH] Migrate servers reducer to typescript

---
 src/container/types.ts                        |  3 +-
 src/servers/reducers/servers.js               | 35 --------------
 src/servers/reducers/servers.ts               | 48 +++++++++++++++++++
 .../{servers.test.js => servers.test.ts}      | 16 ++++---
 4 files changed, 59 insertions(+), 43 deletions(-)
 delete mode 100644 src/servers/reducers/servers.js
 create mode 100644 src/servers/reducers/servers.ts
 rename test/servers/reducers/{servers.test.js => servers.test.ts} (83%)

diff --git a/src/container/types.ts b/src/container/types.ts
index 85486e2e..42647c60 100644
--- a/src/container/types.ts
+++ b/src/container/types.ts
@@ -1,9 +1,10 @@
 import { MercureInfo } from '../mercure/reducers/mercureInfo';
+import { ServersMap } from '../servers/reducers/servers';
 
 export type ConnectDecorator = (props: string[], actions?: string[]) => any;
 
 export interface ShlinkState {
-  servers: any;
+  servers: ServersMap;
   selectedServer: any;
   shortUrlsList: any;
   shortUrlsListParams: any;
diff --git a/src/servers/reducers/servers.js b/src/servers/reducers/servers.js
deleted file mode 100644
index 3173d1d6..00000000
--- a/src/servers/reducers/servers.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { handleActions } from 'redux-actions';
-import { pipe, assoc, map, reduce, dissoc } from 'ramda';
-import { v4 as uuid } from 'uuid';
-
-/* eslint-disable padding-line-between-statements */
-export const EDIT_SERVER = 'shlink/servers/EDIT_SERVER';
-export const DELETE_SERVER = 'shlink/servers/DELETE_SERVER';
-export const CREATE_SERVERS = 'shlink/servers/CREATE_SERVERS';
-/* eslint-enable padding-line-between-statements */
-
-const initialState = {};
-
-const assocId = (server) => assoc('id', server.id || uuid(), server);
-
-export default handleActions({
-  [CREATE_SERVERS]: (state, { newServers }) => ({ ...state, ...newServers }),
-  [DELETE_SERVER]: (state, { serverId }) => dissoc(serverId, state),
-  [EDIT_SERVER]: (state, { serverId, serverData }) => !state[serverId]
-    ? state
-    : assoc(serverId, { ...state[serverId], ...serverData }, state),
-}, initialState);
-
-export const createServer = (server) => createServers([ server ]);
-
-const serversListToMap = reduce((acc, server) => assoc(server.id, server, acc), {});
-
-export const createServers = pipe(
-  map(assocId),
-  serversListToMap,
-  (newServers) => ({ type: CREATE_SERVERS, newServers }),
-);
-
-export const editServer = (serverId, serverData) => ({ type: EDIT_SERVER, serverId, serverData });
-
-export const deleteServer = ({ id }) => ({ type: DELETE_SERVER, serverId: id });
diff --git a/src/servers/reducers/servers.ts b/src/servers/reducers/servers.ts
new file mode 100644
index 00000000..31a66085
--- /dev/null
+++ b/src/servers/reducers/servers.ts
@@ -0,0 +1,48 @@
+import { handleActions } from 'redux-actions';
+import { pipe, assoc, map, reduce, dissoc } from 'ramda';
+import { v4 as uuid } from 'uuid';
+import { RegularServer, NewServerData } from '../data';
+
+/* eslint-disable padding-line-between-statements */
+export const EDIT_SERVER = 'shlink/servers/EDIT_SERVER';
+export const DELETE_SERVER = 'shlink/servers/DELETE_SERVER';
+export const CREATE_SERVERS = 'shlink/servers/CREATE_SERVERS';
+/* eslint-enable padding-line-between-statements */
+
+export type ServersMap = Record<string, RegularServer>;
+
+const initialState: ServersMap = {};
+
+const serverWithId = (server: RegularServer | NewServerData): RegularServer => {
+  if ((server as RegularServer).id) {
+    return server as RegularServer;
+  }
+
+  return assoc('id', uuid(), server);
+};
+
+export default handleActions<ServersMap, any>({
+  [CREATE_SERVERS]: (state, { newServers }: any) => ({ ...state, ...newServers }),
+  [DELETE_SERVER]: (state, { serverId }: any) => dissoc(serverId, state),
+  [EDIT_SERVER]: (state, { serverId, serverData }: any) => !state[serverId]
+    ? state
+    : assoc(serverId, { ...state[serverId], ...serverData }, state),
+}, initialState);
+
+const serversListToMap = reduce<RegularServer, ServersMap>((acc, server) => assoc(server.id, server, acc), {});
+
+export const createServers = pipe(
+  map(serverWithId),
+  serversListToMap,
+  (newServers: ServersMap) => ({ type: CREATE_SERVERS, newServers }),
+);
+
+export const createServer = (server: RegularServer) => createServers([ server ]);
+
+export const editServer = (serverId: string, serverData: Partial<NewServerData>) => ({
+  type: EDIT_SERVER,
+  serverId,
+  serverData,
+});
+
+export const deleteServer = ({ id }: RegularServer) => ({ type: DELETE_SERVER, serverId: id });
diff --git a/test/servers/reducers/servers.test.js b/test/servers/reducers/servers.test.ts
similarity index 83%
rename from test/servers/reducers/servers.test.js
rename to test/servers/reducers/servers.test.ts
index 319e18d2..e4ad7e58 100644
--- a/test/servers/reducers/servers.test.js
+++ b/test/servers/reducers/servers.test.ts
@@ -1,4 +1,5 @@
 import { values } from 'ramda';
+import { Mock } from 'ts-mockery';
 import reducer, {
   createServer,
   deleteServer,
@@ -8,11 +9,12 @@ import reducer, {
   DELETE_SERVER,
   CREATE_SERVERS,
 } from '../../../src/servers/reducers/servers';
+import { RegularServer } from '../../../src/servers/data';
 
 describe('serverReducer', () => {
   const list = {
-    abc123: { id: 'abc123' },
-    def456: { id: 'def456' },
+    abc123: Mock.of<RegularServer>({ id: 'abc123' }),
+    def456: Mock.of<RegularServer>({ id: 'def456' }),
   };
 
   afterEach(jest.clearAllMocks);
@@ -21,14 +23,14 @@ describe('serverReducer', () => {
     it('returns edited server when action is EDIT_SERVER', () =>
       expect(reducer(
         list,
-        { type: EDIT_SERVER, serverId: 'abc123', serverData: { foo: 'foo' } },
+        { type: EDIT_SERVER, serverId: 'abc123', serverData: { foo: 'foo' } } as any,
       )).toEqual({
         abc123: { id: 'abc123', foo: 'foo' },
         def456: { id: 'def456' },
       }));
 
     it('removes server when action is DELETE_SERVER', () =>
-      expect(reducer(list, { type: DELETE_SERVER, serverId: 'abc123' })).toEqual({
+      expect(reducer(list, { type: DELETE_SERVER, serverId: 'abc123' } as any)).toEqual({
         def456: { id: 'def456' },
       }));
 
@@ -38,7 +40,7 @@ describe('serverReducer', () => {
         newServers: {
           ghi789: { id: 'ghi789' },
         },
-      })).toEqual({
+      } as any)).toEqual({
         abc123: { id: 'abc123' },
         def456: { id: 'def456' },
         ghi789: { id: 'ghi789' },
@@ -48,7 +50,7 @@ describe('serverReducer', () => {
   describe('action creators', () => {
     describe('createServer', () => {
       it('returns expected action', () => {
-        const serverToCreate = { id: 'abc123' };
+        const serverToCreate = Mock.of<RegularServer>({ id: 'abc123' });
         const result = createServer(serverToCreate);
 
         expect(result).toEqual(expect.objectContaining({ type: CREATE_SERVERS }));
@@ -66,7 +68,7 @@ describe('serverReducer', () => {
 
     describe('deleteServer', () => {
       it('returns expected action', () => {
-        const serverToDelete = { id: 'abc123' };
+        const serverToDelete = Mock.of<RegularServer>({ id: 'abc123' });
         const result = deleteServer(serverToDelete);
 
         expect(result).toEqual({ type: DELETE_SERVER, serverId: 'abc123' });