mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-23 07:14:21 +00:00
✨ Add types for the types of objects returned by the backend
This commit is contained in:
parent
0c1ce76f94
commit
bcf1b3f238
3 changed files with 61 additions and 256 deletions
|
@ -3,7 +3,7 @@ import * as ReactDOM from "react-dom"
|
||||||
import {useLoginAxios} from "./LoginContext";
|
import {useLoginAxios} from "./LoginContext";
|
||||||
import {useMemo} from "react";
|
import {useMemo} from "react";
|
||||||
import {Box, Heading} from "@steffo/bluelib-react";
|
import {Box, Heading} from "@steffo/bluelib-react";
|
||||||
import {ResearchGroupPanel} from "./ResearchGroupPanel";
|
import {ResearchGroupPanel, ResearchGroupPanelProps} from "./ResearchGroupPanel";
|
||||||
|
|
||||||
|
|
||||||
interface ResearchGroupListBoxProps {
|
interface ResearchGroupListBoxProps {
|
||||||
|
@ -13,6 +13,9 @@ interface ResearchGroupListBoxProps {
|
||||||
|
|
||||||
export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element {
|
export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element {
|
||||||
const api = useLoginAxios()
|
const api = useLoginAxios()
|
||||||
|
const loading = React.useState<boolean>()
|
||||||
|
|
||||||
|
const data = React.useState<ResearchGroupPanelProps[]>([])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
|
@ -20,9 +23,7 @@ export function ResearchGroupListBox({}: ResearchGroupListBoxProps): JSX.Element
|
||||||
Research groups
|
Research groups
|
||||||
</Heading>
|
</Heading>
|
||||||
<div>
|
<div>
|
||||||
<ResearchGroupPanel/>
|
{data.map(group => <ResearchGroupPanel {...group}/>)}
|
||||||
<ResearchGroupPanel/>
|
|
||||||
<ResearchGroupPanel/>
|
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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<boolean>(false)
|
|
||||||
const [resources, setResources] = useState<any[]>([])
|
|
||||||
const [running, setRunning] = useState<boolean>(false)
|
|
||||||
const [error, setError] = useState<Error | null>(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,
|
|
||||||
}
|
|
||||||
}
|
|
56
frontend/src/types.ts
Normal file
56
frontend/src/types.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
export interface DRFList<T> {
|
||||||
|
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,
|
||||||
|
}
|
Loading…
Reference in a new issue