diff --git a/apps/nar-v3/src/auth.js b/apps/nar-v3/src/auth.js index 4c3265b..f1c18b4 100644 --- a/apps/nar-v3/src/auth.js +++ b/apps/nar-v3/src/auth.js @@ -2,7 +2,7 @@ import Keycloak from "keycloak-js"; // We start by configuring the Keycloak javascript client // It needs to know your app id in order to authenticate users for it -const keycloak = Keycloak({ +const keycloak = new Keycloak({ url: "https://iam.ebrains.eu/auth", realm: "hbp", clientId: "neural-activity-resource", @@ -53,7 +53,6 @@ function checkAuth(main) { } if (isAuthenticated) { console.log("...which is authenticated, starting business logic..."); - sessionStorage.setItem("token", keycloak.token); return main(keycloak); } } diff --git a/apps/nar-v3/src/components/Navigation.jsx b/apps/nar-v3/src/components/Navigation.jsx index 4c52fc4..a31c683 100644 --- a/apps/nar-v3/src/components/Navigation.jsx +++ b/apps/nar-v3/src/components/Navigation.jsx @@ -4,12 +4,12 @@ import { Link as RouterLink, useLocation } from "react-router-dom"; function getBreadcrumb(item) { if (item.path) { return ( - + {item.name} ); } else { - return {item.name}; + return {item.name}; } } diff --git a/apps/nar-v3/src/datastore.js b/apps/nar-v3/src/datastore.js index 4b938ff..1758767 100644 --- a/apps/nar-v3/src/datastore.js +++ b/apps/nar-v3/src/datastore.js @@ -5,132 +5,115 @@ function isEmpty(obj) { return Object.keys(obj).length === 0; } -// function isAlmostEmpty(obj) { -// return Object.keys(obj).length <= 1; -// } +let cache = { + "datasets summary": {}, + "datasets detail": {}, + "datasets techniques": {}, + "patch clamp recordings summary": {}, + "patch clamp recordings detail": {}, +}; +//cache["datasets detail"][uuidFromUri(examplePatchClampData["@id"])] = examplePatchClampData; +//cache["datasets detail"]["example"] = examplePatchClampData; -// function byDate(obj1, obj2) { -// // most recent first -// if (obj1.stages[0].start_time < obj2.stages[0].start_time) { -// return 1; -// } -// if (obj1.stages[0].start_time > obj2.stages[0].start_time) { -// return -1; -// } -// return 0; -// } - -class DataStore { - constructor(baseUrl) { - this.baseUrl = baseUrl; - this.cache = { - "datasets summary": {}, - "datasets detail": {}, - "datasets techniques": {}, - "patch clamp recordings summary": {}, - "patch clamp recordings detail": {}, +function buildRequestConfig(auth, method = "GET", body = {}) { + if (auth.token) { + let config = { + headers: { + Authorization: "Bearer " + auth.token, + "Content-Type": "application/json", + }, + method: method, }; - //this.cache["datasets detail"][uuidFromUri(examplePatchClampData["@id"])] = examplePatchClampData; - //this.cache["datasets detail"]["example"] = examplePatchClampData; - } - - buildRequestConfig(method = "GET", body = {}) { - let token = sessionStorage.getItem("token"); - if (token) { - let config = { - headers: { - Authorization: "Bearer " + token, - "Content-Type": "application/json", - }, - method: method, - }; - if (body) { - config.body = body; - } - return config; - } else { - return null; + if (body) { + config.body = body; } + return config; + } else { + return null; } +} - async queryKG(kgQuery, searchParams) { - const searchParamStr = new URLSearchParams(searchParams).toString(); - let url = `${this.baseUrl}/queries?${searchParamStr}`; - const config = this.buildRequestConfig("POST", JSON.stringify(kgQuery)); - if (config) { - const response = await fetch(url, config); +async function queryKG(kgQuery, searchParams, auth) { + const searchParamStr = new URLSearchParams(searchParams).toString(); + let url = `${kgUrl}/queries?${searchParamStr}`; + const config = buildRequestConfig(auth, "POST", JSON.stringify(kgQuery)); + if (config) { + const response = await fetch(url, config); + if (response.ok) { const result = await response.json(); return result; } else { return null; } + } else { + return null; } +} - async getKGItem(cacheLabel, kgQuery, instanceId, stage = kgDefaultStage) { - console.log("getKGItem " + cacheLabel + instanceId); - if (!this.cache[cacheLabel][instanceId]) { - const searchParams = { stage: stage, instanceId: instanceId }; - const result = await this.queryKG(kgQuery, searchParams); - if (result) { - const items = result.data; - this.cache[cacheLabel][instanceId] = items[0]; - } - } - return this.cache[cacheLabel][instanceId]; - } - - async getKGData( - cacheLabel, - kgQuery, - searchFilters, - stage = kgDefaultStage, - size = 1000, - from = 0 - ) { - console.log("getKGData " + cacheLabel); - if (isEmpty(this.cache[cacheLabel])) { - // if the cache is empty we need to fill it - console.log(this.baseUrl); - let searchParams = { - returnTotalResults: true, - stage: stage, - size: size, - from: from, - }; - if (searchFilters) { - searchParams = { ...searchParams, searchFilters }; - } - const result = await this.queryKG(kgQuery, searchParams); - if (result) { - const items = result.data; - for (const index in items) { - this.cache[cacheLabel][items[index].id] = items[index]; - } - } +async function getKGItem(cacheLabel, kgQuery, instanceId, auth, stage = kgDefaultStage) { + console.log("getKGItem " + cacheLabel + instanceId); + if (!cache[cacheLabel][instanceId]) { + const searchParams = { stage: stage, instanceId: instanceId }; + const result = await queryKG(kgQuery, searchParams, auth); + if (result) { + const items = result.data; + cache[cacheLabel][instanceId] = items[0]; } - const itemArray = Object.values(this.cache[cacheLabel]); - console.log(itemArray); - return itemArray; } + return cache[cacheLabel][instanceId]; +} - async count(kgQuery, searchFilters, stage = kgDefaultStage) { +async function getKGData( + cacheLabel, + kgQuery, + auth, + searchFilters, + stage = kgDefaultStage, + size = 1000, + from = 0 +) { + console.log("getKGData " + cacheLabel); + if (isEmpty(cache[cacheLabel])) { + // if the cache is empty we need to fill it + console.log(kgUrl); let searchParams = { returnTotalResults: true, stage: stage, - size: 1, - from: 0, + size: size, + from: from, }; if (searchFilters) { searchParams = { ...searchParams, searchFilters }; } - const result = await this.queryKG(kgQuery, searchParams); + const result = await queryKG(kgQuery, searchParams, auth); if (result) { - return result.total; - } else { - return 0; + const items = result.data; + for (const index in items) { + cache[cacheLabel][items[index].id] = items[index]; + } } } + const itemArray = Object.values(cache[cacheLabel]); + console.log(itemArray); + return itemArray; +} + +async function count(kgQuery, auth, searchFilters, stage = kgDefaultStage) { + let searchParams = { + returnTotalResults: true, + stage: stage, + size: 1, + from: 0, + }; + if (searchFilters) { + searchParams = { ...searchParams, searchFilters }; + } + const result = await queryKG(kgQuery, searchParams, auth); + if (result) { + return result.total; + } else { + return 0; + } } -const datastore = new DataStore(kgUrl); -export { datastore }; +export { queryKG, getKGItem, getKGData, count }; diff --git a/apps/nar-v3/src/main.jsx b/apps/nar-v3/src/main.jsx index 6ed3adc..b41ae03 100644 --- a/apps/nar-v3/src/main.jsx +++ b/apps/nar-v3/src/main.jsx @@ -6,13 +6,13 @@ import { Avatar, CssBaseline, AppBar, Link, Toolbar, Typography, Container } fro import { createTheme, ThemeProvider } from "@mui/material/styles"; import { green } from "@mui/material/colors"; -import Home, { loader as statsLoader } from "./routes/home"; +import Home, { getLoader as statsLoader } from "./routes/home"; import ErrorPage from "./error-page"; import initAuth from "./auth"; -import Datasets, { loader as datasetsLoader } from "./routes/datasets"; -import Dataset, { loader as datasetLoader } from "./routes/dataset"; -import PatchClampIndex, { loader as patchClampIndexLoader } from "./routes/patchClampRecordings"; -import PatchClamp, { loader as patchClampLoader } from "./routes/patchClampRecording"; +import Datasets, { getLoader as datasetsLoader } from "./routes/datasets"; +import Dataset, { getLoader as datasetLoader } from "./routes/dataset"; +import PatchClampIndex, { getLoader as patchClampIndexLoader } from "./routes/patchClampRecordings"; +import PatchClamp, { getLoader as patchClampLoader } from "./routes/patchClampRecording"; const theme = createTheme({ typography: { @@ -36,37 +36,38 @@ const theme = createTheme({ }, }); -const router = createBrowserRouter([ - { - path: "/", - element: , - errorElement: , - loader: statsLoader, - }, - { - path: "datasets/", - element: , - loader: datasetsLoader, - }, - { - path: "datasets/:datasetId", - element: , - loader: datasetLoader, - }, - { - path: "patch-clamp/", - element: , - loader: patchClampIndexLoader, - }, - { - path: "patch-clamp/:expId", - element: , - loader: patchClampLoader, - }, -]); - +function getRouter(auth) { + return createBrowserRouter([ + { + path: "/", + element: , + errorElement: , + loader: statsLoader(auth), + }, + { + path: "datasets/", + element: , + loader: datasetsLoader(auth), + }, + { + path: "datasets/:datasetId", + element: , + loader: datasetLoader(auth), + }, + { + path: "patch-clamp/", + element: , + loader: patchClampIndexLoader(auth), + }, + { + path: "patch-clamp/:expId", + element: , + loader: patchClampLoader(auth), + }, + ]); +} -function renderApp() { +function renderApp(auth) { ReactDOM.createRoot(document.getElementById("root")).render( @@ -78,13 +79,15 @@ function renderApp() { - EBRAINS: Neural Activity Resource (alpha) + + EBRAINS: Neural Activity Resource (alpha) +
- +
diff --git a/apps/nar-v3/src/routes/dataset.jsx b/apps/nar-v3/src/routes/dataset.jsx index 80423cb..1ba3edc 100644 --- a/apps/nar-v3/src/routes/dataset.jsx +++ b/apps/nar-v3/src/routes/dataset.jsx @@ -1,7 +1,7 @@ import React from "react"; import { Await, defer, useLoaderData } from "react-router-dom"; -import { datastore } from "../datastore"; +import { getKGItem } from "../datastore"; import { uuidFromUri } from "../utility.js"; import Navigation from "../components/Navigation"; import DatasetCard from "../components/DatasetCard"; @@ -9,23 +9,27 @@ import ProgressIndicator from "../components/ProgressIndicator"; import { basicDatasetQuery, patchClampDatasetQuery, techniquesQuery } from "./queryLibrary"; -export async function loader({ params }) { - const techniques = await datastore.getKGItem( - "datasets techniques", - techniquesQuery, - params.datasetId - ); - console.log(techniques.technique); - let query = basicDatasetQuery; - if (techniques.technique && techniques.technique.includes("whole cell patch clamp")) { - console.log("Using patch clamp dataset query"); - query = patchClampDatasetQuery; - } else { - console.log("Using basic dataset query"); - } - const datasetPromise = datastore.getKGItem("datasets detail", query, params.datasetId); - console.log(datasetPromise); - return defer({ dataset: datasetPromise }); +export function getLoader(auth) { + const loader = async ({ params }) => { + const techniques = await getKGItem( + "datasets techniques", + techniquesQuery, + params.datasetId, + auth + ); + console.log(techniques.technique); + let query = basicDatasetQuery; + if (techniques.technique && techniques.technique.includes("whole cell patch clamp")) { + console.log("Using patch clamp dataset query"); + query = patchClampDatasetQuery; + } else { + console.log("Using basic dataset query"); + } + const datasetPromise = getKGItem("datasets detail", query, params.datasetId, auth); + console.log(datasetPromise); + return defer({ dataset: datasetPromise }); + }; + return loader; } function Dataset() { diff --git a/apps/nar-v3/src/routes/datasets.jsx b/apps/nar-v3/src/routes/datasets.jsx index 5eaa795..1e9e3b2 100644 --- a/apps/nar-v3/src/routes/datasets.jsx +++ b/apps/nar-v3/src/routes/datasets.jsx @@ -1,16 +1,19 @@ import React from "react"; import { Await, defer, useLoaderData } from "react-router-dom"; -import { datastore } from "../datastore"; +import { getKGData } from "../datastore"; import Navigation from "../components/Navigation"; import DatasetList from "../components/DatasetList"; import ProgressIndicator from "../components/ProgressIndicator"; import { ephysDatasetsQuery } from "./queryLibrary"; -export async function loader() { - const datasetsPromise = datastore.getKGData("datasets summary", ephysDatasetsQuery); - console.log(datasetsPromise); - return defer({ datasets: datasetsPromise }); +export function getLoader(auth) { + const loader = async () => { + const datasetsPromise = getKGData("datasets summary", ephysDatasetsQuery, auth); + console.log(datasetsPromise); + return defer({ datasets: datasetsPromise }); + }; + return loader; } function Datasets() { diff --git a/apps/nar-v3/src/routes/home.jsx b/apps/nar-v3/src/routes/home.jsx index 97a64af..3d3bc43 100644 --- a/apps/nar-v3/src/routes/home.jsx +++ b/apps/nar-v3/src/routes/home.jsx @@ -10,24 +10,27 @@ import Typography from "@mui/material/Typography"; import Container from "@mui/material/Container"; import Chip from "@mui/material/Chip"; -import { datastore } from "../datastore"; +import { count } from "../datastore"; import { query as patchClampRecordingsQuery } from "./patchClampRecordings"; import { ephysDatasetsQuery } from "./queryLibrary"; import ProgressIndicator from "../components/ProgressIndicator"; -export async function loader() { - const statisticsPromise = Promise.all([ - datastore.count(patchClampRecordingsQuery), - datastore.count(ephysDatasetsQuery), - ]); - console.log(statisticsPromise); - return defer({ counts: statisticsPromise }); +export function getLoader(auth) { + const loader = async () => { + const statisticsPromise = Promise.all([ + count(patchClampRecordingsQuery, auth), + count(ephysDatasetsQuery, auth), + ]); + console.log(statisticsPromise); + return defer({ counts: statisticsPromise }); + }; + return loader; } // function getModalityCount(modality) { // console.log(modality); // if (modality === "patchclamp") { -// return 1; //await datastore.count(patchClampRecordingsQuery); +// return 1; //await count(patchClampRecordingsQuery); // } // return 0; // } diff --git a/apps/nar-v3/src/routes/patchClampRecording.jsx b/apps/nar-v3/src/routes/patchClampRecording.jsx index f725e40..43557bc 100644 --- a/apps/nar-v3/src/routes/patchClampRecording.jsx +++ b/apps/nar-v3/src/routes/patchClampRecording.jsx @@ -7,7 +7,7 @@ import { linkProperty as L, reverseLinkProperty as R, } from "../queries"; -import { datastore } from "../datastore"; +import { getKGItem } from "../datastore"; import { uuidFromUri } from "../utility.js"; import Navigation from "../components/Navigation"; import PatchClampRecordingCard from "../components/PatchClampRecordingCard"; @@ -68,14 +68,18 @@ const query = buildKGQuery("core/TissueSample", [ //console.log(query); -export async function loader({ params }) { - const tissueSamplePromise = datastore.getKGItem( - "patch clamp recordings detail", - query, - params.expId - ); - console.log(tissueSamplePromise); - return defer({ tissueSample: tissueSamplePromise }); +export function getLoader(auth) { + const loader = async ({ params }) => { + const tissueSamplePromise = getKGItem( + "patch clamp recordings detail", + query, + params.expId, + auth + ); + console.log(tissueSamplePromise); + return defer({ tissueSample: tissueSamplePromise }); + }; + return loader; } function PatchClamp() { diff --git a/apps/nar-v3/src/routes/patchClampRecordings.jsx b/apps/nar-v3/src/routes/patchClampRecordings.jsx index c1b3678..bff9b4a 100644 --- a/apps/nar-v3/src/routes/patchClampRecordings.jsx +++ b/apps/nar-v3/src/routes/patchClampRecordings.jsx @@ -7,7 +7,7 @@ import { linkProperty as L, reverseLinkProperty as R, } from "../queries"; -import { datastore } from "../datastore"; +import { getKGData } from "../datastore"; import Navigation from "../components/Navigation"; import PatchClampRecordingList from "../components/PatchClampRecordingList"; import ProgressIndicator from "../components/ProgressIndicator"; @@ -26,11 +26,14 @@ export const query = buildKGQuery("core/TissueSample", [ ), ]); -export async function loader() { - const tissueSamplesPromise = datastore.getKGData("patch clamp recordings summary", query); +export function getLoader(auth) { + const loader = async () => { + const tissueSamplesPromise = getKGData("patch clamp recordings summary", query, auth); - console.log(tissueSamplesPromise); - return defer({ tissueSamples: tissueSamplesPromise }); + console.log(tissueSamplesPromise); + return defer({ tissueSamples: tissueSamplesPromise }); + }; + return loader; } function PatchClampIndex() { diff --git a/apps/nar-v3/src/routes/queryLibrary.js b/apps/nar-v3/src/routes/queryLibrary.js index 047e8a1..dd7b3cb 100644 --- a/apps/nar-v3/src/routes/queryLibrary.js +++ b/apps/nar-v3/src/routes/queryLibrary.js @@ -200,6 +200,7 @@ const patchClampDatasetQuery = buildKGQuery("core/DatasetVersion", [ S("@type"), R("cell", "studiedState", [ S("internalIdentifier"), + // todo: handle the case where anatomicalLocation is a ParcellationEntityVersion L("anatomicalLocation", controlledTermProperties, MULTIPLE), L("origin", controlledTermProperties), ]),