From 92b762bbd291dc58e7487d9bc78198866c4e8d69 Mon Sep 17 00:00:00 2001 From: Sebastian Seedorf Date: Wed, 17 Aug 2022 23:27:19 +0200 Subject: [PATCH] Implemented server routes --- .prettierignore | 4 + .prettierrc.json | 18 + components/contexts/GroupProvider.tsx | 94 +- .../home/FactorySelect/FactorySelect.tsx | 1 + next.config.js | 3 + package.json | 2 + pages/api/[id]/factories.ts | 17 + pages/api/[id]/group/[name]/add.ts | 16 + pages/api/[id]/group/[name]/factories.ts | 17 + pages/api/[id]/group/[name]/remove.ts | 16 + pages/api/[id]/group/[name]/rename.ts | 17 + pages/api/dev/schemas.ts | 15 + pages/api/submit.ts | 8 +- src/database/groups.ts | 86 +- src/database/start.ts | 9 +- src/next-types.d.ts | 2 +- src/types/ApiSchemas.ts | 25 + src/types/ApiSchemasFrontend.ts | 30 + src/types/FrontendApi.ts | 7 + src/utils/errors.ts | 36 + src/utils/logger.ts | 7 + src/validation/defaults.ts | 142 + src/validation/index.ts | 113 + src/validation/schemas.ts | 54 + src/validation/transform.ts | 72 + src/validation/types.ts | 10 + yarn.lock | 3625 +++++++++-------- 27 files changed, 2754 insertions(+), 1692 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 pages/api/[id]/factories.ts create mode 100644 pages/api/[id]/group/[name]/add.ts create mode 100644 pages/api/[id]/group/[name]/factories.ts create mode 100644 pages/api/[id]/group/[name]/remove.ts create mode 100644 pages/api/[id]/group/[name]/rename.ts create mode 100644 pages/api/dev/schemas.ts create mode 100644 src/types/ApiSchemas.ts create mode 100644 src/types/ApiSchemasFrontend.ts create mode 100644 src/types/FrontendApi.ts create mode 100644 src/utils/errors.ts create mode 100644 src/utils/logger.ts create mode 100644 src/validation/defaults.ts create mode 100644 src/validation/index.ts create mode 100644 src/validation/schemas.ts create mode 100644 src/validation/transform.ts create mode 100644 src/validation/types.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..22dc82a --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +.cache +public +static +coverage diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..024cb48 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,18 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "arrowParens": "avoid", + "semi": false, + "singleQuote": true, + "jsxSingleQuote": true, + "trailingComma": "none", + "overrides": [ + { + "files": "*.css", + "options": { + "singleQuote": false, + "tabWidth": 4 + } + } + ] +} diff --git a/components/contexts/GroupProvider.tsx b/components/contexts/GroupProvider.tsx index 66e18da..49973ee 100644 --- a/components/contexts/GroupProvider.tsx +++ b/components/contexts/GroupProvider.tsx @@ -1,9 +1,10 @@ -import {createContext, FC, useCallback, useContext, useMemo, useState} from "react"; +import {createContext, FC, useCallback, useContext, useEffect, useMemo, useState} from "react"; import {Group} from "../../src/types"; -import {useLocalStorage} from "../../src/hooks/useLocalStorage"; import {ReactNodeLike} from "prop-types"; import pako from "pako"; import {Dict} from "../../src/types"; +import {fixedEncodeURIComponent} from "../../src/utils"; +import {GroupRenameBody, GroupSetFactoryArrayBody, SetFactoryArrayBody} from "../../src/types/ApiSchemasFrontend"; interface Props { children: ReactNodeLike @@ -68,9 +69,20 @@ interface StoredFile { excludedSuggestions: string[] } -export const GroupProvider: FC = ({children, initial}) => { - const [excludedSuggestions, setExcludedSuggestions] = useState(initial.ignored) - const [basicValues, setBasicValues] = useState(initial.base) +export const postFetchJson = async (url: string, body: Dict) => { + const res = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + }) + return res.json() +} + +export const GroupProvider: FC = ({children, id, initial}) => { + const [excludedSuggestions, _setExcludedSuggestions] = useState(initial.ignored) + const [basicValues, _setBasicValues] = useState(initial.base) const [groups, setGroups] = useState>(initial.groups) const doNotSuggest = useMemo>(() => { @@ -81,35 +93,85 @@ export const GroupProvider: FC = ({children, initial}) => { return new Set([...Object.values(groups).flatMap(group => [...group.exports])]) }, [groups]) + const setExcludedSuggestions = useCallback((val) => { + _setExcludedSuggestions(val) + postFetchJson(`/api/${fixedEncodeURIComponent(id)}/factories`, { + type: 'ignored', + factories: val + } as SetFactoryArrayBody) + .catch(console.error) + }, [id]) + + const setBasicValues = useCallback((val) => { + _setBasicValues(val) + postFetchJson(`/api/${fixedEncodeURIComponent(id)}/factories`, { + type: 'base', + factories: val + } as SetFactoryArrayBody) + .catch(console.error) + }, [id]) + const addGroup = useCallback((name: string, exports: string[] = [], malls: string[] = []) => { + name = name.replace(/[.$]/g, '') if (name in groups) return false setGroups(groups => { groups[name] = { name, exports, malls } - return groups + return {...groups} }) + ;(async () => { + await postFetchJson(`/api/${fixedEncodeURIComponent(id)}/group/${fixedEncodeURIComponent(name)}/add`, {}) + if (exports.length) { + await postFetchJson(`/api/${fixedEncodeURIComponent(id)}/group/${fixedEncodeURIComponent(name)}/factories`, { + type: 'exports', + factories: exports + } as GroupSetFactoryArrayBody) + } + if (malls.length) { + await postFetchJson(`/api/${fixedEncodeURIComponent(id)}/group/${fixedEncodeURIComponent(name)}/factories`, { + type: 'malls', + factories: exports + } as GroupSetFactoryArrayBody) + } + })().catch(console.error) return true - }, [groups, setGroups]) + }, [groups, id]) const removeGroup = useCallback((name: string) => { + name = name.replace(/[.$]/g, '') setGroups(groups => { delete groups[name] - return groups + console.log(groups[name]) + return {...groups} }) - }, [setGroups]) + postFetchJson(`/api/${fixedEncodeURIComponent(id)}/group/${fixedEncodeURIComponent(name)}/remove`, {}) + .catch(console.error) + }, [id]) const renameGroup = useCallback((name: string, newName: string) => { - if (name === newName) return + name = name.replace(/[.$]/g, '') + newName = newName.replace(/[.$]/g, '') + if (newName in groups) return setGroups(groups => { groups[newName] = {...groups[name], name: newName} delete groups[name] - return groups + return {...groups} }) - }, [setGroups]) + postFetchJson(`/api/${fixedEncodeURIComponent(id)}/group/${fixedEncodeURIComponent(name)}/rename`, { + newName + } as GroupRenameBody) + .catch(console.error) + }, [groups, id]) - const setFactories = useCallback((name: string, factories: string[], type: 'inputs'|'intermediates'|'exports'|'malls') => { + const setFactories = useCallback((name: string, factories: string[], type: 'exports'|'malls') => { + name = name.replace(/[.$]/g, '') setGroups(groups => { groups[name] = {...groups[name], [type]: factories} - return groups + return {...groups} }) - }, [setGroups]) + postFetchJson(`/api/${fixedEncodeURIComponent(id)}/group/${fixedEncodeURIComponent(name)}/factories`, { + type, + factories + } as GroupSetFactoryArrayBody) + .catch(console.error) + }, [id]) const getInputType = useCallback((uid: string) => { if (basicValues.includes(uid)) return 'base' else if (exportedFactories.has(uid)) return 'produced' @@ -133,7 +195,7 @@ export const GroupProvider: FC = ({children, initial}) => { setGroups(value.groups) setBasicValues(value.basicValues) setExcludedSuggestions(value.excludedSuggestions) - }, [setBasicValues, setExcludedSuggestions, setGroups]) + }, []) const value: GroupContextType = useMemo(() => ({ doNotSuggest, diff --git a/components/home/FactorySelect/FactorySelect.tsx b/components/home/FactorySelect/FactorySelect.tsx index efc3010..8cd987b 100644 --- a/components/home/FactorySelect/FactorySelect.tsx +++ b/components/home/FactorySelect/FactorySelect.tsx @@ -25,6 +25,7 @@ const FactorySelectBase: FC = ({id, factories, onSetFactories}) => { return