mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-01 05:30:40 +00:00
feat: update datasource engine
This commit is contained in:
parent
4ef1097638
commit
cf3c7dbd35
@ -3,5 +3,6 @@ module.exports = {
|
||||
rules: {
|
||||
'@typescript-eslint/no-parameter-properties': 0,
|
||||
'no-param-reassign': 0,
|
||||
'max-len': 0,
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
printWidth: 120,
|
||||
};
|
||||
|
||||
@ -9,10 +9,8 @@ import {
|
||||
UrlParamsHandler,
|
||||
} from '@ali/lowcode-types';
|
||||
|
||||
class RuntimeDataSourceItem<
|
||||
TParams extends Record<string, unknown> = Record<string, unknown>,
|
||||
TResultData = unknown
|
||||
> implements IRuntimeDataSource<TParams, TResultData> {
|
||||
class RuntimeDataSourceItem<TParams extends Record<string, unknown> = Record<string, unknown>, TResultData = unknown>
|
||||
implements IRuntimeDataSource<TParams, TResultData> {
|
||||
private _data?: TResultData;
|
||||
|
||||
private _error?: Error;
|
||||
@ -21,9 +19,7 @@ class RuntimeDataSourceItem<
|
||||
|
||||
private _dataSourceConfig: RuntimeDataSourceConfig;
|
||||
|
||||
private _request:
|
||||
| RequestHandler<{ data: TResultData }>
|
||||
| UrlParamsHandler<TResultData>;
|
||||
private _request: RequestHandler<{ data: TResultData }> | UrlParamsHandler<TResultData>;
|
||||
|
||||
private _context: IDataSourceRuntimeContext;
|
||||
|
||||
@ -31,9 +27,7 @@ class RuntimeDataSourceItem<
|
||||
|
||||
constructor(
|
||||
dataSourceConfig: RuntimeDataSourceConfig,
|
||||
request:
|
||||
| RequestHandler<{ data: TResultData }>
|
||||
| UrlParamsHandler<TResultData>,
|
||||
request: RequestHandler<{ data: TResultData }> | UrlParamsHandler<TResultData>,
|
||||
context: IDataSourceRuntimeContext,
|
||||
) {
|
||||
this._dataSourceConfig = dataSourceConfig;
|
||||
@ -57,14 +51,14 @@ class RuntimeDataSourceItem<
|
||||
if (!this._dataSourceConfig) return;
|
||||
// 考虑没有绑定对应的 handler 的情况
|
||||
if (!this._request) {
|
||||
throw new Error(`no ${this._dataSourceConfig.type} handler provide`);
|
||||
this._error = new Error(`no ${this._dataSourceConfig.type} handler provide`);
|
||||
this._status = RuntimeDataSourceStatus.Error;
|
||||
throw this._error;
|
||||
}
|
||||
|
||||
// TODO: urlParams 有没有更好的处理方式
|
||||
if (this._dataSourceConfig.type === 'urlParams') {
|
||||
const response = await (this._request as UrlParamsHandler<TResultData>)(
|
||||
this._context,
|
||||
);
|
||||
const response = await (this._request as UrlParamsHandler<TResultData>)(this._context);
|
||||
this._context.setState({
|
||||
[this._dataSourceConfig.id]: response,
|
||||
});
|
||||
@ -100,10 +94,8 @@ class RuntimeDataSourceItem<
|
||||
|
||||
if (!shouldFetch) {
|
||||
this._status = RuntimeDataSourceStatus.Error;
|
||||
this._error = new Error(
|
||||
`the ${this._dataSourceConfig.id} request should not fetch, please check the condition`,
|
||||
);
|
||||
return;
|
||||
this._error = new Error(`the ${this._dataSourceConfig.id} request should not fetch, please check the condition`);
|
||||
throw this._error;
|
||||
}
|
||||
|
||||
let fetchOptions = this._options;
|
||||
|
||||
@ -1,10 +1,4 @@
|
||||
import {
|
||||
transformFunction,
|
||||
getRuntimeValueFromConfig,
|
||||
getRuntimeJsValue,
|
||||
buildOptions,
|
||||
buildShouldFetch,
|
||||
} from './../utils';
|
||||
import { getRuntimeValueFromConfig, getRuntimeJsValue, buildOptions, buildShouldFetch } from './../utils';
|
||||
// 将不同渠道给的 schema 转为 runtime 需要的类型
|
||||
|
||||
import { defaultDataHandler, defaultWillFetch } from '../helpers';
|
||||
@ -16,18 +10,11 @@ import {
|
||||
RuntimeDataSourceConfig,
|
||||
} from '@ali/lowcode-types';
|
||||
|
||||
const adapt2Runtime = (
|
||||
dataSource: InterpretDataSource,
|
||||
context: IDataSourceRuntimeContext,
|
||||
) => {
|
||||
const {
|
||||
list: interpretConfigList,
|
||||
dataHandler: interpretDataHandler,
|
||||
} = dataSource;
|
||||
const dataHandler: (dataMap?: DataSourceMap) => void =
|
||||
interpretDataHandler &&
|
||||
interpretDataHandler.compiled &&
|
||||
transformFunction(interpretDataHandler.compiled, context);
|
||||
const adapt2Runtime = (dataSource: InterpretDataSource, context: IDataSourceRuntimeContext) => {
|
||||
const { list: interpretConfigList, dataHandler: interpretDataHandler } = dataSource;
|
||||
const dataHandler: (dataMap?: DataSourceMap) => void = interpretDataHandler
|
||||
? getRuntimeJsValue(interpretDataHandler, context)
|
||||
: undefined;
|
||||
|
||||
// 为空判断
|
||||
if (!interpretConfigList || !interpretConfigList.length) {
|
||||
@ -36,29 +23,20 @@ const adapt2Runtime = (
|
||||
dataHandler,
|
||||
};
|
||||
}
|
||||
const list: RuntimeDataSourceConfig[] = interpretConfigList.map(
|
||||
(el: InterpretDataSourceConfig) => {
|
||||
return {
|
||||
id: el.id,
|
||||
isInit:
|
||||
getRuntimeValueFromConfig('boolean', el.isInit, context) || true, // 默认 true
|
||||
isSync:
|
||||
getRuntimeValueFromConfig('boolean', el.isSync, context) || false, // 默认 false
|
||||
type: el.type || 'fetch',
|
||||
willFetch: el.willFetch
|
||||
? getRuntimeJsValue(el.willFetch, context)
|
||||
: defaultWillFetch,
|
||||
shouldFetch: buildShouldFetch(el, context),
|
||||
dataHandler: el.dataHandler
|
||||
? getRuntimeJsValue(el.dataHandler, context)
|
||||
: defaultDataHandler,
|
||||
errorHandler: el.errorHandler
|
||||
? getRuntimeJsValue(el.errorHandler, context)
|
||||
: undefined,
|
||||
options: buildOptions(el, context),
|
||||
};
|
||||
},
|
||||
);
|
||||
const list: RuntimeDataSourceConfig[] = interpretConfigList.map((el: InterpretDataSourceConfig) => {
|
||||
return {
|
||||
id: el.id,
|
||||
isInit: getRuntimeValueFromConfig('boolean', el.isInit, context) || true, // 默认 true
|
||||
isSync: getRuntimeValueFromConfig('boolean', el.isSync, context) || false, // 默认 false
|
||||
type: el.type || 'fetch',
|
||||
willFetch: el.willFetch ? getRuntimeJsValue(el.willFetch, context) : defaultWillFetch,
|
||||
shouldFetch: buildShouldFetch(el, context),
|
||||
dataHandler: el.dataHandler ? getRuntimeJsValue(el.dataHandler, context) : defaultDataHandler,
|
||||
errorHandler: el.errorHandler ? getRuntimeJsValue(el.errorHandler, context) : undefined,
|
||||
requestHandler: el.requestHandler ? getRuntimeJsValue(el.requestHandler, context) : undefined,
|
||||
options: buildOptions(el, context),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
list,
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
import {
|
||||
DataSourceMap,
|
||||
RuntimeDataSource,
|
||||
RuntimeDataSourceConfig,
|
||||
} from '@ali/lowcode-types';
|
||||
import { DataSourceMap, RuntimeDataSource, RuntimeDataSourceConfig } from '@ali/lowcode-types';
|
||||
|
||||
export const reloadDataSourceFactory = (
|
||||
dataSource: RuntimeDataSource,
|
||||
dataSourceMap: DataSourceMap,
|
||||
dataHandler?: (dataSourceMap: DataSourceMap) => void,
|
||||
) => async () => {
|
||||
const allAsyncLoadings: Array<Promise<any>> = [];
|
||||
|
||||
@ -16,16 +13,13 @@ export const reloadDataSourceFactory = (
|
||||
.filter(
|
||||
(el: RuntimeDataSourceConfig) =>
|
||||
// eslint-disable-next-line implicit-arrow-linebreak
|
||||
el.type === 'urlParams' &&
|
||||
(typeof el.isInit === 'boolean' ? el.isInit : true),
|
||||
el.type === 'urlParams' && (typeof el.isInit === 'boolean' ? el.isInit : true),
|
||||
)
|
||||
.forEach((el: RuntimeDataSourceConfig) => {
|
||||
dataSourceMap[el.id].load();
|
||||
});
|
||||
|
||||
const remainRuntimeDataSourceList = dataSource.list.filter(
|
||||
(el: RuntimeDataSourceConfig) => el.type !== 'urlParams',
|
||||
);
|
||||
const remainRuntimeDataSourceList = dataSource.list.filter((el: RuntimeDataSourceConfig) => el.type !== 'urlParams');
|
||||
|
||||
// 处理并行
|
||||
for (const ds of remainRuntimeDataSourceList) {
|
||||
@ -63,4 +57,10 @@ export const reloadDataSourceFactory = (
|
||||
}
|
||||
|
||||
await Promise.allSettled(allAsyncLoadings);
|
||||
|
||||
// 所有的初始化请求都结束之后,调用钩子函数
|
||||
|
||||
if (dataHandler) {
|
||||
dataHandler(dataSourceMap);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,17 +1,33 @@
|
||||
import {
|
||||
DataHandler,
|
||||
RequestHandler,
|
||||
RequestHandlersMap,
|
||||
RuntimeDataSourceConfig,
|
||||
RuntimeOptionsConfig,
|
||||
UrlParamsHandler,
|
||||
WillFetch,
|
||||
} from '@ali/lowcode-types';
|
||||
|
||||
// 默认的 dataSourceItem 的 dataHandler
|
||||
export const defaultDataHandler: DataHandler = async <T = unknown>(response: {
|
||||
data: T;
|
||||
}) => response.data;
|
||||
export const defaultDataHandler: DataHandler = async <T = unknown>(response: { data: T }) => response.data;
|
||||
|
||||
// 默认的 dataSourceItem 的 willFetch
|
||||
export const defaultWillFetch: WillFetch = (options: RuntimeOptionsConfig) =>
|
||||
options;
|
||||
export const defaultWillFetch: WillFetch = (options: RuntimeOptionsConfig) => options;
|
||||
|
||||
// 默认的 dataSourceItem 的 shouldFetch
|
||||
export const defaultShouldFetch = () => true;
|
||||
|
||||
type GetRequestHandler<T = unknown> = (
|
||||
ds: RuntimeDataSourceConfig,
|
||||
requestHandlersMap: RequestHandlersMap<{ data: T }>,
|
||||
) => RequestHandler<{ data: T }> | UrlParamsHandler<T>;
|
||||
|
||||
// 从当前 dataSourceItem 中获取 requestHandler
|
||||
export const getRequestHandler: GetRequestHandler = (ds, requestHandlersMap) => {
|
||||
if (ds.type === 'custom') {
|
||||
// 自定义类型处理
|
||||
return (ds.requestHandler as unknown) as RequestHandler<{ data: unknown }>; // 理论上这里应该是能强转的,就算为空,应该在 request 请求的时候触发失败
|
||||
}
|
||||
// type 协议默认值 fetch
|
||||
return requestHandlersMap[ds.type || 'fetch'];
|
||||
};
|
||||
|
||||
@ -9,11 +9,12 @@ import {
|
||||
RuntimeDataSource,
|
||||
RuntimeDataSourceConfig,
|
||||
} from '@ali/lowcode-types';
|
||||
import { getRequestHandler } from '../helpers';
|
||||
|
||||
// TODO: requestConfig mtop 默认的请求 config 怎么处理?
|
||||
/**
|
||||
* @param dataSource
|
||||
* @param context
|
||||
* @param extraConfig: { requestHandlersMap }
|
||||
*/
|
||||
|
||||
export default (
|
||||
@ -25,22 +26,11 @@ export default (
|
||||
) => {
|
||||
const { requestHandlersMap } = extraConfig;
|
||||
|
||||
const runtimeDataSource: RuntimeDataSource = adapt2Runtime(
|
||||
dataSource,
|
||||
context,
|
||||
);
|
||||
const runtimeDataSource: RuntimeDataSource = adapt2Runtime(dataSource, context);
|
||||
|
||||
const dataSourceMap = runtimeDataSource.list.reduce(
|
||||
(
|
||||
prev: Record<string, IRuntimeDataSource>,
|
||||
current: RuntimeDataSourceConfig,
|
||||
) => {
|
||||
prev[current.id] = new RuntimeDataSourceItem(
|
||||
current,
|
||||
// type 协议默认值 fetch
|
||||
requestHandlersMap[current.type || 'fetch'],
|
||||
context,
|
||||
);
|
||||
(prev: Record<string, IRuntimeDataSource>, current: RuntimeDataSourceConfig) => {
|
||||
prev[current.id] = new RuntimeDataSourceItem(current, getRequestHandler(current, requestHandlersMap), context);
|
||||
return prev;
|
||||
},
|
||||
{},
|
||||
@ -48,6 +38,6 @@ export default (
|
||||
|
||||
return {
|
||||
dataSourceMap,
|
||||
reloadDataSource: reloadDataSourceFactory(runtimeDataSource, dataSourceMap),
|
||||
reloadDataSource: reloadDataSourceFactory(runtimeDataSource, dataSourceMap, runtimeDataSource.dataHandler),
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import {
|
||||
IRuntimeDataSource,
|
||||
IDataSourceRuntimeContext,
|
||||
@ -10,17 +9,14 @@ import {
|
||||
|
||||
import { RuntimeDataSourceItem } from '../core';
|
||||
import { reloadDataSourceFactory } from '../core/reloadDataSourceFactory';
|
||||
import {
|
||||
defaultDataHandler,
|
||||
defaultShouldFetch,
|
||||
defaultWillFetch,
|
||||
} from '../helpers';
|
||||
import { defaultDataHandler, defaultShouldFetch, defaultWillFetch, getRequestHandler } from '../helpers';
|
||||
|
||||
// TODO: requestConfig mtop 默认的请求 config 怎么处理?
|
||||
/**
|
||||
* @param dataSource
|
||||
* @param context
|
||||
* @param extraConfig: { requestHandlersMap }
|
||||
*/
|
||||
|
||||
export default (
|
||||
dataSource: RuntimeDataSource,
|
||||
context: IDataSourceRuntimeContext,
|
||||
@ -34,28 +30,19 @@ export default (
|
||||
dataSource.list.forEach(ds => {
|
||||
ds.isInit = ds.isInit || true;
|
||||
ds.isSync = ds.isSync || false;
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
ds.shouldFetch = !ds.shouldFetch
|
||||
? defaultShouldFetch
|
||||
: typeof ds.shouldFetch === 'function'
|
||||
? ds.shouldFetch.bind(context)
|
||||
: ds.shouldFetch;
|
||||
ds.willFetch = ds.willFetch ? ds.willFetch.bind(context) : defaultWillFetch;
|
||||
ds.dataHandler = ds.dataHandler
|
||||
? ds.dataHandler.bind(context)
|
||||
: defaultDataHandler;
|
||||
ds.dataHandler = ds.dataHandler ? ds.dataHandler.bind(context) : defaultDataHandler;
|
||||
});
|
||||
|
||||
const dataSourceMap = dataSource.list.reduce(
|
||||
(
|
||||
prev: Record<string, IRuntimeDataSource>,
|
||||
current: RuntimeDataSourceConfig,
|
||||
) => {
|
||||
prev[current.id] = new RuntimeDataSourceItem(
|
||||
current,
|
||||
// type 协议默认值 fetch
|
||||
requestHandlersMap[current.type || 'fetch'],
|
||||
context,
|
||||
);
|
||||
(prev: Record<string, IRuntimeDataSource>, current: RuntimeDataSourceConfig) => {
|
||||
prev[current.id] = new RuntimeDataSourceItem(current, getRequestHandler(current, requestHandlersMap), context);
|
||||
return prev;
|
||||
},
|
||||
{},
|
||||
@ -63,6 +50,6 @@ export default (
|
||||
|
||||
return {
|
||||
dataSourceMap,
|
||||
reloadDataSource: reloadDataSourceFactory(dataSource, dataSourceMap),
|
||||
reloadDataSource: reloadDataSourceFactory(dataSource, dataSourceMap, dataSource.dataHandler),
|
||||
};
|
||||
};
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
# 关于此场景
|
||||
|
||||
数据源的 type 可以是 `custom` 类型的, 此时需要提供 `requestHandler` 给数据源
|
||||
@ -0,0 +1,67 @@
|
||||
import { RuntimeDataSource } from '@ali/lowcode-types';
|
||||
|
||||
// 这里仅仅是数据源部分的:
|
||||
// @see: https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#XMeF5
|
||||
export const dataSource: RuntimeDataSource = {
|
||||
list: [
|
||||
{
|
||||
id: 'user',
|
||||
isInit: true,
|
||||
type: 'custom',
|
||||
isSync: true,
|
||||
requestHandler: options => {
|
||||
return new Promise(res => {
|
||||
setTimeout(() => {
|
||||
// test return data
|
||||
res({
|
||||
data: {
|
||||
id: 9527,
|
||||
name: 'Alice',
|
||||
uri: options.uri,
|
||||
},
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
},
|
||||
options() {
|
||||
return {
|
||||
uri: 'https://mocks.alibaba-inc.com/user.json',
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'orders',
|
||||
isInit: true,
|
||||
type: 'custom',
|
||||
isSync: true,
|
||||
requestHandler: () => {
|
||||
return new Promise((res, rej) => {
|
||||
setTimeout(() => {
|
||||
// test return data
|
||||
rej(new Error('test error'));
|
||||
}, 1000);
|
||||
});
|
||||
},
|
||||
options() {
|
||||
return {
|
||||
uri: 'https://mocks.alibaba-inc.com/orders.json',
|
||||
params: {
|
||||
userId: this.state.user.id,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
// 这个 api 是假的,调不通的,当前场景是故意需要报错的
|
||||
id: 'members',
|
||||
isInit: true,
|
||||
type: 'custom',
|
||||
isSync: true,
|
||||
options() {
|
||||
return {
|
||||
uri: 'https://mocks.alibaba-inc.com/members.json',
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -0,0 +1,67 @@
|
||||
import { InterpretDataSource } from '@ali/lowcode-types';
|
||||
|
||||
// 这里仅仅是数据源部分的 schema:
|
||||
// @see: https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#XMeF5
|
||||
export const DATA_SOURCE_SCHEMA: InterpretDataSource = {
|
||||
list: [
|
||||
{
|
||||
id: 'user',
|
||||
isInit: true,
|
||||
type: 'custom',
|
||||
isSync: true,
|
||||
requestHandler: {
|
||||
type: 'JSFunction',
|
||||
value: `function(options){
|
||||
return new Promise(res => {
|
||||
setTimeout(() => {
|
||||
// test return data
|
||||
res({
|
||||
data: {
|
||||
id: 9527,
|
||||
name: 'Alice',
|
||||
uri: options.uri,
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
}`,
|
||||
},
|
||||
options: {
|
||||
uri: 'https://mocks.alibaba-inc.com/user.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'orders',
|
||||
isInit: true,
|
||||
type: 'custom',
|
||||
isSync: true,
|
||||
requestHandler: {
|
||||
type: 'JSFunction',
|
||||
value: `function(options){
|
||||
return new Promise((res, rej) => {
|
||||
setTimeout(() => {
|
||||
// test return data
|
||||
return rej(new Error('test error'));
|
||||
}, 1000);
|
||||
});
|
||||
}`,
|
||||
},
|
||||
options: {
|
||||
uri: 'https://mocks.alibaba-inc.com/orders.json',
|
||||
params: {
|
||||
type: 'JSExpression',
|
||||
value: '{ userId: this.state.user.id }',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'members',
|
||||
isInit: true,
|
||||
type: 'custom',
|
||||
isSync: true,
|
||||
options: {
|
||||
uri: 'https://mocks.alibaba-inc.com/members.json',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -0,0 +1,90 @@
|
||||
import {
|
||||
InterpretDataSource,
|
||||
IDataSourceEngine,
|
||||
IDataSourceRuntimeContext,
|
||||
RuntimeDataSource,
|
||||
RuntimeDataSourceStatus,
|
||||
} from '@ali/lowcode-types';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import { bindRuntimeContext, MockContext } from '../../_helpers';
|
||||
|
||||
import type { ExecutionContext, Macro } from 'ava';
|
||||
import type { SinonFakeTimers } from 'sinon';
|
||||
|
||||
export const normalScene: Macro<[
|
||||
{
|
||||
create: (
|
||||
dataSource: any,
|
||||
ctx: IDataSourceRuntimeContext,
|
||||
options?: any
|
||||
) => IDataSourceEngine;
|
||||
dataSource: RuntimeDataSource | InterpretDataSource;
|
||||
}
|
||||
]> = async (
|
||||
t: ExecutionContext<{ clock: SinonFakeTimers }>,
|
||||
{ create, dataSource },
|
||||
) => {
|
||||
const { clock } = t.context;
|
||||
|
||||
const USER_DATA = {
|
||||
id: 9527,
|
||||
name: 'Alice',
|
||||
uri: 'https://mocks.alibaba-inc.com/user.json'
|
||||
};
|
||||
const ERROR_MSG = 'test error';
|
||||
|
||||
|
||||
const context = new MockContext<Record<string, unknown>>({}, (ctx) => create(bindRuntimeContext(dataSource, ctx), ctx));
|
||||
|
||||
const setState = sinon.spy(context, 'setState');
|
||||
|
||||
// 一开始应该是初始状态
|
||||
t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial);
|
||||
t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Initial);
|
||||
t.is(context.dataSourceMap.members.status, RuntimeDataSourceStatus.Initial);
|
||||
|
||||
const loading = context.reloadDataSource();
|
||||
|
||||
await clock.tickAsync(50);
|
||||
|
||||
// 中间应该有 loading 态
|
||||
t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading);
|
||||
|
||||
await clock.tickAsync(1050);
|
||||
|
||||
// 中间应该有 loading 态
|
||||
t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Loading);
|
||||
|
||||
await clock.tickAsync(1050);
|
||||
|
||||
// members 因为没有 requestHandler 直接就挂了
|
||||
t.is(context.dataSourceMap.members.status, RuntimeDataSourceStatus.Error)
|
||||
|
||||
await Promise.all([clock.runAllAsync(), loading]);
|
||||
|
||||
// 最后 user 应该成功了,loaded
|
||||
t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loaded);
|
||||
// 最后 orders 应该失败了,error 状态
|
||||
t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Error);
|
||||
|
||||
// 检查数据源的数据
|
||||
t.deepEqual(context.dataSourceMap.user.data, USER_DATA);
|
||||
t.is(context.dataSourceMap.user.error, undefined);
|
||||
t.deepEqual(context.dataSourceMap.orders.data, undefined);
|
||||
t.not(context.dataSourceMap.orders.error, undefined);
|
||||
t.deepEqual(context.dataSourceMap.members.data, undefined);
|
||||
t.not(context.dataSourceMap.members.error, undefined);
|
||||
t.regex(context.dataSourceMap.orders.error!.message, new RegExp(ERROR_MSG));
|
||||
t.regex(context.dataSourceMap.members.error!.message, new RegExp('no custom handler provide'));
|
||||
|
||||
|
||||
|
||||
// 检查状态数据
|
||||
t.assert(setState.calledOnce);
|
||||
t.deepEqual(context.state.user, USER_DATA);
|
||||
t.is(context.state.orders, undefined);
|
||||
t.is(context.state.members, undefined);
|
||||
};
|
||||
|
||||
normalScene.title = (providedTitle) => providedTitle || 'normal scene';
|
||||
@ -0,0 +1,20 @@
|
||||
import test, { ExecutionContext } from 'ava';
|
||||
import sinon, { SinonFakeTimers } from 'sinon';
|
||||
|
||||
import { create } from '../../../src/interpret';
|
||||
|
||||
import { DATA_SOURCE_SCHEMA } from './_datasource-schema';
|
||||
import { normalScene } from './_macro-normal';
|
||||
|
||||
test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => {
|
||||
t.context.clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => {
|
||||
t.context.clock.restore();
|
||||
});
|
||||
|
||||
test(normalScene, {
|
||||
create,
|
||||
dataSource: DATA_SOURCE_SCHEMA,
|
||||
});
|
||||
@ -0,0 +1,20 @@
|
||||
import test, { ExecutionContext } from 'ava';
|
||||
import sinon, { SinonFakeTimers } from 'sinon';
|
||||
|
||||
import { create } from '../../../src/runtime';
|
||||
|
||||
import { dataSource } from './_datasource-runtime';
|
||||
import { normalScene } from './_macro-normal';
|
||||
|
||||
test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => {
|
||||
t.context.clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => {
|
||||
t.context.clock.restore();
|
||||
});
|
||||
|
||||
test(normalScene, {
|
||||
create,
|
||||
dataSource,
|
||||
});
|
||||
@ -5,11 +5,17 @@ import {
|
||||
|
||||
export type RequestHandler<T = unknown> = (
|
||||
options: RuntimeOptionsConfig,
|
||||
context: IDataSourceRuntimeContext
|
||||
context?: IDataSourceRuntimeContext,
|
||||
) => Promise<T>;
|
||||
|
||||
export type UrlParamsHandler<T = unknown> = (
|
||||
context?: IDataSourceRuntimeContext
|
||||
context?: IDataSourceRuntimeContext,
|
||||
) => Promise<T>;
|
||||
|
||||
export type RequestHandlersMap<T = unknown> = Record<string, RequestHandler<T>>;
|
||||
|
||||
// 仅在 type=custom 的时候生效的 handler
|
||||
export type CustomRequestHandler<T = unknown> = (
|
||||
options: RuntimeOptionsConfig,
|
||||
context?: IDataSourceRuntimeContext,
|
||||
) => Promise<T>;
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { IRuntimeDataSource } from './data-source';
|
||||
import { CustomRequestHandler } from './data-source-handlers';
|
||||
|
||||
// 先定义运行模式的类型
|
||||
export interface RuntimeDataSource {
|
||||
list: RuntimeDataSourceConfig[];
|
||||
// TODO: dataMap 格式不对要处理
|
||||
dataHandler?: (dataMap: DataSourceMap) => void;
|
||||
dataHandler?: (dataSourceMap: DataSourceMap) => void;
|
||||
}
|
||||
|
||||
export type DataSourceMap = Record<string, IRuntimeDataSource>;
|
||||
@ -16,7 +16,7 @@ export interface RuntimeDataSourceConfig {
|
||||
type?: string;
|
||||
willFetch?: WillFetch;
|
||||
shouldFetch?: () => boolean;
|
||||
requestHandler?: () => void; // TODO: 待定
|
||||
requestHandler?: CustomRequestHandler;
|
||||
dataHandler?: DataHandler;
|
||||
errorHandler?: ErrorHandler;
|
||||
options?: RuntimeOptions;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user