diff --git a/src/common/api/narrativeService.ts b/src/common/api/narrativeService.ts index f4186636..ae3af3cc 100644 --- a/src/common/api/narrativeService.ts +++ b/src/common/api/narrativeService.ts @@ -1,8 +1,11 @@ /* narrativeService */ +import { dynamicService } from './serviceWizardApi'; import { baseApi } from './index'; -import { jsonRpcService } from './utils/serviceHelpers'; +// import { jsonRpcService } from './utils/serviceHelpers'; -const narrativeService = jsonRpcService({ +// console.log({ serviceWizardApi }); // eslint-disable-line no-console + +const narrativeService = dynamicService({ name: 'NarrativeService', release: 'release', }); diff --git a/src/common/api/serviceWizardApi.ts b/src/common/api/serviceWizardApi.ts index 54ba8fb3..3b2a3f54 100644 --- a/src/common/api/serviceWizardApi.ts +++ b/src/common/api/serviceWizardApi.ts @@ -5,6 +5,10 @@ import { jsonRpcService } from './utils/serviceHelpers'; const serviceWizard = jsonRpcService({ url: 'services/service_wizard' }); +const dynamicService = jsonRpcService; + +export { dynamicService }; + export interface ServiceStatus { git_commit_hash: string; status: string; @@ -41,10 +45,6 @@ export const serviceWizardApi = baseApi.injectEndpoints({ }), }); -const setConsumed = () => - setConsumedService('serviceWizardApi', serviceWizardApi); - -setConsumed(); +setConsumedService('serviceWizardApi', serviceWizardApi); -export { setConsumed }; export const { getServiceStatus } = serviceWizardApi.endpoints; diff --git a/src/common/api/utils/common.ts b/src/common/api/utils/common.ts new file mode 100644 index 00000000..860baef2 --- /dev/null +++ b/src/common/api/utils/common.ts @@ -0,0 +1,67 @@ +/* api/utils/common */ +import { FetchBaseQueryError } from '@reduxjs/toolkit/query/react'; +/* +JSONRPC Specification details +JSON-RPC 1.0 - https://www.jsonrpc.org/specification_v1 +JSON-RPC 1.1 wd - https://jsonrpc.org/historical/json-rpc-1-1-wd.html +JSON-RPC 2.0 - https://www.jsonrpc.org/specification +- id + - 2.0 allows id to be string, number (with no fractional part) or null + - 1.1 allows id to be "any JSON type" +- version + - a string in both JSONRPC 1.1 and 2.0. +*/ +// KBase mostly uses strings, or string serializable values, so we can too. +type JsonRpcError = { + version: '1.1'; + id: string; + error: { + name: string; + code: number; + message: string; + }; +}; + +export type KBaseBaseQueryError = + | FetchBaseQueryError + | { + status: 'JSONRPC_ERROR'; + data: JsonRpcError; + }; + +export const isJsonRpcError = (obj: unknown): obj is JsonRpcError => { + if ( + typeof obj === 'object' && + obj !== null && + ['version', 'error', 'id'].every((k) => k in obj) + ) { + const { version, error } = obj as { version: string; error: unknown }; + const versionsSupported = new Set(['1.1', '2.0']); + if (!versionsSupported.has(version)) return false; + if ( + typeof error === 'object' && + error !== null && + ['name', 'code', 'message'].every((k) => k in error) + ) { + return true; + } + } + return false; +}; + +/** + * Type predicate to narrow an unknown error to `FetchBaseQueryError` + */ +export function isFetchBaseQueryError( + error: unknown +): error is FetchBaseQueryError { + return typeof error === 'object' && error !== null && 'status' in error; +} + +export const isKBaseBaseQueryError = ( + error: unknown +): error is KBaseBaseQueryError => { + const fbq = isFetchBaseQueryError(error); + const condition = fbq && isJsonRpcError(error.data); + return condition; +}; diff --git a/src/common/api/utils/kbaseBaseQuery.ts b/src/common/api/utils/kbaseBaseQuery.ts index eecc7f9b..206ca8d8 100644 --- a/src/common/api/utils/kbaseBaseQuery.ts +++ b/src/common/api/utils/kbaseBaseQuery.ts @@ -4,10 +4,10 @@ import { BaseQueryFn, FetchArgs, fetchBaseQuery, - FetchBaseQueryError, } from '@reduxjs/toolkit/query/react'; import { RootState } from '../../../app/store'; import { serviceWizardApi } from '../serviceWizardApi'; +import { KBaseBaseQueryError, isJsonRpcError } from './common'; export interface DynamicService { name: string; @@ -39,72 +39,6 @@ export const isDynamic = ( return (service as StaticService).url === undefined; }; -/* -JSONRPC Specification details -JSON-RPC 1.0 - https://www.jsonrpc.org/specification_v1 -JSON-RPC 1.1 wd - https://jsonrpc.org/historical/json-rpc-1-1-wd.html -JSON-RPC 2.0 - https://www.jsonrpc.org/specification -- id - - 2.0 allows id to be string, number (with no fractional part) or null - - 1.1 allows id to be "any JSON type" -- version - - a string in both JSONRPC 1.1 and 2.0. -*/ -// KBase mostly uses strings, or string serializable values, so we can too. -type JsonRpcError = { - version: '1.1'; - id: string; - error: { - name: string; - code: number; - message: string; - }; -}; - -export const isJsonRpcError = (obj: unknown): obj is JsonRpcError => { - if ( - typeof obj === 'object' && - obj !== null && - ['version', 'error', 'id'].every((k) => k in obj) - ) { - const { version, error } = obj as { version: string; error: unknown }; - const versionsSupported = new Set(['1.1', '2.0']); - if (!versionsSupported.has(version)) return false; - if ( - typeof error === 'object' && - error !== null && - ['name', 'code', 'message'].every((k) => k in error) - ) { - return true; - } - } - return false; -}; - -export type KBaseBaseQueryError = - | FetchBaseQueryError - | { - status: 'JSONRPC_ERROR'; - data: JsonRpcError; - }; - -/** - * Type predicate to narrow an unknown error to `FetchBaseQueryError` - */ -export function isFetchBaseQueryError( - error: unknown -): error is FetchBaseQueryError { - return typeof error === 'object' && error !== null && 'status' in error; -} - -export const isKBaseBaseQueryError = ( - error: unknown -): error is KBaseBaseQueryError => { - const fbq = isFetchBaseQueryError(error); - const condition = fbq && isJsonRpcError(error.data); - return condition; -}; - // These helpers let us avoid circular dependencies when using an API endpoint within kbaseBaseQuery const consumedServices: { serviceWizardApi?: typeof serviceWizardApi } = {}; export const setConsumedService = ( diff --git a/src/common/api/utils/parseError.test.ts b/src/common/api/utils/parseError.test.ts index fe2cf83c..cb7a7687 100644 --- a/src/common/api/utils/parseError.test.ts +++ b/src/common/api/utils/parseError.test.ts @@ -1,6 +1,6 @@ import { SerializedError } from '@reduxjs/toolkit'; import { FetchBaseQueryError } from '@reduxjs/toolkit/dist/query'; -import { KBaseBaseQueryError } from './kbaseBaseQuery'; +import { KBaseBaseQueryError } from './common'; import { parseError } from './parseError'; describe('parseError', () => { diff --git a/src/common/api/utils/parseError.ts b/src/common/api/utils/parseError.ts index f88e9e18..ad7a994b 100644 --- a/src/common/api/utils/parseError.ts +++ b/src/common/api/utils/parseError.ts @@ -1,5 +1,5 @@ import { SerializedError } from '@reduxjs/toolkit'; -import { KBaseBaseQueryError } from './kbaseBaseQuery'; +import { KBaseBaseQueryError } from './common'; export function parseError(error: KBaseBaseQueryError | SerializedError): { error: KBaseBaseQueryError | SerializedError; diff --git a/src/features/navigator/NarrativeControl/Copy.tsx b/src/features/navigator/NarrativeControl/Copy.tsx index 22bb4456..b80d0306 100644 --- a/src/features/navigator/NarrativeControl/Copy.tsx +++ b/src/features/navigator/NarrativeControl/Copy.tsx @@ -9,6 +9,7 @@ import { import { Input } from '../../../common/components/Input'; import { useAppDispatch } from '../../../common/hooks'; import { TODOAddLoadingState } from '../common'; +import { useNarrativeServiceStatus } from '../hooks'; import { copyNarrative } from '../navigatorSlice'; import { ControlProps } from './common'; @@ -22,6 +23,7 @@ export interface CopyProps extends ControlProps { export const Copy: FC = ({ narrativeDoc, modalClose, version }) => { const dispatch = useAppDispatch(); + useNarrativeServiceStatus(); const { formState, getValues, register } = useForm({ defaultValues: { narrativeCopyName: `${narrativeDoc.narrative_title} - Copy`, diff --git a/src/features/navigator/NarrativeControl/Delete.tsx b/src/features/navigator/NarrativeControl/Delete.tsx index e7330958..9689901a 100644 --- a/src/features/navigator/NarrativeControl/Delete.tsx +++ b/src/features/navigator/NarrativeControl/Delete.tsx @@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom'; import toast from 'react-hot-toast'; import { Button } from '../../../common/components'; import { useAppDispatch, useAppSelector } from '../../../common/hooks'; -import { isKBaseBaseQueryError } from '../../../common/api/utils/kbaseBaseQuery'; +import { isKBaseBaseQueryError } from '../../../common/api/utils/common'; import { parseError } from '../../../common/api/utils/parseError'; import { deleteWorkspace } from '../../../common/api/workspaceApi'; import { diff --git a/src/features/navigator/Navigator.tsx b/src/features/navigator/Navigator.tsx index 876d45e1..6ce6a1ac 100644 --- a/src/features/navigator/Navigator.tsx +++ b/src/features/navigator/Navigator.tsx @@ -25,7 +25,7 @@ import { normalizeVersion, searchParams, } from './common'; -import { useNarratives, useServices } from './hooks'; +import { useNarratives } from './hooks'; import { loading, navigatorSelected, @@ -210,7 +210,6 @@ const Navigator: FC = () => { term: search, username, }); - useServices(); const items = useAppSelector(narrativeDocs); const narrativeSelected = getNarrativeSelected({ id, obj, verRaw, items }); // hooks that update state diff --git a/src/features/navigator/hooks.ts b/src/features/navigator/hooks.ts index 271a095e..de7be0c5 100644 --- a/src/features/navigator/hooks.ts +++ b/src/features/navigator/hooks.ts @@ -2,14 +2,6 @@ import { useEffect, useMemo } from 'react'; import { useAppDispatch, useAppSelector } from '../../common/hooks'; import { getUsers } from '../../common/api/authService'; import { getNarratives, SearchParams } from '../../common/api/searchApi'; -import { setConsumed } from '../../common/api/serviceWizardApi'; -// import { setConsumedService } from '../../common/api/utils/kbaseBaseQuery'; -/* -import { - ServiceStatus, - ServiceWizardParams, -} from '../../common/api/serviceWizardApi'; -*/ import { getStatus } from '../../common/api/narrativeService'; import { getwsNarrative } from '../../common/api/workspaceApi'; import { Cell } from '../../common/types/NarrativeDoc'; @@ -150,31 +142,7 @@ export const useNarratives = (params: getNarrativesParams) => { }, [dispatch, narrativesPrevious, searchAPIQuery, searchAPIParams, syncd]); }; -export const useServices = () => { - /* - const dispatch = useAppDispatch(); - // const serviceState = useAppSelector(services); - const servicesRequired: ServiceWizardParams['getServiceStatus'][] = useMemo( - () => [ - { - module_name: 'NarrativeService', - version: 'release', - }, - ], - [] - ); - const servicesQuerys = servicesRequired.map((serviceVersion) => - getServiceStatus.useQuery(serviceVersion) - ); - const servicesAvailable: Record = {}; - servicesRequired.forEach((serviceVersion, ix) => { - const currQuery = servicesQuerys[ix]; - servicesAvailable[serviceVersion.module_name] = data; - //dispatch(setServices(servicesAvailable)); - }); - setConsumedService('serviceWizardApi', serviceWizardApi); - */ - setConsumed(); +export const useNarrativeServiceStatus = () => { const nsQuery = getStatus.useQuery(); useEffect(() => { if (nsQuery.isSuccess && nsQuery.data) {