Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added an adaptation layer to simplify dashboard operation logic #25

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions apps/web/src/adapter/core/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useMemo } from 'react';
import { useProvider } from './useProvider';
import { useMsRequest } from './useMsRequest';
import { useMsWebsocket } from './useMsWebsocket';
import type { PluginProps } from '../types';

export const useConnect = <
T extends Record<string, any>,
E extends (...params: any[]) => any,
R extends (...params: any[]) => any,
>({
viewProps,
adapter,
effect,
reducer,
}: {
viewProps: PluginProps<T>;
adapter: Adapter;
effect: E;
reducer: R;
}) => {
const store = useProvider<T>({ viewProps, adapter });
const { config, configJson } = viewProps || {};

const result = useMsRequest({
viewProps,
store,
effect,
reducer,
});

useMsWebsocket<T>({
store,
viewProps,
runAsync: result?.runAsync,
});

return useMemo(() => {
return {
...result,
config,
configJson,
};
}, [config, configJson, result]);
};
29 changes: 29 additions & 0 deletions apps/web/src/adapter/core/useMsRequest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useRequest } from 'ahooks';
import type { InjectStore, PluginProps } from '../types';

export const useMsRequest = <
T extends Record<string, any>,
E extends (...params: any[]) => any,
R extends (...params: any[]) => any,
>({
viewProps,
store,
effect,
reducer,
}: {
viewProps: PluginProps<T>;
store: InjectStore;
effect: E;
reducer: R;
}) => {
const { refreshDeps, searchParams } = store || {};

const request = async (): Promise<ReturnType<R> | void> => {
if (!effect) return;
const data = await effect(searchParams);

if (!reducer) return;
return reducer(data, viewProps);
};
return useRequest(request, { refreshDeps });
};
40 changes: 40 additions & 0 deletions apps/web/src/adapter/core/useMsWebsocket.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useEffect, useMemo } from 'react';
import type { Result } from 'ahooks/lib/useRequest/src/types';
import ws, { getExChangeTopic } from '@/services/ws';
import type { InjectStore, PluginProps } from '../types';

export const useMsWebsocket = <T extends Record<string, any>>({
store,
viewProps,
runAsync,
}: {
store: InjectStore;
viewProps: PluginProps<T>;
runAsync: Result<any, []>['runAsync'];
}) => {
const { enableWs } = store || {};
const { config, configJson } = viewProps;
const { entity } = config || {};
const { isPreview } = configJson || {};

/** 获取订阅主题列表 */
const topics = useMemo(() => {
const entityList = Array.isArray(entity) ? entity : [entity];

return (entityList || [])
.map(entity => {
const entityKey = entity?.rawData?.entityKey?.toString();
if (!entityKey) return;

return getExChangeTopic(entityKey);
})
.filter(Boolean) as string[];
}, [entity]);

// 订阅 WS 主题
useEffect(() => {
if (!topics?.length || !enableWs || isPreview) return;

return ws.subscribe(topics, runAsync);
}, [topics, enableWs, isPreview]);
};
77 changes: 77 additions & 0 deletions apps/web/src/adapter/core/useProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useMemo } from 'react';
import type { Adapter, InjectStore, PluginProps, SearchKeys } from '../types';

const DEFAULT_SEARCH_KEYS = ['entity', 'time', 'metrics'];
export const useProvider = <T extends Record<string, any>>({
viewProps,
adapter,
searchKeys = DEFAULT_SEARCH_KEYS,
}: {
viewProps: PluginProps<T>;
adapter: Adapter;
searchKeys?: SearchKeys;
}): InjectStore => {
const { config, configJson } = viewProps;
const { websocket } = adapter || {};

/** 是否启用WebSocket */
const enableWs = useMemo(() => {
// 默认启用
if (websocket === void 0) return true;

return websocket?.toString() === 'true';
}, [websocket]);

/** 获取查询参数 */
const { searchParams, configParams } = useMemo(() => {
const searchParams: Record<string, any> = {};
const configParams: Record<string, any> = {};

// 没有值时,直接返回
if (!searchKeys?.length) return { searchParams, configParams };

// 获取查询参数
const { configProps } = configJson || {};
(configProps || []).forEach(configProp => {
const { components } = configProp || {};

(components || []).forEach(component => {
const { key } = component || {};

for (const k of searchKeys) {
if (typeof k === 'string') {
if (k === key) {
searchParams[key] = config[key];
configParams[key] = config[key];
break;
}
} else {
const { key: kKey, value: kValue } = k || {};

if (kKey === key) {
searchParams[kValue] = config[key];
configParams[key] = config[key];
break;
}
}
}
});
});

return { searchParams, configParams };
}, [config, configJson, searchKeys]);

/** 收集更新依赖 */
const refreshDeps = useMemo(() => {
return Object.keys(configParams).map(key => {
if (Reflect.has(config, key)) return config[key];
return void 0;
});
}, [config, configParams]);

return {
enableWs,
refreshDeps,
searchParams,
};
};
32 changes: 32 additions & 0 deletions apps/web/src/adapter/effects/getAggregateHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { awaitWrap, entityAPI, getResponseData, isRequestSuccess } from '@/services/http';
import type { InjectStore } from '@/adapter/types';

export const useFetch = () => {
const request = async (entity: EntityOptionType, metrics: DataAggregateType, time: number) => {
const now = Date.now();
const { value: entityId } = entity || {};

const [error, resp] = await awaitWrap(
entityAPI.getAggregateHistory({
entity_id: entityId,
aggregate_type: metrics,
start_timestamp: now - time,
end_timestamp: now,
}),
);
if (error || !isRequestSuccess(resp)) return;

return getResponseData(resp);
};

const run = (searchParams: InjectStore['searchParams']) => {
const { entity, time, metrics } = searchParams || {};
const entityList = Array.isArray(entity) ? entity : [entity].filter(Boolean);

return Promise.all(entityList.map(entity => request(entity, metrics, time)));
};

return {
run,
};
};
28 changes: 28 additions & 0 deletions apps/web/src/adapter/effects/getEntityStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { InjectStore } from '@/adapter/types';
import { awaitWrap, entityAPI, getResponseData, isRequestSuccess } from '@/services/http';

export const useFetch = () => {
const request = async (entity: EntityOptionType) => {
const { value: entityId } = entity || {};

const [error, resp] = await awaitWrap(
entityAPI.getEntityStatus({
id: entityId,
}),
);
if (error || !isRequestSuccess(resp)) return;

return getResponseData(resp);
};

const run = (searchParams: InjectStore['searchParams']) => {
const { entity } = searchParams || {};
const entityList = Array.isArray(entity) ? entity : [entity].filter(Boolean);

return Promise.all(entityList.map(entity => request(entity)));
};

return {
run,
};
};
33 changes: 33 additions & 0 deletions apps/web/src/adapter/effects/getHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { awaitWrap, entityAPI, getResponseData, isRequestSuccess } from '@/services/http';
import type { InjectStore } from '@/adapter/types';

export const useFetch = () => {
const request = async (entity: EntityOptionType, time: number) => {
const now = Date.now();
const { value } = entity || {};

const [error, resp] = await awaitWrap(
entityAPI.getHistory({
entity_id: value,
start_timestamp: now - time,
end_timestamp: now,
page_number: 1,
page_size: 999,
}),
);
if (error || !isRequestSuccess(resp)) return;

return getResponseData(resp);
};

const run = (searchParams: InjectStore['searchParams']) => {
const { entity, time } = searchParams || {};
const entityList = Array.isArray(entity) ? entity : [entity].filter(Boolean);

return Promise.all(entityList.map(entity => request(entity, time)));
};

return {
run,
};
};
10 changes: 10 additions & 0 deletions apps/web/src/adapter/helper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const getRange = (entity: EntityOptionType) => {
const { rawData } = entity || {};
const { entityValueAttribute } = rawData || {};
const { min, max, min_length: minLength, max_length: maxLength } = entityValueAttribute || {};

return {
min: min ?? minLength,
max: max ?? maxLength,
};
};
1 change: 1 addition & 0 deletions apps/web/src/adapter/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type * from './types';
17 changes: 17 additions & 0 deletions apps/web/src/adapter/models/getAggregateHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useConnect } from '../core';
import { useFetch } from '../effects/getAggregateHistory';
import { useReducer } from '../reducers/getAggregateHistory';
import type { PluginProps } from '../types';

export const useModel = <T extends Record<string, any>>({
viewProps,
adapter,
}: {
viewProps: PluginProps<T>;
adapter: Adapter;
}) => {
const { run: effect } = useFetch();
const { run: reducer } = useReducer();

return useConnect({ viewProps, adapter, effect, reducer });
};
17 changes: 17 additions & 0 deletions apps/web/src/adapter/models/getCountAggregate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useConnect } from '../core';
import { useFetch } from '../effects/getAggregateHistory';
import { useReducer } from '../reducers/getCountAggregate';
import type { PluginProps } from '../types';

export const useModel = <T extends Record<string, any>>({
viewProps,
adapter,
}: {
viewProps: PluginProps<T>;
adapter: Adapter;
}) => {
const { run: effect } = useFetch();
const { run: reducer } = useReducer();

return useConnect({ viewProps, adapter, effect, reducer });
};
17 changes: 17 additions & 0 deletions apps/web/src/adapter/models/getEntityStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useConnect } from '../core';
import { useFetch } from '../effects/getEntityStatus';
import { useReducer } from '../reducers/getEntityStatus';
import type { PluginProps } from '../types';

export const useModel = <T extends Record<string, any>>({
viewProps,
adapter,
}: {
viewProps: PluginProps<T>;
adapter: Adapter;
}) => {
const { run: effect } = useFetch();
const { run: reducer } = useReducer();

return useConnect({ viewProps, adapter, effect, reducer });
};
17 changes: 17 additions & 0 deletions apps/web/src/adapter/models/getHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useConnect } from '../core';
import { useFetch } from '../effects/getHistory';
import { useReducer } from '../reducers/getHistory';
import type { PluginProps } from '../types';

export const useModel = <T extends Record<string, any>>({
viewProps,
adapter,
}: {
viewProps: PluginProps<T>;
adapter: Adapter;
}) => {
const { run: effect } = useFetch();
const { run: reducer } = useReducer();

return useConnect({ viewProps, adapter, effect, reducer });
};
11 changes: 11 additions & 0 deletions apps/web/src/adapter/models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useModel as useHistoryModel } from './getHistory';
import { useModel as useAggregateModel } from './getAggregateHistory';
import { useModel as useEntityStatusModel } from './getEntityStatus';
import { useModel as useCountAggregateModel } from './getCountAggregate';

export default {
getHistory: useHistoryModel,
getAggregateHistory: useAggregateModel,
getEntityStatus: useEntityStatusModel,
getCountAggregate: useCountAggregateModel,
};
Loading