Skip to content

Commit

Permalink
Refactor service wizard, add narrative service.
Browse files Browse the repository at this point in the history
  • Loading branch information
dakotablair committed Sep 13, 2023
1 parent 5f69af7 commit 3866202
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 89 deletions.
33 changes: 33 additions & 0 deletions src/common/api/narrativeService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* narrativeService */
import { dynamicService } from './serviceWizardApi';
import { baseApi } from './index';

const narrativeService = dynamicService({
name: 'NarrativeService',
release: 'release',
});

export interface NarrativeServiceParams {
getStatus: void;
}

interface NarrativeServiceResults {
getStatus: unknown;
}

export const narrativeServiceApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getStatus: builder.query<
NarrativeServiceResults['getStatus'],
NarrativeServiceParams['getStatus']
>({
query: () =>
narrativeService({
method: 'NarrativeService.status',
params: [],
}),
}),
}),
});

export const { getStatus } = narrativeServiceApi.endpoints;
42 changes: 24 additions & 18 deletions src/common/api/serviceWizardApi.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
/* serviceWizardApi */
import { baseApi } from './index';
import { setConsumedService } from './utils/kbaseBaseQuery';
import { jsonRpcService } from './utils/serviceHelpers';

const serviceWizard = jsonRpcService({ url: 'services/service_wizard' });

const dynamicService = jsonRpcService;

/* Use this for dynamic services to ensure serviceWizardApi is set. */
export { dynamicService };

interface ServiceStatus {
git_commit_hash: string;
status: string;
version: string;
hash: string;
release_tags: string[];
url: string;
module_name: string;
health: string;
up: number;
}

interface ServiceWizardParams {
serviceStatus: { module_name: string; version: string };
getServiceStatus: { module_name: string; version: string };
}

interface ServiceWizardResults {
serviceStatus: [
{
git_commit_hash: string;
status: string;
version: string;
hash: string;
release_tags: string[];
url: string;
module_name: string;
health: string;
up: number;
}
];
getServiceStatus: ServiceStatus[];
}

export const serviceWizardApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
serviceStatus: builder.query<
ServiceWizardResults['serviceStatus'],
ServiceWizardParams['serviceStatus']
getServiceStatus: builder.query<
ServiceWizardResults['getServiceStatus'],
ServiceWizardParams['getServiceStatus']
>({
query: ({ module_name, version }) =>
serviceWizard({
Expand All @@ -42,4 +48,4 @@ export const serviceWizardApi = baseApi.injectEndpoints({

setConsumedService('serviceWizardApi', serviceWizardApi);

export const { serviceStatus } = serviceWizardApi.endpoints;
export const { getServiceStatus } = serviceWizardApi.endpoints;
67 changes: 67 additions & 0 deletions src/common/api/utils/common.ts
Original file line number Diff line number Diff line change
@@ -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;
};
70 changes: 2 additions & 68 deletions src/common/api/utils/kbaseBaseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 = <T extends keyof typeof consumedServices>(
Expand Down Expand Up @@ -132,7 +66,7 @@ const getServiceUrl = async (
// get serviceWizardApi while avoiding circular imports
// (as serviceWizardApi imports this file)
const serviceStatusQuery =
getConsumedService('serviceWizardApi').endpoints.serviceStatus;
getConsumedService('serviceWizardApi').endpoints.getServiceStatus;

const wizardQueryArgs = {
module_name: name,
Expand Down
2 changes: 1 addition & 1 deletion src/common/api/utils/parseError.test.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/common/api/utils/parseError.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/features/navigator/NarrativeControl/Copy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -22,6 +23,7 @@ export interface CopyProps extends ControlProps {

export const Copy: FC<CopyProps> = ({ narrativeDoc, modalClose, version }) => {
const dispatch = useAppDispatch();
useNarrativeServiceStatus();
const { formState, getValues, register } = useForm<CopyValues>({
defaultValues: {
narrativeCopyName: `${narrativeDoc.narrative_title} - Copy`,
Expand Down
2 changes: 1 addition & 1 deletion src/features/navigator/NarrativeControl/Delete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 11 additions & 0 deletions src/features/navigator/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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 { getStatus } from '../../common/api/narrativeService';
import { getwsNarrative } from '../../common/api/workspaceApi';
import { Cell } from '../../common/types/NarrativeDoc';
import { authToken } from '../auth/authSlice';
Expand Down Expand Up @@ -140,6 +141,16 @@ export const useNarratives = (params: getNarrativesParams) => {
}, [dispatch, narrativesPrevious, searchAPIQuery, searchAPIParams, syncd]);
};

export const useNarrativeServiceStatus = () => {
const nsQuery = getStatus.useQuery();
useEffect(() => {
if (nsQuery.isSuccess && nsQuery.data) {
const data = nsQuery.data;
console.log({ data }); // eslint-disable-line no-console
}
}, [nsQuery.data, nsQuery.isSuccess]);
};

export const useUsers = (params: { users: string[] }) => {
const dispatch = useAppDispatch();
const token = useAppSelector(authToken);
Expand Down

0 comments on commit 3866202

Please sign in to comment.