From 7f8aaea7b8f29aa4c92aa71ab2fcfc1778da04ee Mon Sep 17 00:00:00 2001 From: Maciej Lenartowicz Date: Tue, 5 Nov 2019 11:10:50 +0000 Subject: [PATCH] Resolve "Discuss and implement proper API methods." --- src/api/delete.js | 41 --------------- src/api/get.js | 65 ------------------------ src/api/hooks.js | 86 ++++++++++++++++++++++++++++++++ src/api/patch.js | 40 --------------- src/api/post.js | 45 ----------------- src/api/utils.js | 76 +++++++++++----------------- src/form/components/ForisForm.js | 2 +- src/form/hooks.js | 3 +- src/index.js | 8 +-- 9 files changed, 122 insertions(+), 244 deletions(-) delete mode 100644 src/api/delete.js delete mode 100644 src/api/get.js create mode 100644 src/api/hooks.js delete mode 100644 src/api/patch.js delete mode 100644 src/api/post.js diff --git a/src/api/delete.js b/src/api/delete.js deleted file mode 100644 index dc4574f..0000000 --- a/src/api/delete.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/) - * - * This is free software, licensed under the GNU General Public License v3. - * See /LICENSE for more information. - */ - -import { useReducer, useCallback } from "react"; -import axios from "axios"; - -import { - API_ACTIONS, TIMEOUT, HEADERS, APIReducer, getErrorMessage, -} from "./utils"; - -export function useAPIDelete(url) { - const [state, dispatch] = useReducer(APIReducer, { - isSending: false, - isError: false, - isSuccess: false, - data: null, - }); - - const requestDelete = useCallback(async () => { - dispatch({ type: API_ACTIONS.INIT }); - try { - await axios.delete(url, { - timeout: TIMEOUT, - headers: HEADERS, - }); - dispatch({ type: API_ACTIONS.SUCCESS }); - } catch (error) { - dispatch({ - type: API_ACTIONS.FAILURE, - payload: getErrorMessage(error), - status: error.response.status, - }); - } - }, [url]); - - return [state, requestDelete]; -} diff --git a/src/api/get.js b/src/api/get.js deleted file mode 100644 index 14c9d69..0000000 --- a/src/api/get.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/) - * - * This is free software, licensed under the GNU General Public License v3. - * See /LICENSE for more information. - */ - -import { useReducer, useCallback } from "react"; -import axios from "axios"; - -import { ForisURLs } from "forisUrls"; -import { API_ACTIONS, TIMEOUT } from "./utils"; - -const APIGetReducer = (state, action) => { - switch (action.type) { - case API_ACTIONS.INIT: - return { - ...state, - isLoading: true, - isError: false, - }; - case API_ACTIONS.SUCCESS: - return { - ...state, - isLoading: false, - isError: false, - data: action.payload, - }; - case API_ACTIONS.FAILURE: - if (action.status === 403) window.location.assign(ForisURLs.login); - return { - ...state, - isLoading: false, - isError: true, - data: action.payload, - }; - default: - throw new Error(); - } -}; - -export function useAPIGet(url) { - const [state, dispatch] = useReducer(APIGetReducer, { - isLoading: false, - isError: false, - data: null, - }); - const get = useCallback(async () => { - dispatch({ type: API_ACTIONS.INIT }); - try { - const result = await axios.get(url, { - timeout: TIMEOUT, - }); - dispatch({ type: API_ACTIONS.SUCCESS, payload: result.data }); - } catch (error) { - dispatch({ - type: API_ACTIONS.FAILURE, - payload: error.response.data, - status: error.response.status, - }); - } - }, [url]); - - return [state, get]; -} diff --git a/src/api/hooks.js b/src/api/hooks.js new file mode 100644 index 0000000..a30bd8d --- /dev/null +++ b/src/api/hooks.js @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/) + * + * This is free software, licensed under the GNU General Public License v3. + * See /LICENSE for more information. + */ + +import { useReducer, useCallback } from "react"; + +import { ForisURLs } from "forisUrls"; +import { + API_STATE, API_ACTIONS, API_METHODS, TIMEOUT, HEADERS, getErrorMessage, +} from "./utils"; + +const DATA_METHODS = ["POST", "PATCH", "PUT"]; + +function createAPIHook(method) { + return (url, contentType) => { + const [state, dispatch] = useReducer(APIReducer, { + state: API_STATE.INIT, + data: null, + }); + + const sendRequest = useCallback(async (data) => { + const headers = { ...HEADERS }; + if (contentType) { + headers["Content-Type"] = contentType; + } + + dispatch({ type: API_ACTIONS.INIT }); + try { + const request = API_METHODS[method]; + const config = { timeout: TIMEOUT, headers }; + let result; + if (DATA_METHODS.includes(method)) { + result = await request(url, data, config); + } else { + result = await request(url, config); + } + dispatch({ type: API_ACTIONS.SUCCESS, payload: result.data }); + } catch (error) { + dispatch({ + type: API_ACTIONS.FAILURE, + payload: getErrorMessage(error), + status: error.response.status, + }); + } + }, [url, contentType]); + return [state, sendRequest]; + }; +} + +function APIReducer(state, action) { + switch (action.type) { + case API_ACTIONS.INIT: + return { + ...state, + state: API_STATE.SENDING, + }; + case API_ACTIONS.SUCCESS: + return { + state: API_STATE.SUCCESS, + data: action.payload, + }; + case API_ACTIONS.FAILURE: + if (action.status === 403) { + window.location.assign(ForisURLs.login); + } + return { + state: API_STATE.ERROR, + data: action.payload, + }; + default: + throw new Error(); + } +} + +const useAPIGet = createAPIHook("GET"); +const useAPIPost = createAPIHook("POST"); +const useAPIPatch = createAPIHook("PATCH"); +const useAPIPut = createAPIHook("PUT"); +const useAPIDelete = createAPIHook("DELETE"); + +export { + useAPIGet, useAPIPost, useAPIPatch, useAPIPut, useAPIDelete, +}; diff --git a/src/api/patch.js b/src/api/patch.js deleted file mode 100644 index ba3ed9e..0000000 --- a/src/api/patch.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/) - * - * This is free software, licensed under the GNU General Public License v3. - * See /LICENSE for more information. - */ - -import { useReducer } from "react"; -import axios from "axios"; - -import { - API_ACTIONS, TIMEOUT, HEADERS, APIReducer, getErrorMessage, -} from "./utils"; - -export function useAPIPatch(url) { - const [state, dispatch] = useReducer(APIReducer, { - isSending: false, - isError: false, - isSuccess: false, - data: null, - }); - - const patch = async (data) => { - dispatch({ type: API_ACTIONS.INIT }); - try { - const result = await axios.patch(url, data, { - timeout: TIMEOUT, - headers: HEADERS, - }); - dispatch({ type: API_ACTIONS.SUCCESS, payload: result.data }); - } catch (error) { - dispatch({ - type: API_ACTIONS.FAILURE, - payload: getErrorMessage(error), - status: error.response.status, - }); - } - }; - return [state, patch]; -} diff --git a/src/api/post.js b/src/api/post.js deleted file mode 100644 index 6a3933a..0000000 --- a/src/api/post.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/) - * - * This is free software, licensed under the GNU General Public License v3. - * See /LICENSE for more information. - */ - -import { useReducer } from "react"; -import axios from "axios"; - -import { - API_ACTIONS, TIMEOUT, HEADERS, APIReducer, getErrorMessage, -} from "./utils"; - -export function useAPIPost(url, contentType) { - const [state, dispatch] = useReducer(APIReducer, { - isSending: false, - isError: false, - isSuccess: false, - data: null, - }); - - const headers = { ...HEADERS }; - if (contentType) { - headers["Content-Type"] = contentType; - } - - const post = async (data) => { - dispatch({ type: API_ACTIONS.INIT }); - try { - const result = await axios.post(url, data, { - timeout: TIMEOUT, - headers, - }); - dispatch({ type: API_ACTIONS.SUCCESS, payload: result.data }); - } catch (error) { - dispatch({ - type: API_ACTIONS.FAILURE, - payload: getErrorMessage(error), - status: error.response.status, - }); - } - }; - return [state, post]; -} diff --git a/src/api/utils.js b/src/api/utils.js index fd85c8c..18a76e3 100644 --- a/src/api/utils.js +++ b/src/api/utils.js @@ -5,7 +5,36 @@ * See /LICENSE for more information. */ -import { ForisURLs } from "forisUrls"; +import axios from "axios"; + +export const HEADERS = { + Accept: "application/json", + "Content-Type": "application/json", + "X-CSRFToken": getCookie("_csrf_token"), +}; + +export const TIMEOUT = 5000; + +export const API_ACTIONS = { + INIT: 1, + SUCCESS: 2, + FAILURE: 3, +}; + +export const API_STATE = { + INIT: "init", + SENDING: "sending", + SUCCESS: "success", + ERROR: "error", +}; + +export const API_METHODS = { + GET: axios.get, + POST: axios.post, + PATCH: axios.patch, + PUT: axios.put, + DELETE: axios.delete, +}; function getCookie(name) { let cookieValue = null; @@ -23,51 +52,6 @@ function getCookie(name) { return cookieValue; } -export const HEADERS = { - Accept: "application/json", - "Content-Type": "application/json", - "X-CSRFToken": getCookie("_csrf_token"), -}; - -export const TIMEOUT = 5000; - -export const API_ACTIONS = { - INIT: 1, - SUCCESS: 2, - FAILURE: 3, -}; - -export function APIReducer(state, action) { - switch (action.type) { - case API_ACTIONS.INIT: - return { - ...state, - isSending: true, - isError: false, - isSuccess: false, - }; - case API_ACTIONS.SUCCESS: - return { - ...state, - isSending: false, - isError: false, - isSuccess: true, - data: action.payload, - }; - case API_ACTIONS.FAILURE: - if (action.status === 403) window.location.assign(ForisURLs.login); - return { - ...state, - isSending: false, - isError: true, - isSuccess: false, - data: action.payload, - }; - default: - throw new Error(); - } -} - export function getErrorMessage(error) { let payload = "An unknown error occurred"; if (error.response.headers["content-type"] === "application/json") { diff --git a/src/form/components/ForisForm.js b/src/form/components/ForisForm.js index 12e1854..c03badc 100644 --- a/src/form/components/ForisForm.js +++ b/src/form/components/ForisForm.js @@ -9,7 +9,7 @@ import React, { useEffect, useState } from "react"; import PropTypes from "prop-types"; import { Spinner } from "bootstrap/Spinner"; -import { useAPIPost } from "api/post"; +import { useAPIPost } from "api/hooks"; import { Prompt } from "react-router"; import { useForisModule, useForm } from "../hooks"; diff --git a/src/form/hooks.js b/src/form/hooks.js index 3b14499..ebc48c5 100644 --- a/src/form/hooks.js +++ b/src/form/hooks.js @@ -8,10 +8,9 @@ import { useCallback, useEffect, useReducer } from "react"; import update from "immutability-helper"; -import { useAPIGet } from "api/get"; +import { useAPIGet } from "api/hooks"; import { useWSForisModule } from "webSockets/hooks"; - const FORM_ACTIONS = { updateValue: 1, resetData: 2, diff --git a/src/index.js b/src/index.js index 851d2ba..aad3f7e 100644 --- a/src/index.js +++ b/src/index.js @@ -6,10 +6,10 @@ */ // API -export { useAPIGet } from "api/get"; -export { useAPIPost } from "api/post"; -export { useAPIDelete } from "api/delete"; -export { useAPIPatch } from "api/patch"; +export { + useAPIGet, useAPIPost, useAPIDelete, useAPIPatch, +} from "api/hooks"; +export { API_STATE } from "api/utils"; // Bootstrap export { Alert, ALERT_TYPES } from "bootstrap/Alert";