From 7b7378823423a8241ba0a65edd3e2f69c25e7e9d Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sun, 19 Sep 2021 21:39:54 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20types=20for=20the=20types=20o?= =?UTF-8?q?f=20objects=20returned=20by=20the=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ResearchGroupListBox.tsx | 9 +- frontend/src/hooks/useBackendViewSet.ts | 252 ------------------ frontend/src/types.ts | 56 ++++ 3 files changed, 61 insertions(+), 256 deletions(-) delete mode 100644 frontend/src/hooks/useBackendViewSet.ts create mode 100644 frontend/src/types.ts diff --git a/frontend/src/components/ResearchGroupListBox.tsx b/frontend/src/components/ResearchGroupListBox.tsx index d3c1007..c4e7663 100644 --- a/frontend/src/components/ResearchGroupListBox.tsx +++ b/frontend/src/components/ResearchGroupListBox.tsx @@ -3,7 +3,7 @@ import * as ReactDOM from "react-dom" import {useLoginAxios} from "./LoginContext"; import {useMemo} from "react"; import {Box, Heading} from "@steffo/bluelib-react"; -import {ResearchGroupPanel} from "./ResearchGroupPanel"; +import {ResearchGroupPanel, ResearchGroupPanelProps} from "./ResearchGroupPanel"; interface ResearchGroupListBoxProps { @@ -13,6 +13,9 @@ interface ResearchGroupListBoxProps { export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element { const api = useLoginAxios() + const loading = React.useState() + + const data = React.useState([]) return ( @@ -20,9 +23,7 @@ export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element Research groups
- - - + {data.map(group => )}
) diff --git a/frontend/src/hooks/useBackendViewSet.ts b/frontend/src/hooks/useBackendViewSet.ts deleted file mode 100644 index 657fd1a..0000000 --- a/frontend/src/hooks/useBackendViewSet.ts +++ /dev/null @@ -1,252 +0,0 @@ -import { useCallback, useEffect, useState } from "react" -import {useLoginAxios} from "../components/LoginContext"; - - -/** - * Error thrown when trying to access a backend view which doesn't exist or isn't allowed in the used hook. - */ -export class ViewNotAllowedError extends Error { - view: string - - constructor(view: string) { - super() - - this.view = view - } -} - - - - -/** - * An hook which allows access to a full REST viewset (list, create, retrieve, edit, delete). - * - * @param resourcesPath - The path of the resource directory. - * @param pkName - The name of the primary key attribute of the elements. - * @param allowViews - An object with maps views to a boolean detailing if they're allowed in the viewset or not. - */ -export default function useBackendViewset(resourcesPath: string, pkName: string, - { - list: allowList = true, - create: allowCreate = true, - retrieve: allowRetrieve = true, - edit: allowEdit = true, - destroy: allowDestroy = true, - command: allowCommand = false, - action: allowAction = false, - } = {}, -) { - const api = useLoginAxios() - - const [firstLoad, setFirstLoad] = useState(false) - const [resources, setResources] = useState([]) - const [running, setRunning] = useState(false) - const [error, setError] = useState(null) - - const apiList = useCallback( - async (abort: AbortSignal) => { - if(!allowList) { - throw new ViewNotAllowedError("list") - } - return api.get(`${resourcesPath}`, {signal: abort}) - }, - [api, allowList, resourcesPath], - ) - - const apiRetrieve = useCallback( - async (id: string, abort: AbortSignal) => { - if(!allowRetrieve) { - throw new ViewNotAllowedError("retrieve") - } - return api.get(`${resourcesPath}${id}/`, {signal: abort}) - }, - [api, allowRetrieve, resourcesPath], - ) - - const apiCreate = useCallback( - async (data: any, abort: AbortSignal) => { - if(!allowCreate) { - throw new ViewNotAllowedError("create") - } - return api.post(`${resourcesPath}`, data, {signal: abort}) - }, - [api, allowCreate, resourcesPath], - ) - - const apiEdit = useCallback( - async (id: string, data: any, abort: AbortSignal) => { - if(!allowEdit) { - throw new ViewNotAllowedError("edit") - } - return api.put(`${resourcesPath}${id}/`, data, {signal: abort}) - }, - [api, allowEdit, resourcesPath], - ) - - const apiDestroy = useCallback( - async (id: string, abort: AbortSignal) => { - if(!allowDestroy) { - throw new ViewNotAllowedError("destroy") - } - return api.delete(`${resourcesPath}${id}/`, {signal: abort}) - }, - [api, allowDestroy, resourcesPath], - ) - - /* - const apiCommand = useCallback( - async (method, command, data, init) => { - if(!allowCommand) { - throw new ViewNotAllowedError("command") - } - return apiRequest(method, `${resourcesPath}${command}`, data, init) - }, - [apiRequest, allowCommand, resourcesPath], - ) - - const apiAction = useCallback( - async (method, id, command, data, init) => { - if(!allowAction) { - throw new ViewNotAllowedError("action") - } - return apiRequest(method, `${resourcesPath}${id}/${command}`, data, init) - }, - [apiRequest, allowAction, resourcesPath], - ) - */ - - const listResources = useCallback( - async (abort: AbortSignal) => { - let res - try { - res = await apiList(abort) - } - catch(e) { - setError(e as Error) - return - } - setError(null) - setResources(res.data) - }, - [apiList, setError, setResources], - ) - - const retrieveResource = useCallback( - async (pk: string, abort: AbortSignal) => { - let res: any - try { - res = await apiRetrieve(pk, abort) - } - catch(e) { - setError(e as Error) - return - } - setError(null) - - setResources(r => r.map(resource => { - // @ts-ignore - if(resource[pkName] === pk) { - return res.data - } - return resource - })) - - return res - }, - [apiRetrieve, setError, setResources, pkName], - ) - - const createResource = useCallback( - async (data: any, abort: AbortSignal) => { - let res: any - try { - res = await apiCreate(data, abort) - } - catch(e) { - setError(e as Error) - return - } - setError(null) - - setResources(r => [...r, res]) - return res - }, - [apiCreate, setError, setResources], - ) - - const editResource = useCallback( - async (pk: string, data: any, abort: AbortSignal) => { - let res: any - try { - res = await apiEdit(pk, data, abort) - } - catch(e) { - setError(e as Error) - return - } - setError(null) - - setResources(r => r.map(resource => { - if(resource[pkName] === pk) { - return res - } - return resource - })) - return res - }, - [apiEdit, setError, setResources, pkName], - ) - - const destroyResource = useCallback( - async (pk: string, abort: AbortSignal) => { - try { - await apiDestroy(pk, abort) - } - catch(e) { - setError(e as Error) - return - } - setError(null) - - setResources(r => r.filter(resource => resource[pkName] !== pk)) - return null - }, - [apiDestroy, setError, setResources, pkName], - ) - - useEffect( - () => { - if(allowList && !firstLoad && !running) { - listResources().then(() => setFirstLoad(true)) - - } - }, - [listResources, firstLoad, running, allowList], - ) - - return { - abort, - resources, - firstLoad, - running, - error, - apiRequest, - allowList, - apiList, - listResources, - allowRetrieve, - apiRetrieve, - retrieveResource, - allowCreate, - apiCreate, - createResource, - allowEdit, - apiEdit, - editResource, - allowDestroy, - apiDestroy, - destroyResource, - apiCommand, - apiAction, - } -} \ No newline at end of file diff --git a/frontend/src/types.ts b/frontend/src/types.ts new file mode 100644 index 0000000..7b60282 --- /dev/null +++ b/frontend/src/types.ts @@ -0,0 +1,56 @@ +export interface DRFList { + count: number, + next: string | null, + previous: string | null, + results: T[] +} + + +export type ResearchGroupSlug = string + +export interface ResearchGroup { + owner: number, + members: UserId[], + name: string, + description: string, + access: "OPEN" | "MANUAL", + slug: ResearchGroupSlug, +} + + +export type UserId = number + +export interface User { + id: UserId, + username: string, + first_name: string, + last_name: string, + email: string, +} + + +export type ResearchProjectSlug = string + +export interface ResearchProject { + visibility: "PUBLIC" | "INTERNAL" | "PRIVATE", + slug: ResearchProjectSlug, + name: string, + description: string, + group: ResearchGroupSlug, +} + + +export type NotebookSlug = string + +export interface Notebook { + locked_by: UserId, + slug: NotebookSlug, + legacy_notebook_url: string | null, + jupyter_token: string, + is_running: boolean, + internet_access: true, + container_image: string, + project: ResearchProjectSlug, + name: string, + lab_url: string | null, +} \ No newline at end of file