From 8df40788ba4c6a8176305321e46c4e51d4b3d1dd Mon Sep 17 00:00:00 2001 From: Paulo Gustavo Veiga Date: Wed, 3 Feb 2021 14:27:32 -0800 Subject: [PATCH] Split Client into two classes. --- packages/webapp/src/client/index.ts | 46 ++++++++++ .../webapp/src/client/mock-client/index.ts | 86 ++----------------- .../webapp/src/client/rest-client/index.ts | 54 ++++++++++++ .../components/maps-page/maps-list/index.tsx | 4 +- packages/webapp/src/reducers/serviceSlice.ts | 58 +++++++------ 5 files changed, 138 insertions(+), 110 deletions(-) create mode 100644 packages/webapp/src/client/rest-client/index.ts diff --git a/packages/webapp/src/client/index.ts b/packages/webapp/src/client/index.ts index 5ac9f55d..23f932fc 100644 --- a/packages/webapp/src/client/index.ts +++ b/packages/webapp/src/client/index.ts @@ -32,6 +32,51 @@ export type ErrorInfo = { fields?: Map; } +export const parseResponseOnError = (response: any): ErrorInfo => { + + let result: ErrorInfo | undefined; + if (response) { + const status: number = response.status; + const data = response.data; + console.log(data); + + switch (status) { + case 401: + // this.authFailed(); + break; + default: + if (data) { + // Set global errors ... + if (data.globalErrors) { + let msg; + let errors = data.globalErrors; + if (errors.length > 0) { + msg = errors[0]; + } + result = { msg: errors }; + } + + // Set field errors ... + if (data.fieldErrors) { + // @Todo: Fix this ... + result = { msg: data.fieldErrors }; + result.fields = new Map(); + } + + } else { + result = { msg: response.statusText }; + } + } + } + + // Network related problem ... + if (!result) { + result = { msg: 'Unexpected error. Please, try latter' }; + } + + return result; +} + interface Client { createMap(rest: { name: string; description?: string | undefined }) deleteLabel(label: string): Promise; @@ -44,6 +89,7 @@ interface Client { duplicateMap(id: number, basicInfo: BasicMapInfo): Promise; loadMapInfo(id: number): Promise; changeStarred(id: number): Promise; + } diff --git a/packages/webapp/src/client/mock-client/index.ts b/packages/webapp/src/client/mock-client/index.ts index 44a596d9..8702ceb6 100644 --- a/packages/webapp/src/client/mock-client/index.ts +++ b/packages/webapp/src/client/mock-client/index.ts @@ -1,15 +1,11 @@ -import { BasicMapInfo, ErrorInfo, MapInfo, NewUser } from ".."; -import Client from ".."; +import Client, { BasicMapInfo, ErrorInfo, MapInfo, NewUser, parseResponseOnError } from '..'; import axios from "axios"; class MockClient implements Client { - private baseUrl: string; - private authFailed: () => void private maps: MapInfo[] = []; private labels: string[] = []; - constructor(baseUrl: string, authFailed: () => void) { - this.baseUrl = baseUrl; + constructor() { // Remove, just for develop .... function createMapInfo( @@ -46,7 +42,7 @@ class MockClient implements Client { createMap(rest: { name: string; description?: string | undefined; }) { throw new Error("Method not implemented."); } -s + s fetchLabels(): Promise { console.log("Fetching labels from server") return Promise.resolve(this.labels); @@ -138,20 +134,7 @@ s } registerNewUser(user: NewUser): Promise { - const handler = (success: () => void, reject: (error: ErrorInfo) => void) => { - axios.post(this.baseUrl + '/service/users', - JSON.stringify(user), - { headers: { 'Content-Type': 'application/json' } } - ).then(response => { - // All was ok, let's sent to success page ...; - success(); - }).catch(error => { - const response = error.response; - const errorInfo = this.parseResponseOnError(response); - reject(errorInfo); - }); - } - return new Promise(handler); + return Promise.resolve(); } fetchAllMaps(): Promise { @@ -160,66 +143,7 @@ s } resetPassword(email: string): Promise { - - const handler = (success: () => void, reject: (error: ErrorInfo) => void) => { - axios.post(`${this.baseUrl}/service/users/resetPassword?email=${email}`, - null, - { headers: { 'Content-Type': 'application/json' } } - ).then(response => { - // All was ok, let's sent to success page ...; - success(); - }).catch(error => { - const response = error.response; - const errorInfo = this.parseResponseOnError(response); - reject(errorInfo); - }); - } - return new Promise(handler); - } - - private parseResponseOnError = (response: any): ErrorInfo => { - - let result: ErrorInfo | undefined; - if (response) { - const status: number = response.status; - const data = response.data; - console.log(data); - - switch (status) { - case 401: - this.authFailed(); - break; - default: - if (data) { - // Set global errors ... - if (data.globalErrors) { - let msg; - let errors = data.globalErrors; - if (errors.length > 0) { - msg = errors[0]; - } - result = { msg: errors }; - } - - // Set field errors ... - if (data.fieldErrors) { - // @Todo: Fix this ... - result = { msg: data.fieldErrors }; - result.fields = new Map(); - } - - } else { - result = { msg: response.statusText }; - } - } - } - - // Network related problem ... - if (!result) { - result = { msg: 'Unexpected error. Please, try latter' }; - } - - return result; + return Promise.resolve(); } } diff --git a/packages/webapp/src/client/rest-client/index.ts b/packages/webapp/src/client/rest-client/index.ts new file mode 100644 index 00000000..53f69d47 --- /dev/null +++ b/packages/webapp/src/client/rest-client/index.ts @@ -0,0 +1,54 @@ +import axios from 'axios'; +import { ErrorInfo, MapInfo, NewUser, parseResponseOnError } from '..'; +import MockClient from '../mock-client/'; + +//@Remove inheritance once is it completed. +export default class RestClient extends MockClient { + private baseUrl: string; + private authFailed: () => void + + constructor(baseUrl: string, authFailed: () => void) { + super(); + this.baseUrl = baseUrl; + } + + fetchAllMaps(): Promise { + console.log("Fetching maps from server") + return Promise.resolve([]); + } + + registerNewUser(user: NewUser): Promise { + const handler = (success: () => void, reject: (error: ErrorInfo) => void) => { + axios.post(this.baseUrl + '/service/users', + JSON.stringify(user), + { headers: { 'Content-Type': 'application/json' } } + ).then(response => { + // All was ok, let's sent to success page ...; + success(); + }).catch(error => { + const response = error.response; + const errorInfo = parseResponseOnError(response); + reject(errorInfo); + }); + } + return new Promise(handler); + } + resetPassword(email: string): Promise { + + const handler = (success: () => void, reject: (error: ErrorInfo) => void) => { + axios.put(`${this.baseUrl}/service/users/resetPassword?email=${email}`, + null, + { headers: { 'Content-Type': 'application/json' } } + ).then(response => { + // All was ok, let's sent to success page ...; + success(); + }).catch(error => { + const response = error.response; + const errorInfo = parseResponseOnError(response); + reject(errorInfo); + }); + } + return new Promise(handler); + } +} + diff --git a/packages/webapp/src/components/maps-page/maps-list/index.tsx b/packages/webapp/src/components/maps-page/maps-list/index.tsx index 5e785130..9be85958 100644 --- a/packages/webapp/src/components/maps-page/maps-list/index.tsx +++ b/packages/webapp/src/components/maps-page/maps-list/index.tsx @@ -393,9 +393,9 @@ export const MapsList = (props: MapsListProps) => { {isLoading ? ( - Loading ...) : + Loading ...) : (mapsInfo.length == 0 ? - (No matching records found) : + () : stableSort(mapsInfo, getComparator(order, orderBy)) .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) .map((row: MapInfo) => { diff --git a/packages/webapp/src/reducers/serviceSlice.ts b/packages/webapp/src/reducers/serviceSlice.ts index e05b8690..082a58cd 100644 --- a/packages/webapp/src/reducers/serviceSlice.ts +++ b/packages/webapp/src/reducers/serviceSlice.ts @@ -1,44 +1,47 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import axios from 'axios'; -import Client from '../client'; +import Client from '../client'; import MockClient from '../client/mock-client'; +import RestClient from '../client/rest-client'; -type RutimeConfig = { - apiBaseUrl: string; +interface ConfigInfo { + apiBaseUrl: string } -async function loadRuntimeConfig() { - let result: RutimeConfig | undefined; +class RutimeConfig { + private config: ConfigInfo; - await axios.get("runtime-config.json" - ).then(response => { - // All was ok, let's sent to success page ... - result = response.data as RutimeConfig; - console.log("Dynamic configuration->" + response.data); - }).catch(e => { - console.log(e) - }); + constructor() { - if (!result) { - // Ok, try to create a default configuration relative to the current path ... - console.log("Configuration could not be loaded, falback to default config.") - const location = window.location; - const basePath = location.protocol + "//" + location.host + "/" + location.pathname.split('/')[1] - - result = { - apiBaseUrl: basePath - } } - return result; -} + load() { + + // Config can be inserted in the html page to define the global properties ... + this.config = (window as any).serverconfig; + return this; + } + + buildClient(): Client { + let result: Client; + if (this.config) { + result = new RestClient(this.config.apiBaseUrl, () => { console.log("401 error") }); + console.log("Service using rest client. " + JSON.stringify(this.config)) + + } else { + console.log("Warning:Service using mockservice client") + result = new MockClient(); + } + return result; + } +} interface ServiceState { instance: Client; } -const initialState: ServiceState = { - instance: new MockClient("", () => { console.log("401 error") }) +const initialState: ServiceState = { + instance: new RutimeConfig().load().buildClient() }; export const serviceSlice = createSlice({ @@ -46,7 +49,7 @@ export const serviceSlice = createSlice({ initialState: initialState, reducers: { initialize(state, action: PayloadAction) { - state.instance = new MockClient("", () => { console.log("401 error") }); + // state.instance = new RutimeConfig().load().buildClient() } }, }); @@ -54,5 +57,6 @@ export const serviceSlice = createSlice({ export const activeInstance = (state: any): Client => { return state.service.instance; } + export default serviceSlice.reducer