diff --git a/src/ClientCodeDemo.tsx b/src/ClientCodeDemo.tsx
index a28ff2d..3397259 100644
--- a/src/ClientCodeDemo.tsx
+++ b/src/ClientCodeDemo.tsx
@@ -47,13 +47,13 @@ function DatasetSelector({selectedDatasetIds, setSelectedDatasetIds}: {
value={selectedDatasetIds}
onChange={(e) => setSelectedDatasetIds(e.target.value as string[])}
renderValue={(selected) =>
- query.results!.filter(f => selected.includes(String(f.uuid)))
+ query.results!.filter(f => selected.includes(String(f.id)))
.map(f => f.name as string)
.join(', ')
}
>
{query.results.map((dataset) => (
-
+
))}
diff --git a/src/Components/AttachmentUploadContext.tsx b/src/Components/AttachmentUploadContext.tsx
index e2a6c35..1f7ff1b 100644
--- a/src/Components/AttachmentUploadContext.tsx
+++ b/src/Components/AttachmentUploadContext.tsx
@@ -42,7 +42,7 @@ export default function AttachmentUploadContextProvider({children}: PropsWithChi
},
{
onSuccess: (data: AxiosResponse) => {
- queryClient.setQueryData([LOOKUP_KEYS.ARBITRARY_FILE, data.data.uuid], data.data)
+ queryClient.setQueryData([LOOKUP_KEYS.ARBITRARY_FILE, data.data.id], data.data)
queryClient.invalidateQueries([LOOKUP_KEYS.ARBITRARY_FILE, "list"])
}
}
diff --git a/src/Components/CardActionBar.tsx b/src/Components/CardActionBar.tsx
index 34b31ee..2ccd604 100644
--- a/src/Components/CardActionBar.tsx
+++ b/src/Components/CardActionBar.tsx
@@ -2,10 +2,8 @@ import React, {ReactNode} from "react";
import Tooltip from "@mui/material/Tooltip";
import IconButton from "@mui/material/IconButton";
import EditIcon from "@mui/icons-material/Edit";
-import DeleteIcon from "@mui/icons-material/Delete";
import UndoIcon from "@mui/icons-material/Undo";
import RedoIcon from "@mui/icons-material/Redo";
-import SaveIcon from "@mui/icons-material/Save";
import CloseIcon from "@mui/icons-material/Close";
import Stack from "@mui/material/Stack";
import CountBadge from "./CountBadge";
@@ -138,7 +136,7 @@ export default function CardActionBar(props: CardActionBarProps) {
if (props.onEditSave!())
props.setEditing!(false)
}}>
-
+
{props.onUndo &&
@@ -178,7 +176,7 @@ export default function CardActionBar(props: CardActionBarProps) {
>
props.onDestroy && props.onDestroy()} disabled={!props.destroyable}>
-
+
diff --git a/src/Components/CurrentUserContext.tsx b/src/Components/CurrentUserContext.tsx
index 629f674..41d7b44 100644
--- a/src/Components/CurrentUserContext.tsx
+++ b/src/Components/CurrentUserContext.tsx
@@ -1,7 +1,7 @@
import {createContext, ReactNode, useContext, useState} from "react";
import {Configuration, KnoxUser, LoginApi, User}from "@battery-intelligence-lab/galv";
import {useMutation, useQueryClient} from "@tanstack/react-query";
-import {AxiosResponse} from "axios";
+import {AxiosError, AxiosResponse} from "axios";
import axios from "axios";
import {useSnackbarMessenger} from "./SnackbarMessengerContext";
import Button from "@mui/material/Button";
@@ -39,10 +39,10 @@ export default function CurrentUserContextProvider({children}: {children: ReactN
password
})
const api_handler = new LoginApi(get_config())
- const Login = useMutation(
+ const Login = useMutation, AxiosError, void>(
() => api_handler.loginCreate.bind(new LoginApi(get_config()))(),
{
- onSuccess: (data: AxiosResponse) => {
+ onSuccess: (data) => {
window.localStorage.setItem('user', JSON.stringify(data.data))
setUser(data.data as unknown as LoginUser)
queryClient.removeQueries({predicate: () => true})
diff --git a/src/Components/FetchResourceContext.tsx b/src/Components/FetchResourceContext.tsx
index eedb334..52f3001 100644
--- a/src/Components/FetchResourceContext.tsx
+++ b/src/Components/FetchResourceContext.tsx
@@ -1,11 +1,19 @@
import {createContext, ReactNode, useContext} from "react";
import {useCurrentUser} from "./CurrentUserContext";
-import {API_HANDLERS, API_SLUGS, AutocompleteKey, DISPLAY_NAMES, is_lookup_key, LookupKey} from "../constants";
+import {
+ API_HANDLERS,
+ API_SLUGS,
+ AutocompleteKey,
+ DISPLAY_NAMES,
+ is_lookup_key,
+ LookupKey
+} from "../constants";
import {AxiosError, AxiosResponse} from "axios";
import {
+ MutationFunction,
QueryFunction,
useInfiniteQuery,
- type UseInfiniteQueryResult, useQuery,
+ type UseInfiniteQueryResult, useMutation, UseMutationOptions, UseMutationResult, useQuery,
useQueryClient, UseQueryOptions,
UseQueryResult
} from "@tanstack/react-query";
@@ -25,6 +33,30 @@ export type ListQueryResult = UseInfiniteQueryResult & {
results: T[] | null | undefined
}
+type RetrieveOptions = {
+ extra_query_options?: UseQueryOptions, AxiosError>,
+ with_result?: (result: AxiosResponse) => AxiosResponse,
+ on_error?: (error: AxiosError) => AxiosResponse|undefined
+}
+type UpdateTVariables = Partial & {id: string|number}
+type UpdateOptions = {
+ extra_query_options?: UseMutationOptions, AxiosError>,
+ before_cache?: (result: AxiosResponse) => AxiosResponse,
+ after_cache?: (result: AxiosResponse, variables: UpdateTVariables) => void,
+ on_error?: (error: AxiosError, variables: UpdateTVariables) => AxiosResponse|undefined
+}
+type CreateOptions = {
+ extra_query_options?: UseMutationOptions, AxiosError>,
+ before_cache?: (result: AxiosResponse) => AxiosResponse,
+ after_cache?: (result: AxiosResponse, variables: Partial) => void,
+ on_error?: (error: AxiosError, variables: Partial) => AxiosResponse|undefined
+}
+type DeleteOptions = {
+ extra_query_options?: UseMutationOptions, AxiosError>,
+ after?: () => void,
+ on_error?: (error: AxiosError, variables: T) => void
+}
+
export interface IFetchResourceContext {
// Returns null when lookup_key is undefined. Otherwise, returns undefined until data are fetched, then T[]
useListQuery: (
@@ -33,18 +65,31 @@ export interface IFetchResourceContext {
useRetrieveQuery: (
lookup_key: LookupKey,
resource_id: string|number,
- options?: {
- extra_query_options?: UseQueryOptions, AxiosError>,
- with_result?: (r: AxiosResponse) => AxiosResponse,
- on_error?: (e: AxiosError) => AxiosResponse|undefined
- }
+ options?: RetrieveOptions
) => UseQueryResult, AxiosError>
+ useUpdateQuery: (
+ lookup_key: LookupKey,
+ options?: UpdateOptions
+ ) => UseMutationResult, AxiosError, UpdateTVariables>
+ useCreateQuery: (
+ lookup_key: LookupKey,
+ options?: CreateOptions
+ ) => UseMutationResult, AxiosError, Partial>
+ useDeleteQuery: (
+ lookup_key: LookupKey,
+ options?: DeleteOptions
+ ) => UseMutationResult, AxiosError, T>
}
export const FetchResourceContext = createContext({} as IFetchResourceContext)
export const useFetchResource = () => useContext(FetchResourceContext)
+const get_error_detail = (e: AxiosError) => e.response?.data?.detail ??
+ Object.entries(e.response?.data ?? {})
+ .map(([k, v]) => `${k}: ${v}`)
+ .join(', ')
+
export default function FetchResourceContextProvider({children}: {children: ReactNode}) {
const extract_limit_offset = (url: string|null|undefined) => {
const safe_number = (n: string | null) => n && !isNaN(parseInt(n))? parseInt(n) : undefined
@@ -82,7 +127,7 @@ export default function FetchResourceContextProvider({children}: {children: Reac
else
data = resource
queryClient.setQueryData(
- [lookup_key, resource.uuid ?? resource.id ?? "no id in List response"],
+ [lookup_key, resource.id ?? "no id in List response"],
data
)
})
@@ -129,13 +174,13 @@ export default function FetchResourceContextProvider({children}: {children: Reac
const api_handler = new API_HANDLERS[lookup_key](config)
const get = api_handler[
`${API_SLUGS[lookup_key]}Retrieve` as keyof typeof api_handler
- ] as (uuid: string) => Promise>
+ ] as (id: string) => Promise>
const after = options?.with_result? options.with_result : (r: AxiosResponse) => r
const on_error_fn = options?.on_error? options.on_error : (e: AxiosError) => {
postSnackbarMessage({
message: `Error retrieving ${DISPLAY_NAMES[lookup_key]}/${resource_id}
- (HTTP ${e.response?.status} - ${e.response?.statusText}): ${e.response?.data?.detail}`,
+ (HTTP ${e.response?.status} - ${e.response?.statusText}): ${get_error_detail(e)}`,
severity: 'error'
})
}
@@ -160,7 +205,165 @@ export default function FetchResourceContextProvider({children}: {children: Reac
return useQuery, AxiosError>(query_options)
}
- return
+ const useUpdateQuery: IFetchResourceContext["useUpdateQuery"] = (
+ lookup_key: LookupKey,
+ options?: UpdateOptions
+ ) => {
+ const queryClient = useQueryClient()
+ const {postSnackbarMessage} = useSnackbarMessenger()
+ const config = new Configuration({
+ basePath: process.env.VITE_GALV_API_BASE_URL,
+ accessToken: useCurrentUser().user?.token
+ })
+ const api_handler = new API_HANDLERS[lookup_key](config)
+ const partialUpdate = api_handler[
+ `${API_SLUGS[lookup_key]}PartialUpdate` as keyof typeof api_handler
+ ] as (id: string, data: Partial) => Promise>
+
+ const pre_cache = options?.before_cache? options.before_cache : (r: AxiosResponse) => r
+ // (r, v) => ({r, v}) does nothing except stop TS from complaining about unused variables
+ const post_cache = options?.after_cache?
+ options.after_cache : (r: AxiosResponse, v: UpdateTVariables) => ({r, v})
+ // Need v so TS recognises the function as callable with 2 arguments
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const on_error_fn = options?.on_error? options.on_error : (e: AxiosError, v: UpdateTVariables) => {
+ postSnackbarMessage({
+ message: `Error updating ${DISPLAY_NAMES[lookup_key]}/${v.id ?? v.id}
+ (HTTP ${e.response?.status} - ${e.response?.statusText}): ${get_error_detail(e)}`,
+ severity: 'error'
+ })
+ }
+
+ const mutationFn: MutationFunction, Partial> =
+ (data: Partial) => partialUpdate
+ .bind(api_handler)(String(data.id ?? data.id), data)
+ .then(pre_cache)
+
+ const mutation_options: UseMutationOptions, AxiosError, Partial> = {
+ mutationKey: [lookup_key, 'update'],
+ // @ts-expect-error - TS incorrectly infers that TVariables can be of type void
+ mutationFn: mutationFn,
+ // @ts-expect-error - TS incorrectly infers that TVariables can be of type void
+ onSuccess: (data: AxiosResponse, variables: UpdateTVariables) => {
+ // Update cache
+ const queryKey = [lookup_key, variables.id]
+ queryClient.setQueryData(queryKey, data)
+ // Invalidate list cache
+ queryClient.invalidateQueries([lookup_key, 'list'])
+ // Invalidate autocomplete cache
+ queryClient.invalidateQueries(['autocomplete'])
+ post_cache(data, variables)
+ },
+ // @ts-expect-error - TS incorrectly infers that TVariables can be of type void
+ onError: on_error_fn,
+ ...options?.extra_query_options
+ }
+ return useMutation, AxiosError, UpdateTVariables>(mutation_options)
+ }
+
+ const useCreateQuery: IFetchResourceContext["useCreateQuery"] = (
+ lookup_key: LookupKey,
+ options?: CreateOptions
+ ) => {
+ const queryClient = useQueryClient()
+ const {postSnackbarMessage} = useSnackbarMessenger()
+ const config = new Configuration({
+ basePath: process.env.VITE_GALV_API_BASE_URL,
+ accessToken: useCurrentUser().user?.token
+ })
+ const api_handler = new API_HANDLERS[lookup_key](config)
+ const create = api_handler[
+ `${API_SLUGS[lookup_key]}Create` as keyof typeof api_handler
+ ] as (data: Partial) => Promise>
+
+ const pre_cache = options?.before_cache? options.before_cache : (r: AxiosResponse) => r
+ // (r, v) => ({r, v}) does nothing except stop TS from complaining about unused variables
+ const post_cache = options?.after_cache?
+ options.after_cache : (r: AxiosResponse, v: Partial) => ({r, v})
+ const on_error_fn = options?.on_error? options.on_error : (e: AxiosError, v: Partial) => {
+ postSnackbarMessage({
+ message: `Error creating ${DISPLAY_NAMES[lookup_key]}
+ ${v.name ?? v.title ?? v.identifier ?? v.model ?? v.username}
+ (HTTP ${e.response?.status} - ${e.response?.statusText}): ${get_error_detail(e)}`,
+ severity: 'error'
+ })
+ }
+
+ const mutationFn: MutationFunction, Partial> =
+ (data: Partial) => create.bind(api_handler)(data).then(pre_cache)
+
+ const mutation_options: UseMutationOptions, AxiosError, Partial> = {
+ mutationKey: [lookup_key, 'create'],
+ // @ts-expect-error - TS incorrectly infers that TVariables can be of type void
+ mutationFn: mutationFn,
+ // @ts-expect-error - TS incorrectly infers that TVariables can be of type void
+ onSuccess: (data: AxiosResponse, variables: Partial) => {
+ // Update cache
+ const queryKey = [lookup_key, data.data.id ?? "no id in Create response"]
+ queryClient.setQueryData(queryKey, data)
+ // Invalidate list cache
+ queryClient.invalidateQueries([lookup_key, 'list'])
+ // Invalidate autocomplete cache
+ queryClient.invalidateQueries(['autocomplete'])
+ post_cache(data, variables)
+ },
+ // @ts-expect-error - TS incorrectly infers that TVariables can be of type void
+ onError: on_error_fn,
+ ...options?.extra_query_options
+ }
+ return useMutation, AxiosError, Partial>(mutation_options)
+ }
+
+ const useDeleteQuery: IFetchResourceContext["useDeleteQuery"] = (
+ lookup_key: LookupKey,
+ options?: DeleteOptions
+ ) => {
+ const queryClient = useQueryClient()
+ const {postSnackbarMessage} = useSnackbarMessenger()
+ const config = new Configuration({
+ basePath: process.env.VITE_GALV_API_BASE_URL,
+ accessToken: useCurrentUser().user?.token
+ })
+ const api_handler = new API_HANDLERS[lookup_key](config)
+ const destroy = api_handler[
+ `${API_SLUGS[lookup_key]}Destroy` as keyof typeof api_handler
+ ] as (id: string) => Promise>
+
+ const on_error_fn = options?.on_error? options.on_error : (e: AxiosError, v: T) => {
+ postSnackbarMessage({
+ message: `Error deleting ${DISPLAY_NAMES[lookup_key]}/${v.id ?? v.id}
+ (HTTP ${e.response?.status} - ${e.response?.statusText}): ${get_error_detail(e)}`,
+ severity: 'error'
+ })
+ }
+
+ const mutationFn: MutationFunction, T> =
+ (data: T) => destroy.bind(api_handler)(String(data.id))
+
+ const mutation_options: UseMutationOptions, AxiosError, T> = {
+ mutationKey: [lookup_key, 'delete'],
+ // @ts-expect-error - TS incorrectly infers that TVariables can be of type void
+ mutationFn: mutationFn,
+ // @ts-expect-error - TS incorrectly infers that TVariables can be of type void
+ onSuccess: (data: AxiosResponse, variables: T) => {
+ // Invalidate cache
+ queryClient.removeQueries([lookup_key, variables.id])
+ // Invalidate list cache
+ queryClient.invalidateQueries([lookup_key, 'list'])
+ // Invalidate autocomplete cache
+ queryClient.invalidateQueries(['autocomplete'])
+ if (options?.after) options.after()
+ },
+ // @ts-expect-error - TS incorrectly infers that TVariables can be of type void
+ onError: on_error_fn,
+ ...options?.extra_query_options
+ }
+ return useMutation, AxiosError, T>(mutation_options)
+ }
+
+ return
{children}
}
diff --git a/src/Components/Mapping.tsx b/src/Components/Mapping.tsx
index df189bb..778418c 100644
--- a/src/Components/Mapping.tsx
+++ b/src/Components/Mapping.tsx
@@ -1,5 +1,5 @@
-import {ICONS, LOOKUP_KEYS, PATHS} from "../constants";
-import React, {useEffect, useState} from "react";
+import {ICONS, key_to_type, LOOKUP_KEYS, PATHS} from "../constants";
+import React, {useState} from "react";
import Stack from "@mui/material/Stack";
import ApiResourceContextProvider, {useApiResource} from "./ApiResourceContext";
import Button from "@mui/material/Button";
@@ -33,7 +33,13 @@ import Modal from "@mui/material/Modal";
import ListItemIcon from "@mui/material/ListItemIcon";
import TextField from "@mui/material/TextField";
import FilledInput from "@mui/material/FilledInput";
-import {BaseResource} from "./ResourceCard";
+import {BaseResource, Permissions} from "./ResourceCard";
+import PrettyResource from "./prettify/PrettyResource";
+import Box from "@mui/material/Box";
+import Alert from "@mui/material/Alert";
+import {to_type_value_notation_wrapper, TypeValueNotation, TypeValueNotationWrapper} from "./TypeValueNotation";
+import PrettyObject from "./prettify/PrettyObject";
+import {Theme} from "@mui/material/styles";
type ColumnType = {
url: string,
@@ -45,6 +51,7 @@ type ColumnType = {
is_required: boolean
}
type ObservedFile = BaseResource & {
+ id: string
summary: Record>
applicable_mappings: DB_MappingResource[]
mapping?: string
@@ -58,12 +65,18 @@ type MapEntry = {
}
type Map = Record
type MappingResource = {
- uuid: string
+ id: string
url: string
name: string
is_valid: boolean
+ in_use?: boolean
map: Map
missing?: number
+ team: string|null
+ permissions?: Permissions
+ read_access_level?: number
+ edit_access_level?: number
+ delete_access_level?: number
}
// A Map but the column_type is an id, as stored in the database
@@ -269,7 +282,7 @@ function MappingTable(
})
)
- return
+ return
{Object.keys(summary).map((key, i) => {
@@ -282,7 +295,7 @@ function MappingTable(
{
Object.values(summary).reduce((prev, cur) => {
return cur.length > prev.length ? cur : prev
- }, []).map((arr, i) => {
+ }, []).map((_arr, i) => {
return
{Object.values(summary).map((col, n) => {
return {col[i]}
@@ -298,20 +311,25 @@ function MappingTable(
{/* Recognise */}
- {Object.keys(summary).map((key, i) =>
- {
+ {Object.keys(summary).map((key, i) => {
+ if (!mappingResource.permissions?.write) return
+
+ {map[key]?.column_type.name ?? "-"}
+
+
+ return {
{
const old = {...map}
delete old[key]
- const value = ct? {...old, [key]: {column_type: ct}} : old
+ const value = ct ? {...old, [key]: {column_type: ct}} : old
safeSetMapping(value)
}}
selected_id={map[key]?.column_type.id ?? null}
reset_name={key}
/>
}
- )}
+ })}
{/* Rebase and Rescale */}
{/* Any new column is int/float */
@@ -327,6 +345,14 @@ function MappingTable(
{Object.keys(summary).map((key, i) => {
+ if (!mappingResource.permissions?.write) return
+
+ x' = (x + {map[key]?.addition}) x {map[key]?.multiplier}
+
+
const float = map[key]?.column_type.data_type === "float"
if (!float && map[key]?.column_type.data_type !== "int")
return
@@ -341,6 +367,7 @@ function MappingTable(
value={map[key].addition ?? 0}
sx={{width: width(map[key].addition ?? 0)}}
type="number"
+ // @ts-expect-error MUI doesn't expose pattern property, but it does forward it
pattern={pattern}
aria-label="addition"
onChange={(e) => {
@@ -356,6 +383,7 @@ function MappingTable(
value={map[key].multiplier ?? 1}
sx={{width: width(map[key].multiplier ?? 1)}}
type="number"
+ // @ts-expect-error MUI doesn't expose pattern property, but it does forward it
pattern={pattern}
aria-label="multiplier"
onChange={(e) => {
@@ -386,6 +414,11 @@ function MappingTable(
{/* Rename */}
{Object.keys(summary).map((key, i) => {
+ if (!mappingResource.permissions?.write) return
+
+ {key}
+
+
if (!map[key]) return
{key}
@@ -431,67 +464,94 @@ function MappingTable(
}
-export function Mapping() {
- const blank_map = () => ({uuid: "", url: "", name: "", is_valid: false, map: {}})
- const {apiResource: file, apiQuery: fileQuery} = useApiResource()
- const {useListQuery} = useFetchResource()
+function MappingManager({file}: {file: ObservedFile}) {
+ const blank_map = () => ({
+ id: "", url: "", name: "", team: null, is_valid: false, map: {},
+ permissions: {read: true, write: true, admin: true},
+ read_access_level: 2, edit_access_level: 3, delete_access_level: 3
+ })
+ const {useListQuery, useCreateQuery, useUpdateQuery, useDeleteQuery} = useFetchResource()
const {results: columns} = useListQuery(LOOKUP_KEYS.COLUMN_FAMILY)
const [more, setMore] = React.useState(false)
- const [mapping, setMapping] = useState(blank_map())
+ const [advancedPropertiesOpen, setAdvancedPropertiesOpen] = React.useState(false)
+ const [mapping, setMapping] = useState(
+ () => {
+ const m = file?.applicable_mappings?.find(m => m.url === file.mapping)
+ return m? {...m} : blank_map()
+ }
+ )
+ const navigate = useNavigate()
+ const updateFileMutation = useUpdateQuery(LOOKUP_KEYS.FILE)
+ const updateFile = (new_mapping: DB_MappingResource) => updateFileMutation.mutate(
+ {...file!, mapping: new_mapping.url},
+ {onSuccess: () => navigate(0)}
+ )
+ const createMapMutation = useCreateQuery(
+ LOOKUP_KEYS.MAPPING,
+ { after_cache: (r) => updateFile(r.data) }
+ )
+ const createMap = (data: Omit) =>
+ createMapMutation.mutate(data, {onSuccess: (data) => updateFile(data.data)})
+ const updateMapMutation = useUpdateQuery(LOOKUP_KEYS.MAPPING)
+ const updateMap = (data: DB_MappingResource) => updateMapMutation.mutate(data)
+ const deleteMapMutation = useDeleteQuery(LOOKUP_KEYS.MAPPING)
+ const deleteMap = (data: DB_MappingResource) => deleteMapMutation.mutate(data, {onSuccess: () => navigate(0)})
+
const summary = file?.summary as Record>
// Summary data with children in array form
- let array_summary: Record = {}
+ const array_summary: Record = {}
+
+ const original_mapping = file?.applicable_mappings?.find((m) => m.id === mapping.id)
+
+ const file_mapping_has_changed = original_mapping?.url !== file?.mapping
+ const mapping_map_has_changed = JSON.stringify(mapping.map) !== JSON.stringify(original_mapping?.map)
+ const mapping_has_changed = mapping_map_has_changed ||
+ mapping.name !== original_mapping?.name ||
+ mapping.team !== original_mapping?.team ||
+ mapping.read_access_level !== original_mapping?.read_access_level ||
+ mapping.edit_access_level !== original_mapping?.edit_access_level ||
+ mapping.delete_access_level !== original_mapping?.delete_access_level
+
+ const mapping_is_dirty = // new map with unsaved changes
+ (mapping.id === "" && Object.keys(mapping.map).length > 0) ||
+ // old map with unsaved changes
+ (mapping.id !== "" && mapping_has_changed)
+
+ const mapping_can_be_saved = mapping_is_dirty &&
+ mapping.name !== "" &&
+ mapping.team !== "" &&
+ (mapping.permissions?.write ?? false)
- const mapping_is_dirty = () => // new map with unsaved changes
- (!mapping.uuid && Object.keys(mapping.map).length > 0) ||
- // old map with unsaved changes
- (mapping.uuid && file?.applicable_mappings?.find(m => m.uuid === mapping.uuid)?.map !== mapping.map)
+ const map_can_be_applied = mapping.id !== "" && file_mapping_has_changed && (file?.permissions?.write ?? false)
const safeSetMapping = (value?: string) => {
- const new_mapping = file?.applicable_mappings?.find((m) => m.uuid === value)
+ const new_mapping = file?.applicable_mappings?.find((m) => m.id === value)
// Check whether we need to warn about discarding changes.
- if (mapping_is_dirty() && !window.confirm(`Discard unsaved changes to map ${mapping.name}?`))
+ if (mapping_is_dirty && !window.confirm(`Discard unsaved changes to mapping '${mapping.name}'?`))
return
- setMapping(new_mapping ?? blank_map())
+ setMapping(new_mapping? {...new_mapping} : blank_map())
}
- useEffect(() => {
- array_summary = {}
- Object.keys(summary).forEach((key) => {
- const arr = [];
- const max = Object.keys(summary).reduce((prev, cur) => {
- return parseInt(cur) > prev ? parseInt(cur) : prev
- }, 0)
- for (let i = 0; i < max; i++) {
- arr.push(summary[key][i] ?? undefined)
- }
- array_summary[key] = Object.values(summary[key])
- })
- safeSetMapping(file?.mapping)
- }, [file])
-
- const loadingBody =
-
- const errorBody = E}
- title="Error"
- subheader="Error loading mapping"
- />
+ Object.keys(summary).forEach((key) => {
+ const arr = [];
+ const max = Object.keys(summary).reduce((prev, cur) => {
+ return parseInt(cur) > prev ? parseInt(cur) : prev
+ }, 0)
+ for (let i = 0; i < max; i++) {
+ arr.push(summary[key][i] ?? undefined)
}
- />
+ array_summary[key] = Object.values(summary[key])
+ })
- const contentBody = t.spacing(1)}}
elevation={0}
>
Column Mapping for {
- file?.uuid &&
-
+ file?.id &&
+
}
@@ -511,66 +571,198 @@ export function Mapping() {
- Mapping:
- {array_summary && mapping && columns &&
- setMapping(map_to_db_map(map))}
- summary={array_summary}
- />
- }
- {/* Load mapping */
- file?.applicable_mappings &&
-
- Load mapping:
-
-
- }
- {
-
- setMapping({...mapping, name: e.target.value})}
- />
-
-
+ { !file?
+ Failed to load file data, please try refreshing the page. :
+ <>
+ {/* Load mapping */
+ file?.applicable_mappings &&
+
+
+ Mapping:
+
+
+ }
+ onClick={() => {
+ let confirmed = false
+ if (mapping_can_be_saved) {
+ if (!mapping.id) {
+ createMap({
+ name: mapping.name,
+ team: mapping.team,
+ map: mapping.map
+ })
+ return // automatically applies on creation
+ } else {
+ if (mapping_map_has_changed && mapping.in_use) {
+ if (!window.confirm(`
+Mapping '${mapping.name}' is in use.
+
+Updating its map will cause affected datafiles to be re-imported. This can be an long operation, especially where multiple files are affected.
+
+Do you wish to continue?`
+ ))
+ return
+ confirmed = true
+ }
+ updateMap(mapping)
+ }
+ }
+ if (map_can_be_applied) {
+ if (!confirmed && file.state === "IMPORTED") {
+ if (!window.confirm("Apply new mapping to file? This will cause the data to be re-imported.")) {
+ return
+ }
+ }
+ updateFile(mapping)
+ }
+ }}
+ disabled={!mapping_can_be_saved && !map_can_be_applied}
+ >
+ {mapping_can_be_saved && (mapping.id?
+ map_can_be_applied?
+ "Update and apply mapping": "Update mapping" :
+ "Create"
+ )}
+ {!mapping_can_be_saved && "Apply mapping"}
+
+
+ }
+ {
+
+
+ { mapping.permissions?.write && <>
+ setMapping({...mapping, name: e.target.value})}
+ error={mapping_is_dirty && !mapping.name}
+ helperText={mapping_is_dirty && !mapping.name? "Name is required" : undefined}
+ />
+ {
+ if (typeof v._value !== "string") return
+ setMapping({...mapping, team: v._value})
+ }}
+ />
+ >}
+ {
+ mapping.permissions?.write &&
+ }
+
+ <>
+ {
+ mapping.in_use && mapping_map_has_changed &&
+
+ Updating a map that is used for datafiles will cause those datafiles to be re-parsed.
+
+ }
+ {!mapping.team && mapping_can_be_saved && Mapping must belong to a team.}
+ >
+
+ t.spacing(2),
+ paddingTop: (t: Theme) => t.spacing(1)
+ }}
+ >
+ {
+ const is_number = (x: unknown): x is number => typeof x === "number"
+ const as_number = (x: unknown) => is_number(x)? x : undefined
+ setMapping({
+ ...mapping,
+ read_access_level: as_number(v.read_access_level._value) ?? mapping.read_access_level,
+ edit_access_level: as_number(v.edit_access_level._value) ?? mapping.edit_access_level,
+ delete_access_level: as_number(v.delete_access_level._value) ?? mapping.delete_access_level
+ })
+ }}
+ extractPermissions={true}
+ canEditKeys={false}
+ />
+ }
+ onClick={() => {
+ if (window.confirm(`Delete mapping '${mapping.name}'?`)) {
+ deleteMap(mapping)
+ }
+ }}
+ disabled={mapping.id === "" || !mapping.permissions?.destroy}
+ >
+ Delete mapping {mapping.name || original_mapping?.name }
+
+
+
+
+ }
+ {array_summary && mapping && columns &&
+
+ setMapping(map_to_db_map(map))}
+ summary={array_summary}
+ />
+
+ }
+ >
}
+}
+
+export function Mapping() {
+ const {apiResource: file, apiQuery: fileQuery} = useApiResource()
+
+ const loadingBody =
+
+ const errorBody = E}
+ title="Error"
+ subheader="Error loading mapping"
+ />
+ }
+ />
return }
/>
}
diff --git a/src/Components/Representation.tsx b/src/Components/Representation.tsx
index 6979156..b8af76d 100644
--- a/src/Components/Representation.tsx
+++ b/src/Components/Representation.tsx
@@ -14,11 +14,11 @@ export function representation({data, lookup_key}: {data: BaseResource, lookup_k
.map((e) => e[1])
.join(" ")
- return s.length? s : `${DISPLAY_NAMES[lookup_key]} ${data.uuid ?? data.id}`
+ return s.length? s : `${DISPLAY_NAMES[lookup_key]} ${data.id ?? data.id}`
} catch (error) {
- console.error(`Could not represent ${lookup_key} ${data?.uuid ?? data?.id}`, {args: {data, lookup_key}, error})
+ console.error(`Could not represent ${lookup_key} ${data?.id ?? data?.id}`, {args: {data, lookup_key}, error})
}
- return String(data.uuid ?? data.id ?? 'unknown')
+ return String(data.id ?? data.id ?? 'unknown')
}
export type RepresentationProps = {
diff --git a/src/Components/ResourceCard.tsx b/src/Components/ResourceCard.tsx
index b079632..bbcdbd1 100644
--- a/src/Components/ResourceCard.tsx
+++ b/src/Components/ResourceCard.tsx
@@ -66,13 +66,14 @@ import ResourceStatuses from "./ResourceStatuses";
export type Permissions = { read?: boolean, write?: boolean, create?: boolean, destroy?: boolean }
type child_keys = "cells"|"equipment"|"schedules"
-export type BaseResource = ({uuid: string} | {id: number}) & {
+type CoreProperties = {
url: string,
permissions?: Permissions,
- team?: string,
+ team?: string|null,
family?: string,
cycler_tests?: string[],
} & {[key in child_keys]?: string[]} & SerializableObject
+export type BaseResource = { id: string|number } & CoreProperties
export type Family = BaseResource & ({cells: string[]} | {equipment: string[]} | {schedules: string[]})
export type Resource = { family: string, cycler_tests: string[] } & BaseResource
export type AutocompleteResource = { value: string, ld_value: string, url: string, id: number }
@@ -143,7 +144,7 @@ function ResourceCard(
const api_handler = new API_HANDLERS[lookup_key](config)
const patch = api_handler[
`${API_SLUGS[lookup_key]}PartialUpdate` as keyof typeof api_handler
- ] as (uuid: string, data: SerializableObject) => Promise>
+ ] as (id: string, data: SerializableObject) => Promise>
const queryClient = useQueryClient()
const update_mutation =
useMutation, AxiosError, SerializableObject>(
@@ -212,7 +213,7 @@ function ResourceCard(
return
const destroy = api_handler[
`${API_SLUGS[lookup_key]}Destroy` as keyof typeof api_handler
- ] as (uuid: string) => Promise>
+ ] as (id: string) => Promise>
destroy.bind(api_handler)(String(resource_id))
.then(() => queryClient.invalidateQueries([lookup_key, 'list']))
.then(() => {
@@ -305,12 +306,12 @@ function ResourceCard(
Inherited from
{family?
: FAMILY_ICON && }/> }
}
{family && family_key && {
const data = deep_copy(d)
@@ -418,7 +419,7 @@ function ResourceCard(
lookup_key={lookup_key}
prefix={family_key && family ?
: undefined}
diff --git a/src/Components/ResourceChip.tsx b/src/Components/ResourceChip.tsx
index 9e52731..4610d44 100644
--- a/src/Components/ResourceChip.tsx
+++ b/src/Components/ResourceChip.tsx
@@ -23,7 +23,7 @@ export type ResourceFamilyChipProps = {
export function ResourceChip(
{resource_id, lookup_key, loading, error, success, short_name, ...chipProps}: ResourceFamilyChipProps
) {
- // console.log(`ResourceChip`, {uuid, lookup_key, loading, error, success, chipProps})
+ // console.log(`ResourceChip`, {id, lookup_key, loading, error, success, chipProps})
const { classes } = useStyles();
const {passesFilters} = useContext(FilterContext)
@@ -44,7 +44,7 @@ export function ResourceChip(
lookup_key={lookup_key}
prefix={(!short_name && family) ?
: undefined
@@ -64,7 +64,7 @@ export function ResourceChip(
lookup_key={lookup_key}
prefix={(!short_name && family) ?
: undefined
diff --git a/src/Components/ResourceList.tsx b/src/Components/ResourceList.tsx
index f3145c7..3cd0268 100644
--- a/src/Components/ResourceList.tsx
+++ b/src/Components/ResourceList.tsx
@@ -46,7 +46,7 @@ export function ResourceList({lookup_key}: ResourceListP
content = query.results.map(
(resource: T, i) =>
)
diff --git a/src/Components/ResourceStatuses.tsx b/src/Components/ResourceStatuses.tsx
index a120475..9c89d3d 100644
--- a/src/Components/ResourceStatuses.tsx
+++ b/src/Components/ResourceStatuses.tsx
@@ -42,7 +42,7 @@ export default function ResourceStatuses({lookup_key}: {lookup_key: LookupKey})
statuses.push(
+
}
diff --git a/src/Components/SelectedResourcesPane.tsx b/src/Components/SelectedResourcesPane.tsx
index 5354b17..246ac76 100644
--- a/src/Components/SelectedResourcesPane.tsx
+++ b/src/Components/SelectedResourcesPane.tsx
@@ -72,7 +72,7 @@ export function DownloadButton({target_urls, ...props}: {target_urls: string|str
const api_handler = new API_HANDLERS[components.lookup_key](config)
const get = api_handler[
`${API_SLUGS[components.lookup_key]}Retrieve` as keyof typeof api_handler
- ] as (uuid: string) => Promise>
+ ] as (id: string) => Promise>
return get.bind(api_handler)(components.resource_id)
}
diff --git a/src/Components/__mocks__/Representation.tsx b/src/Components/__mocks__/Representation.tsx
index a16d2c3..1515e2f 100644
--- a/src/Components/__mocks__/Representation.tsx
+++ b/src/Components/__mocks__/Representation.tsx
@@ -8,7 +8,7 @@ import {BaseResource} from "../ResourceCard";
import {LookupKey} from "../../constants";
export function representation(params: {data: BaseResource, lookup_key: LookupKey}): string {
- return `representation: ${params.lookup_key} [${params.data.uuid ?? params.data.id}]`
+ return `representation: ${params.lookup_key} [${params.data.id ?? params.data.id}]`
}
export default function Representation(params: RepresentationProps) {
diff --git a/src/Components/misc.tsx b/src/Components/misc.tsx
index d398fe0..ec5ce06 100644
--- a/src/Components/misc.tsx
+++ b/src/Components/misc.tsx
@@ -1,7 +1,7 @@
import {is_lookup_key, LookupKey, PATHS, Serializable} from "../constants";
export type ObjectReferenceProps =
- { uuid: string } |
+ { id: string } |
{ id: number } |
{ url: string }
@@ -10,8 +10,8 @@ export function id_from_ref_props(props: ObjectRefere
if (typeof props === 'number')
return props as T
if (typeof props === 'object') {
- if ('uuid' in props) {
- return props.uuid as T
+ if ('id' in props) {
+ return props.id as T
} else if ('id' in props) {
return props.id as T
}
@@ -29,7 +29,7 @@ export function id_from_ref_props(props: ObjectRefere
}
/**
- * If `url` looks like a valid url for a resource, return the lookup key and uuid.
+ * If `url` looks like a valid url for a resource, return the lookup key and id.
* @param url
*/
export function get_url_components(url: string):
diff --git a/src/Components/prettify/PrettyObject.tsx b/src/Components/prettify/PrettyObject.tsx
index 2060a4c..f1139d6 100644
--- a/src/Components/prettify/PrettyObject.tsx
+++ b/src/Components/prettify/PrettyObject.tsx
@@ -134,7 +134,7 @@ export function PrettyObjectFromQuery(
const target_api_handler = new API_HANDLERS[lookup_key](config)
const target_get = target_api_handler[
`${API_SLUGS[lookup_key]}Retrieve` as keyof typeof target_api_handler
- ] as (uuid: string) => Promise>
+ ] as (id: string) => Promise>
const target_query = useQuery, AxiosError>({
queryKey: [lookup_key, resource_id],
diff --git a/src/Components/prettify/PrettyResource.tsx b/src/Components/prettify/PrettyResource.tsx
index c8ec132..bf71df2 100644
--- a/src/Components/prettify/PrettyResource.tsx
+++ b/src/Components/prettify/PrettyResource.tsx
@@ -8,7 +8,7 @@ import clsx from "clsx";
import ButtonBase from "@mui/material/ButtonBase";
import ResourceChip from "../ResourceChip";
import {PrettyComponentProps, PrettyString} from "./Prettify";
-import Autocomplete, {createFilterOptions} from "@mui/material/Autocomplete";
+import Autocomplete, {AutocompleteProps, createFilterOptions} from "@mui/material/Autocomplete";
import CircularProgress from "@mui/material/CircularProgress";
import {representation} from "../Representation";
import {useFetchResource} from "../FetchResourceContext";
@@ -23,7 +23,8 @@ export type PrettyResourceSelectProps = {
} & PrettyComponentProps & Partial>
export const PrettyResourceSelect = (
- {target, onChange, allow_new, lookup_key}: PrettyResourceSelectProps
+ {target, onChange, allow_new, lookup_key, edit_mode, ...autocompleteProps}:
+ PrettyResourceSelectProps & Partial, "onChange">>
) => {
const { useListQuery } = useFetchResource();
const [modalOpen, setModalOpen] = useState(false)
@@ -90,6 +91,7 @@ export const PrettyResourceSelect = (
}
(
}}
+ {...autocompleteProps}
/>
>
}
@@ -173,7 +176,6 @@ export default function PrettyResource(
if (!lookup_key)
throw new Error(`PrettyResource: lookup_key is undefined and unobtainable from value ${target._value}`)
return {
- queryClient.setQueryData([LOOKUP_KEYS.FILE, resource.uuid], {...r, data: resource})
+ queryClient.setQueryData([LOOKUP_KEYS.FILE, resource.id], {...r, data: resource})
})
} catch (e) {
console.error("Error updating cache from list data.", e)
diff --git a/src/client_templates/python.py b/src/client_templates/python.py
index 7134c85..30074b3 100644
--- a/src/client_templates/python.py
+++ b/src/client_templates/python.py
@@ -56,7 +56,7 @@
if verbose:
print(f"Downloading dataset {dataset_id}")
files_api = tag_to_api.FilesApi(api_client) # Get the specific API class we need
- r = files_api.files_retrieve({"uuid": dataset_id}) # Call the API method to retrieve the dataset
+ r = files_api.files_retrieve({"id": dataset_id}) # Call the API method to retrieve the dataset
# Response.response contains the raw response information
if r.response.status != 200:
raise Exception((
diff --git a/src/constants.ts b/src/constants.ts
index c4c6694..168d6aa 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -69,6 +69,8 @@ import {
TypeChangerSupportedTypeName
} from "./Components/prettify/TypeChanger";
import {TypeValueNotation} from "./Components/TypeValueNotation";
+import SaveIcon from "@mui/icons-material/Save";
+import DeleteIcon from "@mui/icons-material/Delete";
/**
* The basic unit of data passed around the frontend is a Serializable.
@@ -196,6 +198,8 @@ export const ICONS = {
MANAGE_ACCOUNT: ManageAccountsIcon,
LOGOUT: LogoutIcon,
CREATE: AddCircleIcon,
+ DELETE: DeleteIcon,
+ SAVE: SaveIcon,
FORK: ForkRightIcon,
CANCEL: CancelIcon,
CHECK: CheckCircleIcon,
@@ -360,7 +364,7 @@ export const API_HANDLERS = {
* ```
* const target_get = target_api_handler[
* `${API_SLUGS[lookup_key]}Retrieve` as keyof typeof target_api_handler
- * ] as (uuid: string) => Promise>
+ * ] as (id: string) => Promise>
* ```
*/
export const API_SLUGS = {
@@ -441,7 +445,7 @@ const team_fields: {[key: string]: Field} = {
validation_results: {readonly: true, type: "object", many: true},
}
const generic_fields: {[key: string]: Field} = {
- uuid: {readonly: true, type: "string"},
+ id: {readonly: true, type: "string"},
...always_fields,
}
const autocomplete_fields: {[key: string]: Field} = {
@@ -727,18 +731,18 @@ export const FIELDS = {
/**
* Names used by the backend to filter by each resource type.
* E.g. to look up all cells in a cell family, we would filter using
- * the querystring `?family_uuid=uuid`.
+ * the querystring `?family_id=id`.
* It is the responsibility of the frontend to ensure that the
* filter names are employed in the correct context --
* cell, equipment, and schedule all share the 'family' filter,
* so the url path must also be appropriate.
export const FILTER_NAMES = {
- [LOOKUP_KEYS.CELL_FAMILY]: "family_uuid",
- [LOOKUP_KEYS.EQUIPMENT_FAMILY]: "family_uuid",
- [LOOKUP_KEYS.SCHEDULE_FAMILY]: "family_uuid",
- [LOOKUP_KEYS.CELL]: "cell_uuid",
- [LOOKUP_KEYS.EQUIPMENT]: "equipment_uuid",
- [LOOKUP_KEYS.SCHEDULE]: "schedule_uuid",
+ [LOOKUP_KEYS.CELL_FAMILY]: "family_id",
+ [LOOKUP_KEYS.EQUIPMENT_FAMILY]: "family_id",
+ [LOOKUP_KEYS.SCHEDULE_FAMILY]: "family_id",
+ [LOOKUP_KEYS.CELL]: "cell_id",
+ [LOOKUP_KEYS.EQUIPMENT]: "equipment_id",
+ [LOOKUP_KEYS.SCHEDULE]: "schedule_id",
[LOOKUP_KEYS.TEAM]: "team_id",
} as const
*/
diff --git a/src/styles/UseStyles.ts b/src/styles/UseStyles.ts
index 743c6ed..c402bbe 100644
--- a/src/styles/UseStyles.ts
+++ b/src/styles/UseStyles.ts
@@ -269,6 +269,9 @@ export default makeStyles()((theme) => {
zIndex: 5000,
},
typeChangerResourcePopover: {},
+ mappingTable: {
+ '& td > svg': {verticalAlign: "middle"},
+ },
mappingTableHeadRow: {
'& th, & td': {
fontWeight: "bold",
diff --git a/src/test/ResourceCard.test.tsx b/src/test/ResourceCard.test.tsx
index 59a2da7..481ee51 100644
--- a/src/test/ResourceCard.test.tsx
+++ b/src/test/ResourceCard.test.tsx
@@ -37,7 +37,7 @@ const api_data: {
} = {
cell: {
url: "http://example.com/cells/0001-0001-0001-0001",
- uuid: "0001-0001-0001-0001",
+ id: "0001-0001-0001-0001",
identifier: 'Test Cell 1',
family: "http://example.com/cell_families/1000-1000-1000-1000",
team: "http://example.com/teams/1",
@@ -96,7 +96,7 @@ const api_data: {
},
cell_family: {
url: "http://example.com/cell_families/1000-1000-1000-1000",
- uuid: "1000-1000-1000-1000",
+ id: "1000-1000-1000-1000",
model: "Best Cell",
manufacturer: "PowerCorp",
team: "http://example.com/teams/1",
@@ -113,7 +113,7 @@ const api_data: {
},
cell_family_2: {
url: "http://example.com/cell_families/1200-1200-1200-1200",
- uuid: "1200-1200-1200-1200",
+ id: "1200-1200-1200-1200",
model: "Value Cell",
manufacturer: "BudgetCorp",
team: "http://example.com/teams/1",
@@ -183,11 +183,11 @@ const do_render = async () => {
mockedAxios.request.mockImplementation((config: AxiosRequestConfig) => {
if (config.url) {
const url = config.url.replace(/\/$/, "")
- if (url.endsWith(api_data.cell.uuid))
+ if (url.endsWith(api_data.cell.id))
return make_axios_response(api_data.cell, {config})
- if (url.endsWith(api_data.cell_family.uuid))
+ if (url.endsWith(api_data.cell_family.id))
return make_axios_response(api_data.cell_family, {config})
- if (url.endsWith(api_data.cell_family_2.uuid))
+ if (url.endsWith(api_data.cell_family_2.id))
return make_axios_response(api_data.cell_family_2, {config})
if (/access_levels/.test(url))
return make_axios_response(access_levels_response, {config})
@@ -239,9 +239,9 @@ describe('ResourceCard', () => {
const read_only_heading = await screen.findByRole('heading', { name: /^Read-only properties$/ });
const read_only_table = read_only_heading.parentElement!.parentElement!.nextElementSibling;
expect(read_only_table).not.toBe(null);
- const uuid_heading = within(read_only_table as HTMLElement).getByText(/uuid/);
- within(uuid_heading.parentElement!.parentElement!.nextElementSibling as HTMLElement)
- .getByText(api_data.cell.uuid);
+ const id_heading = within(read_only_table as HTMLElement).getByText(/id/);
+ within(id_heading.parentElement!.parentElement!.nextElementSibling as HTMLElement)
+ .getByText(api_data.cell.id);
const editable_heading = await screen.findByRole('heading', { name: /^Editable properties$/ });
// Editable properties has a permissions table as its first sibling
@@ -592,15 +592,15 @@ describe('ResourceCard', () => {
await user.click(screen.getByRole('button', {name: /^Edit this /i}));
const id_label = screen.getByRole("rowheader", {name: /^key cf$/});
const input = within(id_label.parentElement! as HTMLElement).getByRole('combobox');
- expect(input).toHaveValue(`representation: CELL_FAMILY [${api_data.cell_family.uuid}]`);
+ expect(input).toHaveValue(`representation: CELL_FAMILY [${api_data.cell_family.id}]`);
await user.click(input)
await user.clear(input)
await user.keyboard("2") // should match the second cell family
const autocomplete = await screen.findByRole('listbox');
- const option = within(autocomplete).getByText(`representation: CELL_FAMILY [${api_data.cell_family_2.uuid}]`);
+ const option = within(autocomplete).getByText(`representation: CELL_FAMILY [${api_data.cell_family_2.id}]`);
await user.click(option);
await wait()
- expect(input).toHaveValue(`representation: CELL_FAMILY [${api_data.cell_family_2.uuid}]`);
+ expect(input).toHaveValue(`representation: CELL_FAMILY [${api_data.cell_family_2.id}]`);
})
})
diff --git a/src/test/ResourceChip.test.tsx b/src/test/ResourceChip.test.tsx
index 8f76266..32116d6 100644
--- a/src/test/ResourceChip.test.tsx
+++ b/src/test/ResourceChip.test.tsx
@@ -22,7 +22,7 @@ const mockedAxios = axios as jest.Mocked;
const ResourceChip = jest.requireActual('../Components/ResourceChip').default;
const data = {
- uuid: "0001-0001-0001-0001",
+ id: "0001-0001-0001-0001",
identifier: 'Test Cell 1',
family: "http://example.com/cell_families/1000-1000-1000-1000",
team: "http://example.com/teams/1",
@@ -37,7 +37,7 @@ const data = {
}
const family_data = {
- uuid: "1000-1000-1000-1000",
+ id: "1000-1000-1000-1000",
identifier: 'Test Cell Family 1',
team: "http://example.com/teams/1"
}
@@ -59,5 +59,5 @@ it('renders', async () => {
)
await screen.findByText(/DummyRepresentation/)
- expect(screen.getByText(t => t.includes(data.uuid))).toBeInTheDocument()
+ expect(screen.getByText(t => t.includes(data.id))).toBeInTheDocument()
})
diff --git a/src/test/ResourceCreator.test.tsx b/src/test/ResourceCreator.test.tsx
index a47dada..5ac9c58 100644
--- a/src/test/ResourceCreator.test.tsx
+++ b/src/test/ResourceCreator.test.tsx
@@ -24,7 +24,7 @@ const mockedAxios = axios as jest.Mocked;
const ResourceCreator = jest.requireActual('../Components/ResourceCreator').default;
const family_data = {
- uuid: "1000-1000-1000-1000",
+ id: "1000-1000-1000-1000",
identifier: 'Test Cell Family 1',
team: "http://example.com/teams/1"
}
diff --git a/src/test/ResourceList.test.tsx b/src/test/ResourceList.test.tsx
index 0d9e003..960b6bc 100644
--- a/src/test/ResourceList.test.tsx
+++ b/src/test/ResourceList.test.tsx
@@ -21,9 +21,9 @@ const mockedAxios = axios as jest.Mocked;
const ResourceList = jest.requireActual('../Components/ResourceList').default;
const results = [
- {uuid: "0001-0001-0001-0001", identifier: 'Test Cell 1', family: "http://example.com/cell_families/1000-1000-1000-1000"},
- {uuid: "0002-0002-0002-0002", identifier: 'Test Cell 2', family: "http://example.com/cell_families/1000-1000-1000-1000"},
- {uuid: "0003-0003-0003-0003", identifier: 'Test Cell 3', family: "http://example.com/cell_families/2000-2000-2000-2000"},
+ {id: "0001-0001-0001-0001", identifier: 'Test Cell 1', family: "http://example.com/cell_families/1000-1000-1000-1000"},
+ {id: "0002-0002-0002-0002", identifier: 'Test Cell 2', family: "http://example.com/cell_families/1000-1000-1000-1000"},
+ {id: "0003-0003-0003-0003", identifier: 'Test Cell 3', family: "http://example.com/cell_families/2000-2000-2000-2000"},
]
@@ -39,7 +39,7 @@ it('renders', async () => {
)
- await screen.findByText(t => t.includes(results[0].uuid))
+ await screen.findByText(t => t.includes(results[0].id))
expect(screen.getByRole('heading', {name: 'Cells'})).toBeInTheDocument();
expect(screen.getAllByText(/ResourceCard/)).toHaveLength(3);