chore: refactor to remove duplication

issue #46

From now on, only a single getJSON function will be used to call the backend api. No more duplications
This commit is contained in:
2024-09-21 17:42:55 -03:00
parent 1c7576109e
commit 1b7d32b27d
9 changed files with 69 additions and 199 deletions
+34
View File
@@ -0,0 +1,34 @@
import { API_TOKEN } from '../app-constants/app-constants';
const tokenState = localStorage.getItem(API_TOKEN);
function handleError(httpStatusCode: number) {
if (httpStatusCode === 403) {
throw new Error('Forbidden! Access denied');
}
if (httpStatusCode === 500) {
throw new Error('Internal Server Error!');
}
throw new Error('Unknown error');
}
const api = {
getJSON: async (url: string) => {
const response = await fetch(url, {
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${tokenState}`
}
});
if (response.ok) {
const data = await response.json();
return data;
} else {
handleError(response.status);
}
}
}
export default api;
+2 -34
View File
@@ -1,5 +1,6 @@
import { API_TOKEN } from '../app-constants/app-constants';
import { SigninResponse } from '../types/SigninResponse';
import api from './api';
import ApiConfig from './apiConfig';
/**
@@ -88,41 +89,8 @@ function logoutUser(): void {
localStorage.removeItem(API_TOKEN);
}
/**
* Sends a GET request to the server to refresh the user token.
*
* @returns {Promise<SigninResponse>} A promise that resolves to SigninResponse if the request was
* successful.
* @throws {Error} An error object if there was an error
*/
async function refreshToken(): Promise<SigninResponse> {
const tokenState = localStorage.getItem(API_TOKEN);
if (tokenState) {
const response = await fetch(ApiConfig.refreshTokenUrl, {
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${tokenState}`
}
});
if (response.ok) {
const data = await response.json();
localStorage.setItem(API_TOKEN, data.token);
return {
token: data.token
};
}
throw new Error(response.statusText);
}
throw new Error('No saved token!');
}
export {
registerUser,
authenticateUser,
logoutUser,
refreshToken
logoutUser
};
-78
View File
@@ -1,78 +0,0 @@
import { API_TOKEN } from '../app-constants/app-constants';
import { HomeSearchResponse } from '../types/HomeSearchResponse';
import { SummaryResponse } from '../types/SummaryResponse';
import ApiConfig from './apiConfig';
/**
* Sends a GET request to the server to get summaries for the home page.
*
* @returns {Promise<SummaryResponse>} A promise that resolves to SummaryResponse if the request
* was successful.
* @throws {Error} An error object if there was an error
*/
async function getHomeSummary(): Promise<SummaryResponse> {
try {
const tokenState = localStorage.getItem(API_TOKEN);
const response = await fetch(`${ApiConfig.homeUrl}/summary`, {
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${tokenState}`
}
});
if (response.ok) {
const data = await response.json();
return data;
}
if (response.status === 403) {
throw new Error('Forbidden! Access denied');
}
if (response.status === 500) {
throw new Error('Internal Server Error!');
}
} catch (e) {
if (typeof e === 'string') {
throw new Error(e as string);
}
}
throw new Error('Unknown error');
}
/**
* Sends a GET request to the server to get summaries for the home page.
*
* @returns {Promise<HomeSearchResponse>} A promise that resolves to HomeSearchResponse if the
* request was successful.
* @throws {Error} An error object if there was an error
*/
async function searchHomeRequest(term: string): Promise<HomeSearchResponse> {
try {
const tokenState = localStorage.getItem(API_TOKEN);
const response = await fetch(`${ApiConfig.homeUrl}/search?term=${term}`, {
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${tokenState}`
}
});
if (response.ok) {
const data = await response.json();
return data;
}
if (response.status === 403) {
throw new Error('Forbidden! Access denied');
}
if (response.status === 500) {
throw new Error('Internal Server Error!');
}
} catch (e) {
if (typeof e === 'string') {
throw new Error(e as string);
}
}
throw new Error('Unknown error');
}
export { getHomeSummary, searchHomeRequest };
+2 -37
View File
@@ -2,6 +2,7 @@ import { API_TOKEN } from '../app-constants/app-constants';
import TaskNoteRequest from '../types/TaskNoteRequest';
import { NoteResponse } from '../types/NoteResponse';
import ApiConfig from './apiConfig';
import api from './api';
/**
* Sends a POST request to the server to create a note.
@@ -43,42 +44,6 @@ async function addNoteRequest(note: TaskNoteRequest): Promise<NoteResponse> {
throw new Error('Unknown error');
}
/**
* Sends a GET request to the server to fetch all notes.
*
* @returns {Promise<NoteResponse[]>} A promise that resolves to an array of NoteResponse if the
* request was successful.
* @throws {Error} An error object if there was an error
*/
async function getNotesRequest(): Promise<NoteResponse[]> {
try {
const tokenState = localStorage.getItem(API_TOKEN);
const response = await fetch(ApiConfig.notesUrl, {
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${tokenState}`
}
});
if (response.ok) {
const data = await response.json();
return data;
}
if (response.status === 403) {
throw new Error('Forbidden! Access denied');
}
if (response.status === 500) {
throw new Error('Internal Server Error!');
}
} catch (e) {
if (typeof e === 'string') {
throw new Error(e as string);
}
}
throw new Error('Unknown error');
}
/**
* Sends a PATCH request to the server to update a note.
*
@@ -157,5 +122,5 @@ async function deleteNoteRequest(id: number): Promise<undefined> {
}
export {
addNoteRequest, getNotesRequest, updateNoteRequest, deleteNoteRequest
addNoteRequest, updateNoteRequest, deleteNoteRequest
};
+1 -33
View File
@@ -39,38 +39,6 @@ async function addTaskRequest(task: TaskNoteRequest): Promise<TaskResponse | Err
throw new Error('Unknown error');
}
/**
*
*/
async function getTasksRequest(): Promise<TaskResponse[]> {
try {
const tokenState = localStorage.getItem(API_TOKEN);
const response = await fetch(ApiConfig.tasksUrl, {
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${tokenState}`
}
});
if (response.ok) {
const data = await response.json();
return data;
}
if (response.status === 403) {
throw new Error('Forbidden! Access denied');
}
if (response.status === 500) {
throw new Error('Internal Server Error!');
}
} catch (e) {
if (typeof e === 'string') {
throw new Error(e as string);
}
}
throw new Error('Unknown error');
}
/**
*
*/
@@ -136,5 +104,5 @@ async function deleteTaskRequest(id: number): Promise<undefined> {
}
export {
addTaskRequest, getTasksRequest, updateTaskDoneRequest, deleteTaskRequest
addTaskRequest, updateTaskDoneRequest, deleteTaskRequest
};
+4 -5
View File
@@ -7,9 +7,10 @@ import { SigninResponse } from '../types/SigninResponse';
import {
authenticateUser,
logoutUser,
refreshToken,
registerUser
} from '../api-service/authService';
import api from '../api-service/api';
import ApiConfig from '../api-service/apiConfig';
interface Props {
children: React.ReactNode;
@@ -23,10 +24,8 @@ const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }: Pro
const fetchCurrentSession = async (pathname: string): Promise<SigninResponse | undefined> => {
try {
const bearerToken: SigninResponse = await refreshToken();
if (bearerToken && bearerToken.token) {
setSigned(true);
}
const bearerToken: SigninResponse = await api.getJSON(ApiConfig.refreshTokenUrl);
setSigned(true);
return bearerToken;
} catch (e) {
if (e instanceof Error) {
+16 -5
View File
@@ -5,11 +5,13 @@ import {
Button, Card, Col, Container, Form, InputGroup, Row
} from 'react-bootstrap';
import './style.css';
import { getHomeSummary, searchHomeRequest } from '../../api-service/homeService';
import { useNavigate } from 'react-router-dom';
import { SummaryResponse } from '../../types/SummaryResponse';
import { HomeSearchResponse } from '../../types/HomeSearchResponse';
import { TaskResponse } from '../../types/TaskResponse';
import { NoteResponse } from '../../types/NoteResponse';
import api from '../../api-service/api';
import ApiConfig from '../../api-service/apiConfig';
/**
*
@@ -20,6 +22,7 @@ function Home(): JSX.Element {
const [validated, setValidated] = useState<boolean>(true);
const [formInvalid, setFormInvalid] = useState<boolean>(false);
const [searchResults, setSearchResults] = useState<HomeSearchResponse | null>(null);
const navigate = useNavigate();
const handleError = (e: unknown): void => {
if (typeof e === 'string') {
@@ -31,7 +34,7 @@ function Home(): JSX.Element {
const getSummary = async () => {
try {
const response = await getHomeSummary();
const response = await api.getJSON(`${ApiConfig.homeUrl}/summary`);
setSummary(response);
} catch (e) {
handleError(e);
@@ -40,7 +43,7 @@ function Home(): JSX.Element {
const searchTerm = async (term: string): Promise<boolean> => {
try {
const response: HomeSearchResponse = await searchHomeRequest(term);
const response: HomeSearchResponse = await api.getJSON(`${ApiConfig.homeUrl}/search?term=${term}`);
setSearchResults(response);
return true;
} catch (e) {
@@ -91,7 +94,11 @@ function Home(): JSX.Element {
? 'Pending Tasks'
: 'No pending tasks!'}
</Card.Text>
<Button variant="primary" href="/tasks">
<Button
variant="primary"
type="button"
onClick={() => navigate('/tasks')}
>
Go to Tasks
</Button>
</Card.Body>
@@ -106,7 +113,11 @@ function Home(): JSX.Element {
<Card.Body className="d-flex flex-column align-items-center justify-content-center">
<Card.Title className="display-4">10</Card.Title>
<Card.Text>Notes</Card.Text>
<Button variant="success" href="/notes">
<Button
variant="success"
type="button"
onClick={() => navigate('/notes')}
>
Go to Notes
</Button>
</Card.Body>
+7 -5
View File
@@ -7,8 +7,10 @@ import TaskNoteRequest from '../../types/TaskNoteRequest';
import { NoteResponse } from '../../types/NoteResponse';
import './style.css';
import {
addNoteRequest, deleteNoteRequest, getNotesRequest, updateNoteRequest
addNoteRequest, deleteNoteRequest, updateNoteRequest
} from '../../api-service/noteService';
import api from '../../api-service/api';
import ApiConfig from '../../api-service/apiConfig';
type NoteAction = 'add' | 'edit';
@@ -36,11 +38,11 @@ function Note(): JSX.Element {
};
const loadNotes = async () => {
const notesFetched: NoteResponse[] | Error = await getNotesRequest();
if (notesFetched instanceof Error) {
handleError(notesFetched);
} else {
try {
const notesFetched: NoteResponse[] = await api.getJSON(ApiConfig.notesUrl);
setNotes(notesFetched);
} catch (e) {
handleError(e);
}
};
+3 -2
View File
@@ -5,12 +5,13 @@ import {
import {
addTaskRequest,
deleteTaskRequest,
getTasksRequest,
updateTaskDoneRequest
} from '../../api-service/taskService';
import TaskNoteRequest from '../../types/TaskNoteRequest';
import { TaskResponse } from '../../types/TaskResponse';
import './style.css';
import api from '../../api-service/api';
import ApiConfig from '../../api-service/apiConfig';
/**
*
@@ -32,7 +33,7 @@ function Task(): JSX.Element {
};
const loadTasks = async () => {
const tasksFetched: TaskResponse[] | Error = await getTasksRequest();
const tasksFetched: TaskResponse[] | Error = await api.getJSON(ApiConfig.tasksUrl);
if (tasksFetched instanceof Error) {
handleError(tasksFetched);
} else {