From 3782bfe1f83dcd6323f25f3634c19f07b5ac6280 Mon Sep 17 00:00:00 2001 From: Dave Walker Date: Tue, 21 Nov 2023 14:30:19 +0000 Subject: [PATCH] Ensure maps API key is properly retrieved and retained --- src/music-catalogue-ui/components/app.js | 26 +++-------- .../components/componentPicker.js | 3 +- .../components/locationMap.js | 10 +++-- src/music-catalogue-ui/components/login.js | 11 +++++ .../components/retailerDetails.js | 9 ++-- .../components/retailerList.js | 10 +---- .../components/retailerRow.js | 7 +-- src/music-catalogue-ui/helpers/apiToken.js | 22 +++------ src/music-catalogue-ui/helpers/storage.js | 45 +++++++++++++++++++ src/music-catalogue-ui/hooks/useMapsApiKey.js | 34 -------------- 10 files changed, 82 insertions(+), 95 deletions(-) create mode 100644 src/music-catalogue-ui/helpers/storage.js delete mode 100644 src/music-catalogue-ui/hooks/useMapsApiKey.js diff --git a/src/music-catalogue-ui/components/app.js b/src/music-catalogue-ui/components/app.js index 1769743..1135a57 100644 --- a/src/music-catalogue-ui/components/app.js +++ b/src/music-catalogue-ui/components/app.js @@ -1,11 +1,12 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useState } from "react"; import Login from "./login"; import pages from "@/helpers/navigation"; import ComponentPicker from "./componentPicker"; import { apiClearToken } from "@/helpers/apiToken"; import useIsLoggedIn from "@/hooks/useIsLoggedIn"; import MenuBar from "./menuBar"; -import { apiFetchSecret } from "@/helpers/apiSecrets"; +import { clearStorageValue } from "@/helpers/storage"; +import secrets from "@/helpers/secrets"; /** * Default application state: @@ -32,9 +33,6 @@ const App = () => { // Application state const [context, setContext] = useState(defaultContext); - // Google Maps API key - const [mapsApiKey, setMapsApiKey] = useState(null); - // Callback to set the context const navigate = ({ page = pages.artists, @@ -65,29 +63,16 @@ const App = () => { }, [setIsLoggedIn]); const logout = useCallback(() => { + clearStorageValue(secrets.mapsApiKey); apiClearToken(); setIsLoggedIn(false); }, [setIsLoggedIn]); - // Get the Google Maps API key and store it in state - useEffect(() => { - const fetchMapsApiKey = async () => { - try { - var fetchedKey = await apiFetchSecret("Maps API Key", logout); - setMapsApiKey(fetchedKey); - } catch {} - }; - - if (mapsApiKey == null) { - fetchMapsApiKey(); - } - }, [mapsApiKey, logout]); - // If the user's logged in, show the banner and current component. Otherwise, // show the login page return ( <> - {isLoggedIn && mapsApiKey != null ? ( + {isLoggedIn ? ( <>
@@ -95,7 +80,6 @@ const App = () => {
diff --git a/src/music-catalogue-ui/components/componentPicker.js b/src/music-catalogue-ui/components/componentPicker.js index ac5ad36..9202b5d 100644 --- a/src/music-catalogue-ui/components/componentPicker.js +++ b/src/music-catalogue-ui/components/componentPicker.js @@ -21,7 +21,7 @@ import RetailerEditor from "./retailerEditor"; * @param {*} logout * @returns */ -const ComponentPicker = ({ context, mapsApiKey, navigate, logout }) => { +const ComponentPicker = ({ context, navigate, logout }) => { switch (context.page) { case pages.artists: return ( @@ -80,7 +80,6 @@ const ComponentPicker = ({ context, mapsApiKey, navigate, logout }) => { case pages.retailerDetails: return ( { - const [location, setLocation] = useState({ lat: latitude, lng: longitude }); +const LocationMap = ({ latitude, longitude }) => { + const location = { lat: latitude, lng: longitude }; + const mapsApiKey = getStorageValue(secrets.mapsApiKey); return ( <>
diff --git a/src/music-catalogue-ui/components/login.js b/src/music-catalogue-ui/components/login.js index 396e25e..e26decc 100644 --- a/src/music-catalogue-ui/components/login.js +++ b/src/music-catalogue-ui/components/login.js @@ -1,7 +1,10 @@ import styles from "./login.module.css"; +import secrets from "@/helpers/secrets"; import { React, useState, useCallback } from "react"; import { apiAuthenticate } from "@/helpers/apiAuthenticate"; import { apiSetToken } from "@/helpers/apiToken"; +import { apiFetchSecret } from "@/helpers/apiSecrets"; +import { setStorageValue } from "@/helpers/storage"; /** * Component to render the login page @@ -24,6 +27,14 @@ const Login = ({ login }) => { if (token != null) { // Successful, so store the token apiSetToken(token); + + // Now attempt to get the Google Maps API key + const apiKey = await apiFetchSecret(secrets.mapsApiKey); + if (apiKey != null) { + // Successful, so store it + setStorageValue(secrets.mapsApiKey, apiKey); + } + login(true); } }, diff --git a/src/music-catalogue-ui/components/retailerDetails.js b/src/music-catalogue-ui/components/retailerDetails.js index 775227a..c9ed939 100644 --- a/src/music-catalogue-ui/components/retailerDetails.js +++ b/src/music-catalogue-ui/components/retailerDetails.js @@ -1,13 +1,17 @@ +import { getStorageValue } from "@/helpers/storage"; import pages from "../helpers/navigation"; import LocationMap from "./locationMap"; import styles from "./retailerDetails.module.css"; +import secrets from "@/helpers/secrets"; /** * Component to show retailer addressing and web site details and map location - * @param {*} param0 + * @param {*} retailer + * @param {*} navigate + * @param {*} logout * @returns */ -const RetailerDetails = ({ mapsApiKey, retailer, navigate, logout }) => { +const RetailerDetails = ({ retailer, navigate, logout }) => { // Set a flag indicating if we have enough data to show the map const canShowMap = retailer.latitude != null && retailer.longitude != null; @@ -23,7 +27,6 @@ const RetailerDetails = ({ mapsApiKey, retailer, navigate, logout }) => {
{canShowMap == true ? ( { +const RetailerList = ({ navigate, logout }) => { const { retailers: retailers, setRetailers } = useRetailers(logout); return ( @@ -30,12 +29,7 @@ const RetailerList = ({ mapsApiKey, navigate, logout }) => { {retailers != null && ( {retailers.map((r) => ( - + ))} )} diff --git a/src/music-catalogue-ui/components/retailerRow.js b/src/music-catalogue-ui/components/retailerRow.js index 6815cc2..b111107 100644 --- a/src/music-catalogue-ui/components/retailerRow.js +++ b/src/music-catalogue-ui/components/retailerRow.js @@ -4,12 +4,11 @@ import { faPenToSquare } from "@fortawesome/free-solid-svg-icons"; /** * Component to render a row containing the details for a single retailer - * @param {*} mapsApiKey * @param {*} artist * @param {*} navigate * @returns */ -const RetailerRow = ({ mapsApiKey, retailer, navigate }) => { +const RetailerRow = ({ retailer, navigate }) => { return ( { navigate({ page: pages.retailerDetails, retailer: retailer, - mapsApiKey: mapsApiKey, }) } > @@ -28,7 +26,6 @@ const RetailerRow = ({ mapsApiKey, retailer, navigate }) => { navigate({ page: pages.retailerDetails, retailer: retailer, - mapsApiKey: mapsApiKey, }) } > @@ -39,7 +36,6 @@ const RetailerRow = ({ mapsApiKey, retailer, navigate }) => { navigate({ page: pages.retailerDetails, retailer: retailer, - mapsApiKey: mapsApiKey, }) } > @@ -50,7 +46,6 @@ const RetailerRow = ({ mapsApiKey, retailer, navigate }) => { navigate({ page: pages.retailerDetails, retailer: retailer, - mapsApiKey: mapsApiKey, }) } > diff --git a/src/music-catalogue-ui/helpers/apiToken.js b/src/music-catalogue-ui/helpers/apiToken.js index 163875a..019069d 100644 --- a/src/music-catalogue-ui/helpers/apiToken.js +++ b/src/music-catalogue-ui/helpers/apiToken.js @@ -1,33 +1,21 @@ +import { setStorageValue, getStorageValue, clearStorageValue } from "./storage"; + /** * Store the JWT token * @param {*} token */ -const apiSetToken = (token) => { - // TODO: Move to HTTP Cookie - localStorage.setItem("token", token); -}; +const apiSetToken = (token) => setStorageValue("token", token); /** * Retrieve the current JWT token * @param {*} token * @returns */ -const apiGetToken = () => { - try { - // TODO: Move to HTTP Cookie - const token = localStorage.getItem("token"); - return token; - } catch { - return null; - } -}; +const apiGetToken = () => getStorageValue("token"); /** * Clear the current JWT token */ -const apiClearToken = () => { - // TODO: Move to HTTP Cookie - localStorage.removeItem("token"); -}; +const apiClearToken = () => clearStorageValue("token"); export { apiClearToken, apiSetToken, apiGetToken }; diff --git a/src/music-catalogue-ui/helpers/storage.js b/src/music-catalogue-ui/helpers/storage.js new file mode 100644 index 0000000..68f68a9 --- /dev/null +++ b/src/music-catalogue-ui/helpers/storage.js @@ -0,0 +1,45 @@ +/** + * Convert a name that may be mixed case and with spaces to a local storage key + * @param {*} name + * @returns + */ +const getStorageKey = (name) => { + const key = name.toLowerCase().replace(/ /g, "-"); + return key; +}; + +/** + * Store a named value + * @param {*} token + * @param {*} value + */ +const setStorageValue = (name, value) => { + const key = getStorageKey(name); + localStorage.setItem(key, value); +}; + +/** + * Retrieve a stored value + * @param {*} name + * @returns + */ +const getStorageValue = (name) => { + try { + const key = getStorageKey(name); + const token = localStorage.getItem(key); + return token; + } catch { + return null; + } +}; + +/** + * Clear a named storage value + * @param {*} name + */ +const clearStorageValue = (name) => { + const key = getStorageKey(name); + localStorage.removeItem(key); +}; + +export { setStorageValue, getStorageValue, clearStorageValue }; diff --git a/src/music-catalogue-ui/hooks/useMapsApiKey.js b/src/music-catalogue-ui/hooks/useMapsApiKey.js deleted file mode 100644 index 22535c7..0000000 --- a/src/music-catalogue-ui/hooks/useMapsApiKey.js +++ /dev/null @@ -1,34 +0,0 @@ -import { apiFetchSecret, apiGetSecret } from "@/helpers/apiSecrets"; -import { useState, useEffect } from "react"; -import secrets from "@/helpers/secrets"; - -/** - * Hook that uses the API helpers to retrieve the maps API key from the - * Music Catalogue REST API - * @param {*} logout - * @returns - */ -const useMapsApiKey = (logout) => { - // Current list of artists and the method to change it - const [apiKey, setApiKey] = useState(null); - - useEffect(() => { - const fetchApiKey = async () => { - try { - // Get a list of artists via the service and store it in state - var fetchedApiKey = await apiFetchSecret(secrets.mapsApiKey); - setApiKey(fetchedApiKey); - apiSetSecret(fetchedApiKey); - } catch {} - }; - - const currentApiKey = apiGetSecret(secrets.mapsApiKey); - if (currentApiKey == null) { - fetchApiKey(); - } - }, [logout]); - - return { apiKey, setApiKey }; -}; - -export default useMapsApiKey;