diff --git a/package-lock.json b/package-lock.json index 50766bfd..e5a9c527 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bento_public", - "version": "0.19.0", + "version": "0.19.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bento_public", - "version": "0.19.0", + "version": "0.19.1", "license": "LGPL-3.0-only", "dependencies": { "@ant-design/icons": "^5.3.1", diff --git a/package.json b/package.json index 0cc08eb1..fd301d76 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bento_public", - "version": "0.19.0", + "version": "0.19.1", "description": "A publicly accessible portal for clinical datasets, where users are able to see high-level statistics of the data available through predefined variables of interest and search the data using limited variables at a time. This portal allows users to gain a generic understanding of the data available (secure and firewalled) without the need to access it directly. Initially, this portal facilitates the search in English language only, but the French language will be added at a later time.", "main": "index.js", "scripts": { diff --git a/src/js/components/Beacon/BeaconQueryUi.tsx b/src/js/components/Beacon/BeaconQueryUi.tsx index 282e09cc..580593e5 100644 --- a/src/js/components/Beacon/BeaconQueryUi.tsx +++ b/src/js/components/Beacon/BeaconQueryUi.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState, ReactNode } from 'react'; -import { useAppSelector, useAppDispatch, useTranslationDefault, useBeaconWithAuthIfAllowed } from '@/hooks'; +import { useAppSelector, useAppDispatch, useTranslationDefault, useQueryWithAuthIfAllowed } from '@/hooks'; import { Button, Card, Col, Form, Row, Space, Tooltip, Typography } from 'antd'; import { InfoCircleOutlined } from '@ant-design/icons'; import { useIsAuthenticated } from 'bento-auth-js'; @@ -91,7 +91,7 @@ const BeaconQueryUi = () => { }, [isFetchingBeaconConfig, isAuthenticated]); // Disables max query param if user is authenticated and authorized - useBeaconWithAuthIfAllowed(); + useQueryWithAuthIfAllowed(); // beacon request handling diff --git a/src/js/components/Search/MakeQueryOption.tsx b/src/js/components/Search/MakeQueryOption.tsx index 63d46b97..f6fe4e24 100644 --- a/src/js/components/Search/MakeQueryOption.tsx +++ b/src/js/components/Search/MakeQueryOption.tsx @@ -29,6 +29,11 @@ const MakeQueryOption = ({ queryField }: MakeQueryOptionProps) => { dispatch(makeGetKatsuPublic()); }; + // TODO: allow disabling max query parameters for authenticated and authorized users when Katsu has AuthZ + // useQueryWithAuthIfAllowed() + // const maxQueryParametersRequired = useAppSelector((state) => state.config.maxQueryParametersRequired); + // const hasMaxFilters = maxQueryParametersRequired && queryParamCount >= maxQueryParameters; + // const disabled = isChecked ? false : hasMaxFilters; const disabled = isChecked ? false : queryParamCount >= maxQueryParameters; return ( diff --git a/src/js/components/Search/Search.tsx b/src/js/components/Search/Search.tsx index 1ab07e58..f1456343 100644 --- a/src/js/components/Search/Search.tsx +++ b/src/js/components/Search/Search.tsx @@ -32,6 +32,10 @@ const RoutedSearch: React.FC = () => { attemptedFetch, } = useAppSelector((state) => state.query); + // TODO: allow disabling max query parameters for authenticated and authorized users when Katsu has AuthZ + // const maxQueryParametersRequired = useAppSelector((state) => state.config.maxQueryParametersRequired); + // const allowedQueryParamsCount = maxQueryParametersRequired ? maxQueryParameters : queryParamCount; + const searchFields = useMemo( () => searchSections.flatMap(({ fields }) => fields.map((field) => ({ id: field.id, options: field.options }))), [searchSections] @@ -45,6 +49,7 @@ const RoutedSearch: React.FC = () => { const queryParamArray = Array.from(query.entries()).map(([key, value]) => ({ key, value })); + // TODO: to disable max query parameters, slice with allowedQueryParamsCount instead const validQueryParamArray = queryParamArray .filter(({ key, value }) => validateQueryParam(key, value)) .slice(0, maxQueryParameters); diff --git a/src/js/constants/configConstants.ts b/src/js/constants/configConstants.ts index 6a37f09b..2d3cb501 100644 --- a/src/js/constants/configConstants.ts +++ b/src/js/constants/configConstants.ts @@ -3,6 +3,7 @@ import { PORTAL_URL } from '@/config'; export const MAX_CHARTS = 3; export const katsuPublicOverviewUrl = `${PORTAL_URL}/api/metadata/api/public_overview`; +export const katsuPublicRulesUrl = `${PORTAL_URL}/api/metadata/api/public_rules`; export const searchFieldsUrl = `${PORTAL_URL}/api/metadata/api/public_search_fields`; export const katsuUrl = `${PORTAL_URL}/api/metadata/api/public`; export const provenanceUrl = `${PORTAL_URL}/api/metadata/api/public_dataset`; diff --git a/src/js/features/config/config.store.ts b/src/js/features/config/config.store.ts index bee1afbb..6fdbd6eb 100644 --- a/src/js/features/config/config.store.ts +++ b/src/js/features/config/config.store.ts @@ -1,17 +1,18 @@ import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; import axios from 'axios'; -import { katsuPublicOverviewUrl } from '@/constants/configConstants'; +import { katsuPublicRulesUrl } from '@/constants/configConstants'; import { printAPIError } from '@/utils/error.util'; import { ServiceInfoStore, ServicesResponse } from '@/types/services'; import { RootState } from '@/store'; import { PUBLIC_URL } from '@/config'; -import { KatsuPublicOverviewResponse } from '@/types/configResponse'; +import { DiscoveryRules } from '@/types/configResponse'; -export const makeGetConfigRequest = createAsyncThunk( +export const makeGetConfigRequest = createAsyncThunk( 'config/getConfigData', (_, { rejectWithValue }) => + // TODO: should be project/dataset scoped with url params axios - .get(katsuPublicOverviewUrl) + .get(katsuPublicRulesUrl) .then((res) => res.data) .catch(printAPIError(rejectWithValue)) ); @@ -57,13 +58,10 @@ const configStore = createSlice({ builder.addCase(makeGetConfigRequest.pending, (state) => { state.isFetchingConfig = true; }); - builder.addCase( - makeGetConfigRequest.fulfilled, - (state, { payload }: PayloadAction) => { - state.maxQueryParameters = payload.max_query_parameters; - state.isFetchingConfig = false; - } - ); + builder.addCase(makeGetConfigRequest.fulfilled, (state, { payload }: PayloadAction) => { + state.maxQueryParameters = payload.max_query_parameters; + state.isFetchingConfig = false; + }); builder.addCase(makeGetConfigRequest.rejected, (state) => { state.isFetchingConfig = false; }); diff --git a/src/js/hooks.ts b/src/js/hooks.ts index 761bf64b..1eba40be 100644 --- a/src/js/hooks.ts +++ b/src/js/hooks.ts @@ -41,12 +41,12 @@ export const useHasResourcePermissionWrapper = (resource: Resource, permission: }; }; -export const useBeaconWithAuthIfAllowed = () => { +export const useQueryWithAuthIfAllowed = () => { const dispatch = useAppDispatch(); const { hasPermission } = useHasResourcePermissionWrapper(RESOURCE_EVERYTHING, queryData); useEffect(() => { if (hasPermission) { - console.log('Beacon: user authorized for no max query parameters.'); + console.log('Beacon | Search: user authorized for no max query parameters.'); dispatch(setMaxQueryParametersRequired(false)); } }, [dispatch, hasPermission]); diff --git a/src/js/index.tsx b/src/js/index.tsx index 62d7d0d7..21a556cc 100644 --- a/src/js/index.tsx +++ b/src/js/index.tsx @@ -31,7 +31,7 @@ import { BentoAuthContextProvider, nop, } from 'bento-auth-js'; -import { useBeaconWithAuthIfAllowed } from '@/hooks'; +import { useQueryWithAuthIfAllowed } from '@/hooks'; // Store and configuration imports import { store } from './store'; @@ -97,7 +97,7 @@ const App = () => { useSessionWorkerTokenRefresh(sessionWorker, createSessionWorker, nop); - useBeaconWithAuthIfAllowed(); + useQueryWithAuthIfAllowed(); return ( diff --git a/src/js/types/configResponse.ts b/src/js/types/configResponse.ts index 0f1672ab..8e559938 100644 --- a/src/js/types/configResponse.ts +++ b/src/js/types/configResponse.ts @@ -1,7 +1,10 @@ export interface KatsuPublicOverviewResponse { - count_threshold: number; counts: object; fields: object; layout: []; +} + +export interface DiscoveryRules { max_query_parameters: number; + count_threshold: number; }