diff --git a/config/default.yaml b/config/default.yaml index 73e50e5ba..259e78fdc 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -78,6 +78,8 @@ services: base: https://www.ebi.ac.uk/ols/api/select unified_astronomy_thesaurus: base: https://vocabs.ands.org.au/repository/api/lda/aas/the-unified-astronomy-thesaurus/current/concept.json + local_contexts: + base: https://sandbox.localcontextshub.org/api/v1 grouper: allUsers: GrouperAll diff --git a/public/static/locales/en/localcontexts.json b/public/static/locales/en/localcontexts.json new file mode 100644 index 000000000..71fee72bd --- /dev/null +++ b/public/static/locales/en/localcontexts.json @@ -0,0 +1,7 @@ +{ + "dataHasLocalContextsLabelsNotices": "This data has associated Local Contexts labels or notices.", + "localContextsAttrDefaultValue": "The \"{{projectTitle}}\" project has Labels and/or Notices applied through the Local Contexts Hub. For more information, refer to the project page: {{projectPage}}", + "localContextsHubError": "Error fetching Local Contexts Hub project information.", + "localContextsHubProjectURI": "Local Contexts Hub Project URI", + "projectMoreInfoWithLink": "For more information on the Local Contexts project for this data, please visit the project's page: {{projectTitle}}" +} diff --git a/src/components/data/listing/Listing.js b/src/components/data/listing/Listing.js index 646a5be7e..d32adeaa8 100644 --- a/src/components/data/listing/Listing.js +++ b/src/components/data/listing/Listing.js @@ -55,6 +55,13 @@ import { DEFAULTS_MAPPING_QUERY_KEY, } from "serviceFacades/instantlaunches"; +import { + getFilesystemMetadata, + FILESYSTEM_METADATA_QUERY_KEY, + LOCAL_CONTEXTS_QUERY_KEY, + getLocalContextsProject, +} from "serviceFacades/metadata"; + import { announce } from "components/announcer/CyVerseAnnouncer"; import { ERROR, INFO } from "components/announcer/AnnouncerConstants"; import buildID from "components/utils/DebugIDUtil"; @@ -64,7 +71,7 @@ import { useBagAddItems } from "serviceFacades/bags"; import { useQueryClient, useMutation, useQuery } from "react-query"; -import { Button, Typography, useTheme } from "@mui/material"; +import { Button, Stack, Typography, useTheme } from "@mui/material"; import DEDialog from "components/utils/DEDialog"; import PublicLinks from "../PublicLinks"; import constants from "../../../constants"; @@ -82,6 +89,12 @@ import { } from "serviceFacades/dashboard"; import { getUserQuota } from "common/resourceUsage"; +import LocalContextsLabelDisplay from "components/metadata/LocalContextsLabelDisplay"; +import { + LocalContextsAttrs, + parseProjectID, +} from "components/models/metadata/LocalContexts"; + function Listing(props) { const { baseId, @@ -102,7 +115,7 @@ function Listing(props) { } = props; const [config] = useConfig(); - const { t } = useTranslation(["data", "common"]); + const { t } = useTranslation(["data", "common", "localcontexts"]); const [userProfile] = useUserProfile(); const uploadTracker = useUploadTrackingState(); @@ -112,6 +125,7 @@ function Listing(props) { const [selected, setSelected] = useState([]); const [lastSelectIndex, setLastSelectIndex] = useState(-1); const [data, setData] = useState({ total: 0, listing: [] }); + const [localContextsProjectURI, setLocalContextsProjectURI] = useState(); const [detailsEnabled, setDetailsEnabled] = useState(false); const [detailsOpen, setDetailsOpen] = useState(false); const [advancedSearchOpen, setAdvancedSearchOpen] = useState(false); @@ -204,6 +218,8 @@ function Listing(props) { path, }); setData({ + id: respData?.id, + label: respData?.label, total: respData?.total, permission: respData?.permission, listing: [ @@ -217,6 +233,8 @@ function Listing(props) { })), ].map((i) => camelcaseit(i)), // camelcase the fields for each object, for consistency. }); + + setLocalContextsProjectURI(null); }, }); @@ -234,6 +252,42 @@ function Listing(props) { { exact: true } ); + useQuery({ + queryKey: [FILESYSTEM_METADATA_QUERY_KEY, { dataId: data?.id }], + queryFn: () => getFilesystemMetadata({ dataId: data?.id }), + enabled: !!data?.id, + onSuccess: (metadata) => { + const { avus } = metadata; + + const rightsURI = avus + ?.find((avu) => avu.attr === LocalContextsAttrs.LOCAL_CONTEXTS) + ?.avus?.find( + (childAVU) => + childAVU.attr === LocalContextsAttrs.RIGHTS_URI + )?.value; + + setLocalContextsProjectURI(rightsURI); + }, + onError: (error) => + console.log( + "Unable to fetch metadata for folder " + data?.label, + error + ), // fail silently. + }); + + const projectID = parseProjectID(localContextsProjectURI); + + const { data: localContextsProject } = useQuery({ + queryKey: [LOCAL_CONTEXTS_QUERY_KEY, projectID], + queryFn: () => getLocalContextsProject({ projectID }), + enabled: !!localContextsProjectURI, + onError: (error) => + console.log("Error fetching Local Contexts project.", { + localContextsProjectURI, + error, + }), // fail silently. + }); + const { isFetching: isFetchingDefaultsMapping } = useQuery({ queryKey: [DEFAULTS_MAPPING_QUERY_KEY], queryFn: getDefaultsMapping, @@ -324,6 +378,7 @@ function Listing(props) { useEffect(() => { setSelected([]); + setLocalContextsProjectURI(null); }, [path, rowsPerPage, orderBy, order, page, uploadsCompleted]); const viewUploadQueue = useCallback(() => { @@ -677,6 +732,21 @@ function Listing(props) { onMoveSelected={onMoveSelected} uploadsEnabled={uploadsEnabled} /> + {localContextsProjectURI && localContextsProject && ( + + + }} + /> + + + + )} {!isGridView && ( getFilesystemMetadata({ dataId: resourceId }), + enabled: resourceId && isFolder, + onSuccess: (metadata) => { + const { avus } = metadata; + + const rightsURI = avus + ?.find((avu) => avu.attr === LocalContextsAttrs.LOCAL_CONTEXTS) + ?.avus?.find( + (childAVU) => + childAVU.attr === LocalContextsAttrs.RIGHTS_URI + )?.value; + + if (rightsURI) { + setLocalContextsProjectURI(rightsURI); + } + }, + onError: (error) => + console.log( + "Unable to fetch metadata for folder " + resource.label, + error + ), // fail silently. + }); + + const projectID = parseProjectID(localContextsProjectURI); + + const { data: project } = useQuery({ + queryKey: [LOCAL_CONTEXTS_QUERY_KEY, projectID], + queryFn: () => getLocalContextsProject({ projectID }), + enabled: !!localContextsProjectURI, + onError: (error) => + console.log("Error fetching Local Contexts project.", { + localContextsProjectURI, + error, + }), // fail silently. + }); + + return ( + + + { + handlePathChange( + resource.path, + resource.type, + resourceId + ); + }} + > + {resource.label} + + {localContextsProjectURI && project && ( + + )} + + {instantLaunch && ( + + )} + + + ); +} + function SizeCell({ resource }) { return {formatFileSize(resource.fileSize)}; } @@ -161,7 +271,6 @@ function TableView(props) { const dataRecordFields = dataFields(t); const tableId = buildID(baseId, ids.LISTING_TABLE); const trashPath = useBaseTrashPath(); - const theme = useTheme(); const [displayColumns, setDisplayColumns] = useState( getLocalStorageCols(rowDotMenuVisibility, dataRecordFields) || @@ -310,7 +419,8 @@ function TableView(props) { listing.map((resource, index) => { const resourceName = resource.label; const resourceId = resource.id; - const resourcePath = resource.path; + const rowId = buildID(tableId, resourceName); + const isSelected = selected.indexOf(resourceId) !== -1; const isInvalid = @@ -337,7 +447,7 @@ function TableView(props) { role="checkbox" tabIndex={0} hover - id={buildID(tableId, resourceName)} + id={rowId} key={resourceId} selected={isSelected} aria-checked={isSelected} @@ -354,8 +464,7 @@ function TableView(props) { checked={isSelected} tabIndex={0} id={buildID( - tableId, - resourceName, + rowId, ids.checkbox )} onChange={(event) => @@ -380,42 +489,15 @@ function TableView(props) { type={resource.type} /> - - { - handlePathChange( - resourcePath, - resource.type, - resource.id - ); - }} - > - {resource.label} - - - {instantLaunch && ( - - )} - + {getColumnDetails(displayColumns).map( (column, index) => ( @@ -431,10 +513,7 @@ function TableView(props) { {rowDotMenuVisibility && ( { const [searchValue, setSearchValue] = React.useState(value); @@ -48,6 +49,7 @@ const FormSearchField = ({ return ( option[labelKey] === value[labelKey] } diff --git a/src/components/metadata/LocalContextsLabelDisplay.js b/src/components/metadata/LocalContextsLabelDisplay.js new file mode 100644 index 000000000..11a9daa4a --- /dev/null +++ b/src/components/metadata/LocalContextsLabelDisplay.js @@ -0,0 +1,124 @@ +import React from "react"; + +import { + Card, + CardContent, + CardMedia, + IconButton, + Stack, + Tooltip, + Typography, + useTheme, +} from "@mui/material"; + +import { Trans, useTranslation } from "i18n"; + +import DEDialog from "components/utils/DEDialog"; +import ExternalLink from "components/utils/ExternalLink"; + +const sizeToSpacing = (size, theme) => + size === "large" + ? theme.spacing(8) + : size === "small" + ? theme.spacing(3) + : theme.spacing(5); + +const LocalContextsLabel = ({ baseId, label, project, size = "medium" }) => { + const { t } = useTranslation("localcontexts"); + + const [dialogOpen, setDialogOpen] = React.useState(false); + const theme = useTheme(); + const labelURI = label.svg_url || label.img_url; + + return ( + <> + + setDialogOpen(true)}> + {label.name} + + + setDialogOpen(false)} + > + + + + + + {label.label_text || label.default_text} + + + + + + + , + ProjectLink: ( + + ), + }} + /> + + + + + + ); +}; + +const LocalContextsLabelDisplay = ({ project, size = "medium" }) => { + const labels = [ + ...(project?.notice || []), + ...(project?.bc_labels || []), + ...(project?.tk_labels || []), + ].filter((label) => label); + + return ( + + {labels.map((label) => ( + + ))} + + ); +}; + +export default LocalContextsLabelDisplay; diff --git a/src/components/metadata/form/MetadataFormToolbar.js b/src/components/metadata/form/MetadataFormToolbar.js index bfacf66f9..a7614c470 100644 --- a/src/components/metadata/form/MetadataFormToolbar.js +++ b/src/components/metadata/form/MetadataFormToolbar.js @@ -38,6 +38,7 @@ const MetadataFormToolbar = (props) => { const { baseId, title, + dirty, saveDisabled, showSave, onSave, @@ -59,7 +60,7 @@ const MetadataFormToolbar = (props) => { return ( - + { setShowImportConfirmationDialog(true) } targetResource={targetResource} + dirty={dirty} /> {irodsAVUs?.length > 0 && ( diff --git a/src/components/metadata/templates/LocalContextsField.js b/src/components/metadata/templates/LocalContextsField.js new file mode 100644 index 000000000..c5ddb4462 --- /dev/null +++ b/src/components/metadata/templates/LocalContextsField.js @@ -0,0 +1,241 @@ +/** + * This custom metadata template field displays only a Local Contexts Hub + * project URL field, and auto-populates DataCite metadata and child AVUs using + * the project ID parsed from the URL and the Local Contexts Hub API response. + * + * @author psarando + */ +import React from "react"; + +import { useQuery } from "react-query"; + +import { useTranslation } from "i18n"; + +import LocalContextsLabelDisplay from "../LocalContextsLabelDisplay"; + +import ErrorTypographyWithDialog from "components/error/ErrorTypographyWithDialog"; +import getFormError from "components/forms/getFormError"; +import { + LocalContextsAttrs, + parseProjectID, +} from "components/models/metadata/LocalContexts"; + +import { + LOCAL_CONTEXTS_QUERY_KEY, + getLocalContextsProject, +} from "serviceFacades/metadata"; + +import { Skeleton, TextField } from "@mui/material"; + +const findAVU = (avus, attr) => avus?.find((avu) => avu.attr === attr); + +const LocalContextsField = ({ + avu, + onUpdate, + helperText, + form: { setFieldValue, ...form }, + field: { value, onChange, ...field }, + ...props +}) => { + const { t } = useTranslation("localcontexts"); + + const [rightsURIAVU, setRightsURIAVU] = React.useState( + () => + findAVU(avu.avus, LocalContextsAttrs.RIGHTS_URI) || { + attr: LocalContextsAttrs.RIGHTS_URI, + value: "", + unit: "", + } + ); + + const [projectHubURI, setProjectHubURI] = React.useState( + rightsURIAVU.value + ); + + const [projectHubError, setProjectHubError] = React.useState(); + + const [rightsIDSchemeURIAVU] = React.useState(() => { + let rightsIDScheme = findAVU( + avu.avus, + LocalContextsAttrs.RIGHTS_ID_SCHEME + ); + + if ( + rightsIDScheme?.value !== LocalContextsAttrs.RIGHTS_ID_SCHEME_VALUE + ) { + rightsIDScheme = { + attr: LocalContextsAttrs.RIGHTS_ID_SCHEME, + value: LocalContextsAttrs.RIGHTS_ID_SCHEME_VALUE, + unit: "", + }; + } + + return rightsIDScheme; + }); + + const [schemeURIAVU] = React.useState(() => { + let schemeURI = findAVU(avu.avus, LocalContextsAttrs.SCHEME_URI); + + if (schemeURI?.value !== LocalContextsAttrs.SCHEME_URI_VALUE) { + schemeURI = { + attr: LocalContextsAttrs.SCHEME_URI, + value: LocalContextsAttrs.SCHEME_URI_VALUE, + unit: "", + }; + } + + return schemeURI; + }); + + const { touched, errors } = form; + const fieldError = getFormError(field.name, touched, errors); + const projectID = parseProjectID(projectHubURI); + + const { data: project, isFetching } = useQuery({ + queryKey: [LOCAL_CONTEXTS_QUERY_KEY, projectID], + queryFn: () => + getLocalContextsProject({ + projectID, + }), + enabled: projectHubURI && !fieldError, + onSuccess: (project) => { + let newValue = avu.value || ""; + + const projectLabels = [ + ...(project?.notice?.filter((label) => label?.name) || []), + ...(project?.bc_labels?.filter((label) => label?.name) || []), + ...(project?.tk_labels?.filter((label) => label?.name) || []), + ]; + + if (projectLabels.length === 1) { + newValue = + projectLabels[0].label_text || + projectLabels[0].default_text; + } else { + newValue = t("localContextsAttrDefaultValue", { + projectTitle: project?.title, + projectPage: project?.project_page, + }); + } + + const newLabels = projectLabels.map((label) => label.name); + + let newAVUs = avu.avus || []; + + const currentLabels = newAVUs + .filter( + (childAVU) => childAVU.attr === LocalContextsAttrs.RIGHTS_ID + ) + ?.map((childAVU) => childAVU.value); + + const missingLabels = newLabels.filter( + (label) => !currentLabels.includes(label) + ); + + const extraLabels = currentLabels.filter( + (label) => !newLabels.includes(label) + ); + + const requiresUpdate = + avu.value !== newValue || + missingLabels.length > 0 || + extraLabels.length > 0; + + if (requiresUpdate) { + newAVUs = newAVUs.filter( + (childAVU) => + childAVU.attr !== LocalContextsAttrs.RIGHTS_URI && + childAVU.attr !== LocalContextsAttrs.RIGHTS_ID_SCHEME && + childAVU.attr !== LocalContextsAttrs.SCHEME_URI && + (childAVU.attr !== LocalContextsAttrs.RIGHTS_ID || + newLabels.includes(childAVU.value)) + ); + + newAVUs = [ + ...newAVUs, + rightsURIAVU, + schemeURIAVU, + rightsIDSchemeURIAVU, + ...missingLabels.map((label) => ({ + attr: LocalContextsAttrs.RIGHTS_ID, + value: label, + unit: "", + })), + ]; + + onUpdate({ ...avu, value: newValue, avus: newAVUs }); + } + }, + onError: (error) => { + setProjectHubError( + + ); + }, + }); + + const updateProjectHubURI = (uri) => { + setProjectHubURI(uri); + setProjectHubError(null); + + let newAVUs = avu.avus || []; + + if (rightsURIAVU.value !== uri) { + const newRightsURIAVU = { + attr: LocalContextsAttrs.RIGHTS_URI, + value: uri, + unit: "", + }; + setRightsURIAVU(newRightsURIAVU); + + newAVUs = newAVUs.filter( + (childAVU) => + childAVU.attr !== LocalContextsAttrs.RIGHTS_URI && + childAVU.attr !== LocalContextsAttrs.RIGHTS_ID_SCHEME && + childAVU.attr !== LocalContextsAttrs.SCHEME_URI + ); + + newAVUs = [ + ...newAVUs, + newRightsURIAVU, + schemeURIAVU, + rightsIDSchemeURIAVU, + ]; + + onUpdate({ ...avu, avus: newAVUs }); + } + }; + + const errorMsg = fieldError || projectHubError; + + return ( + <> + updateProjectHubURI(event?.target?.value)} + /> + {isFetching ? ( + + + + ) : ( + + )} + + ); +}; + +export default LocalContextsField; diff --git a/src/components/metadata/templates/index.js b/src/components/metadata/templates/index.js index 8f9ca08a6..34e8174de 100644 --- a/src/components/metadata/templates/index.js +++ b/src/components/metadata/templates/index.js @@ -43,6 +43,8 @@ import FormCheckboxStringValue from "components/forms/FormCheckboxStringValue"; import AstroThesaurusSearchField from "./AstroThesaurusSearchField"; import OntologyLookupServiceSearchField from "./OntologyLookupServiceSearchField"; +import LocalContextsField from "./LocalContextsField"; + import SlideUpTransition from "../SlideUpTransition"; import { @@ -66,6 +68,7 @@ import { } from "@mui/icons-material"; import { Skeleton } from "@mui/material"; +import { LocalContextsAttrs } from "components/models/metadata/LocalContexts"; const useStyles = makeStyles(styles); @@ -238,7 +241,7 @@ const MetadataTemplateAttributeForm = (props) => { fieldProps = { ...fieldProps, searchAstroThesaurusTerms, - isDisabled: !writable, + readOnly: !writable, }; break; @@ -248,7 +251,7 @@ const MetadataTemplateAttributeForm = (props) => { ...fieldProps, searchOLSTerms, attribute, - isDisabled: !writable, + readOnly: !writable, }; break; @@ -270,6 +273,17 @@ const MetadataTemplateAttributeForm = (props) => { return null; } + const isLocalContextsAttr = + attribute.name === + LocalContextsAttrs.LOCAL_CONTEXTS; + if (isLocalContextsAttr) { + FieldComponent = LocalContextsField; + fieldProps.avu = avu; + fieldProps.onUpdate = (avu) => { + arrayHelpers.replace(index, avu); + }; + } + const avuFieldName = `${field}.avus[${index}]`; const avuError = getFormError( avuFieldName, @@ -298,7 +312,8 @@ const MetadataTemplateAttributeForm = (props) => { ); - const childAVUs = attribute.attributes && + const childAVUs = !isLocalContextsAttr && + attribute.attributes && attribute.attributes.length > 0 && ( { attributes .filter((attribute) => attribute.required) .forEach((attribute) => { - if ( - avus.filter((avu) => avu.attr === attribute.name) - .length < 1 - ) { + if (!avus.find((avu) => avu.attr === attribute.name)) { avus.push(newAVU(attribute)); } @@ -701,19 +713,34 @@ const MetadataTemplateView = (props) => { const avuArrayErrors = []; avus.forEach((avu, avuIndex) => { const avuErrors = {}; - const value = avu.value; const attrTemplate = attributeMap[avu.attr]; if (!attrTemplate) { return; } - if (attrTemplate.required && value === "") { + let attrType = attrTemplate.type; + let value = avu.value; + + const isLocalContexts = + avu.attr === LocalContextsAttrs.LOCAL_CONTEXTS; + if (isLocalContexts) { + const rightsURIAVU = avu.avus?.find( + (childAVU) => + childAVU.attr === LocalContextsAttrs.RIGHTS_URI + ); + + attrType = AttributeTypes.URL; + value = rightsURIAVU?.value; + } + + const isRequired = isLocalContexts || attrTemplate.required; + if (isRequired && value === "") { avuErrors.value = t("required"); avuErrors.error = true; avuArrayErrors[avuIndex] = avuErrors; } else if (value) { - switch (attrTemplate.type) { + switch (attrType) { case AttributeTypes.NUMBER: case AttributeTypes.INTEGER: const numVal = Number(value); @@ -795,12 +822,11 @@ const MetadataTemplateView = (props) => { const attrTemplate = attributeMap[avu.attr]; const isNumberAttr = - attrTemplate && - (attrTemplate.type === AttributeTypes.NUMBER || - attrTemplate.type === AttributeTypes.INTEGER); + attrTemplate?.type === AttributeTypes.NUMBER || + attrTemplate?.type === AttributeTypes.INTEGER; const isGroupingAttr = - attrTemplate && attrTemplate.type === AttributeTypes.GROUPING; + attrTemplate?.type === AttributeTypes.GROUPING; const hasChildAVUs = avu.avus && avu.avus.length > 0; diff --git a/src/components/models/metadata/LocalContexts.js b/src/components/models/metadata/LocalContexts.js new file mode 100644 index 000000000..487ea6192 --- /dev/null +++ b/src/components/models/metadata/LocalContexts.js @@ -0,0 +1,14 @@ +// Remove any trailing slash from the rightsURI +// and take the final part of the path as the project ID. +export const parseProjectID = (projectHubURI) => + projectHubURI?.replace(/\/$/, "").split("/").at(-1); + +export const LocalContextsAttrs = { + LOCAL_CONTEXTS: "LocalContexts", + RIGHTS_URI: "rightsURI", + RIGHTS_ID: "rightsIdentifier", + RIGHTS_ID_SCHEME: "rightsIdentifierScheme", + RIGHTS_ID_SCHEME_VALUE: "Local Contexts", + SCHEME_URI: "schemeURI", + SCHEME_URI_VALUE: "https://localcontexts.org", +}; diff --git a/src/pages/data/ds/[...pathItems].js b/src/pages/data/ds/[...pathItems].js index 3c220d55a..7f02db92a 100644 --- a/src/pages/data/ds/[...pathItems].js +++ b/src/pages/data/ds/[...pathItems].js @@ -205,6 +205,7 @@ export async function getServerSideProps(context) { title, ...(await serverSideTranslations(locale, [ "data", + "localcontexts", "metadata", "upload", "urlImport", diff --git a/src/server/api/metadata.js b/src/server/api/metadata.js index 955ce3984..a46d0516f 100644 --- a/src/server/api/metadata.js +++ b/src/server/api/metadata.js @@ -7,7 +7,7 @@ import express from "express"; import * as auth from "../auth"; -import { olsURL, uatURL } from "../configuration"; +import { localContextsURL, olsURL, uatURL } from "../configuration"; import logger from "../logging"; import { handler as externalHandler } from "./external"; @@ -119,5 +119,15 @@ export default function metadataRouter() { }) ); + logger.info("adding the GET /api/local-contexts/projects/:id handler"); + api.get("/local-contexts/projects/:id", async (req, res) => { + const localContextsHandler = externalHandler({ + method: "GET", + url: `${localContextsURL}/projects/${req.params.id}`, + }); + + return localContextsHandler(req, res); + }); + return api; } diff --git a/src/server/configuration.js b/src/server/configuration.js index 00ee1d6af..f372200f4 100644 --- a/src/server/configuration.js +++ b/src/server/configuration.js @@ -289,6 +289,14 @@ export const olsURL = config.get("services.ontology_lookup_service.base"); */ export const uatURL = config.get("services.unified_astronomy_thesaurus.base"); +/** + * The Local Contexts Hub API. + * https://github.com/localcontexts/localcontextshub/wiki/API-Documentation + * + * @type {string} + */ +export const localContextsURL = config.get("services.local_contexts.base"); + /** * The base URL for the User Portal's API */ diff --git a/src/serviceFacades/metadata.js b/src/serviceFacades/metadata.js index 27942a863..a3c8a0e7e 100644 --- a/src/serviceFacades/metadata.js +++ b/src/serviceFacades/metadata.js @@ -12,6 +12,7 @@ const FILESYSTEM_METADATA_TEMPLATE_LISTING_QUERY_KEY = "fetchFilesystemMetadataTemplateListingKey"; const SEARCH_OLS_QUERY_KEY = "searchOntologyLookupServiceKey"; const SEARCH_UAT_QUERY_KEY = "searchUnifiedAstronomyThesaurusKey"; +const LOCAL_CONTEXTS_QUERY_KEY = "localContextsKey"; function getFilesystemMetadataTemplateListing() { return callApi({ @@ -138,10 +139,20 @@ function searchUnifiedAstronomyThesaurus({ searchTerm, orderBy }) { .then((apiResponse) => apiResponse.data); } +function getLocalContextsProject({ projectID }) { + return axiosInstance + .request({ + url: `/api/local-contexts/projects/${projectID}`, + method: "GET", + }) + .then((apiResponse) => apiResponse.data); +} + export { FILESYSTEM_METADATA_QUERY_KEY, FILESYSTEM_METADATA_TEMPLATE_QUERY_KEY, FILESYSTEM_METADATA_TEMPLATE_LISTING_QUERY_KEY, + LOCAL_CONTEXTS_QUERY_KEY, SEARCH_OLS_QUERY_KEY, SEARCH_UAT_QUERY_KEY, getFilesystemMetadata, @@ -150,6 +161,7 @@ export { saveFilesystemMetadata, setFilesystemMetadata, applyBulkMetadataFromFile, + getLocalContextsProject, searchOntologyLookupService, searchUnifiedAstronomyThesaurus, };