diff --git a/cypress/e2e/dashboard.cy.ts b/cypress/e2e/dashboard.cy.ts
index 286cb91..ff27368 100644
--- a/cypress/e2e/dashboard.cy.ts
+++ b/cypress/e2e/dashboard.cy.ts
@@ -5,18 +5,18 @@ describe('template spec', () => {
cy.intercept(
{
method: 'GET',
- url: `${PATHS.CELL}`,
+ url: `${PATHS.Cell}`,
},
- { fixture: PATHS.CELL.replace(/^\//, '') },
+ { fixture: PATHS.Cell.replace(/^\//, '') },
)
cy.intercept(
{
method: 'GET',
- url: `${PATHS.CELL}/*`,
+ url: `${PATHS.Cell}/*`,
},
async (req) => {
const id = req.url.split('/').pop()
- const cells = await cy.fixture(PATHS.CELL.replace(/^\//, ''))
+ const cells = await cy.fixture(PATHS.Cell.replace(/^\//, ''))
req.reply({
body: cells.results.find((cell) => cell.uuid === id),
})
diff --git a/src/App.tsx b/src/App.tsx
index b7a8110..a8d75be 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -118,7 +118,7 @@ export function Core() {
{open && 'Outputs'}
- {[LOOKUP_KEYS.EXPERIMENT, LOOKUP_KEYS.CYCLER_TEST].map(
+ {[LOOKUP_KEYS.Experiment, LOOKUP_KEYS.CyclerTest].map(
(lookupKey) => (
),
@@ -127,11 +127,11 @@ export function Core() {
{open && 'Resources'}
{[
- LOOKUP_KEYS.FILE,
- LOOKUP_KEYS.CELL,
- LOOKUP_KEYS.EQUIPMENT,
- LOOKUP_KEYS.SCHEDULE,
- LOOKUP_KEYS.ARBITRARY_FILE,
+ LOOKUP_KEYS.File,
+ LOOKUP_KEYS.Cell,
+ LOOKUP_KEYS.Equipment,
+ LOOKUP_KEYS.Schedule,
+ LOOKUP_KEYS.ArbitraryFile,
].map((lookupKey) => (
))}
@@ -139,10 +139,10 @@ export function Core() {
{open && 'Inputs'}
{[
- LOOKUP_KEYS.PATH,
- LOOKUP_KEYS.VALIDATION_SCHEMA,
- LOOKUP_KEYS.COLUMN_FAMILY,
- LOOKUP_KEYS.UNIT,
+ LOOKUP_KEYS.Path,
+ LOOKUP_KEYS.ValidationSchema,
+ LOOKUP_KEYS.ColumnFamily,
+ LOOKUP_KEYS.Unit,
].map((lookupKey) => (
))}
@@ -150,10 +150,10 @@ export function Core() {
{open && 'Management'}
{[
- LOOKUP_KEYS.LAB,
- LOOKUP_KEYS.TEAM,
- LOOKUP_KEYS.HARVESTER,
- LOOKUP_KEYS.ADDITIONAL_STORAGE,
+ LOOKUP_KEYS.Lab,
+ LOOKUP_KEYS.Team,
+ LOOKUP_KEYS.Harvester,
+ LOOKUP_KEYS.AdditionalStorage,
].map((lookupKey) => (
))}
@@ -275,7 +275,7 @@ export function Core() {
return (
@@ -292,7 +292,7 @@ export function Core() {
return <>>
}
- return
+ return
}
/* A looks through its children s and renders the first one that matches the current URL. */
@@ -302,7 +302,7 @@ export function Core() {
{/*} />*/}
}
/>
{/* Handle direct resource lookups */}
diff --git a/src/ClientCodeDemo.tsx b/src/ClientCodeDemo.tsx
index 7d80682..34e749e 100644
--- a/src/ClientCodeDemo.tsx
+++ b/src/ClientCodeDemo.tsx
@@ -35,7 +35,7 @@ function DatasetSelector({
fileQueryLimit?: number
}) {
const { useListQuery } = useFetchResource()
- const query = useListQuery(LOOKUP_KEYS.FILE, {
+ const query = useListQuery(LOOKUP_KEYS.File, {
limit: fileQueryLimit ?? DEFAULT_FETCH_LIMIT,
})
diff --git a/src/Components/ApiResourceContext.tsx b/src/Components/ApiResourceContext.tsx
index 8ff3dbb..2598adf 100644
--- a/src/Components/ApiResourceContext.tsx
+++ b/src/Components/ApiResourceContext.tsx
@@ -46,10 +46,14 @@ export const get_select_function =
(lookupKey: LookupKey) =>
(data: AxiosResponse) => {
Object.entries(FIELDS[lookupKey]).forEach(([k, v]) => {
- if (has(v, 'transformation'))
+ if (has(v, 'transformation')) {
+ console.warn(
+ `[FIELDS deprecation] ${lookupKey}.${k} has a transformation`,
+ )
data.data[k as keyof typeof data.data] = v.transformation(
data.data[k as keyof typeof data.data],
)
+ }
})
return data as AxiosResponse
}
diff --git a/src/Components/AttachmentUploadContext.tsx b/src/Components/AttachmentUploadContext.tsx
index d2d4521..6dd289c 100644
--- a/src/Components/AttachmentUploadContext.tsx
+++ b/src/Components/AttachmentUploadContext.tsx
@@ -58,11 +58,11 @@ export default function AttachmentUploadContextProvider({
},
onSuccess: async (data: AxiosResponse) => {
queryClient.setQueryData(
- [LOOKUP_KEYS.ARBITRARY_FILE, data.data.id],
+ [LOOKUP_KEYS.ArbitraryFile, data.data.id],
data.data,
)
await queryClient.invalidateQueries({
- queryKey: [LOOKUP_KEYS.ARBITRARY_FILE, 'list'],
+ queryKey: [LOOKUP_KEYS.ArbitraryFile, 'list'],
})
callback(data.data.url)
},
diff --git a/src/Components/AuthImage.tsx b/src/Components/AuthImage.tsx
index 3578864..af7fe61 100644
--- a/src/Components/AuthImage.tsx
+++ b/src/Components/AuthImage.tsx
@@ -30,7 +30,7 @@ export default function AuthImage({
'Galv-Storage-No-Redirect': true,
}
const query = useQuery({
- queryKey: [LOOKUP_KEYS.FILE, file.id, 'png'],
+ queryKey: [LOOKUP_KEYS.File, file.id, 'png'],
queryFn: async () => {
const response = await axios.get(file.png, {
headers,
diff --git a/src/Components/CardActionBar.tsx b/src/Components/CardActionBar.tsx
index e031e60..1ff58d3 100644
--- a/src/Components/CardActionBar.tsx
+++ b/src/Components/CardActionBar.tsx
@@ -32,7 +32,7 @@ import { has_value, id_from_ref_props } from './misc'
import clsx from 'clsx'
import UseStyles from '../styles/UseStyles'
import { useSelectionManagement } from './SelectionManagementContext'
-import { representation } from './Representation'
+import { genericRepresentation } from './representation/GenericRepresentation'
import SafeTooltip from './SafeTooltip'
export type CardActionBarProps = {
@@ -67,17 +67,33 @@ export type CardActionBarProps = {
*/
export default function CardActionBar(props: CardActionBarProps) {
const { classes, theme } = UseStyles()
- const { apiResource } = useApiResource()
+ const { apiResource, apiResourceDescription } = useApiResource()
const iconProps: Partial = {
...props.iconProps,
}
const selectable = props.selectable ?? typeof apiResource?.id === 'string'
const { toggleSelected, isSelected } = useSelectionManagement()
+ // Check differences between FIELDS context and apiResourceDescription context
+ const api_context = Object.values(apiResourceDescription ?? {}).filter(
+ (e) => e.galv_resource,
+ )
+ const fields_context = Object.values(FIELDS[props.lookupKey]).filter((e) =>
+ is_lookupKey(e.type),
+ )
+ // const api_only = api_context.filter((e) => !fields_context.includes(e))
+ const fields_only = fields_context.filter((e) => !api_context.includes(e))
+ if (fields_only.length > 0) {
+ console.warn(
+ `[FIELDS deprecation] FIELD[${props.lookupKey}] has fields not present in apiResourceDescription.`,
+ fields_only,
+ )
+ }
+
const context_section = (
<>
- {Object.entries(FIELDS[props.lookupKey])
- .filter((e) => is_lookupKey(e[1].type))
+ {Object.entries(apiResourceDescription ?? {})
+ .filter((e) => e[1].galv_resource)
.map(([k, v]) => {
const relative_lookupKey = v.type as LookupKey
let content: ReactNode
@@ -225,7 +241,7 @@ export default function CardActionBar(props: CardActionBarProps) {
const destroy_section = (
- {props.lookupKey === LOOKUP_KEYS.FILE && props.reimportable && (
+ {props.lookupKey === LOOKUP_KEYS.File && props.reimportable && (
{
if (!user) return
- const api = new API_HANDLERS[LOOKUP_KEYS.USER](api_config)
+ const api = new API_HANDLERS[LOOKUP_KEYS.User](api_config)
return api
.usersRetrieve({ id: user.id })
.then((response: AxiosResponse) => {
diff --git a/src/Components/FetchResourceContext.tsx b/src/Components/FetchResourceContext.tsx
index 4be5aa8..01e791a 100644
--- a/src/Components/FetchResourceContext.tsx
+++ b/src/Components/FetchResourceContext.tsx
@@ -54,7 +54,8 @@ export type FieldDescription = {
| 'choice'
| 'json'
| string
- many: boolean
+ galv_resource: boolean // Whether the `type` is a Galv Resource
+ many: boolean // Whether the field is a list
help_text: string
required: boolean
read_only: boolean
@@ -558,7 +559,7 @@ export default function FetchResourceContextProvider({
queryClient.invalidateQueries({
queryKey: [lookupKey, 'list'],
})
- if (lookupKey === LOOKUP_KEYS.LAB) {
+ if (lookupKey === LOOKUP_KEYS.Lab) {
refresh_user()
}
// Invalidate autocomplete cache
diff --git a/src/Components/Mapping.tsx b/src/Components/Mapping.tsx
index 3657ec5..fe40751 100644
--- a/src/Components/Mapping.tsx
+++ b/src/Components/Mapping.tsx
@@ -166,7 +166,7 @@ function CreateColumnType({
onCreate(new_resource_url)
}}
onDiscard={() => setOpen(false)}
- lookupKey={LOOKUP_KEYS.COLUMN_FAMILY}
+ lookupKey={LOOKUP_KEYS.ColumnFamily}
/>
>
@@ -184,7 +184,7 @@ function SelectColumnType({
reset_name: string
}) {
const { useListQuery } = useFetchResource()
- const query = useListQuery(LOOKUP_KEYS.COLUMN_FAMILY)
+ const query = useListQuery(LOOKUP_KEYS.ColumnFamily)
const { classes } = useStyles()
const [createModalOpen, setCreateModalOpen] = useState(false)
@@ -826,7 +826,7 @@ function MappingManager({
})
const { classes } = useStyles()
const { useListQuery, useCreateQuery, useUpdateQuery } = useFetchResource()
- const col_query = useListQuery(LOOKUP_KEYS.COLUMN_FAMILY)
+ const col_query = useListQuery(LOOKUP_KEYS.ColumnFamily)
const [more, setMore] = React.useState(false)
const [advancedPropertiesOpen, setAdvancedPropertiesOpen] =
React.useState(false)
@@ -838,14 +838,14 @@ function MappingManager({
return m ? { ...m } : blank_map()
})
const navigate = useNavigate()
- const updateFileMutation = useUpdateQuery(LOOKUP_KEYS.FILE)
+ 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,
+ LOOKUP_KEYS.ColumnMapping,
{ after_cache: (r) => updateFile(r.data) },
)
const createMap = (data: ApplicableMappingResource) => {
@@ -864,12 +864,12 @@ function MappingManager({
})
}
const updateMapMutation = useUpdateQuery(
- LOOKUP_KEYS.MAPPING,
+ LOOKUP_KEYS.ColumnMapping,
)
const updateMap = (data: DB_MappingResource) =>
updateMapMutation.mutate(data, { onSuccess: () => navigate(0) })
// TODO: Implement deleteMapMutation when backend supplies delete permissions
- // const deleteMapMutation = useDeleteQuery(LOOKUP_KEYS.MAPPING)
+ // const deleteMapMutation = useDeleteQuery(LOOKUP_KEYS.ColumnMapping)
// const deleteMap = (data: DB_MappingResource) => deleteMapMutation.mutate(data, {onSuccess: () => navigate(0)})
if (col_query?.hasNextPage && !col_query.isFetchingNextPage)
@@ -963,7 +963,7 @@ function MappingManager({
{file?.id && (
)}
@@ -1231,12 +1231,12 @@ Do you wish to continue?`)
diff --git a/src/Components/ResourceChip.tsx b/src/Components/ResourceChip.tsx
index adb15a1..3c5b3fd 100644
--- a/src/Components/ResourceChip.tsx
+++ b/src/Components/ResourceChip.tsx
@@ -8,7 +8,7 @@ import QueryWrapper, { QueryWrapperProps } from './QueryWrapper'
import ErrorChip from './error/ErrorChip'
import { FAMILY_LOOKUP_KEYS, GalvResource, PATHS } from '../constants'
import ErrorBoundary from './ErrorBoundary'
-import Representation from './Representation'
+import Representation from './representation/GenericRepresentation'
import { FilterContext } from './filtering/FilterContext'
import ApiResourceContextProvider, {
ApiResourceContextProviderProps,
diff --git a/src/Components/ResourceCreator.tsx b/src/Components/ResourceCreator.tsx
index af849d3..90df114 100644
--- a/src/Components/ResourceCreator.tsx
+++ b/src/Components/ResourceCreator.tsx
@@ -109,7 +109,7 @@ export function TokenCreator({
weeks: 60 * 60 * 24 * 7,
}
- const lookupKey = LOOKUP_KEYS.TOKEN
+ const lookupKey = LOOKUP_KEYS.Token
const ICON = ICONS[lookupKey]
@@ -364,7 +364,7 @@ export function ResourceCreator({
)
const handleSave = () => {
- if (lookupKey === LOOKUP_KEYS.ARBITRARY_FILE) {
+ if (lookupKey === LOOKUP_KEYS.ArbitraryFile) {
create_attachment_mutation.mutate({
...clean(UndoRedo.current),
file,
@@ -492,28 +492,45 @@ export default function WrappedResourceCreator(
const [modalOpen, setModalOpen] = useState(false)
const { user, refresh_user } = useCurrentUser()
const navigate = useNavigate()
+ const { useDescribeQuery } = useFetchResource()
+ const description_query = useDescribeQuery(props.lookupKey)
+ const [createable, setCreateable] = useState(false)
- const get_can_create = (lookupKey: LookupKey) => {
+ useEffect(() => {
// We can always create tokens because they represent us, and labs because someone has to.
- if (lookupKey === LOOKUP_KEYS.TOKEN) return !!user
- if (lookupKey === LOOKUP_KEYS.LAB) return !!user
+ if (
+ props.lookupKey === LOOKUP_KEYS.Token ||
+ props.lookupKey === LOOKUP_KEYS.Lab
+ ) {
+ setCreateable(!!user)
+ return
+ }
const lab_admin_resources = [
- LOOKUP_KEYS.TEAM,
- LOOKUP_KEYS.ADDITIONAL_STORAGE,
+ LOOKUP_KEYS.Team,
+ LOOKUP_KEYS.AdditionalStorage,
] as LookupKey[]
- if (lab_admin_resources.includes(lookupKey)) return user?.is_lab_admin
+ if (lab_admin_resources.includes(props.lookupKey)) {
+ setCreateable(!!user?.is_lab_admin)
+ return
+ }
- const fields = FIELDS[lookupKey]
- return Object.keys(fields).includes('team')
- }
+ console.log(`Checking if ${props.lookupKey} can be created`, {
+ description_query_data: description_query.data,
+ })
+
+ setCreateable(
+ !!description_query.data?.data &&
+ Object.keys(description_query.data.data).includes('team'),
+ )
+ }, [description_query.data, props.lookupKey, user])
- if (!get_can_create(props.lookupKey)) return <>>
+ if (!createable) return <>>
const ADD_ICON = ICONS.CREATE
// Files are a special case and there's a whole page for handling them
- if (props.lookupKey === LOOKUP_KEYS.FILE)
+ if (props.lookupKey === LOOKUP_KEYS.File)
return (
diff --git a/src/Components/card/summaries/CardSummary.tsx b/src/Components/card/summaries/CardSummary.tsx
index e72e03b..b354d05 100644
--- a/src/Components/card/summaries/CardSummary.tsx
+++ b/src/Components/card/summaries/CardSummary.tsx
@@ -38,17 +38,17 @@ import CardContent from '@mui/material/CardContent'
const CUSTOM_SUMMARIES: Partial<
Record ReactNode>
> = {
- [LOOKUP_KEYS.ARBITRARY_FILE]: ArbitraryFileSummary,
- [LOOKUP_KEYS.ADDITIONAL_STORAGE]: AdditionalStorageSummary,
- [LOOKUP_KEYS.HARVESTER]: HarvesterSummary,
- [LOOKUP_KEYS.TEAM]: TeamSummary,
- [LOOKUP_KEYS.LAB]: LabSummary,
- [LOOKUP_KEYS.UNIT]: UnitSummary,
- [LOOKUP_KEYS.COLUMN_FAMILY]: ColumnSummary,
- [LOOKUP_KEYS.PATH]: PathSummary,
- [LOOKUP_KEYS.FILE]: FileSummary,
- [LOOKUP_KEYS.CYCLER_TEST]: CyclerTestSummary,
- [LOOKUP_KEYS.EXPERIMENT]: ExperimentSummary,
+ [LOOKUP_KEYS.ArbitraryFile]: ArbitraryFileSummary,
+ [LOOKUP_KEYS.AdditionalStorage]: AdditionalStorageSummary,
+ [LOOKUP_KEYS.Harvester]: HarvesterSummary,
+ [LOOKUP_KEYS.Team]: TeamSummary,
+ [LOOKUP_KEYS.Lab]: LabSummary,
+ [LOOKUP_KEYS.Unit]: UnitSummary,
+ [LOOKUP_KEYS.ColumnFamily]: ColumnSummary,
+ [LOOKUP_KEYS.Path]: PathSummary,
+ [LOOKUP_KEYS.File]: FileSummary,
+ [LOOKUP_KEYS.CyclerTest]: CyclerTestSummary,
+ [LOOKUP_KEYS.Experiment]: ExperimentSummary,
} as const
/**
@@ -71,6 +71,10 @@ export default function CardSummary({
return
}
+ console.warn(
+ `Using generic fallback summary for ${lookupKey}. This will be deprecated soon.`,
+ )
+
const is_family_child = (child_key: LookupKey, family_key: LookupKey) => {
if (!get_is_family(family_key)) return false
if (!get_has_family(child_key)) return false
diff --git a/src/Components/card/summaries/FileSummary.tsx b/src/Components/card/summaries/FileSummary.tsx
index 549d24a..ee91be6 100644
--- a/src/Components/card/summaries/FileSummary.tsx
+++ b/src/Components/card/summaries/FileSummary.tsx
@@ -81,7 +81,7 @@ function MappingQuickSelect({
mappings.find((m) => m.url === file.mapping)?.url ?? '',
)
const { useUpdateQuery } = useFetchResource()
- const updateQuery = useUpdateQuery(LOOKUP_KEYS.FILE, {
+ const updateQuery = useUpdateQuery(LOOKUP_KEYS.File, {
after_cache: () => {
setStatus(
Mapping updated successfully,
@@ -202,7 +202,7 @@ function FileStatus({
file.permissions.write && (