Skip to content

Commit

Permalink
refactored api
Browse files Browse the repository at this point in the history
refactored auto start and stop services
fixed auto login
  • Loading branch information
seeden committed Aug 6, 2023
1 parent d306424 commit 166ea55
Show file tree
Hide file tree
Showing 18 changed files with 290 additions and 295 deletions.
2 changes: 1 addition & 1 deletion packages/api-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
},
"dependencies": {
"@babel/runtime": "7.16.7",
"@chia-network/api": "1.0.0",
"bignumber.js": "9.0.2",
"core-js": "3.20.3",
"debug": "4.3.3",
Expand All @@ -38,7 +39,6 @@
"@babel/plugin-transform-runtime": "7.16.8",
"@babel/preset-env": "7.16.8",
"@babel/preset-typescript": "7.16.7",
"@chia-network/api": "1.0.0",
"@reduxjs/toolkit": "1.7.1",
"@rollup/plugin-babel": "5.3.0",
"@rollup/plugin-commonjs": "21.0.1",
Expand Down
1 change: 1 addition & 0 deletions packages/api-react/src/@types/ServiceConstructor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export default interface ServiceConstructor {
new (...args: any[]): any;
isClient?: boolean;
isDaemon?: boolean;
}
11 changes: 9 additions & 2 deletions packages/api-react/src/chiaLazyBaseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@ async function getInstance<TService extends ServiceConstructor>(
} else {
const client = await getInstance(Client, api);

if (service.isDaemon) {
return client.daemon as InstanceType<TService>;
}

const Service = service;
const serviceInstance = new Service(client);
instances.set(service, serviceInstance);
// client is async operation, so we need to check if instance is already set
if (!instances.has(Service)) {
instances.set(service, new Service(client));
}
}
}

Expand Down Expand Up @@ -64,6 +70,7 @@ const chiaLazyBaseQuery = async <
try {
const instance = await getInstance(service, api);
const arrayArgs = Array.isArray(args) ? args : [args];

const data = (await instance[command](...arrayArgs)) as TResult;

return {
Expand Down
101 changes: 34 additions & 67 deletions packages/api-react/src/hooks/useService.ts
Original file line number Diff line number Diff line change
@@ -1,133 +1,100 @@
import { ServiceNameValue } from '@chia-network/api';
import { useEffect, useState, useMemo, useCallback } from 'react';
import { useEffect, useState, useCallback } from 'react';

import { useClientStartServiceMutation } from '../services/client';
import { useStopServiceMutation, useRunningServicesQuery } from '../services/daemon';
import { useStartServiceMutation, useStopServiceMutation } from '../services/daemon';

export type ServiceState = 'starting' | 'running' | 'stopping' | 'stopped';
export enum ServiceState {
STARTING = 'starting',
RUNNING = 'running',
STOPPING = 'stopping',
STOPPED = 'stopped',
}

type Options = {
keepState?: ServiceState;
disabled?: boolean;
disableWait?: boolean; // Don't wait for ping when starting service
};

export default function useService(
service: ServiceNameValue,
options: Options = {}
): {
isLoading: boolean;
isProcessing: boolean;
isRunning: boolean;
state: ServiceState;
start: () => Promise<void>;
stop: () => Promise<void>;
error?: Error | unknown;
error: Error | undefined;
service: ServiceNameValue;
} {
const { keepState, disabled = false, disableWait = false } = options;
const { keepState, disabled = false } = options;

const [error, setError] = useState<Error | undefined>();
const [state, setState] = useState<ServiceState>(ServiceState.STOPPED);

const [isStarting, setIsStarting] = useState<boolean>(false);
const [isStopping, setIsStopping] = useState<boolean>(false);
const [startService] = useClientStartServiceMutation();
const [startService] = useStartServiceMutation();
const [stopService] = useStopServiceMutation();
const [latestIsProcessing, setLatestIsProcessing] = useState<boolean>(false);

// isRunning is not working when stopService is called (backend issue)
const {
data: runningServices,
isLoading,
refetch,
error,
} = useRunningServicesQuery(undefined, {
pollingInterval: latestIsProcessing ? 1000 : 10_000,
skip: disabled,
selectFromResult: (state) => ({
data: state.data,
error: state.error,
isLoading: state.isLoading,
}),
});

const isRunning = useMemo(
() => !!(runningServices && runningServices?.includes(service)),
[runningServices, service]
);

const isProcessing = isStarting || isStopping;

useEffect(() => {
setLatestIsProcessing(isProcessing);
}, [isProcessing]);

let state: ServiceState = 'stopped';
if (isRunning) {
state = 'running';
} else if (isStarting) {
state = 'starting';
} else if (isStopping) {
state = 'stopping';
}
const isLoading = [ServiceState.STARTING, ServiceState.STOPPING].includes(state);

const handleStart = useCallback(async () => {
if (isProcessing) {
if (isLoading || disabled || state === ServiceState.RUNNING) {
return;
}

try {
setIsStarting(true);
setState(ServiceState.STARTING);

await startService({
service,
disableWait,
}).unwrap();

refetch();
setState(ServiceState.RUNNING);
} catch (e) {
setState(ServiceState.STOPPED);
setError(e as Error);
console.error(e);
} finally {
setIsStarting(false);
}
}, [disableWait, isProcessing, refetch, service, startService]);
}, [isLoading, service, startService, disabled, state]);

const handleStop = useCallback(async () => {
if (isProcessing) {
if (isLoading || disabled || state === ServiceState.STOPPED) {
return;
}

try {
setIsStopping(true);
setState(ServiceState.STOPPING);
await stopService({
service,
}).unwrap();

refetch();
setState(ServiceState.STOPPED);
} catch (e) {
setState(ServiceState.RUNNING);
setError(e as Error);
console.error(e);
} finally {
setIsStopping(false);
}
}, [isProcessing, refetch, service, stopService]);
}, [isLoading, service, stopService, disabled, state]);

useEffect(() => {
if (disabled || !runningServices) {
if (disabled || isLoading) {
return;
}

if (keepState === 'running' && keepState !== state && !isProcessing && isRunning === false) {
if (keepState === 'running' && keepState !== state) {
handleStart();
} else if (keepState === 'stopped' && keepState !== state && !isProcessing && isRunning === true) {
} else if (keepState === 'stopped' && keepState !== state) {
handleStop();
}
}, [runningServices, keepState, service, state, isProcessing, disabled, isRunning, handleStart, handleStop]);
}, [keepState, service, state, isLoading, disabled, handleStart, handleStop]);

return {
state,
isLoading,
isProcessing,
isRunning,
error,
isRunning: state === ServiceState.RUNNING,
start: handleStart,
stop: handleStop,
service,
error,
};
}
11 changes: 5 additions & 6 deletions packages/api-react/src/hooks/useServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ type Options = {
function getServiceKeepState(service: ServiceNameValue, options: Options): ServiceState | undefined {
const { keepRunning, keepStopped } = options;
if (keepRunning && keepRunning.includes(service)) {
return 'running';
return ServiceState.RUNNING;
}
if (keepStopped && keepStopped.includes(service)) {
return 'stopped';
return ServiceState.STOPPED;
}
return undefined;
}
Expand Down Expand Up @@ -68,7 +68,6 @@ export default function useMonitorServices(

const datalayerServerState = useService(ServiceName.DATALAYER_SERVER, {
...getServiceOptions(ServiceName.DATALAYER_SERVER, services, options),
disableWait: true,
});

const states = [
Expand All @@ -87,9 +86,9 @@ export default function useMonitorServices(
const isLoading = !!states.find((state) => state.isLoading);
const error = states.find((state) => state.error)?.error;

const starting = states.filter((state) => state.state === 'starting').map((state) => state.service);
const stopping = states.filter((state) => state.state === 'stopping').map((state) => state.service);
const running = states.filter((state) => state.state === 'running').map((state) => state.service);
const starting = states.filter((state) => state.state === ServiceState.STARTING).map((state) => state.service);
const stopping = states.filter((state) => state.state === ServiceState.STOPPING).map((state) => state.service);
const running = states.filter((state) => state.state === ServiceState.RUNNING).map((state) => state.service);

const objectToReturn = {
isLoading,
Expand Down
7 changes: 1 addition & 6 deletions packages/api-react/src/services/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,7 @@ export const clientApi = apiWithTag.injectEndpoints({
}
},
}),

clientStartService: mutation(build, Client, 'startService'),

clientStopService: mutation(build, Client, 'stopService'),
}),
});

export const { useCloseMutation, useGetStateQuery, useClientStartServiceMutation, useClientStopServiceMutation } =
clientApi;
export const { useCloseMutation, useGetStateQuery } = clientApi;
8 changes: 1 addition & 7 deletions packages/api-react/src/services/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,7 @@ export const daemonApi = apiWithTag.injectEndpoints({
command: 'onKeyringStatusChanged',
service: Daemon,
onUpdate: (draft, data) => {
// empty base array
draft.splice(0);

const { status, ...rest } = data;

// assign new items
Object.assign(draft, rest);
Object.assign(draft, data);
},
},
]),
Expand Down
2 changes: 0 additions & 2 deletions packages/api-react/src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ export const {

useCloseMutation,
useGetStateQuery,
useClientStartServiceMutation,
useClientStopServiceMutation,
} = client;

// daemon hooks
Expand Down
Loading

0 comments on commit 166ea55

Please sign in to comment.