diff --git a/packages/code-generator/CHANGELOG.md b/packages/code-generator/CHANGELOG.md index b0c9204ed..06e71b2ca 100644 --- a/packages/code-generator/CHANGELOG.md +++ b/packages/code-generator/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.13...@ali/lowcode-code-generator@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-code-generator + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.13...@ali/lowcode-code-generator@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-code-generator + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-code-generator@1.0.12...@ali/lowcode-code-generator@1.0.13) (2020-11-02) diff --git a/packages/code-generator/package.json b/packages/code-generator/package.json index 6767e9bb7..172dba448 100644 --- a/packages/code-generator/package.json +++ b/packages/code-generator/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-code-generator", - "version": "1.0.13", + "version": "1.0.15", "description": "出码引擎 for LowCode Engine", "main": "lib/index.js", "module": "es/index.js", diff --git a/packages/datasource-engine/.eslintignore b/packages/datasource-engine/.eslintignore new file mode 100644 index 000000000..fecb7c26d --- /dev/null +++ b/packages/datasource-engine/.eslintignore @@ -0,0 +1,3 @@ +/node_modules +/es +/lib \ No newline at end of file diff --git a/packages/datasource-engine/.eslintrc.js b/packages/datasource-engine/.eslintrc.js index dd5b56a53..6cbc6c8e9 100644 --- a/packages/datasource-engine/.eslintrc.js +++ b/packages/datasource-engine/.eslintrc.js @@ -1,6 +1,8 @@ module.exports = { - extends: '../../.eslintrc.js', + extends: '../../.eslintrc', rules: { - '@typescript-eslint/no-parameter-properties': 1, - } -} \ No newline at end of file + '@typescript-eslint/no-parameter-properties': 0, + 'no-param-reassign': 0, + 'max-len': 0, + }, +}; diff --git a/packages/datasource-engine/.gitignore b/packages/datasource-engine/.gitignore index 28b6557db..d3509362e 100644 --- a/packages/datasource-engine/.gitignore +++ b/packages/datasource-engine/.gitignore @@ -3,3 +3,4 @@ .DS_Store /es/ /lib/ +/dist diff --git a/packages/datasource-engine/.prettierrc.js b/packages/datasource-engine/.prettierrc.js new file mode 100644 index 000000000..21ec86215 --- /dev/null +++ b/packages/datasource-engine/.prettierrc.js @@ -0,0 +1,5 @@ +module.exports = { + singleQuote: true, + trailingComma: 'all', + printWidth: 120, +}; diff --git a/packages/datasource-engine/CHANGELOG.md b/packages/datasource-engine/CHANGELOG.md index 49e3b48f8..59956e5d6 100644 --- a/packages/datasource-engine/CHANGELOG.md +++ b/packages/datasource-engine/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-datasource-engine@0.1.17...@ali/lowcode-datasource-engine@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-engine + + +## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-datasource-engine@0.1.17...@ali/lowcode-datasource-engine@1.0.13) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-engine + ## [0.1.17](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-datasource-engine@0.1.16...@ali/lowcode-datasource-engine@0.1.17) (2020-11-02) diff --git a/packages/datasource-engine/ava.config.js b/packages/datasource-engine/ava.config.js new file mode 100644 index 000000000..987ecb184 --- /dev/null +++ b/packages/datasource-engine/ava.config.js @@ -0,0 +1,8 @@ +export default { + babel: { + compileEnhancements: false, + }, + files: ['./test/core/*.ts', './test/scenes/**/*.test.ts'], + require: ['ts-node/register/transpile-only'], + extensions: ['ts'], +}; diff --git a/packages/datasource-engine/handlers/fetch/index.d.ts b/packages/datasource-engine/handlers/fetch/index.d.ts deleted file mode 100644 index 86829236a..000000000 --- a/packages/datasource-engine/handlers/fetch/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type * from '../../es/handlers/fetch'; diff --git a/packages/datasource-engine/handlers/fetch/index.js b/packages/datasource-engine/handlers/fetch/index.js deleted file mode 100644 index 5abed6279..000000000 --- a/packages/datasource-engine/handlers/fetch/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../lib/handlers/fetch').default; diff --git a/packages/datasource-engine/handlers/mtop/index.d.ts b/packages/datasource-engine/handlers/mtop/index.d.ts deleted file mode 100644 index 1d11c303b..000000000 --- a/packages/datasource-engine/handlers/mtop/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type * from '../../es/handlers/mtop'; diff --git a/packages/datasource-engine/handlers/mtop/index.js b/packages/datasource-engine/handlers/mtop/index.js deleted file mode 100644 index 89f131c78..000000000 --- a/packages/datasource-engine/handlers/mtop/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../lib/handlers/mtop').default; diff --git a/packages/datasource-engine/handlers/url-params/index.d.ts b/packages/datasource-engine/handlers/url-params/index.d.ts deleted file mode 100644 index 58e059ec8..000000000 --- a/packages/datasource-engine/handlers/url-params/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type * from '../../es/handlers/url-params'; diff --git a/packages/datasource-engine/handlers/url-params/index.js b/packages/datasource-engine/handlers/url-params/index.js deleted file mode 100644 index f9b01c61e..000000000 --- a/packages/datasource-engine/handlers/url-params/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../lib/handlers/url-params').default; diff --git a/packages/datasource-engine/interpret.d.ts b/packages/datasource-engine/interpret.d.ts new file mode 100644 index 000000000..b37fce092 --- /dev/null +++ b/packages/datasource-engine/interpret.d.ts @@ -0,0 +1 @@ +export { create } from './dist/interpret'; diff --git a/packages/datasource-engine/interpret.js b/packages/datasource-engine/interpret.js new file mode 100644 index 000000000..42c42028e --- /dev/null +++ b/packages/datasource-engine/interpret.js @@ -0,0 +1 @@ +module.exports = require('./dist/interpret'); diff --git a/packages/datasource-engine/package.json b/packages/datasource-engine/package.json index af1a837bc..840e80b0c 100644 --- a/packages/datasource-engine/package.json +++ b/packages/datasource-engine/package.json @@ -1,34 +1,36 @@ { "name": "@ali/lowcode-datasource-engine", - "version": "0.1.17", + "version": "1.0.14", "description": "DataSource Engine for lowcode", "main": "lib/index.js", "module": "es/index.js", - "typings": "es/index.d.ts", + "typings": "dist/index.d.ts", "files": [ - "handlers", + "dist", "src", - "lib", - "es" + "interpret*", + "runtime*" ], "scripts": { - "start": "tsc --watch", - "clean": "rm -rf es lib", - "build": "rm -rf es lib && tsc --module esnext --target es6 && mv lib es && tsc", + "build": "rm -rf dist && tsc --outDir ./dist --module esnext", + "test": "ava", "prepublishOnly": "npm run build" }, - "author": "", - "license": "ISC", - "publishConfig": { - "registry": "https://registry.npm.alibaba-inc.com" - }, - "devDependencies": { + "dependencies": { + "@ali/lowcode-types": "^1.0.13", "typescript": "^3.9.7" }, - "dependencies": { - "@ali/universal-mtop": "^5.1.9", - "query-string": "^6.13.1", - "tslib": "^2.0.1", - "universal-request": "^2.2.0" + "devDependencies": { + "@ava/babel": "^1.0.1", + "@types/sinon": "^9.0.5", + "ava": "3.11.1", + "get-port": "^5.1.1", + "json5": "^2.1.3", + "sinon": "^9.0.3", + "ts-node": "^8.10.2", + "tslib": "^2.0.1" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" } } diff --git a/packages/datasource-engine/readme.md b/packages/datasource-engine/readme.md new file mode 100644 index 000000000..d5be75f84 --- /dev/null +++ b/packages/datasource-engine/readme.md @@ -0,0 +1,51 @@ +## 关于 @ali/lc-datasource-engine + +低代码引擎数据源核心代码 + +## doc + +[原理介绍](https://yuque.antfin-inc.com/docs/share/6ba9dab7-0712-4302-a5bb-b17d4a5f8505?# 《DataSource Engine》) + + +[fetch流程图](https://yuque.antfin-inc.com/docs/share/e9baef9a-3586-40fc-8708-eaeee0d7937e?# 《fetch 流程》) + + +## 使用 + +```ts +// 面向运行时渲染,直接给 schema +import { create } from '@ali/lowcode-datasource-engine/interpret'; + +// 面向出码,需要给处理过后的内容 +import { create } from '@ali/lowcode-datasource-engine/runtime'; + +import { createFetchHandler } from '@ali/lowcode-datasource-fetch-handler'; + +import { createMtopHandler } from '@ali/lowcode-datasource-mtop-handler'; + +// dataSource 可以是 schema 协议内容 或者是运行时的转化后的配置内容 (出码专用) + + +// context 上下文(setState 为必选) +const dataSourceEngine = create(dataSource, context, { + requestHandlersMap: { // 可选参数,以下内容为当前默认的内容 + urlParams: handlersMap.urlParams('?bar=1&test=2'), + fetch: createFetchHandler, + mtop: createMtopHandler + }, +}); + + +console.log(dsf.dataSourceMap) // 符合集团协议的 datasourceMap https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#QUSn5 + +dsf.dataSourceMap['id'].load() // 加载 + +dsf.dataSourceMap['id'].status // 获取状态 + +dsf.dataSourceMap['id'].data // 获取数据 + +dsf.dataSourceMap['id'].error // 获取错误信息 + +dsf.reloadDataSource(); // 刷新所有数据源 + +``` diff --git a/packages/datasource-engine/runtime.d.ts b/packages/datasource-engine/runtime.d.ts new file mode 100644 index 000000000..61344f9b4 --- /dev/null +++ b/packages/datasource-engine/runtime.d.ts @@ -0,0 +1 @@ +export { create } from './dist/runtime'; diff --git a/packages/datasource-engine/runtime.js b/packages/datasource-engine/runtime.js new file mode 100644 index 000000000..28f19824a --- /dev/null +++ b/packages/datasource-engine/runtime.js @@ -0,0 +1 @@ +module.exports = require('./dist/runtime/index'); diff --git a/packages/datasource-engine/src/core/DataSourceEngine.ts b/packages/datasource-engine/src/core/DataSourceEngine.ts deleted file mode 100644 index d24ef2420..000000000 --- a/packages/datasource-engine/src/core/DataSourceEngine.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { - DataSourceConfig, - DataSourceEngineOptions, - IDataSourceEngine, - IDataSourceEngineFactory, - IRuntimeContext, -} from '../types'; -import { RuntimeDataSource } from './RuntimeDataSource'; - -export class DataSourceEngine implements IDataSourceEngine { - private _dataSourceMap: Record = {}; - - constructor( - private _dataSourceConfig: DataSourceConfig, - private _runtimeContext: IRuntimeContext, - private _options?: DataSourceEngineOptions, - ) { - // eslint-disable-next-line no-unused-expressions - _dataSourceConfig.list?.forEach((ds) => { - // 确保数据源都有处理器 - const requestHandler = ds.requestHandler || _options?.requestHandlersMap?.[ds.type]; - if (!requestHandler) { - throw new Error(`No request handler for "${ds.type}" data source`); - } - - this._dataSourceMap[ds.id] = new RuntimeDataSource( - ds.id, - ds.type, - getValue(ds.options) || {}, - requestHandler.bind(_runtimeContext), - ds.dataHandler ? ds.dataHandler.bind(_runtimeContext) : undefined, - (data) => { - _runtimeContext.setState({ [ds.id]: data }); - }, - ); - }); - } - - public get dataSourceMap() { - return this._dataSourceMap; - } - - public async reloadDataSource() { - try { - const allDataSourceConfigList = this._dataSourceConfig.list || []; - - // urlParams 类型的优先加载 - for (const ds of allDataSourceConfigList) { - if (ds.type === 'urlParams' && (getValue(ds.isInit) ?? true)) { - await this._dataSourceMap[ds.id].load(); - } - } - - // 然后是所有其他的 - const remainDataSourceConfigList = allDataSourceConfigList.filter((x) => x.type !== 'urlParams'); - - // 先发起异步的 - const asyncLoadings: Array> = []; - for (const ds of remainDataSourceConfigList) { - if (getValue(ds.isInit) ?? true) { - const options = getValue(ds.options); - if (options && !options.isSync) { - this._dataSourceMap[ds.id].setOptions(options); - asyncLoadings.push(this._dataSourceMap[ds.id].load(options?.params).catch(() => {})); - } - } - } - - try { - // 再按先后顺序发起同步请求 - for (const ds of remainDataSourceConfigList) { - if (getValue(ds.isInit) ?? true) { - const options = getValue(ds.options); - if (options && options.isSync) { - this._dataSourceMap[ds.id].setOptions(options); - await this._dataSourceMap[ds.id].load(options?.params); - await sleep(0); // TODO: 如何优雅地解决 setState 的异步问题? - } - } - } - } catch (e) { - // ignore error - } - - await Promise.all(asyncLoadings); - } finally { - const allDataHandler = this._dataSourceConfig.dataHandler; - if (allDataHandler) { - await allDataHandler(this._getDataMapOfAll()); - } - } - } - - private _getDataMapOfAll(): Record { - const dataMap: Record = {}; - - Object.entries(this._dataSourceMap).forEach(([dsId, ds]) => { - dataMap[dsId] = ds.data; - }); - - return dataMap; - } -} - -export const create: IDataSourceEngineFactory['create'] = (dataSourceConfig, runtimeContext, options) => { - return new DataSourceEngine(dataSourceConfig, runtimeContext, options); -}; - -function getValue(valueOrValueGetter: T | (() => T)): T; -function getValue(valueOrValueGetter: T | (() => T)): T | undefined { - if (typeof valueOrValueGetter === 'function') { - try { - return valueOrValueGetter(); - } catch (e) { - return undefined; - } - } else { - return valueOrValueGetter; - } -} - -function sleep(ms = 0) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} diff --git a/packages/datasource-engine/src/core/RuntimeDataSource.ts b/packages/datasource-engine/src/core/RuntimeDataSource.ts deleted file mode 100644 index 89ec2d930..000000000 --- a/packages/datasource-engine/src/core/RuntimeDataSource.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { DataSourceOptions, IRuntimeDataSource, RequestHandler, RuntimeDataSourceStatus } from '../types'; -import { DataSourceResponse } from '../types/DataSourceResponse'; - -export class RuntimeDataSource< - TParams extends Record = Record, - TRequestResult = unknown, - TResultData = unknown -> implements IRuntimeDataSource { - private _status: RuntimeDataSourceStatus = RuntimeDataSourceStatus.Initial; - - private _data?: TResultData; - - private _error?: Error; - - private _latestOptions: DataSourceOptions; - - constructor( - private _id: string, - private _type: string, - private _initialOptions: DataSourceOptions, - private _requestHandler: RequestHandler, DataSourceResponse>, - private _dataHandler: - | (( - data: DataSourceResponse | undefined, - error: unknown | undefined, - ) => TResultData | Promise) - | undefined, - private _onLoaded: (data: TResultData) => void, - ) { - this._latestOptions = _initialOptions; - } - - public get status() { - return this._status; - } - - public get data() { - return this._data; - } - - public get error() { - return this._error; - } - - public async load(params?: TParams): Promise { - try { - this._latestOptions = { - ...this._latestOptions, - params: { - ...this._latestOptions.params, - ...params, - } as TParams, - }; - - this._status = RuntimeDataSourceStatus.Loading; - - const data = await this._request(this._latestOptions); - - this._status = RuntimeDataSourceStatus.Loaded; - - this._onLoaded(data); - - this._data = data; - return data; - } catch (err) { - this._error = err; - this._status = RuntimeDataSourceStatus.Error; - throw err; - } - } - - public setOptions(options: DataSourceOptions) { - this._latestOptions = options; - } - - private async _request(options: DataSourceOptions) { - try { - const response = await this._requestHandler(options); - - const data = this._dataHandler - ? await this._dataHandler(response, undefined) - : ((response.data as unknown) as TResultData); - - return data; - } catch (err) { - if (this._dataHandler) { - const data = await this._dataHandler(undefined, err); - return data; - } - - throw err; - } - } -} diff --git a/packages/datasource-engine/src/core/RuntimeDataSourceItem.ts b/packages/datasource-engine/src/core/RuntimeDataSourceItem.ts new file mode 100644 index 000000000..5d71e2e1b --- /dev/null +++ b/packages/datasource-engine/src/core/RuntimeDataSourceItem.ts @@ -0,0 +1,143 @@ +/* eslint-disable @typescript-eslint/indent */ +import { + IDataSourceRuntimeContext, + IRuntimeDataSource, + RequestHandler, + RuntimeDataSourceConfig, + RuntimeDataSourceStatus, + RuntimeOptionsConfig, + UrlParamsHandler, +} from '@ali/lowcode-types'; + +class RuntimeDataSourceItem = Record, TResultData = unknown> + implements IRuntimeDataSource { + private _data?: TResultData; + + private _error?: Error; + + private _status = RuntimeDataSourceStatus.Initial; + + private _dataSourceConfig: RuntimeDataSourceConfig; + + private _request: RequestHandler<{ data: TResultData }> | UrlParamsHandler; + + private _context: IDataSourceRuntimeContext; + + private _options?: RuntimeOptionsConfig; + + constructor( + dataSourceConfig: RuntimeDataSourceConfig, + request: RequestHandler<{ data: TResultData }> | UrlParamsHandler, + context: IDataSourceRuntimeContext, + ) { + this._dataSourceConfig = dataSourceConfig; + this._request = request; + this._context = context; + } + + get data() { + return this._data; + } + + get error() { + return this._error; + } + + get status() { + return this._status; + } + + async load(params?: TParams) { + if (!this._dataSourceConfig) return; + // 考虑没有绑定对应的 handler 的情况 + if (!this._request) { + 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)(this._context); + this._context.setState({ + [this._dataSourceConfig.id]: response, + }); + + this._data = response; + this._status = RuntimeDataSourceStatus.Loaded; + return response; + } + + if (!this._dataSourceConfig.options) { + throw new Error(`${this._dataSourceConfig.id} has no options`); + } + + if (typeof this._dataSourceConfig.options === 'function') { + this._options = this._dataSourceConfig.options(); + } + + // 考虑转换之后是 null 的场景 + if (!this._options) { + throw new Error(`${this._dataSourceConfig.id} options transform error`); + } + + // 临时变量存,每次可能结果不一致,不做缓存 + let shouldFetch = true; + + if (this._dataSourceConfig.shouldFetch) { + if (typeof this._dataSourceConfig.shouldFetch === 'function') { + shouldFetch = this._dataSourceConfig.shouldFetch(); + } else if (typeof this._dataSourceConfig.shouldFetch === 'boolean') { + shouldFetch = this._dataSourceConfig.shouldFetch; + } + } + + if (!shouldFetch) { + this._status = RuntimeDataSourceStatus.Error; + this._error = new Error(`the ${this._dataSourceConfig.id} request should not fetch, please check the condition`); + throw this._error; + } + + let fetchOptions = this._options; + + // willFetch + try { + fetchOptions = await this._dataSourceConfig.willFetch!(this._options); + } catch (error) { + console.error(error); + } + + // 约定如果 params 有内容,直接做替换,如果没有就用默认的 options 的 + if (params && fetchOptions) { + fetchOptions.params = params; + } + + const dataHandler = this._dataSourceConfig.dataHandler!; + const { errorHandler } = this._dataSourceConfig; + + // 调用实际的请求,获取到对应的数据和状态后赋值给当前的 dataSource + try { + this._status = RuntimeDataSourceStatus.Loading; + + // _context 会给传,但是用不用由 handler 说了算 + const result = await (this._request as RequestHandler<{ + data: TResultData; + }>)(fetchOptions, this._context).then(dataHandler, errorHandler); + + // setState + this._context.setState({ + [this._dataSourceConfig.id]: result, + }); + // 结果赋值 + this._data = result; + this._status = RuntimeDataSourceStatus.Loaded; + return this._data; + } catch (error) { + this._error = error; + this._status = RuntimeDataSourceStatus.Error; + throw error; + } + } +} + +export { RuntimeDataSourceItem }; diff --git a/packages/datasource-engine/src/core/adapter.ts b/packages/datasource-engine/src/core/adapter.ts new file mode 100644 index 000000000..71f930bea --- /dev/null +++ b/packages/datasource-engine/src/core/adapter.ts @@ -0,0 +1,47 @@ +import { getRuntimeValueFromConfig, getRuntimeJsValue, buildOptions, buildShouldFetch } from './../utils'; +// 将不同渠道给的 schema 转为 runtime 需要的类型 + +import { defaultDataHandler, defaultWillFetch } from '../helpers'; +import { + DataSourceMap, + IDataSourceRuntimeContext, + InterpretDataSource, + InterpretDataSourceConfig, + RuntimeDataSourceConfig, +} from '@ali/lowcode-types'; + +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) { + return { + list: [], + 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, + requestHandler: el.requestHandler ? getRuntimeJsValue(el.requestHandler, context) : undefined, + options: buildOptions(el, context), + }; + }); + + return { + list, + dataHandler, + }; +}; + +export { adapt2Runtime }; diff --git a/packages/datasource-engine/src/core/index.ts b/packages/datasource-engine/src/core/index.ts index fb9854290..846867d09 100644 --- a/packages/datasource-engine/src/core/index.ts +++ b/packages/datasource-engine/src/core/index.ts @@ -1 +1,2 @@ -export { create } from './DataSourceEngine'; +export { RuntimeDataSourceItem } from './RuntimeDataSourceItem'; +export { adapt2Runtime } from './adapter'; diff --git a/packages/datasource-engine/src/core/reloadDataSourceFactory.ts b/packages/datasource-engine/src/core/reloadDataSourceFactory.ts new file mode 100644 index 000000000..87a111ca0 --- /dev/null +++ b/packages/datasource-engine/src/core/reloadDataSourceFactory.ts @@ -0,0 +1,66 @@ +import { DataSourceMap, RuntimeDataSource, RuntimeDataSourceConfig } from '@ali/lowcode-types'; + +export const reloadDataSourceFactory = ( + dataSource: RuntimeDataSource, + dataSourceMap: DataSourceMap, + dataHandler?: (dataSourceMap: DataSourceMap) => void, +) => async () => { + const allAsyncLoadings: Array> = []; + + // TODO: 那么,如果有新的类型过来,这个地方怎么处理??? + // 单独处理 urlParams 类型的 + dataSource.list + .filter( + (el: RuntimeDataSourceConfig) => + // eslint-disable-next-line implicit-arrow-linebreak + 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'); + + // 处理并行 + for (const ds of remainRuntimeDataSourceList) { + if (!ds.options) { + continue; + } + if ( + // 需要考虑出码直接不传值的情况 + ds.isInit && + !ds.isSync + ) { + allAsyncLoadings.push(dataSourceMap[ds.id].load()); + } + } + + // 处理串行 + for (const ds of remainRuntimeDataSourceList) { + if (!ds.options) { + continue; + } + + if ( + // 需要考虑出码直接不传值的情况 + ds.isInit && + ds.isSync + ) { + try { + // eslint-disable-next-line no-await-in-loop + await dataSourceMap[ds.id].load(); + } catch (e) { + // TODO: 这个错误直接吃掉? + console.error(e); + } + } + } + + await Promise.allSettled(allAsyncLoadings); + + // 所有的初始化请求都结束之后,调用钩子函数 + + if (dataHandler) { + dataHandler(dataSourceMap); + } +}; diff --git a/packages/datasource-engine/src/handlers/fetch.ts b/packages/datasource-engine/src/handlers/fetch.ts deleted file mode 100644 index d0f6d344b..000000000 --- a/packages/datasource-engine/src/handlers/fetch.ts +++ /dev/null @@ -1,24 +0,0 @@ -import request from 'universal-request'; -import type { AsObject, RequestOptions } from 'universal-request/lib/types'; - -import { DataSourceOptions, RequestHandler } from '../types'; - -const fetchHandler: RequestHandler = async ({ - url, - uri, - data, - params, - ...otherOptions -}: DataSourceOptions) => { - const reqOptions = { - url: ((url || uri) as unknown) as string, - data: ((data || params) as unknown) as AsObject, - ...otherOptions, - }; - - const res = await request(reqOptions as RequestOptions); - - return res; -}; - -export default fetchHandler; diff --git a/packages/datasource-engine/src/handlers/mtop.ts b/packages/datasource-engine/src/handlers/mtop.ts deleted file mode 100644 index 4894b9ad9..000000000 --- a/packages/datasource-engine/src/handlers/mtop.ts +++ /dev/null @@ -1,18 +0,0 @@ -import mtop from '@ali/universal-mtop'; -import { RequestHandler } from '../types'; - -const mtopHandler: RequestHandler = async (options) => { - const { api, uri, data, params, type, method, ...otherOptions } = options; - const reqOptions = { - ...otherOptions, - api: api || uri, - data: data || params, - type: type || method, - }; - - const res = await mtop(reqOptions); - - return res; -}; - -export default mtopHandler; diff --git a/packages/datasource-engine/src/handlers/url-params.ts b/packages/datasource-engine/src/handlers/url-params.ts deleted file mode 100644 index 67540c9f2..000000000 --- a/packages/datasource-engine/src/handlers/url-params.ts +++ /dev/null @@ -1,12 +0,0 @@ -import qs from 'query-string'; -import { RequestHandler } from '../types'; - -export default function urlParamsHandler(search: string | Record): RequestHandler { - const urlParams = typeof search === 'string' ? qs.parse(search) : search; - - return async () => { - return { - data: urlParams, - }; - }; -} diff --git a/packages/datasource-engine/src/helpers/index.ts b/packages/datasource-engine/src/helpers/index.ts new file mode 100644 index 000000000..6202e61b2 --- /dev/null +++ b/packages/datasource-engine/src/helpers/index.ts @@ -0,0 +1,33 @@ +import { + DataHandler, + RequestHandler, + RequestHandlersMap, + RuntimeDataSourceConfig, + RuntimeOptionsConfig, + UrlParamsHandler, + WillFetch, +} from '@ali/lowcode-types'; + +// 默认的 dataSourceItem 的 dataHandler +export const defaultDataHandler: DataHandler = async (response: { data: T }) => response.data; + +// 默认的 dataSourceItem 的 willFetch +export const defaultWillFetch: WillFetch = (options: RuntimeOptionsConfig) => options; + +// 默认的 dataSourceItem 的 shouldFetch +export const defaultShouldFetch = () => true; + +type GetRequestHandler = ( + ds: RuntimeDataSourceConfig, + requestHandlersMap: RequestHandlersMap<{ data: T }>, +) => RequestHandler<{ data: T }> | UrlParamsHandler; + +// 从当前 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']; +}; diff --git a/packages/datasource-engine/src/index.ts b/packages/datasource-engine/src/index.ts index a5a9e0535..560958856 100644 --- a/packages/datasource-engine/src/index.ts +++ b/packages/datasource-engine/src/index.ts @@ -1,2 +1,4 @@ -export * from './core'; -export * from './types'; +import createInterpret from './interpret/DataSourceEngineFactory'; +import createRuntime from './runtime/RuntimeDataSourceEngineFactory'; + +export { createInterpret, createRuntime }; diff --git a/packages/datasource-engine/src/interpret/DataSourceEngineFactory.ts b/packages/datasource-engine/src/interpret/DataSourceEngineFactory.ts new file mode 100644 index 000000000..15d4dc5d8 --- /dev/null +++ b/packages/datasource-engine/src/interpret/DataSourceEngineFactory.ts @@ -0,0 +1,43 @@ +import { adapt2Runtime } from '../core/adapter'; +import { RuntimeDataSourceItem } from '../core/RuntimeDataSourceItem'; +import { reloadDataSourceFactory } from '../core/reloadDataSourceFactory'; +import { + IDataSourceRuntimeContext, + InterpretDataSource, + IRuntimeDataSource, + RequestHandlersMap, + RuntimeDataSource, + RuntimeDataSourceConfig, +} from '@ali/lowcode-types'; +import { getRequestHandler } from '../helpers'; + +/** + * @param dataSource + * @param context + * @param extraConfig: { requestHandlersMap } + */ + +export default ( + dataSource: InterpretDataSource, + context: IDataSourceRuntimeContext, + extraConfig: { + requestHandlersMap: RequestHandlersMap<{ data: unknown }>; + } = { requestHandlersMap: {} }, +) => { + const { requestHandlersMap } = extraConfig; + + const runtimeDataSource: RuntimeDataSource = adapt2Runtime(dataSource, context); + + const dataSourceMap = runtimeDataSource.list.reduce( + (prev: Record, current: RuntimeDataSourceConfig) => { + prev[current.id] = new RuntimeDataSourceItem(current, getRequestHandler(current, requestHandlersMap), context); + return prev; + }, + {}, + ); + + return { + dataSourceMap, + reloadDataSource: reloadDataSourceFactory(runtimeDataSource, dataSourceMap, runtimeDataSource.dataHandler), + }; +}; diff --git a/packages/datasource-engine/src/interpret/index.ts b/packages/datasource-engine/src/interpret/index.ts new file mode 100644 index 000000000..e139eac42 --- /dev/null +++ b/packages/datasource-engine/src/interpret/index.ts @@ -0,0 +1,3 @@ +import createInterpret from './DataSourceEngineFactory'; + +export const create = createInterpret; diff --git a/packages/datasource-engine/src/runtime/RuntimeDataSourceEngineFactory.ts b/packages/datasource-engine/src/runtime/RuntimeDataSourceEngineFactory.ts new file mode 100644 index 000000000..e1cdead62 --- /dev/null +++ b/packages/datasource-engine/src/runtime/RuntimeDataSourceEngineFactory.ts @@ -0,0 +1,55 @@ +/* eslint-disable @typescript-eslint/indent */ +import { + IRuntimeDataSource, + IDataSourceRuntimeContext, + RequestHandlersMap, + RuntimeDataSourceConfig, + RuntimeDataSource, +} from '@ali/lowcode-types'; + +import { RuntimeDataSourceItem } from '../core'; +import { reloadDataSourceFactory } from '../core/reloadDataSourceFactory'; +import { defaultDataHandler, defaultShouldFetch, defaultWillFetch, getRequestHandler } from '../helpers'; + +/** + * @param dataSource + * @param context + * @param extraConfig: { requestHandlersMap } + */ + +export default ( + dataSource: RuntimeDataSource, + context: IDataSourceRuntimeContext, + extraConfig: { + requestHandlersMap: RequestHandlersMap<{ data: unknown }>; + } = { requestHandlersMap: {} }, +) => { + const { requestHandlersMap } = extraConfig; + + // TODO: 对于出码类型,需要做一层数据兼容,给一些必要的值设置默认值,先兜底几个必要的 + 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; + }); + + const dataSourceMap = dataSource.list.reduce( + (prev: Record, current: RuntimeDataSourceConfig) => { + prev[current.id] = new RuntimeDataSourceItem(current, getRequestHandler(current, requestHandlersMap), context); + return prev; + }, + {}, + ); + + return { + dataSourceMap, + reloadDataSource: reloadDataSourceFactory(dataSource, dataSourceMap, dataSource.dataHandler), + }; +}; diff --git a/packages/datasource-engine/src/runtime/index.ts b/packages/datasource-engine/src/runtime/index.ts new file mode 100644 index 000000000..8123c7207 --- /dev/null +++ b/packages/datasource-engine/src/runtime/index.ts @@ -0,0 +1,3 @@ +import createRuntime from './RuntimeDataSourceEngineFactory'; + +export const create = createRuntime; diff --git a/packages/datasource-engine/src/typings.d.ts b/packages/datasource-engine/src/typings.d.ts new file mode 100644 index 000000000..d82d18b87 --- /dev/null +++ b/packages/datasource-engine/src/typings.d.ts @@ -0,0 +1,3 @@ +declare module '@ali/mirror-io-client-mopen'; +declare module '@ali/mirror-io-client-mtop'; +declare module '@ali/mirror-io-client-universal-mtop'; diff --git a/packages/datasource-engine/src/utils.ts b/packages/datasource-engine/src/utils.ts new file mode 100644 index 000000000..19b277615 --- /dev/null +++ b/packages/datasource-engine/src/utils.ts @@ -0,0 +1,193 @@ +/* eslint-disable no-new-func */ + +import { + CompositeValue, + IDataSourceRuntimeContext, + InterpretDataSourceConfig, + isJSExpression, + isJSFunction, + JSExpression, + JSFunction, + JSONObject, + RuntimeOptionsConfig, +} from '@ali/lowcode-types'; + +export const transformExpression = ( + code: string, + context: IDataSourceRuntimeContext, +) => { + try { + return new Function(`return (${code})`).call(context); + } catch (error) { + console.error( + `transformExpression error, code is ${code}, context is ${context}, error is ${error}`, + ); + } +}; + +export const transformFunction = ( + code: string, + context: IDataSourceRuntimeContext, +) => { + try { + return new Function(`return (${code})`).call(context).bind(context); + } catch (error) { + console.error( + `transformFunction error, code is ${code}, context is ${context}, error is ${error}`, + ); + } +}; + +export const transformBoolStr = (str: string) => { + return str !== 'false'; +}; + +export const getRuntimeJsValue = ( + value: JSExpression | JSFunction, + context: IDataSourceRuntimeContext, +) => { + if (!['JSExpression', 'JSFunction'].includes(value.type)) { + console.error(`translate error, value is ${JSON.stringify(value)}`); + return ''; + } + // TODO: 类型修复 + const code = value.compiled || value.value; + return value.type === 'JSFunction' + ? transformFunction(code, context) + : transformExpression(code, context); +}; + +export const getRuntimeBaseValue = (type: string, value: any) => { + switch (type) { + case 'string': + return `${value}`; + case 'boolean': + return typeof value === 'string' + ? transformBoolStr(value as string) + : value; + case 'number': + return Number(value); + default: + return value; + } +}; + +export const getRuntimeValueFromConfig = ( + type: string, + value: CompositeValue, + context: IDataSourceRuntimeContext, +) => { + if (!value) return undefined; + if (isJSExpression(value) || isJSFunction(value)) { + return getRuntimeBaseValue(type, getRuntimeJsValue(value, context)); + } + return getRuntimeBaseValue(type, value); +}; + +export const buildJsonObj = ( + params: JSONObject | JSExpression, + context: IDataSourceRuntimeContext, +) => { + const result: Record = {}; + if (isJSExpression(params)) { + return transformExpression(params.value, context); + } + Object.keys(params).forEach((key: string) => { + const currentParam: any = params[key]; + if (isJSExpression(currentParam)) { + result[key] = transformExpression(currentParam.value, context); + } else { + result[key] = getRuntimeBaseValue(currentParam.type, currentParam.value); + } + }); + + return result; +}; + +export const buildShouldFetch = ( + ds: InterpretDataSourceConfig, + context: IDataSourceRuntimeContext, +) => { + if (!ds.options || !ds.shouldFetch) { + return true; // 默认为 true + } + if (isJSExpression(ds.shouldFetch) || isJSFunction(ds.shouldFetch)) { + return getRuntimeJsValue(ds.shouldFetch, context); + } + + return getRuntimeBaseValue('boolean', ds.shouldFetch); +}; + +export const buildOptions = ( + ds: InterpretDataSourceConfig, + context: IDataSourceRuntimeContext, +) => { + const { options } = ds; + if (!options) return undefined; + // eslint-disable-next-line space-before-function-paren + return function() { + // 默认值 + const fetchOptions: RuntimeOptionsConfig = { + uri: '', + params: {}, + method: 'GET', + isCors: true, + timeout: 5000, + headers: undefined, + v: '1.0', + }; + Object.keys(options).forEach((key: string) => { + switch (key) { + case 'uri': + fetchOptions.uri = getRuntimeValueFromConfig( + 'string', + options.uri, + context, + ); + break; + case 'params': + fetchOptions.params = buildJsonObj(options.params!, context); + break; + case 'method': + fetchOptions.method = getRuntimeValueFromConfig( + 'string', + options.method, + context, + ); + break; + case 'isCors': + fetchOptions.isCors = getRuntimeValueFromConfig( + 'boolean', + options.isCors, + context, + ); + break; + case 'timeout': + fetchOptions.timeout = getRuntimeValueFromConfig( + 'number', + options.timeout, + context, + ); + break; + case 'headers': + fetchOptions.headers = buildJsonObj(options.headers!, context); + break; + case 'v': + fetchOptions.v = getRuntimeValueFromConfig( + 'string', + options.v, + context, + ); + break; + default: + // 其余的除了做表达式或者 function 的转换,直接透传 + fetchOptions[key] = getRuntimeValueFromConfig( + 'unknown', + options[key], + context, + ); + } + }); + return fetchOptions; + }; +}; diff --git a/packages/datasource-engine/test/_helpers/bind-runtime-context.ts b/packages/datasource-engine/test/_helpers/bind-runtime-context.ts new file mode 100644 index 000000000..d8c4d471e --- /dev/null +++ b/packages/datasource-engine/test/_helpers/bind-runtime-context.ts @@ -0,0 +1,25 @@ +export function bindRuntimeContext(x: T, ctx: U): T { + if (typeof x === 'function') { + return x.bind(ctx); + } + + if (typeof x !== 'object') { + return x; + } + + if (x === null) { + return null; + } + + if (Array.isArray(x)) { + return (x.map((item) => bindRuntimeContext(item, ctx)) as unknown) as T; + } + + const res = {} as Record; + + Object.entries(x).forEach(([k, v]) => { + res[k] = bindRuntimeContext(v, ctx); + }); + + return (res as unknown) as T; +} diff --git a/packages/datasource-engine/test/_helpers/delay.ts b/packages/datasource-engine/test/_helpers/delay.ts new file mode 100644 index 000000000..81b6039c3 --- /dev/null +++ b/packages/datasource-engine/test/_helpers/delay.ts @@ -0,0 +1,3 @@ +export async function delay(ms: number = 0) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/packages/datasource-engine/test/_helpers/index.ts b/packages/datasource-engine/test/_helpers/index.ts new file mode 100644 index 000000000..3ef1ff216 --- /dev/null +++ b/packages/datasource-engine/test/_helpers/index.ts @@ -0,0 +1,3 @@ +export * from './mock-context'; +export * from './delay'; +export * from './bind-runtime-context'; diff --git a/packages/datasource-engine/test/_helpers/mock-context.ts b/packages/datasource-engine/test/_helpers/mock-context.ts new file mode 100644 index 000000000..7b62f29b7 --- /dev/null +++ b/packages/datasource-engine/test/_helpers/mock-context.ts @@ -0,0 +1,53 @@ +import { + IDataSourceRuntimeContext, + IDataSourceEngine +} from '@ali/lowcode-types'; + +export class MockContext = Record> + implements IDataSourceRuntimeContext { + private _dataSourceEngine: IDataSourceEngine; + + public constructor( + private _state: TState, + private _createDataSourceEngine: ( + context: IDataSourceRuntimeContext + ) => IDataSourceEngine, + private _customMethods: Record any> = {} + ) { + this._dataSourceEngine = _createDataSourceEngine(this); + + // 自定义方法 + Object.assign(this, _customMethods); + } + + public get state() { + return this._state; + } + + public setState(state: Partial) { + this._state = { + ...this._state, + ...state, + }; + } + + public get dataSourceMap() { + return this._dataSourceEngine.dataSourceMap; + } + + public async reloadDataSource(): Promise { + this._dataSourceEngine.reloadDataSource(); + } + + public get page(): any { + throw new Error('this.page should not be accessed by datasource-engine'); + } + + public get component(): any { + throw new Error( + 'this.component should not be accessed by datasource-engine' + ); + } + + [customMethod: string]: any; +} diff --git a/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/README.md b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/README.md new file mode 100644 index 000000000..355853ddd --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/README.md @@ -0,0 +1,3 @@ +# 关于此场景 + +数据源的 type 可以是 `custom` 类型的, 此时需要提供 `requestHandler` 给数据源 diff --git a/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/_datasource-runtime.ts b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/_datasource-runtime.ts new file mode 100644 index 000000000..fc46c3459 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/_datasource-runtime.ts @@ -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', + }; + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/_datasource-schema.ts b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/_datasource-schema.ts new file mode 100644 index 000000000..0a71914ca --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/_datasource-schema.ts @@ -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', + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/_macro-normal.ts b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/_macro-normal.ts new file mode 100644 index 000000000..1fe39c61e --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/_macro-normal.ts @@ -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>({}, (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'; diff --git a/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/normal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/normal-interpret.test.ts new file mode 100644 index 000000000..9dac84e8d --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/normal-interpret.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/normal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/normal-runtime.test.ts new file mode 100644 index 000000000..761979c30 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-custom-request-handlers/normal-runtime.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/README.md b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/README.md new file mode 100644 index 000000000..e7ab03e28 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/README.md @@ -0,0 +1,3 @@ +# 关于此场景 + +这个是一个及其简单的场景 -- 就是直接调用 fetch,没有啥 dataHandler 之类的 diff --git a/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_datasource-runtime.ts b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_datasource-runtime.ts new file mode 100644 index 000000000..58002ead2 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_datasource-runtime.ts @@ -0,0 +1,15 @@ +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', + type: 'fetch', + options: () => ({ + uri: 'https://mocks.alibaba-inc.com/user.json', + }), + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_datasource-schema.ts b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_datasource-schema.ts new file mode 100644 index 000000000..848a85687 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_datasource-schema.ts @@ -0,0 +1,15 @@ +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', + type: 'fetch', + options: { + uri: 'https://mocks.alibaba-inc.com/user.json', + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_macro-abnormal.ts b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_macro-abnormal.ts new file mode 100644 index 000000000..78c0f5cb2 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_macro-abnormal.ts @@ -0,0 +1,72 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { delay, MockContext } from '../../_helpers'; +// import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +import type { ExecutionContext, Macro } from 'ava'; +import type { SinonFakeTimers } from 'sinon'; + +export const abnormalScene: Macro<[ + { + create: ( + dataSource: any, + ctx: IDataSourceRuntimeContext, + options: any + ) => IDataSourceEngine; + dataSource: RuntimeDataSource | InterpretDataSource; + } +]> = async ( + t: ExecutionContext<{ clock: SinonFakeTimers }>, + { create, dataSource }, + ) => { + const { clock } = t.context; + const ERROR_MSG = 'test error'; + const fetchHandler = sinon.fake(async () => { + await delay(100); + throw new Error(ERROR_MSG); + }); + + const context = new MockContext>({}, (ctx) => create(dataSource, ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + })); + + const setState = sinon.spy(context, 'setState'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后应该失败了,error 状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Error); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, undefined); + t.not(context.dataSourceMap.user.error, undefined); + t.regex(context.dataSourceMap.user.error!.message, new RegExp(ERROR_MSG)); + + // 检查状态数据 + t.assert(setState.notCalled); + t.deepEqual(context.state.user, undefined); + + // fetchHandler 不应该被调 + t.assert(fetchHandler.calledOnce); + }; + +abnormalScene.title = (providedTitle) => providedTitle || 'abnormal scene'; diff --git a/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_macro-normal.ts b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_macro-normal.ts new file mode 100644 index 000000000..9d2a7e353 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/_macro-normal.ts @@ -0,0 +1,81 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { delay, MockContext } from '../../_helpers'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +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 = { + name: 'Alice', + age: 18, + }; + + const fetchHandler = sinon.fake(async () => { + await delay(100); + return { data: USER_DATA }; + }); + + const context = new MockContext>({}, (ctx) => create(dataSource, ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + })); + + const setState = sinon.spy(context, 'setState'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后应该成功了,loaded 状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loaded); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, USER_DATA); + t.deepEqual(context.dataSourceMap.user.error, undefined); + + // 检查状态数据 + t.assert(setState.calledOnce); + t.deepEqual(context.state.user, USER_DATA); + + // fetchHandler 应该被调用了一次 + t.assert(fetchHandler.calledOnce); + + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + // 检查调用参数 + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); + }; + +normalScene.title = (providedTitle) => providedTitle || 'normal scene'; diff --git a/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/abnormal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/abnormal-interpret.test.ts new file mode 100644 index 000000000..816523b10 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/abnormal-interpret.test.ts @@ -0,0 +1,22 @@ +import test, { ExecutionContext } from 'ava'; +import sinon from 'sinon'; + +import { create } from '../../../src/interpret'; + +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; +import { abnormalScene } from './_macro-abnormal'; + +import type { SinonFakeTimers } from 'sinon'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource: DATA_SOURCE_SCHEMA, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/abnormal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/abnormal-runtime.test.ts new file mode 100644 index 000000000..c338af3b1 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/abnormal-runtime.test.ts @@ -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 { abnormalScene } from './_macro-abnormal'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/normal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/normal-interpret.test.ts new file mode 100644 index 000000000..9dac84e8d --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/normal-interpret.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/normal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/normal-runtime.test.ts new file mode 100644 index 000000000..761979c30 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-simplest-defaults/normal-runtime.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/README.md b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/README.md new file mode 100644 index 000000000..3aa7d33e4 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/README.md @@ -0,0 +1,3 @@ +# 关于此场景 + +有些场景下,多个数据源之间有依赖关系,这时候可以将其都设置为 `isSync: true`, 这样这些数据源就会按配置面板的顺序进行串行调用。 diff --git a/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_datasource-runtime.ts b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_datasource-runtime.ts new file mode 100644 index 000000000..c5fe1194c --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_datasource-runtime.ts @@ -0,0 +1,33 @@ +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: 'fetch', + isSync: true, + options() { + return { + uri: 'https://mocks.alibaba-inc.com/user.json', + }; + }, + }, + { + id: 'orders', + isInit: true, + type: 'fetch', + isSync: true, + options() { + return { + uri: 'https://mocks.alibaba-inc.com/orders.json', + params: { + userId: this.state.user.id, + }, + }; + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_datasource-schema.ts b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_datasource-schema.ts new file mode 100644 index 000000000..2c75c180a --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_datasource-schema.ts @@ -0,0 +1,30 @@ +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: 'fetch', + isSync: true, + options: { + uri: 'https://mocks.alibaba-inc.com/user.json', + }, + }, + { + id: 'orders', + isInit: true, + type: 'fetch', + isSync: true, + options: { + uri: 'https://mocks.alibaba-inc.com/orders.json', + params: { + type: 'JSExpression', + value: '{ userId: this.state.user.id }', + }, + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_macro-abnormal.ts b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_macro-abnormal.ts new file mode 100644 index 000000000..f0fa43060 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_macro-abnormal.ts @@ -0,0 +1,96 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { bindRuntimeContext, delay, MockContext } from '../../_helpers'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +import type { ExecutionContext, Macro } from 'ava'; +import type { SinonFakeTimers } from 'sinon'; + +export const abnormalScene: 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', + }; + const ERROR_MSG = 'test error'; + const fetchHandler = sinon.fake(async ({ uri }) => { + await delay(100); + if (/user/.test(uri)) { + return { data: USER_DATA }; + } else { + throw new Error(ERROR_MSG); + } + }); + + const context = new MockContext>({}, (ctx) => create(bindRuntimeContext(dataSource, ctx), ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + })); + + const setState = sinon.spy(context, 'setState'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await clock.tickAsync(50); + + t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Loading); + + 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.regex(context.dataSourceMap.orders.error!.message, new RegExp(ERROR_MSG)); + + // 检查状态数据 + t.assert(setState.calledOnce); + t.deepEqual(context.state.user, USER_DATA); + t.is(context.state.orders, undefined); + + // fetchHandler 应该被调用了2次 + t.assert(fetchHandler.calledTwice); + + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + // 检查调用参数 + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); +}; + +abnormalScene.title = (providedTitle) => providedTitle || 'abnormal scene'; diff --git a/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_macro-normal.ts b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_macro-normal.ts new file mode 100644 index 000000000..c6a95c672 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/_macro-normal.ts @@ -0,0 +1,92 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { bindRuntimeContext, delay, MockContext } from '../../_helpers'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +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', + }; + + const ORDERS_DATA = [{ id: 123 }, { id: 456 }]; + + const fetchHandler = sinon.fake(async ({ uri }) => { + await delay(100); + return { data: /user/.test(uri) ? USER_DATA : ORDERS_DATA }; + }); + + const context = new MockContext>({}, (ctx) => create(bindRuntimeContext(dataSource, ctx), ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + })); + + const setState = sinon.spy(context, 'setState'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await clock.tickAsync(50); + + t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Loading); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后应该成功了,loaded 状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loaded); + t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Loaded); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, USER_DATA); + t.deepEqual(context.dataSourceMap.user.error, undefined); + t.deepEqual(context.dataSourceMap.orders.data, ORDERS_DATA); + t.deepEqual(context.dataSourceMap.orders.error, undefined); + + // 检查状态数据 + t.assert(setState.calledTwice); + t.deepEqual(context.state.user, USER_DATA); + t.deepEqual(context.state.orders, ORDERS_DATA); + + // fetchHandler 应该被调用了2次 + t.assert(fetchHandler.calledTwice); + + // 检查调用参数 + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); + }; + +normalScene.title = (providedTitle) => providedTitle || 'normal scene'; diff --git a/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/abnormal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/abnormal-interpret.test.ts new file mode 100644 index 000000000..816523b10 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/abnormal-interpret.test.ts @@ -0,0 +1,22 @@ +import test, { ExecutionContext } from 'ava'; +import sinon from 'sinon'; + +import { create } from '../../../src/interpret'; + +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; +import { abnormalScene } from './_macro-abnormal'; + +import type { SinonFakeTimers } from 'sinon'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource: DATA_SOURCE_SCHEMA, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/abnormal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/abnormal-runtime.test.ts new file mode 100644 index 000000000..c338af3b1 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/abnormal-runtime.test.ts @@ -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 { abnormalScene } from './_macro-abnormal'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/normal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/normal-interpret.test.ts new file mode 100644 index 000000000..9dac84e8d --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/normal-interpret.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/normal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/normal-runtime.test.ts new file mode 100644 index 000000000..761979c30 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-sync-datasources-dealt-in-serial/normal-runtime.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-url-params/README.md b/packages/datasource-engine/test/scenes/p0-0-url-params/README.md new file mode 100644 index 000000000..e7ab03e28 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-url-params/README.md @@ -0,0 +1,3 @@ +# 关于此场景 + +这个是一个及其简单的场景 -- 就是直接调用 fetch,没有啥 dataHandler 之类的 diff --git a/packages/datasource-engine/test/scenes/p0-0-url-params/_datasource-runtime.ts b/packages/datasource-engine/test/scenes/p0-0-url-params/_datasource-runtime.ts new file mode 100644 index 000000000..09b060b49 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-url-params/_datasource-runtime.ts @@ -0,0 +1,13 @@ +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: 'urlParams', + isInit: true, + type: 'urlParams', + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-0-url-params/_datasource-schema.ts b/packages/datasource-engine/test/scenes/p0-0-url-params/_datasource-schema.ts new file mode 100644 index 000000000..20fcb2154 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-url-params/_datasource-schema.ts @@ -0,0 +1,13 @@ +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: 'urlParams', + isInit: true, + type: 'urlParams', + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-0-url-params/_macro-normal.ts b/packages/datasource-engine/test/scenes/p0-0-url-params/_macro-normal.ts new file mode 100644 index 000000000..0156af315 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-url-params/_macro-normal.ts @@ -0,0 +1,72 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { 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 URL_PARAMS = { + name: 'Alice', + age: '18', + }; + + const urlParamsHandler = sinon.fake(async () => { + return URL_PARAMS; // TODO: 别的都是返回的套了一层 data 的,但是 urlParams 的为啥不一样? + }); + + const context = new MockContext>({}, (ctx) => create(dataSource, ctx, { + requestHandlersMap: { + urlParams: urlParamsHandler, + }, + })); + + const setState = sinon.spy(context, 'setState'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.urlParams.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后应该成功了,loaded 状态 + t.is(context.dataSourceMap.urlParams.status, RuntimeDataSourceStatus.Loaded); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.urlParams.data, URL_PARAMS); + t.deepEqual(context.dataSourceMap.urlParams.error, undefined); + + // 检查状态数据 + t.assert(setState.calledOnce); + t.deepEqual(context.state.urlParams, URL_PARAMS); + + // fetchHandler 应该被调用了一次 + t.assert(urlParamsHandler.calledOnce); + + // 检查调用参数 url 没有 options + t.deepEqual(urlParamsHandler.firstCall.args, [context]); + }; + +normalScene.title = (providedTitle) => providedTitle || 'normal scene'; diff --git a/packages/datasource-engine/test/scenes/p0-0-url-params/normal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-0-url-params/normal-interpret.test.ts new file mode 100644 index 000000000..9dac84e8d --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-url-params/normal-interpret.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-0-url-params/normal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-0-url-params/normal-runtime.test.ts new file mode 100644 index 000000000..761979c30 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-0-url-params/normal-runtime.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/README.md b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/README.md new file mode 100644 index 000000000..cccb18844 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/README.md @@ -0,0 +1,26 @@ +# 关于此场景 + +这个是一个很常见的场景 -- 查出来的数据里面套的还有一层数据,可能有异常状态得需要处理下。 + +比如,期望的正常的数据应该是: + +```json +{ + "success": true, + "data": { + // ... + } +} +``` + +而异常场景下,服务端会返回: + +```json +{ + "success": false, + "message": "这是错误原因", + "code": "错误码" +} +``` + +-- 这时候期望有异常监控埋点。 diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_datasource-runtime.ts b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_datasource-runtime.ts new file mode 100644 index 000000000..4b06ca7b8 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_datasource-runtime.ts @@ -0,0 +1,30 @@ +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: 'fetch', + options: () => ({ + uri: 'https://mocks.alibaba-inc.com/user.json', + }), + dataHandler: function dataHandler(response: any) { + const { data } = response; + if (!data) { + throw new Error('empty data!'); + } + + if (data.success) { + return data.data; + } + + this.recordError({ type: 'FETCH_ERROR', detail: data }); + + throw new Error(data.message); + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_datasource-schema.ts b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_datasource-schema.ts new file mode 100644 index 000000000..2352df683 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_datasource-schema.ts @@ -0,0 +1,35 @@ +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: 'fetch', + options: { + uri: 'https://mocks.alibaba-inc.com/user.json', + }, + dataHandler: { + type: 'JSFunction', + value: ` +function dataHandler(response){ + const { data } = response; + if (!data) { + throw new Error('empty data!'); + } + + if (data.success){ + return data.data; + } + + this.recordError({ type: 'FETCH_ERROR', detail: data }); + + throw new Error(data.message); +} +`, + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_macro-abnormal.ts b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_macro-abnormal.ts new file mode 100644 index 000000000..a07619bd4 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_macro-abnormal.ts @@ -0,0 +1,95 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { bindRuntimeContext, delay, MockContext } from '../../_helpers'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +import type { ExecutionContext, Macro } from 'ava'; +import type { SinonFakeTimers } from 'sinon'; + +export const abnormalScene: Macro<[ + { + create: ( + dataSource: any, + ctx: IDataSourceRuntimeContext, + options: any + ) => IDataSourceEngine; + dataSource: RuntimeDataSource | InterpretDataSource; + } +]> = async ( + t: ExecutionContext<{ clock: SinonFakeTimers }>, + { create, dataSource }, + ) => { + const { clock } = t.context; + const ERROR_MSG = 'test error'; + const fetchHandler = sinon.fake(async () => { + await delay(100); + return { + data: { + success: false, + message: ERROR_MSG, + code: 'E_FOO', + }, + }; + }); + + const context = new MockContext>( + {}, + (ctx) => create(bindRuntimeContext(dataSource, ctx), ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + }), + { + recordError() { }, + }, + ); + + const setState = sinon.spy(context, 'setState'); + const recordError = sinon.spy(context, 'recordError'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后应该失败了,error 状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Error); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, undefined); + t.not(context.dataSourceMap.user.error, undefined); + + t.regex(context.dataSourceMap.user.error!.message, new RegExp(ERROR_MSG)); + + // 检查状态数据 + t.assert(setState.notCalled); + t.deepEqual(context.state.user, undefined); + + // fetchHandler 应该被调用了一次 + t.assert(fetchHandler.calledOnce); + + // 检查调用参数 + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); + + // 埋点应该也会被调用 + t.assert(recordError.calledOnce); + t.snapshot(recordError.firstCall.args); + }; + +abnormalScene.title = (providedTitle) => providedTitle || 'abnormal scene'; diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_macro-normal.ts b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_macro-normal.ts new file mode 100644 index 000000000..8774ef200 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/_macro-normal.ts @@ -0,0 +1,96 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { bindRuntimeContext, delay, MockContext } from '../../_helpers'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +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 = { + name: 'Alice', + age: 18, + }; + + const fetchHandler = sinon.fake(async () => { + await delay(100); + return { + data: { + success: true, + data: USER_DATA, + }, + }; + }); + + const context = new MockContext>( + {}, + (ctx) => create(bindRuntimeContext(dataSource, ctx), ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + }), + { + recordError() { }, + }, + ); + + const setState = sinon.spy(context, 'setState'); + const recordError = sinon.spy(context, 'recordError'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后应该成功了,loaded 状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loaded); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, USER_DATA); + t.deepEqual(context.dataSourceMap.user.error, undefined); + + // 检查状态数据 + t.assert(setState.calledOnce); + t.deepEqual(context.state.user, USER_DATA); + + // fetchHandler 应该被调用了一次 + t.assert(fetchHandler.calledOnce); + + // 检查调用参数 + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); + + // 埋点不应该被调用 + t.assert(recordError.notCalled); + }; + +normalScene.title = (providedTitle) => providedTitle || 'normal scene'; diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/abnormal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/abnormal-interpret.test.ts new file mode 100644 index 000000000..7f3169a50 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/abnormal-interpret.test.ts @@ -0,0 +1,21 @@ +import test, { ExecutionContext } from 'ava'; +import sinon from 'sinon'; + +import { create } from '../../../src/interpret'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; +import { abnormalScene } from './_macro-abnormal'; + +import type { SinonFakeTimers } from 'sinon'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource: DATA_SOURCE_SCHEMA, +}); diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/abnormal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/abnormal-runtime.test.ts new file mode 100644 index 000000000..c338af3b1 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/abnormal-runtime.test.ts @@ -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 { abnormalScene } from './_macro-abnormal'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource, +}); diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/normal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/normal-interpret.test.ts new file mode 100644 index 000000000..9dac84e8d --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/normal-interpret.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/normal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/normal-runtime.test.ts new file mode 100644 index 000000000..761979c30 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/normal-runtime.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-interpret.test.ts.md b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-interpret.test.ts.md new file mode 100644 index 000000000..5baeac319 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-interpret.test.ts.md @@ -0,0 +1,20 @@ +# Snapshot report for `test/scenes/custom-response-status/abnormal-interpret.test.ts` + +The actual snapshot is saved in `abnormal-interpret.test.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## abnormal scene + +> Snapshot 1 + + [ + { + detail: { + code: 'E_FOO', + message: 'test error', + success: false, + }, + type: 'FETCH_ERROR', + }, + ] diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-interpret.test.ts.snap b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-interpret.test.ts.snap new file mode 100644 index 000000000..6a9794bde Binary files /dev/null and b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-interpret.test.ts.snap differ diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-runtime.test.ts.md b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-runtime.test.ts.md new file mode 100644 index 000000000..7c65d528f --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-runtime.test.ts.md @@ -0,0 +1,20 @@ +# Snapshot report for `test/scenes/p0-1-custom-response-status/abnormal-runtime.test.ts` + +The actual snapshot is saved in `abnormal-runtime.test.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## abnormal scene + +> Snapshot 1 + + [ + { + detail: { + code: 'E_FOO', + message: 'test error', + success: false, + }, + type: 'FETCH_ERROR', + }, + ] diff --git a/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-runtime.test.ts.snap b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-runtime.test.ts.snap new file mode 100644 index 000000000..6a9794bde Binary files /dev/null and b/packages/datasource-engine/test/scenes/p0-1-custom-response-status/snapshots/abnormal-runtime.test.ts.snap differ diff --git a/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/README.md b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/README.md new file mode 100644 index 000000000..d2eb4a470 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/README.md @@ -0,0 +1,3 @@ +# 关于此场景 + +有些数据源的错误可以忽略(吃掉)-- 通过 dataHandler 捕获 error,只要其不重新抛出 error 而且不返回 rejected 状态的 Promise. diff --git a/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_datasource-runtime.ts b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_datasource-runtime.ts new file mode 100644 index 000000000..d9ac8d6d0 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_datasource-runtime.ts @@ -0,0 +1,21 @@ +import { RuntimeDataSource } from '@ali/lowcode-types'; + +export const DEFAULT_USER_DATA = { id: 0, name: 'guest' }; // 返回一个兜底的数据 + +// 这里仅仅是数据源部分的: +// @see: https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#XMeF5 +export const dataSource: RuntimeDataSource = { + list: [ + { + id: 'user', + isInit: true, + type: 'fetch', + options: () => ({ + uri: 'https://mocks.alibaba-inc.com/user.json', + }), + dataHandler: function dataHandler(response: any) { + return response.data; + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_datasource-schema.ts b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_datasource-schema.ts new file mode 100644 index 000000000..1b33622a9 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_datasource-schema.ts @@ -0,0 +1,24 @@ +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: 'fetch', + options: { + uri: 'https://mocks.alibaba-inc.com/user.json', + }, + dataHandler: { + type: 'JSFunction', + value: ` +function dataHandler(response) { + return response.data; +} +`, + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_macro-abnormal.ts b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_macro-abnormal.ts new file mode 100644 index 000000000..4b5a2237e --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_macro-abnormal.ts @@ -0,0 +1,78 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { delay, MockContext } from '../../_helpers'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +import type { ExecutionContext, Macro } from 'ava'; +import type { SinonFakeTimers } from 'sinon'; + +export const abnormalScene: Macro<[ + { + create: ( + dataSource: any, + ctx: IDataSourceRuntimeContext, + options: any + ) => IDataSourceEngine; + dataSource: RuntimeDataSource | InterpretDataSource; + } +]> = async ( + t: ExecutionContext<{ clock: SinonFakeTimers }>, + { create, dataSource }, + ) => { + const { clock } = t.context; + const ERROR_MSG = 'test error'; + const fetchHandler = sinon.fake(async () => { + await delay(100); + throw new Error(ERROR_MSG); + }); + + const context = new MockContext>({}, (ctx) => create(dataSource, ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + })); + + const setState = sinon.spy(context, 'setState'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await Promise.all([clock.runAllAsync(), loading]); + + // 注意 error 是会被吃掉了,还是 loaded 状态 + // FIXME: 根据协议内容,dataHandler 返回的结果是需要抛出错误的,那么 fetchHandler 的错误难道不需要处理? + // TODO: 提案:request 如果挂了,不应该需要走 dataHandler 了,没有意义 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Error); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, undefined); + t.not(context.dataSourceMap.user.error, undefined); + t.regex(context.dataSourceMap.user.error!.message, new RegExp(ERROR_MSG)); + + // 检查状态数据 + t.assert(setState.notCalled); + + // fetchHandler 应该没调 + t.assert.skip(fetchHandler.notCalled); + + // 检查调用参数 + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); + }; + +abnormalScene.title = (providedTitle) => providedTitle || 'abnormal scene'; diff --git a/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_macro-normal.ts b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_macro-normal.ts new file mode 100644 index 000000000..c8484c434 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/_macro-normal.ts @@ -0,0 +1,81 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { delay, MockContext } from '../../_helpers'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +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 = { + name: 'Alice', + age: 18, + }; + + const fetchHandler = sinon.fake(async () => { + await delay(100); + return { data: USER_DATA }; + }); + + const context = new MockContext>({}, (ctx) => create(dataSource, ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + })); + + const setState = sinon.spy(context, 'setState'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后应该成功了,loaded 状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loaded); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, USER_DATA); + t.deepEqual(context.dataSourceMap.user.error, undefined); + + // 检查状态数据 + t.assert(setState.calledOnce); + t.deepEqual(context.state.user, USER_DATA); + + // fetchHandler 应该被调用了一次 + t.assert(fetchHandler.calledOnce); + + // 检查调用参数 + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); + }; + +normalScene.title = (providedTitle) => providedTitle || 'normal scene'; diff --git a/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/abnormal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/abnormal-interpret.test.ts new file mode 100644 index 000000000..816523b10 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/abnormal-interpret.test.ts @@ -0,0 +1,22 @@ +import test, { ExecutionContext } from 'ava'; +import sinon from 'sinon'; + +import { create } from '../../../src/interpret'; + +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; +import { abnormalScene } from './_macro-abnormal'; + +import type { SinonFakeTimers } from 'sinon'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource: DATA_SOURCE_SCHEMA, +}); diff --git a/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/abnormal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/abnormal-runtime.test.ts new file mode 100644 index 000000000..c338af3b1 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/abnormal-runtime.test.ts @@ -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 { abnormalScene } from './_macro-abnormal'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource, +}); diff --git a/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/normal-interpret.test.ts b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/normal-interpret.test.ts new file mode 100644 index 000000000..9dac84e8d --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/normal-interpret.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/normal-runtime.test.ts b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/normal-runtime.test.ts new file mode 100644 index 000000000..761979c30 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p0-1-data-handler-can-eat-errors/normal-runtime.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/README.md b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/README.md new file mode 100644 index 000000000..4f8590da4 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/README.md @@ -0,0 +1,3 @@ +# 关于此场景 + +某些场景下 dataHandler 不能同步返回,比如可能需要读取某个特殊的异步的数据源并合并响应数据。 diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_datasource-runtime.ts b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_datasource-runtime.ts new file mode 100644 index 000000000..19d35f910 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_datasource-runtime.ts @@ -0,0 +1,30 @@ +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: 'fetch', + options: () => ({ + uri: 'https://mocks.alibaba-inc.com/user.json', + }), + dataHandler: async function dataHandler(response: any) { + const { data } = response; + if (!data) { + throw new Error('empty data!'); + } + + if (data.success) { + return data.data; + } + + this.recordError({ type: 'FETCH_ERROR', detail: data }); + + throw new Error(data.message); + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_datasource-schema.ts b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_datasource-schema.ts new file mode 100644 index 000000000..f163ea6c2 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_datasource-schema.ts @@ -0,0 +1,35 @@ +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: 'fetch', + options: { + uri: 'https://mocks.alibaba-inc.com/user.json', + }, + dataHandler: { + type: 'JSFunction', + value: ` +async function dataHandler(response){ + const { data } = response; + if (!data) { + throw new Error('empty data!'); + } + + if (data.success){ + return data.data; + } + + this.recordError({ type: 'FETCH_ERROR', detail: data }); + + throw new Error(data.message); +} +`, + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_macro-abnormal.ts b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_macro-abnormal.ts new file mode 100644 index 000000000..a07619bd4 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_macro-abnormal.ts @@ -0,0 +1,95 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { bindRuntimeContext, delay, MockContext } from '../../_helpers'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +import type { ExecutionContext, Macro } from 'ava'; +import type { SinonFakeTimers } from 'sinon'; + +export const abnormalScene: Macro<[ + { + create: ( + dataSource: any, + ctx: IDataSourceRuntimeContext, + options: any + ) => IDataSourceEngine; + dataSource: RuntimeDataSource | InterpretDataSource; + } +]> = async ( + t: ExecutionContext<{ clock: SinonFakeTimers }>, + { create, dataSource }, + ) => { + const { clock } = t.context; + const ERROR_MSG = 'test error'; + const fetchHandler = sinon.fake(async () => { + await delay(100); + return { + data: { + success: false, + message: ERROR_MSG, + code: 'E_FOO', + }, + }; + }); + + const context = new MockContext>( + {}, + (ctx) => create(bindRuntimeContext(dataSource, ctx), ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + }), + { + recordError() { }, + }, + ); + + const setState = sinon.spy(context, 'setState'); + const recordError = sinon.spy(context, 'recordError'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后应该失败了,error 状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Error); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, undefined); + t.not(context.dataSourceMap.user.error, undefined); + + t.regex(context.dataSourceMap.user.error!.message, new RegExp(ERROR_MSG)); + + // 检查状态数据 + t.assert(setState.notCalled); + t.deepEqual(context.state.user, undefined); + + // fetchHandler 应该被调用了一次 + t.assert(fetchHandler.calledOnce); + + // 检查调用参数 + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); + + // 埋点应该也会被调用 + t.assert(recordError.calledOnce); + t.snapshot(recordError.firstCall.args); + }; + +abnormalScene.title = (providedTitle) => providedTitle || 'abnormal scene'; diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_macro-normal.ts b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_macro-normal.ts new file mode 100644 index 000000000..8774ef200 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/_macro-normal.ts @@ -0,0 +1,96 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { bindRuntimeContext, delay, MockContext } from '../../_helpers'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +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 = { + name: 'Alice', + age: 18, + }; + + const fetchHandler = sinon.fake(async () => { + await delay(100); + return { + data: { + success: true, + data: USER_DATA, + }, + }; + }); + + const context = new MockContext>( + {}, + (ctx) => create(bindRuntimeContext(dataSource, ctx), ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + }), + { + recordError() { }, + }, + ); + + const setState = sinon.spy(context, 'setState'); + const recordError = sinon.spy(context, 'recordError'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后应该成功了,loaded 状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loaded); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, USER_DATA); + t.deepEqual(context.dataSourceMap.user.error, undefined); + + // 检查状态数据 + t.assert(setState.calledOnce); + t.deepEqual(context.state.user, USER_DATA); + + // fetchHandler 应该被调用了一次 + t.assert(fetchHandler.calledOnce); + + // 检查调用参数 + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); + + // 埋点不应该被调用 + t.assert(recordError.notCalled); + }; + +normalScene.title = (providedTitle) => providedTitle || 'normal scene'; diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/abnormal-interpret.test.ts b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/abnormal-interpret.test.ts new file mode 100644 index 000000000..7f3169a50 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/abnormal-interpret.test.ts @@ -0,0 +1,21 @@ +import test, { ExecutionContext } from 'ava'; +import sinon from 'sinon'; + +import { create } from '../../../src/interpret'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; +import { abnormalScene } from './_macro-abnormal'; + +import type { SinonFakeTimers } from 'sinon'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource: DATA_SOURCE_SCHEMA, +}); diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/abnormal-runtime.test.ts b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/abnormal-runtime.test.ts new file mode 100644 index 000000000..c338af3b1 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/abnormal-runtime.test.ts @@ -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 { abnormalScene } from './_macro-abnormal'; + +test.before((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock = sinon.useFakeTimers(); +}); + +test.after((t: ExecutionContext<{ clock: SinonFakeTimers }>) => { + t.context.clock.restore(); +}); + +test(abnormalScene, { + create, + dataSource, +}); diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/normal-interpret.test.ts b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/normal-interpret.test.ts new file mode 100644 index 000000000..9dac84e8d --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/normal-interpret.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/normal-runtime.test.ts b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/normal-runtime.test.ts new file mode 100644 index 000000000..761979c30 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/normal-runtime.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-interpret.test.ts.md b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-interpret.test.ts.md new file mode 100644 index 000000000..558b612d6 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-interpret.test.ts.md @@ -0,0 +1,20 @@ +# Snapshot report for `test/scenes/data-handler-returns-promise/abnormal-interpret.test.ts` + +The actual snapshot is saved in `abnormal-interpret.test.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## abnormal scene + +> Snapshot 1 + + [ + { + detail: { + code: 'E_FOO', + message: 'test error', + success: false, + }, + type: 'FETCH_ERROR', + }, + ] diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-interpret.test.ts.snap b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-interpret.test.ts.snap new file mode 100644 index 000000000..6a9794bde Binary files /dev/null and b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-interpret.test.ts.snap differ diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-runtime.test.ts.md b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-runtime.test.ts.md new file mode 100644 index 000000000..98deb8904 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-runtime.test.ts.md @@ -0,0 +1,20 @@ +# Snapshot report for `test/scenes/p1-0-data-handler-returns-promise/abnormal-runtime.test.ts` + +The actual snapshot is saved in `abnormal-runtime.test.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## abnormal scene + +> Snapshot 1 + + [ + { + detail: { + code: 'E_FOO', + message: 'test error', + success: false, + }, + type: 'FETCH_ERROR', + }, + ] diff --git a/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-runtime.test.ts.snap b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-runtime.test.ts.snap new file mode 100644 index 000000000..6a9794bde Binary files /dev/null and b/packages/datasource-engine/test/scenes/p1-0-data-handler-returns-promise/snapshots/abnormal-runtime.test.ts.snap differ diff --git a/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/README.md b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/README.md new file mode 100644 index 000000000..efde6173c --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/README.md @@ -0,0 +1,3 @@ +# 关于此场景 + +某些场景下 shouldFetch 的结果可以影响请求是否被发出去。 diff --git a/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/_datasource-runtime.ts b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/_datasource-runtime.ts new file mode 100644 index 000000000..49d30425e --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/_datasource-runtime.ts @@ -0,0 +1,32 @@ +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: 'fetch', + isSync: true, + options: () => ({ + uri: 'https://mocks.alibaba-inc.com/user.json', + }), + }, + { + id: 'orders', + isInit: true, + type: 'fetch', + isSync: true, + shouldFetch: () => false, + options() { + return { + uri: 'https://mocks.alibaba-inc.com/orders.json', + params: { + userId: this.state.user.id, + }, + }; + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/_datasource-schema.ts b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/_datasource-schema.ts new file mode 100644 index 000000000..4032c9a35 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/_datasource-schema.ts @@ -0,0 +1,38 @@ +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: 'fetch', + isSync: true, + options: { + uri: 'https://mocks.alibaba-inc.com/user.json', + }, + }, + { + id: 'orders', + isInit: true, + type: 'fetch', + isSync: true, + shouldFetch: { + type: 'JSFunction', + value: ` + function (){ + return false; + } + `, + }, + options: { + uri: 'https://mocks.alibaba-inc.com/orders.json', + params: { + type: 'JSExpression', + value: '{ userId: this.state.user.id }', + }, + }, + }, + ], +}; diff --git a/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/_macro-normal.ts b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/_macro-normal.ts new file mode 100644 index 000000000..a12e0a48c --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/_macro-normal.ts @@ -0,0 +1,108 @@ +import { + InterpretDataSource, + IDataSourceEngine, + IDataSourceRuntimeContext, + RuntimeDataSource, + RuntimeDataSourceStatus, +} from '@ali/lowcode-types'; +import sinon from 'sinon'; + +import { bindRuntimeContext, delay, MockContext } from '../../_helpers'; + +import type { ExecutionContext, Macro } from 'ava'; +import type { SinonFakeTimers } from 'sinon'; +import { DATA_SOURCE_SCHEMA } from './_datasource-schema'; + +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 ORDERS_ERROR_MSG = + 'the orders request should not fetch, please check the condition'; + + const USER_DATA = { + name: 'Alice', + age: 18, + }; + + const fetchHandler = sinon.fake(async () => { + await delay(100); + return { + data: USER_DATA, + }; + }); + + const context = new MockContext>( + {}, + (ctx) => create(bindRuntimeContext(dataSource, ctx), ctx, { + requestHandlersMap: { + fetch: fetchHandler, + }, + }), + { + recordError() { }, + }, + ); + + const setState = sinon.spy(context, 'setState'); + // const recordError = sinon.spy(context, 'recordError'); + + // 一开始应该是初始状态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Initial); + t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Initial); + + const loading = context.reloadDataSource(); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loading); + + await clock.tickAsync(50); + + // 中间应该有 loading 态 + t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Error); + + await Promise.all([clock.runAllAsync(), loading]); + + // 最后 user 成功, orders 失败 + t.is(context.dataSourceMap.user.status, RuntimeDataSourceStatus.Loaded); + t.is(context.dataSourceMap.orders.status, RuntimeDataSourceStatus.Error); + + // 检查数据源的数据 + t.deepEqual(context.dataSourceMap.user.data, USER_DATA); + t.is(context.dataSourceMap.user.error, undefined); + t.regex( + context.dataSourceMap.orders.error!.message, + new RegExp(ORDERS_ERROR_MSG), + ); + + // 检查状态数据 + t.assert(setState.calledOnce); + t.deepEqual(context.state.user, USER_DATA); + + // fetchHandler 应该被调用了 1 次 + t.assert(fetchHandler.calledOnce); + + // 检查调用参数 + + const firstListItemOptions = DATA_SOURCE_SCHEMA.list[0].options; + const fetchHandlerCallArgs = fetchHandler.firstCall.args[0]; + t.is(firstListItemOptions.uri, fetchHandlerCallArgs.uri); + + // // 埋点应该也会被调用 + // t.assert(recordError.calledOnce); + // t.snapshot(recordError.firstCall.args); + }; + +normalScene.title = (providedTitle) => providedTitle || 'normal scene'; diff --git a/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/normal-interpret.test.ts b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/normal-interpret.test.ts new file mode 100644 index 000000000..9dac84e8d --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/normal-interpret.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/normal-runtime.test.ts b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/normal-runtime.test.ts new file mode 100644 index 000000000..761979c30 --- /dev/null +++ b/packages/datasource-engine/test/scenes/p1-1-shouldfetch-control-request/normal-runtime.test.ts @@ -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, +}); diff --git a/packages/datasource-engine/tsconfig.json b/packages/datasource-engine/tsconfig.json index e06677d2b..4c30ce1c8 100644 --- a/packages/datasource-engine/tsconfig.json +++ b/packages/datasource-engine/tsconfig.json @@ -1,31 +1,22 @@ { "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, - "lib": [ - "DOM", - "ES2015", - "ES2016", - "ES2017", - "ES2018", - "ES2019" - ] /* Specify library files to be included in the compilation. */, + // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, "declaration": true /* Generates corresponding '.d.ts' file. */, // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true /* Generates corresponding '.map' file. */, + // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./lib" /* Redirect output structure to the directory. */, - "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, + "outDir": "dist" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - "removeComments": false /* Do not emit comments to output. */, + // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ "importHelpers": true /* Import emit helpers from 'tslib'. */, // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ @@ -51,14 +42,10 @@ "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - "rootDirs": [ - "src" - ] /* List of root folders whose combined content represents the structure of the project at runtime. */, - "typeRoots": [ - "./node_modules/@types" - ] /* List of folders to include type definitions from. */, + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ @@ -74,7 +61,8 @@ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ /* Advanced Options */ - "skipLibCheck": true /* Skip type checking of declaration files. */, - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "skipLibCheck": true + }, + "include": ["src"] } diff --git a/packages/datasource-fetch-handler/.eslintignore b/packages/datasource-fetch-handler/.eslintignore new file mode 100644 index 000000000..fecb7c26d --- /dev/null +++ b/packages/datasource-fetch-handler/.eslintignore @@ -0,0 +1,3 @@ +/node_modules +/es +/lib \ No newline at end of file diff --git a/packages/datasource-fetch-handler/.eslintrc.js b/packages/datasource-fetch-handler/.eslintrc.js new file mode 100644 index 000000000..8c13fcaf4 --- /dev/null +++ b/packages/datasource-fetch-handler/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: '../../.eslintrc', + rules: { + '@typescript-eslint/no-parameter-properties': 1, + 'no-param-reassign': 0, + }, +}; diff --git a/packages/datasource-fetch-handler/.prettierrc.js b/packages/datasource-fetch-handler/.prettierrc.js new file mode 100644 index 000000000..de2f53cdf --- /dev/null +++ b/packages/datasource-fetch-handler/.prettierrc.js @@ -0,0 +1,4 @@ +module.exports = { + singleQuote: true, + trailingComma: 'all', +}; diff --git a/packages/datasource-fetch-handler/CHANGELOG.md b/packages/datasource-fetch-handler/CHANGELOG.md new file mode 100644 index 000000000..4ac8a7c9f --- /dev/null +++ b/packages/datasource-fetch-handler/CHANGELOG.md @@ -0,0 +1,20 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +## 1.0.4 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-fetch-handler + + +## 1.0.3 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-fetch-handler diff --git a/packages/datasource-fetch-handler/package.json b/packages/datasource-fetch-handler/package.json new file mode 100644 index 000000000..67b68f4d2 --- /dev/null +++ b/packages/datasource-fetch-handler/package.json @@ -0,0 +1,27 @@ +{ + "name": "@ali/lowcode-datasource-fetch-handler", + "version": "1.0.4", + "main": "lib/index.js", + "module": "es/index.js", + "typings": "es/index.d.ts", + "files": [ + "src", + "lib", + "es" + ], + "scripts": { + "dev": "tsc --watch", + "clean": "rm -rf es lib", + "build": "npm run clean && tsc && tsc --outDir ./lib --module commonjs ", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@ali/lowcode-types": "1.0.13-alpha.1", + "typescript": "^3.9.7", + "universal-request": "^2.2.0" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + }, + "gitHead": "829e504c52a294fc28966ab0e491f4d2de1d4c90" +} diff --git a/packages/datasource-fetch-handler/src/index.ts b/packages/datasource-fetch-handler/src/index.ts new file mode 100644 index 000000000..2bb5df893 --- /dev/null +++ b/packages/datasource-fetch-handler/src/index.ts @@ -0,0 +1,20 @@ +import { RuntimeOptionsConfig } from '@ali/lowcode-types'; + +import request from 'universal-request'; +import { RequestOptions, AsObject } from 'universal-request/lib/types'; + +// config 留着扩展 +export function createFetchHandler(config?: Record) { + return async function (options: RuntimeOptionsConfig) { + const requestConfig: RequestOptions = { + ...options, + url: options.uri, + method: options.method as RequestOptions['method'], + data: options.params as AsObject, + headers: options.headers as AsObject, + ...config, + }; + const response = await request(requestConfig); + return response; + }; +} diff --git a/packages/datasource-fetch-handler/tsconfig.json b/packages/datasource-fetch-handler/tsconfig.json new file mode 100644 index 000000000..4f788e5ed --- /dev/null +++ b/packages/datasource-fetch-handler/tsconfig.json @@ -0,0 +1,68 @@ +{ + "compilerOptions": { + "outDir": "es", + "target": "es6", + "module": "esnext", + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + // "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + // "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "lib": [ + "ESNext", + "DOM" + ] /* Specify library files to be included in the compilation. */, + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "es" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + /* Advanced Options */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/datasource-jsonp-handler/.eslintignore b/packages/datasource-jsonp-handler/.eslintignore new file mode 100644 index 000000000..fecb7c26d --- /dev/null +++ b/packages/datasource-jsonp-handler/.eslintignore @@ -0,0 +1,3 @@ +/node_modules +/es +/lib \ No newline at end of file diff --git a/packages/datasource-jsonp-handler/.eslintrc.js b/packages/datasource-jsonp-handler/.eslintrc.js new file mode 100644 index 000000000..8c13fcaf4 --- /dev/null +++ b/packages/datasource-jsonp-handler/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: '../../.eslintrc', + rules: { + '@typescript-eslint/no-parameter-properties': 1, + 'no-param-reassign': 0, + }, +}; diff --git a/packages/datasource-jsonp-handler/.prettierrc.js b/packages/datasource-jsonp-handler/.prettierrc.js new file mode 100644 index 000000000..de2f53cdf --- /dev/null +++ b/packages/datasource-jsonp-handler/.prettierrc.js @@ -0,0 +1,4 @@ +module.exports = { + singleQuote: true, + trailingComma: 'all', +}; diff --git a/packages/datasource-jsonp-handler/CHANGELOG.md b/packages/datasource-jsonp-handler/CHANGELOG.md new file mode 100644 index 000000000..eb1e240b7 --- /dev/null +++ b/packages/datasource-jsonp-handler/CHANGELOG.md @@ -0,0 +1,20 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +## 1.0.1 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-jsonp-handler + + +# 1.0.0 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-jsonp-handler diff --git a/packages/datasource-jsonp-handler/package.json b/packages/datasource-jsonp-handler/package.json new file mode 100644 index 000000000..3e62255a7 --- /dev/null +++ b/packages/datasource-jsonp-handler/package.json @@ -0,0 +1,29 @@ +{ + "name": "@ali/lowcode-datasource-jsonp-handler", + "version": "1.0.1", + "main": "lib/index.js", + "module": "es/index.js", + "typings": "es/index.d.ts", + "files": [ + "src", + "lib", + "es" + ], + "scripts": { + "dev": "tsc --watch", + "clean": "rm -rf es lib", + "build": "npm run clean && tsc && tsc --outDir ./lib --module commonjs ", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@ali/lowcode-types": "1.0.13-alpha.1", + "jsonp": "^0.2.1", + "typescript": "^3.9.7" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + }, + "devDependencies": { + "@types/jsonp": "^0.2.0" + } +} diff --git a/packages/datasource-jsonp-handler/src/index.ts b/packages/datasource-jsonp-handler/src/index.ts new file mode 100644 index 000000000..ca2d58bf8 --- /dev/null +++ b/packages/datasource-jsonp-handler/src/index.ts @@ -0,0 +1,31 @@ +import { RuntimeOptionsConfig } from '@ali/lowcode-types'; +import jsonp from 'jsonp'; + +const handleJsonpFetch = (url: string, param: string, name: string) => { + return new Promise((res, rej) => { + jsonp(url, { param, name }, (error: Error | null, data: any) => { + if (error) { + return rej(error); + } + res({ data }); + }); + }); +}; + +// config 留着扩展 +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function createJsonpHandler(config?: Record) { + // eslint-disable-next-line space-before-function-paren + return async function(options: RuntimeOptionsConfig) { + const params = + typeof options.params === 'object' + ? JSON.stringify(options.params) + : options.params; + const response = await handleJsonpFetch( + options.uri, + params || '', + options.name as string | '', + ); + return response; + }; +} diff --git a/packages/datasource-jsonp-handler/tsconfig.json b/packages/datasource-jsonp-handler/tsconfig.json new file mode 100644 index 000000000..eeea7a79b --- /dev/null +++ b/packages/datasource-jsonp-handler/tsconfig.json @@ -0,0 +1,65 @@ +{ + "compilerOptions": { + "outDir": "es", + "target": "es6", + "module": "esnext", + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + // "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + // "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "lib": ["ESNext", "DOM"] /* Specify library files to be included in the compilation. */, + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "es" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + /* Advanced Options */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/datasource-mopen-handler/.eslintignore b/packages/datasource-mopen-handler/.eslintignore new file mode 100644 index 000000000..fecb7c26d --- /dev/null +++ b/packages/datasource-mopen-handler/.eslintignore @@ -0,0 +1,3 @@ +/node_modules +/es +/lib \ No newline at end of file diff --git a/packages/datasource-mopen-handler/.eslintrc.js b/packages/datasource-mopen-handler/.eslintrc.js new file mode 100644 index 000000000..8c13fcaf4 --- /dev/null +++ b/packages/datasource-mopen-handler/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: '../../.eslintrc', + rules: { + '@typescript-eslint/no-parameter-properties': 1, + 'no-param-reassign': 0, + }, +}; diff --git a/packages/datasource-mopen-handler/.prettierrc.js b/packages/datasource-mopen-handler/.prettierrc.js new file mode 100644 index 000000000..de2f53cdf --- /dev/null +++ b/packages/datasource-mopen-handler/.prettierrc.js @@ -0,0 +1,4 @@ +module.exports = { + singleQuote: true, + trailingComma: 'all', +}; diff --git a/packages/datasource-mopen-handler/CHANGELOG.md b/packages/datasource-mopen-handler/CHANGELOG.md new file mode 100644 index 000000000..536cbb2f6 --- /dev/null +++ b/packages/datasource-mopen-handler/CHANGELOG.md @@ -0,0 +1,20 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +## 1.0.4 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-mopen-handler + + +## 1.0.3 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-mopen-handler diff --git a/packages/datasource-mopen-handler/package.json b/packages/datasource-mopen-handler/package.json new file mode 100644 index 000000000..a0c093745 --- /dev/null +++ b/packages/datasource-mopen-handler/package.json @@ -0,0 +1,27 @@ +{ + "name": "@ali/lowcode-datasource-mopen-handler", + "version": "1.0.4", + "main": "lib/index.js", + "module": "es/index.js", + "typings": "es/index.d.ts", + "files": [ + "src", + "lib", + "es" + ], + "scripts": { + "dev": "tsc --watch", + "clean": "rm -rf es lib", + "build": "npm run clean && tsc && tsc --outDir ./lib --module commonjs ", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@ali/lowcode-types": "^1.0.13", + "@ali/mirror-io-client-mopen": "1.0.0-beta.16", + "typescript": "^3.9.7" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + }, + "gitHead": "829e504c52a294fc28966ab0e491f4d2de1d4c90" +} diff --git a/packages/datasource-mopen-handler/src/index.ts b/packages/datasource-mopen-handler/src/index.ts new file mode 100644 index 000000000..20fa4d2cc --- /dev/null +++ b/packages/datasource-mopen-handler/src/index.ts @@ -0,0 +1,33 @@ +import { + MopenClient, + MopenClientConfig, + MOPEN_APPKEY_XSPACE_PRE_ONLINE, + MOPEN_DOMAIN_TAOBAO_PRE_ONLINE, +} from '@ali/mirror-io-client-mopen'; +import { RuntimeOptionsConfig } from '@ali/lowcode-types'; + +type Method = 'get' | 'post' | 'GET' | 'POST'; + +type DataType = 'jsonp' | 'json' | 'originaljsonp'; + +export function createMopenHandler( + config: MopenClientConfig = { + mtopDomain: MOPEN_DOMAIN_TAOBAO_PRE_ONLINE, + appKey: MOPEN_APPKEY_XSPACE_PRE_ONLINE, + }, +) { + return async function(options: RuntimeOptionsConfig): Promise<{ data: T }> { + const { data, response } = await MopenClient.request({ + config, + ...options, + api: options.uri, + v: options.v as string, + data: options.params, + type: (options.method as Method) || 'get', + dataType: (options.dataType as DataType) || 'json', + timeout: options.timeout, + headers: options.headers, + }); + return { ...response, data }; + }; +} diff --git a/packages/datasource-mopen-handler/tsconfig.json b/packages/datasource-mopen-handler/tsconfig.json new file mode 100644 index 000000000..4f788e5ed --- /dev/null +++ b/packages/datasource-mopen-handler/tsconfig.json @@ -0,0 +1,68 @@ +{ + "compilerOptions": { + "outDir": "es", + "target": "es6", + "module": "esnext", + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + // "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + // "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "lib": [ + "ESNext", + "DOM" + ] /* Specify library files to be included in the compilation. */, + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "es" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + /* Advanced Options */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/datasource-mtop-handler/.eslintignore b/packages/datasource-mtop-handler/.eslintignore new file mode 100644 index 000000000..fecb7c26d --- /dev/null +++ b/packages/datasource-mtop-handler/.eslintignore @@ -0,0 +1,3 @@ +/node_modules +/es +/lib \ No newline at end of file diff --git a/packages/datasource-mtop-handler/.eslintrc.js b/packages/datasource-mtop-handler/.eslintrc.js new file mode 100644 index 000000000..8c13fcaf4 --- /dev/null +++ b/packages/datasource-mtop-handler/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: '../../.eslintrc', + rules: { + '@typescript-eslint/no-parameter-properties': 1, + 'no-param-reassign': 0, + }, +}; diff --git a/packages/datasource-mtop-handler/.prettierrc.js b/packages/datasource-mtop-handler/.prettierrc.js new file mode 100644 index 000000000..a425d3f76 --- /dev/null +++ b/packages/datasource-mtop-handler/.prettierrc.js @@ -0,0 +1,4 @@ +module.exports = { + singleQuote: true, + trailingComma: 'es5', +}; diff --git a/packages/datasource-mtop-handler/CHANGELOG.md b/packages/datasource-mtop-handler/CHANGELOG.md new file mode 100644 index 000000000..05018ec7a --- /dev/null +++ b/packages/datasource-mtop-handler/CHANGELOG.md @@ -0,0 +1,20 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +## 1.0.3 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-mtop-handler + + +## 1.0.2 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-mtop-handler diff --git a/packages/datasource-mtop-handler/package.json b/packages/datasource-mtop-handler/package.json new file mode 100644 index 000000000..079521dc3 --- /dev/null +++ b/packages/datasource-mtop-handler/package.json @@ -0,0 +1,27 @@ +{ + "name": "@ali/lowcode-datasource-mtop-handler", + "version": "1.0.3", + "main": "lib/index.js", + "module": "es/index.js", + "typings": "es/index.d.ts", + "files": [ + "src", + "lib", + "es" + ], + "scripts": { + "dev": "tsc --watch", + "clean": "rm -rf es lib", + "build": "npm run clean && tsc && tsc --outDir ./lib --module commonjs ", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@ali/lowcode-types": "1.0.13-alpha.1", + "@ali/universal-mtop": "^5.1.9", + "typescript": "^3.9.7" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + }, + "gitHead": "829e504c52a294fc28966ab0e491f4d2de1d4c90" +} diff --git a/packages/datasource-mtop-handler/src/index.ts b/packages/datasource-mtop-handler/src/index.ts new file mode 100644 index 000000000..a582076da --- /dev/null +++ b/packages/datasource-mtop-handler/src/index.ts @@ -0,0 +1,38 @@ +import mtopRequest from '@ali/universal-mtop'; + +import { RuntimeOptionsConfig } from '@ali/lowcode-types'; + +export type Method = 'get' | 'post' | 'GET' | 'POST'; + +export type DataType = 'jsonp' | 'json' | 'originaljsonp'; + +// 考虑一下 mtop 类型的问题,官方没有提供 ts 文件 +export function createMtopHandler(config?: MTopConfig) { + if (config && Object.keys(config).length > 0) { + Object.keys(config).forEach((key: string) => { + mtopRequest.config(key, config[key]); + }); + } + // eslint-disable-next-line space-before-function-paren + return async function(options: RuntimeOptionsConfig): Promise<{ data: T }> { + const response = await mtopRequest.request({ + api: options.uri, + v: (options.v as string) || '1.0', + data: options.params, + type: (options.method as Method) || 'get', + dataType: (options.dataType as DataType) || 'json', + timeout: options.timeout, + headers: options.headers, + }); + if (response.ret && response.ret[0].indexOf('SUCCESS::') > -1) { + // 校验成功 + return response; + } + // 默认异常 + let errorMsg = '未知异常'; + if (response.ret && response.ret[0]) { + errorMsg = response.ret[0]; + } + throw new Error(errorMsg); + }; +} diff --git a/packages/datasource-mtop-handler/src/typings.d.ts b/packages/datasource-mtop-handler/src/typings.d.ts new file mode 100644 index 000000000..11f4d02cd --- /dev/null +++ b/packages/datasource-mtop-handler/src/typings.d.ts @@ -0,0 +1,11 @@ +declare interface MTopConfig { + prefix?: string; + subDomain?: string; + mainDomain?: string; + [index: string]: unknown; +} + +declare module '@ali/universal-mtop' { + const request: (config: any) => Promise<{ data: T; ret: string[] }>; + const config: (key: string, value: unknown) => void; +} diff --git a/packages/datasource-mtop-handler/tsconfig.json b/packages/datasource-mtop-handler/tsconfig.json new file mode 100644 index 000000000..4f788e5ed --- /dev/null +++ b/packages/datasource-mtop-handler/tsconfig.json @@ -0,0 +1,68 @@ +{ + "compilerOptions": { + "outDir": "es", + "target": "es6", + "module": "esnext", + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + // "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + // "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "lib": [ + "ESNext", + "DOM" + ] /* Specify library files to be included in the compilation. */, + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "es" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + /* Advanced Options */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/datasource-universal-mtop-handler/.eslintignore b/packages/datasource-universal-mtop-handler/.eslintignore new file mode 100644 index 000000000..fecb7c26d --- /dev/null +++ b/packages/datasource-universal-mtop-handler/.eslintignore @@ -0,0 +1,3 @@ +/node_modules +/es +/lib \ No newline at end of file diff --git a/packages/datasource-universal-mtop-handler/.eslintrc.js b/packages/datasource-universal-mtop-handler/.eslintrc.js new file mode 100644 index 000000000..8c13fcaf4 --- /dev/null +++ b/packages/datasource-universal-mtop-handler/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: '../../.eslintrc', + rules: { + '@typescript-eslint/no-parameter-properties': 1, + 'no-param-reassign': 0, + }, +}; diff --git a/packages/datasource-universal-mtop-handler/.prettierrc.js b/packages/datasource-universal-mtop-handler/.prettierrc.js new file mode 100644 index 000000000..de2f53cdf --- /dev/null +++ b/packages/datasource-universal-mtop-handler/.prettierrc.js @@ -0,0 +1,4 @@ +module.exports = { + singleQuote: true, + trailingComma: 'all', +}; diff --git a/packages/datasource-universal-mtop-handler/CHANGELOG.md b/packages/datasource-universal-mtop-handler/CHANGELOG.md new file mode 100644 index 000000000..a664d8128 --- /dev/null +++ b/packages/datasource-universal-mtop-handler/CHANGELOG.md @@ -0,0 +1,20 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +## 1.0.3 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-universal-mtop-handler + + +## 1.0.2 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-universal-mtop-handler diff --git a/packages/datasource-universal-mtop-handler/package.json b/packages/datasource-universal-mtop-handler/package.json new file mode 100644 index 000000000..2eb403067 --- /dev/null +++ b/packages/datasource-universal-mtop-handler/package.json @@ -0,0 +1,27 @@ +{ + "name": "@ali/lowcode-datasource-universal-mtop-handler", + "version": "1.0.3", + "main": "lib/index.js", + "module": "es/index.js", + "typings": "es/index.d.ts", + "files": [ + "src", + "lib", + "es" + ], + "scripts": { + "dev": "tsc --watch", + "clean": "rm -rf es lib", + "build": "npm run clean && tsc && tsc --outDir ./lib --module commonjs ", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@ali/lowcode-types": "^1.0.13", + "@ali/mirror-io-client-universal-mtop": "1.0.0-beta.16", + "typescript": "^3.9.7" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + }, + "gitHead": "829e504c52a294fc28966ab0e491f4d2de1d4c90" +} diff --git a/packages/datasource-universal-mtop-handler/src/index.ts b/packages/datasource-universal-mtop-handler/src/index.ts new file mode 100644 index 000000000..81cad731d --- /dev/null +++ b/packages/datasource-universal-mtop-handler/src/index.ts @@ -0,0 +1,29 @@ +import { + UniversalMtopClient, + UniversalMtopClientConfig, +} from '@ali/mirror-io-client-universal-mtop'; + +import { RuntimeOptionsConfig } from '@ali/lowcode-types'; + +type Method = 'get' | 'post' | 'GET' | 'POST'; + +type DataType = 'jsonp' | 'json' | 'originaljsonp'; + +export function createMopenHandler( + config?: UniversalMtopClientConfig, +) { + return async function (options: RuntimeOptionsConfig): Promise<{ data: T }> { + const { data, response } = await UniversalMtopClient.request({ + config, + ...options, + api: options.uri, + v: options.v as string, + data: options.params, + type: (options.method as Method) || 'get', + dataType: (options.dataType as DataType) || 'json', + timeout: options.timeout, + headers: options.headers, + }); + return { ...response, data }; + }; +} diff --git a/packages/datasource-universal-mtop-handler/tsconfig.json b/packages/datasource-universal-mtop-handler/tsconfig.json new file mode 100644 index 000000000..4f788e5ed --- /dev/null +++ b/packages/datasource-universal-mtop-handler/tsconfig.json @@ -0,0 +1,68 @@ +{ + "compilerOptions": { + "outDir": "es", + "target": "es6", + "module": "esnext", + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + // "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + // "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "lib": [ + "ESNext", + "DOM" + ] /* Specify library files to be included in the compilation. */, + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "es" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + /* Advanced Options */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/datasource-url-params-handler/.eslintignore b/packages/datasource-url-params-handler/.eslintignore new file mode 100644 index 000000000..fecb7c26d --- /dev/null +++ b/packages/datasource-url-params-handler/.eslintignore @@ -0,0 +1,3 @@ +/node_modules +/es +/lib \ No newline at end of file diff --git a/packages/datasource-url-params-handler/.eslintrc.js b/packages/datasource-url-params-handler/.eslintrc.js new file mode 100644 index 000000000..8c13fcaf4 --- /dev/null +++ b/packages/datasource-url-params-handler/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: '../../.eslintrc', + rules: { + '@typescript-eslint/no-parameter-properties': 1, + 'no-param-reassign': 0, + }, +}; diff --git a/packages/datasource-url-params-handler/.prettierrc.js b/packages/datasource-url-params-handler/.prettierrc.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/datasource-url-params-handler/CHANGELOG.md b/packages/datasource-url-params-handler/CHANGELOG.md new file mode 100644 index 000000000..cc9b7ad34 --- /dev/null +++ b/packages/datasource-url-params-handler/CHANGELOG.md @@ -0,0 +1,20 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +## 1.0.3 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-url-params-handler + + +## 1.0.2 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-datasource-url-params-handler diff --git a/packages/datasource-url-params-handler/package.json b/packages/datasource-url-params-handler/package.json new file mode 100644 index 000000000..c7f60feee --- /dev/null +++ b/packages/datasource-url-params-handler/package.json @@ -0,0 +1,27 @@ +{ + "name": "@ali/lowcode-datasource-url-params-handler", + "version": "1.0.3", + "main": "lib/index.js", + "module": "es/index.js", + "typings": "es/index.d.ts", + "files": [ + "src", + "lib", + "es" + ], + "scripts": { + "dev": "tsc --watch", + "clean": "rm -rf es lib", + "build": "npm run clean && tsc && tsc --outDir ./lib --module commonjs ", + "prepublishOnly": "npm run build" + }, + "dependencies": { + "@ali/lowcode-types": "^1.0.13", + "query-string": "^6.13.1", + "typescript": "^3.9.7" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + }, + "gitHead": "829e504c52a294fc28966ab0e491f4d2de1d4c90" +} diff --git a/packages/datasource-url-params-handler/src/index.ts b/packages/datasource-url-params-handler/src/index.ts new file mode 100644 index 000000000..589f2d2c8 --- /dev/null +++ b/packages/datasource-url-params-handler/src/index.ts @@ -0,0 +1,16 @@ +import qs from 'query-string'; +import { UrlParamsHandler } from '@ali/lowcode-types'; + +export function createUrlParamsHandler( + searchString: string | T = '', +): UrlParamsHandler { + // eslint-disable-next-line space-before-function-paren + return async function(): Promise { + if (typeof searchString === 'string') { + const params = (qs.parse(searchString) as unknown) as T; + return params; + } + + return searchString; + }; +} diff --git a/packages/datasource-url-params-handler/tsconfig.json b/packages/datasource-url-params-handler/tsconfig.json new file mode 100644 index 000000000..eeea7a79b --- /dev/null +++ b/packages/datasource-url-params-handler/tsconfig.json @@ -0,0 +1,65 @@ +{ + "compilerOptions": { + "outDir": "es", + "target": "es6", + "module": "esnext", + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + // "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + // "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "lib": ["ESNext", "DOM"] /* Specify library files to be included in the compilation. */, + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "es" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + /* Advanced Options */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/demo-server/CHANGELOG.md b/packages/demo-server/CHANGELOG.md index b0f0f3c87..6477a6c98 100644 --- a/packages/demo-server/CHANGELOG.md +++ b/packages/demo-server/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo-server@1.0.13...@ali/lowcode-demo-server@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-demo-server + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo-server@1.0.13...@ali/lowcode-demo-server@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-demo-server + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo-server@1.0.12...@ali/lowcode-demo-server@1.0.13) (2020-11-02) diff --git a/packages/demo-server/package.json b/packages/demo-server/package.json index 86eaf1751..e65a8c8d4 100644 --- a/packages/demo-server/package.json +++ b/packages/demo-server/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-demo-server", - "version": "1.0.13", + "version": "1.0.15", "private": true, "description": "低代码引擎 DEMO Server 端", "scripts": { @@ -30,7 +30,7 @@ } }, "dependencies": { - "@ali/lowcode-code-generator": "^1.0.13", + "@ali/lowcode-code-generator": "^1.0.15", "@ali/lowcode-types": "^1.0.13", "@nestjs/common": "^7.0.0", "@nestjs/core": "^7.0.0", diff --git a/packages/demo/CHANGELOG.md b/packages/demo/CHANGELOG.md index 473b1198d..874c4d1ee 100644 --- a/packages/demo/CHANGELOG.md +++ b/packages/demo/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@1.0.13...@ali/lowcode-demo@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-demo + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@1.0.13...@ali/lowcode-demo@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-demo + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-demo@1.0.12...@ali/lowcode-demo@1.0.13) (2020-11-02) diff --git a/packages/demo/package.json b/packages/demo/package.json index 89180ef92..e14fe3c31 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-demo", - "version": "1.0.13", + "version": "1.0.15", "private": true, "description": "低代码引擎 DEMO", "scripts": { @@ -10,21 +10,21 @@ }, "config": {}, "dependencies": { - "@ali/lowcode-editor-core": "^1.0.13", - "@ali/lowcode-editor-skeleton": "^1.0.13", - "@ali/lowcode-plugin-components-pane": "^1.0.13", - "@ali/lowcode-plugin-designer": "^1.0.13", - "@ali/lowcode-plugin-event-bind-dialog": "^1.0.13", - "@ali/lowcode-plugin-outline-pane": "^1.0.13", - "@ali/lowcode-plugin-sample-logo": "^1.0.13", - "@ali/lowcode-plugin-sample-preview": "^1.0.13", + "@ali/lowcode-editor-core": "^1.0.15", + "@ali/lowcode-editor-skeleton": "^1.0.15", + "@ali/lowcode-plugin-components-pane": "^1.0.15", + "@ali/lowcode-plugin-designer": "^1.0.15", + "@ali/lowcode-plugin-event-bind-dialog": "^1.0.14", + "@ali/lowcode-plugin-outline-pane": "^1.0.14", + "@ali/lowcode-plugin-sample-logo": "^1.0.14", + "@ali/lowcode-plugin-sample-preview": "^1.0.14", "@ali/lowcode-plugin-settings-pane": "^0.8.8", - "@ali/lowcode-plugin-undo-redo": "^1.0.13", - "@ali/lowcode-plugin-variable-bind-dialog": "^1.0.13", - "@ali/lowcode-plugin-zh-en": "^1.0.13", - "@ali/lowcode-react-renderer": "^1.0.13", - "@ali/lowcode-runtime": "^1.0.15", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-plugin-undo-redo": "^1.0.14", + "@ali/lowcode-plugin-variable-bind-dialog": "^1.0.14", + "@ali/lowcode-plugin-zh-en": "^1.0.14", + "@ali/lowcode-react-renderer": "^1.0.14", + "@ali/lowcode-runtime": "^1.0.16", + "@ali/lowcode-utils": "^1.0.14", "@ali/ve-action-pane": "^4.7.0-beta.0", "@ali/ve-datapool-pane": "^6.4.3", "@ali/ve-history-pane": "4.0.0", diff --git a/packages/demo/public/schema.json b/packages/demo/public/schema.json index 616ff3bef..131b55860 100644 --- a/packages/demo/public/schema.json +++ b/packages/demo/public/schema.json @@ -10,7 +10,86 @@ }, "fileName": "test", "dataSource": { - "list": [] + "list": [ + { + "type": "http", + "id": "请求商家数据,是一个 HTTP 请求,是一个 HTTP 请求,是一个 HTTP 请求", + "isInit": true, + "options": { + "uri": "https://www.taobao.com", + "params": { + "a": 1, + "b": true, + "c": "3" + } + } + }, + { + "type": "http", + "id": "请求商家数据2,是一个 HTTP 请求,是一个 HTTP 请求,是一个 HTTP 请求", + "isInit": true, + "options": { + "uri": "https://www.taobao.com", + "params": { + "a": 1, + "b": true, + "c": "3" + } + } + }, + { + "type": "http", + "id": "请求商家数据3,是一个 HTTP 请求,是一个 HTTP 请求,是一个 HTTP 请求", + "isInit": true, + "options": { + "uri": "https://www.taobao.com", + "params": { + "a": 1, + "b": true, + "c": "3" + } + } + }, + { + "type": "http", + "id": "请求商家数据4,是一个 HTTP 请求,是一个 HTTP 请求,是一个 HTTP 请求", + "isInit": true, + "options": { + "uri": "https://www.taobao.com", + "params": { + "a": 1, + "b": true, + "c": "3" + } + } + }, + { + "type": "http", + "id": "请求商家数据5,是一个 HTTP 请求,是一个 HTTP 请求,是一个 HTTP 请求", + "isInit": false, + "options": { + "uri": "https://www.taobao.com", + "params": { + "a": 1, + "b": true, + "c": "3" + } + } + }, + { + "type": "http", + "id": "请求商家数据6,是一个 HTTP 请求,是一个 HTTP 请求,是一个 HTTP 请求", + "isInit": true, + "options": { + "uri": "https://www.taobao.com", + "params": { + "a": 1, + "b": true, + "c": "3" + } + } + } + ] }, "state": { "text": "outter", @@ -578,4 +657,4 @@ ] } ] - } \ No newline at end of file + } diff --git a/packages/demo/src/editor/components.ts b/packages/demo/src/editor/components.ts index bf5302491..caa52a36f 100644 --- a/packages/demo/src/editor/components.ts +++ b/packages/demo/src/editor/components.ts @@ -2,7 +2,8 @@ import logo from '@ali/lowcode-plugin-sample-logo'; import samplePreview from '@ali/lowcode-plugin-sample-preview'; import undoRedo from '@ali/lowcode-plugin-undo-redo'; import componentsPane from '@ali/lowcode-plugin-components-pane'; -import outline from '@ali/lowcode-plugin-outline-pane'; +import outline, { OutlinePane } from '@ali/lowcode-plugin-outline-pane'; +import dataSourcePane from '@ali/lowcode-plugin-datasource-pane'; import zhEn from '@ali/lowcode-plugin-zh-en'; import eventBindDialog from '@ali/lowcode-plugin-event-bind-dialog'; import variableBindDialog from '@ali/lowcode-plugin-variable-bind-dialog'; @@ -23,4 +24,5 @@ export default { sourceEditor, codeout, saveload, + dataSourcePane, }; diff --git a/packages/demo/src/editor/config.ts b/packages/demo/src/editor/config.ts index fbbbb30bb..bda055b0b 100644 --- a/packages/demo/src/editor/config.ts +++ b/packages/demo/src/editor/config.ts @@ -1,3 +1,8 @@ +import { DataSourceImportPluginCode } from '@ali/lowcode-plugin-datasource-pane'; +import { createMtopHandler } from '@ali/lowcode-datasource-mtop-handler'; +import { createFetchHandler } from '@ali/lowcode-datasource-fetch-handler'; +import { createJsonpHandler } from '@ali/lowcode-datasource-jsonp-handler'; + export default { plugins: { topArea: [ @@ -97,6 +102,60 @@ export default { }, }, }, + { + pluginKey: 'dataSourcePane', + pluginProps: { + importPlugins: [ + { + name: 'code2', + title: '源码2', + content: DataSourceImportPluginCode, + }, + ], + dataSourceTypes: [ + { + type: 'mopen', + schema: { + type: 'object', + properties: { + options: { + type: 'object', + properties: { + uri: { + title: 'api', + }, + v: { + title: 'v', + type: 'string', + }, + appKey: { + title: 'appKey', + type: 'string', + }, + }, + }, + }, + }, + }, + ], + }, + type: 'PanelIcon', + props: { + align: 'top', + icon: 'shujuyuan', + description: '数据源面板', + panelProps: { + floatable: true, + height: 300, + help: undefined, + hideTitleBar: false, + maxHeight: 800, + maxWidth: 1200, + title: '数据源面板', + width: 600, + }, + }, + }, { pluginKey: 'zhEn', type: 'Custom', @@ -121,13 +180,18 @@ export default { const assets = await editor.utils.get('./assets.json'); editor.set('assets', assets); const simulatorUrl = [ - 'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/0.9.50/react-simulator-renderer.css', - //'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/0.9.50/react-simulator-renderer.js', + 'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/1.0.0/react-simulator-renderer.css', + //'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/1.0.0/react-simulator-renderer.js', // for debug 'http://localhost:3333/js/react-simulator-renderer.js', // 'http://localhost:3333/js/react-simulator-renderer.css', ]; editor.set('simulatorUrl', simulatorUrl); + editor.set('requestHandlersMap', { + mtop: createMtopHandler(), + fetch: createFetchHandler(), + jsonp: createJsonpHandler() + }); // editor.set('renderEnv', 'rax'); const schema = await editor.utils.get('./schema.json'); diff --git a/packages/designer/CHANGELOG.md b/packages/designer/CHANGELOG.md index d6dea100b..ecfc7aa4e 100644 --- a/packages/designer/CHANGELOG.md +++ b/packages/designer/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@1.0.13...@ali/lowcode-designer@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-designer + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@1.0.13...@ali/lowcode-designer@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-designer + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-designer@1.0.12...@ali/lowcode-designer@1.0.13) (2020-11-02) diff --git a/packages/designer/package.json b/packages/designer/package.json index 4ea42853d..813c7a33e 100644 --- a/packages/designer/package.json +++ b/packages/designer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-designer", - "version": "1.0.13", + "version": "1.0.15", "description": "Designer for Ali LowCode Engine", "main": "lib/index.js", "module": "es/index.js", @@ -15,9 +15,9 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-editor-core": "^1.0.15", "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "classnames": "^2.2.6", "event": "^1.0.0", "react": "^16", diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts index 4679b3338..237313976 100644 --- a/packages/designer/src/builtin-simulator/host.ts +++ b/packages/designer/src/builtin-simulator/host.ts @@ -49,6 +49,9 @@ export interface BuiltinSimulatorProps { device?: 'mobile' | 'iphone' | string; deviceClassName?: string; environment?: Asset; + // @TODO 补充类型 + /** @property 请求处理器配置 */ + requestHandlersMap?: any; extraEnvironment?: Asset; library?: LibraryItem[]; simulatorUrl?: Asset; @@ -209,11 +212,14 @@ export class BuiltinSimulatorHost implements ISimulatorHost { this.libraryMap[item.package] = item.library; + if (item.async) { + this.asyncLibraryMap[item.package] = item; + } if (item.urls) { libraryAsset.push(item.urls); } @@ -254,7 +263,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.13...@ali/lowcode-editor-core@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-core + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.13...@ali/lowcode-editor-core@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-core + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-core@1.0.12...@ali/lowcode-editor-core@1.0.13) (2020-11-02) diff --git a/packages/editor-core/package.json b/packages/editor-core/package.json index 78fdc6d7f..fca012343 100644 --- a/packages/editor-core/package.json +++ b/packages/editor-core/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-core", - "version": "1.0.13", + "version": "1.0.15", "description": "Core Api for Ali lowCode engine", "license": "MIT", "main": "lib/index.js", @@ -16,7 +16,7 @@ }, "dependencies": { "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "@alifd/next": "^1.19.16", "@recore/obx": "^1.0.9", "@recore/obx-react": "^1.0.8", diff --git a/packages/editor-preset-general/CHANGELOG.md b/packages/editor-preset-general/CHANGELOG.md index b14fe1848..80bacc643 100644 --- a/packages/editor-preset-general/CHANGELOG.md +++ b/packages/editor-preset-general/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-preset-general@1.0.13...@ali/lowcode-editor-preset-general@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-preset-general + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-preset-general@1.0.13...@ali/lowcode-editor-preset-general@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-preset-general + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-preset-general@1.0.12...@ali/lowcode-editor-preset-general@1.0.13) (2020-11-02) diff --git a/packages/editor-preset-general/package.json b/packages/editor-preset-general/package.json index 838f68c81..acd758550 100644 --- a/packages/editor-preset-general/package.json +++ b/packages/editor-preset-general/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-preset-general", - "version": "1.0.13", + "version": "1.0.15", "description": "Ali General Editor Preset", "main": "lib/index.js", "files": [ @@ -14,12 +14,12 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-editor-core": "^1.0.13", - "@ali/lowcode-editor-skeleton": "^1.0.13", - "@ali/lowcode-plugin-designer": "^1.0.13", - "@ali/lowcode-plugin-outline-pane": "^1.0.13", + "@ali/lowcode-editor-core": "^1.0.15", + "@ali/lowcode-editor-skeleton": "^1.0.15", + "@ali/lowcode-plugin-designer": "^1.0.15", + "@ali/lowcode-plugin-outline-pane": "^1.0.14", "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "@alifd/next": "^1.19.12", "@alife/theme-lowcode-dark": "^0.1.0", "@alife/theme-lowcode-light": "^0.1.0", @@ -27,7 +27,7 @@ "react-dom": "^16.8.1" }, "devDependencies": { - "@ali/lowcode-editor-setters": "^1.0.13", + "@ali/lowcode-editor-setters": "^1.0.15", "@alib/build-scripts": "^0.1.18", "@types/events": "^3.0.0", "@types/react": "^16.8.3", diff --git a/packages/editor-preset-vision/CHANGELOG.md b/packages/editor-preset-vision/CHANGELOG.md index 08befaafc..434089963 100644 --- a/packages/editor-preset-vision/CHANGELOG.md +++ b/packages/editor-preset-vision/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-preset-vision@1.0.13...@ali/lowcode-editor-preset-vision@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-preset-vision + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-preset-vision@1.0.13...@ali/lowcode-editor-preset-vision@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-preset-vision + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-preset-vision@1.0.12...@ali/lowcode-editor-preset-vision@1.0.13) (2020-11-02) diff --git a/packages/editor-preset-vision/package.json b/packages/editor-preset-vision/package.json index d253ca2f0..8fac4add1 100644 --- a/packages/editor-preset-vision/package.json +++ b/packages/editor-preset-vision/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-preset-vision", - "version": "1.0.13", + "version": "1.0.15", "description": "Vision Polyfill for Ali lowCode engine", "main": "lib/index.js", "private": true, @@ -15,11 +15,11 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-designer": "^1.0.13", - "@ali/lowcode-editor-core": "^1.0.13", - "@ali/lowcode-editor-skeleton": "^1.0.13", - "@ali/lowcode-plugin-designer": "^1.0.13", - "@ali/lowcode-plugin-outline-pane": "^1.0.13", + "@ali/lowcode-designer": "^1.0.15", + "@ali/lowcode-editor-core": "^1.0.15", + "@ali/lowcode-editor-skeleton": "^1.0.15", + "@ali/lowcode-plugin-designer": "^1.0.15", + "@ali/lowcode-plugin-outline-pane": "^1.0.14", "@ali/ve-i18n-util": "^2.0.0", "@ali/ve-icons": "^4.1.9", "@ali/ve-less-variables": "2.0.3", diff --git a/packages/editor-setters/CHANGELOG.md b/packages/editor-setters/CHANGELOG.md index 9dab472fc..f72e4551d 100644 --- a/packages/editor-setters/CHANGELOG.md +++ b/packages/editor-setters/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-setters@1.0.13...@ali/lowcode-editor-setters@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-setters + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-setters@1.0.13...@ali/lowcode-editor-setters@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-setters + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-setters@1.0.12...@ali/lowcode-editor-setters@1.0.13) (2020-11-02) diff --git a/packages/editor-setters/package.json b/packages/editor-setters/package.json index 4071d163f..24f86525c 100644 --- a/packages/editor-setters/package.json +++ b/packages/editor-setters/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-setters", - "version": "1.0.13", + "version": "1.0.15", "description": "Builtin setters for Ali lowCode engine", "files": [ "es", @@ -21,7 +21,7 @@ "@ali/iceluna-comp-react-node": "^1.0.5", "@ali/iceluna-sdk": "^1.0.5-beta.24", "@ali/intl-universal": "^0.4.9", - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-editor-core": "^1.0.15", "@ali/ve-choice-control": "^4.0.0", "@ali/ve-code-control": "^4.0.0", "@ali/ve-color-control": "^4.0.0", diff --git a/packages/editor-setters/src/function-setter/index.tsx b/packages/editor-setters/src/function-setter/index.tsx index e4c0971cc..a9adebd6c 100644 --- a/packages/editor-setters/src/function-setter/index.tsx +++ b/packages/editor-setters/src/function-setter/index.tsx @@ -1,3 +1,4 @@ + import React, { PureComponent } from 'react'; // import PropTypes from 'prop-types'; import { Button, Icon, Dialog } from '@alifd/next'; @@ -64,8 +65,8 @@ export default class FunctionSetter extends PureComponent { editor.on(`${this.emitEventName}.bindEvent`, this.bindEvent); } - bindEvent = (eventName) => { - this.bindEventCallback(eventName); + bindEvent = (eventName, paramStr) => { + this.bindEventCallback(eventName, paramStr); }; @@ -75,9 +76,17 @@ export default class FunctionSetter extends PureComponent { } - bindFunction = () => { - const { field } = this.props; - field.editor.emit('eventBindDialog.openDialog', field.name, this.emitEventName); + bindFunction = (isEdit) => { + const { field, value } = this.props; + + let paramStr; + + if (value) { + paramStr = this.parseFunctionParam(value.value); + } + + + field.editor.emit('eventBindDialog.openDialog', field.name, this.emitEventName, paramStr, isEdit); }; openDialog = () => { @@ -102,14 +111,14 @@ export default class FunctionSetter extends PureComponent { parseFunctionName = (functionString: string) => { // 因为函数格式是固定的,所以可以按照字符换去匹配获取函数名 - const funNameStr = functionString.split('this.')[1]; + return functionString.split('this.')[1]?.split('.')[0]; + }; - - if (funNameStr) { - const endIndex = funNameStr.indexOf('('); - return funNameStr.substr(0, endIndex); - } else { - return ''; + parseFunctionParam = (functionString:string) => { + // eslint-disable-next-line no-useless-escape + const matchList = functionString.match(/\[(\w|\s|\,|")*\]/); + if (matchList?.length) { + return matchList[0].substring(1, matchList[0].length - 1); } }; @@ -158,7 +167,7 @@ export default class FunctionSetter extends PureComponent {
this.focusFunctionName(functionName)}>{functionName} - + this.bindFunction(true)} />
); @@ -177,14 +186,16 @@ export default class FunctionSetter extends PureComponent { }; - bindEventCallback = (eventName: string) => { + bindEventCallback = (eventName: string, paramStr:string) => { const { onChange } = this.props; + onChange({ type: 'JSFunction', - value: `function(){ return this.${eventName}() }`, + value: `function(){ return this.${eventName}.apply(this,Array.prototype.slice.call(arguments).concat([${paramStr || ''}])) }`, }); }; + render() { const { value } = this.props; const { isShowDialog } = this.state; diff --git a/packages/editor-skeleton/CHANGELOG.md b/packages/editor-skeleton/CHANGELOG.md index 779f85e34..918265caf 100644 --- a/packages/editor-skeleton/CHANGELOG.md +++ b/packages/editor-skeleton/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-skeleton@1.0.13...@ali/lowcode-editor-skeleton@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-skeleton + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-skeleton@1.0.13...@ali/lowcode-editor-skeleton@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-skeleton + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-editor-skeleton@1.0.12...@ali/lowcode-editor-skeleton@1.0.13) (2020-11-02) diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json index aa48212df..2c946380e 100644 --- a/packages/editor-skeleton/package.json +++ b/packages/editor-skeleton/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-skeleton", - "version": "1.0.13", + "version": "1.0.15", "description": "alibaba lowcode editor skeleton", "main": "lib/index.js", "module": "es/index.js", @@ -19,10 +19,10 @@ "editor" ], "dependencies": { - "@ali/lowcode-designer": "^1.0.13", - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-designer": "^1.0.15", + "@ali/lowcode-editor-core": "^1.0.15", "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "@ali/ve-icons": "latest", "@ali/ve-less-variables": "^2.0.0", "@alifd/next": "^1.20.12", diff --git a/packages/material-parser/CHANGELOG.md b/packages/material-parser/CHANGELOG.md index f20edea07..201c84566 100644 --- a/packages/material-parser/CHANGELOG.md +++ b/packages/material-parser/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.13...@ali/lowcode-material-parser@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-material-parser + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.13...@ali/lowcode-material-parser@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-material-parser + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-material-parser@1.0.12...@ali/lowcode-material-parser@1.0.13) (2020-11-02) diff --git a/packages/material-parser/package.json b/packages/material-parser/package.json index a25549788..2336a21ec 100644 --- a/packages/material-parser/package.json +++ b/packages/material-parser/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-material-parser", - "version": "1.0.13", + "version": "1.0.15", "description": "material parser for Ali lowCode engine", "main": "lib/index.js", "files": [ diff --git a/packages/plugin-components-pane/CHANGELOG.md b/packages/plugin-components-pane/CHANGELOG.md index 3c216ccdf..a8fb3899c 100644 --- a/packages/plugin-components-pane/CHANGELOG.md +++ b/packages/plugin-components-pane/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-components-pane@1.0.13...@ali/lowcode-plugin-components-pane@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-components-pane + + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-components-pane@1.0.13...@ali/lowcode-plugin-components-pane@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-components-pane + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-components-pane@1.0.12...@ali/lowcode-plugin-components-pane@1.0.13) (2020-11-02) diff --git a/packages/plugin-components-pane/package.json b/packages/plugin-components-pane/package.json index 53de2d890..b56489442 100644 --- a/packages/plugin-components-pane/package.json +++ b/packages/plugin-components-pane/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-components-pane", - "version": "1.0.13", + "version": "1.0.15", "description": "alibaba lowcode editor component-list plugin", "files": [ "es/", @@ -21,8 +21,8 @@ "author": "xiayang.xy", "dependencies": { "@ali/intl-universal": "^0.4.12", - "@ali/lowcode-designer": "^1.0.13", - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-designer": "^1.0.15", + "@ali/lowcode-editor-core": "^1.0.15", "@ali/lowcode-types": "^1.0.13", "@ali/vu-layer": "^4.0.1", "@ali/vu-uxcore-legao-design": "^1.4.0", diff --git a/packages/plugin-datasource-pane/CHANGELOG.md b/packages/plugin-datasource-pane/CHANGELOG.md new file mode 100644 index 000000000..a6c967243 --- /dev/null +++ b/packages/plugin-datasource-pane/CHANGELOG.md @@ -0,0 +1,20 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +## 1.0.14 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-datasource-pane + + +## 1.0.13 (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-datasource-pane diff --git a/packages/plugin-datasource-pane/DEV.md b/packages/plugin-datasource-pane/DEV.md new file mode 100644 index 000000000..8abcbe74c --- /dev/null +++ b/packages/plugin-datasource-pane/DEV.md @@ -0,0 +1,9 @@ +## 插件开发 + +[https://yuque.antfin-inc.com/ali-lowcode/docs/ip4awq](插件开发文档)。 + +[lowcode 组件库文档](https://fusion.alibaba-inc.com/22117/design/style/icon?themeid=4579)。 + +[搭建协议](https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema)。 + +本地开发需要在 v14.4.0 的 node 环境下能完成 setup diff --git a/packages/plugin-datasource-pane/README.md b/packages/plugin-datasource-pane/README.md new file mode 100644 index 000000000..c8233772b --- /dev/null +++ b/packages/plugin-datasource-pane/README.md @@ -0,0 +1,83 @@ +## 低代码引擎 - 数据源面板插件 + +对页面的数据源进行管理(新建,编辑,导入)。 + +一个 pluginProps 的例子 + +``` +{ + importPlugins: [ + { + name: 'code', + title: '源码', + content: DataSourceImportPluginCode, + }, + ], + dataSourceTypes: [ + { + type: 'mopen', + schema: { + type: 'object', + properties: { + options: { + type: 'object', + properties: { + uri: { + title: 'api', + }, + v: { + title: 'v', + type: 'string', + }, + appKey: { + title: 'appKey', + type: 'string', + }, + }, + }, + }, + }, + }, + ], +} +``` + +## 数据源类型定义 + +内置 fetch,mtop,jsonp 类型,支持传入自定义类型。 + +``` +type DataSourceType = { + type: string; + optionsSchema: JSONSchema6 +}; +``` + +数据源类型需要在集团规范约束下扩展。目前只允许在 options 下添加扩展字段。 + +比如 mtop 类型,需要添加 options.v (版本)字段。 + +## 导入插件 + +默认支持源码导入,可以传入自定义插件。 + +``` +interface DataSourcePaneImportPlugin { + name: string; + title: string; + component: React.ReactNode; + componentProps?: DataSourcePaneImportPluginCustomProps; +} + +interface DataSourcePaneImportPluginComponentProps { + onImport?: (dataSourceList: DataSourceConfig[]) => void; + onCancel?: () => void; + dataSourceTypes?: DataSourceType[]; +} + +interface DataSourcePaneImportPluginCustomProps extends DataSourcePaneImportPluginComponentProps { + [customPropName: string]: any; +} +``` + + diff --git a/packages/plugin-datasource-pane/TODO.md b/packages/plugin-datasource-pane/TODO.md new file mode 100644 index 000000000..e5fd18a65 --- /dev/null +++ b/packages/plugin-datasource-pane/TODO.md @@ -0,0 +1,19 @@ +TODO +--- + +* 多语言 +* 定制样式 +* 不使用 bind +* 表达式 setter 的联想 +* [later]表达式和其他类型的切换 + +## 提案 + +* mock +* 变量,上下文的提案 + +## 问题 + +* 变量,上下文放数据源里管理是否合适 +* mockUrl 和 mockData +* 设计器的设计语言无法统一 diff --git a/packages/plugin-datasource-pane/build.json b/packages/plugin-datasource-pane/build.json new file mode 100644 index 000000000..e765b2233 --- /dev/null +++ b/packages/plugin-datasource-pane/build.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + "build-plugin-component", + "build-plugin-fusion" + ] +} diff --git a/packages/plugin-datasource-pane/package.json b/packages/plugin-datasource-pane/package.json new file mode 100644 index 000000000..f8ea1b5ec --- /dev/null +++ b/packages/plugin-datasource-pane/package.json @@ -0,0 +1,50 @@ +{ + "name": "@ali/lowcode-plugin-datasource-pane", + "version": "1.0.14", + "description": "低代码引擎数据源面板", + "main": "lib/index.js", + "files": [ + "lib" + ], + "scripts": { + "build": "build-scripts build", + "cloud-build": "build-scripts build --skip-demo", + "test": "ava", + "test:snapshot": "ava --update-snapshots" + }, + "ava": { + "compileEnhancements": false, + "snapshotDir": "test/fixtures/__snapshots__", + "extensions": [ + "ts" + ], + "require": [ + "ts-node/register" + ] + }, + "license": "MIT", + "devDependencies": { + "@types/json-schema": "^7.0.6", + "@types/react": "^16.9.49", + "@types/traverse": "^0.6.32", + "monaco-editor": "^0.20.0" + }, + "dependencies": { + "@ali/lowcode-editor-setters": "^1.0.15", + "@alib/build-scripts": "^0.1.3", + "@alifd/next": "^1.20.28", + "@formily/next": "^1.3.2", + "@formily/next-components": "^1.3.2", + "@formily/react-schema-renderer": "^1.3.2", + "@types/traverse": "^0.6.32", + "ajv": "^6.12.4", + "lodash": "^4.17.20", + "react-monaco-editor": "^0.40.0", + "styled-components": "^5.2.0", + "traverse": "^0.6.6" + }, + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-plugin-datasource-pane@1.0.12/build/index.html", + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + } +} diff --git a/packages/plugin-datasource-pane/src/datasource-form.tsx b/packages/plugin-datasource-pane/src/datasource-form.tsx new file mode 100644 index 000000000..ecc89632d --- /dev/null +++ b/packages/plugin-datasource-pane/src/datasource-form.tsx @@ -0,0 +1,265 @@ +// @todo schema default +import React, { PureComponent } from 'react'; +import { Button } from '@alifd/next'; +import { SchemaForm, FormButtonGroup, Submit } from '@formily/next'; +import { ArrayTable, Input, Switch, NumberPicker } from '@formily/next-components'; +import _isPlainObject from 'lodash/isPlainObject'; +import _isArray from 'lodash/isArray'; +import _cloneDeep from 'lodash/cloneDeep'; +import _mergeWith from 'lodash/mergeWith'; +import _get from 'lodash/get'; +import traverse from 'traverse'; +import { DataSourceConfig } from '@ali/lowcode-types'; +import { ParamValue, JSFunction } from './form-components'; +import { DataSourceType } from './types'; + +// @todo $ref + +const SCHEMA = { + type: 'object', + properties: { + type: { + title: '类型', + type: 'string', + editable: false, + }, + id: { + type: 'string', + title: '数据源 ID', + required: true, + }, + isInit: { + title: '是否自动请求', + type: 'boolean', + default: true, + }, + dataHandler: { + type: 'string', + title: '单个数据结果处理函数', + required: true, + 'x-component': 'JSFunction', + default: 'function() {}', + }, + options: { + type: 'object', + title: '请求参数', + required: true, + properties: { + uri: { + type: 'string', + title: '请求地址', + required: true, + }, + params: { + title: '请求参数', + type: 'object', + default: {}, + }, + method: { + type: 'string', + title: '请求方法', + required: true, + enum: ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE'], + default: 'GET', + }, + isCors: { + type: 'boolean', + title: '是否支持跨域', + required: true, + default: true, + }, + timeout: { + type: 'number', + title: '超时时长(毫秒)', + default: 5000, + }, + headers: { + type: 'object', + title: '请求头信息', + default: {}, + }, + }, + }, + }, +}; + +export interface DataSourceFormProps { + dataSourceType: DataSourceType; + dataSource?: DataSourceConfig; + onComplete?: (dataSource: DataSourceConfig) => void; + onCancel?: () => void; +} + +export type DataSourceFormState = {}; + +/** + * 通过是否存在 ID 来决定读写状态 + */ +export class DataSourceForm extends PureComponent { + state = {}; + + handleFormSubmit = (formData: any) => { + // @todo mutable? + if (_isArray(_get(formData, 'options.params'))) { + formData.options.params = formData.options.params.reduce((acc: any, cur: any) => { + if (!cur.name) return acc; + acc[cur.name] = cur.value; + return acc; + }, {}); + } + if (_isArray(_get(formData, 'options.headers'))) { + formData.options.headers = formData.options.headers.reduce((acc: any, cur: any) => { + if (!cur.name) return acc; + acc[cur.name] = cur.value; + return acc; + }, {}); + } + // console.log('submit', formData); + this.props.onComplete?.(formData); + }; + + handleCancel = () => { + this.props.onCancel?.(); + }; + + deriveInitialData = (dataSource: object = {}) => { + const { dataSourceType } = this.props; + const result: any = _cloneDeep(dataSource); + + if (_isPlainObject(_get(result, 'options.params'))) { + result.options.params = Object.keys(result.options.params).reduce((acc: any, cur: any) => { + acc.push({ + name: cur, + value: result.options.params[cur], + }); + return acc; + }, []); + } + if (_isPlainObject(_get(result, 'options.headers'))) { + result.options.headers = Object.keys(result.options.headers).reduce((acc: any, cur: any) => { + acc.push({ + name: cur, + value: result.options.headers[cur], + }); + return acc; + }, []); + } + + result.type = dataSourceType.type; + + return result; + }; + + deriveSchema = () => { + const { dataSourceType } = this.props; + + // @todo 减小覆盖的风险 + const formSchema: any = _mergeWith({}, SCHEMA, dataSourceType.schema, (objValue, srcValue) => { + if (_isArray(objValue)) { + return srcValue; + } + }); + + if (_get(formSchema, 'properties.options.properties.params')) { + formSchema.properties.options.properties.params = { + ...formSchema.properties.options.properties.params, + type: 'array', + 'x-component': 'ArrayTable', + 'x-component-props': { + operationsWidth: 100, + }, + items: { + type: 'object', + properties: { + name: { + title: '参数名', + type: 'string', + }, + value: { + title: '参数值', + type: 'string', + 'x-component': 'ParamValue', + 'x-component-props': { + types: ['string', 'boolean', 'expression', 'number'], + }, + }, + }, + }, + }; + delete formSchema.properties.options.properties.params.properties; + } + if (_get(formSchema, 'properties.options.properties.headers')) { + formSchema.properties.options.properties.headers = { + ...formSchema.properties.options.properties.headers, + type: 'array', + 'x-component': 'ArrayTable', + 'x-component-props': { + operationsWidth: 100, + }, + items: { + type: 'object', + properties: { + name: { + title: '参数名', + type: 'string', + }, + value: { + title: '参数值', + type: 'string', + 'x-component': 'ParamValue', + 'x-component-props': { + types: ['string'], + }, + }, + }, + }, + }; + delete formSchema.properties.options.properties.headers.properties; + } + + return traverse(formSchema).forEach(function(node) { + if (node?.type && !node['x-component']) { + if (node.type === 'string') { + node['x-component'] = 'Input'; + } else if (node.type === 'number') { + node['x-component'] = 'NumberPicker'; + } else if (node.type === 'boolean') { + node['x-component'] = 'Switch'; + } + } + }); + }; + + render() { + const { dataSource } = this.props; + + return ( +
+ + + 提交 + + + +
+ ); + } +} diff --git a/packages/plugin-datasource-pane/src/form-components/expression.tsx b/packages/plugin-datasource-pane/src/form-components/expression.tsx new file mode 100644 index 000000000..59da91da6 --- /dev/null +++ b/packages/plugin-datasource-pane/src/form-components/expression.tsx @@ -0,0 +1,122 @@ +/** + * 表达式控件,在原类型基础上切换成表达式模式 + */ +/* import React, { PureComponent, ReactElement, FC } from 'react'; +import { Button, Input, Radio, NumberPicker, Switch, Icon } from '@alifd/next'; +import { ArrayTable } from '@formily/next-components'; +import { connect } from '@formily/react-schema-renderer'; +import _isPlainObject from 'lodash/isPlainObject'; +import _isArray from 'lodash/isArray'; +import _isNumber from 'lodash/isNumber'; +import _isString from 'lodash/isString'; +import _isBoolean from 'lodash/isBoolean'; +import _get from 'lodash/get'; +import _tap from 'lodash/tap'; +import { ExpressionSetter } from '@ali/lowcode-editor-setters'; + +const { Group: RadioGroup } = Radio; + +export interface ExpressionProps { + className: string; + value: any; + onChange?: () => void; + type: 'string' | 'number' | 'boolan' | 'array'; +} + +export interface ExpressionState { + useExpression: false; +} + +class ExpressionComp extends PureComponent { + static isFieldComponent = true; + + state = { + useExpression: '', + }; + + constructor(props) { + super(props); + this.state.useExpression = this.isUseExpression(this.props.value); + this.handleChange = this.handleChange; + } + + isUseExpression = (value: any) => { + if (_isPlainObject(value) && value.type === 'JSFunction') { + return true; + } + return false; + }; + + // @todo 需要再 bind 一次? + handleChange = (value) => { + this.props?.onChange(value); + } + + componentDidUpdate(prevProps) { + if (this.props.value !== prevProps.value) { + this.setState({ + value: this.props.value, + useExpression: this.isUseExpression(this.props.value), + }); + } + } + + handleUseExpressionChange = (useExpression) => { + this.setState(({ value }) => { + let nextValue = value || ''; + if (useExpression) { + nextValue = { + type: 'JSFunction', + value: '' + }; + } else { + nextVaule = null; + } + return { + value: nextValue, + useExpression, + }; + }); + }; + + renderOriginal = () => { + const { value, type } = this.props; + + if (type === 'string') { + return ; + } else if (type === 'boolean') { + return ; + } else if (type === 'number') { + return ; + } else if (type === 'array') { + return ; + } + return null; + }; + + renderExpression = () => { + const { value, type } = this.props; + + // @todo 传入上下文才有智能提示 + return ( + + ); + }; + + render() { + const { useExpression } = this.state; + return ( +
+ {!useExpression && this.renderOriginal()} + {useExpression && this.renderExpression()} + +
+ ); + } +} + +export const Expression = connect({ + getProps: (componentProps, fieldProps) => { + debugger; + } +})(ExpressionComp); */ diff --git a/packages/plugin-datasource-pane/src/form-components/index.ts b/packages/plugin-datasource-pane/src/form-components/index.ts new file mode 100644 index 000000000..9560f79da --- /dev/null +++ b/packages/plugin-datasource-pane/src/form-components/index.ts @@ -0,0 +1,2 @@ +export * from './param-value'; +export * from './jsfunction'; diff --git a/packages/plugin-datasource-pane/src/form-components/jsfunction.tsx b/packages/plugin-datasource-pane/src/form-components/jsfunction.tsx new file mode 100644 index 000000000..531d8341b --- /dev/null +++ b/packages/plugin-datasource-pane/src/form-components/jsfunction.tsx @@ -0,0 +1,52 @@ +import React, { PureComponent } from 'react'; +import { connect } from '@formily/react-schema-renderer'; +import MonacoEditor, { EditorWillMount } from 'react-monaco-editor'; +import _noop from 'lodash/noop'; +import { editor } from 'monaco-editor'; + +export interface JSFunctionProps { + className: string; + value: any; + onChange?: (val: any) => void; +} + +export type JSFunctionState = {}; + +class JSFunctionComp extends PureComponent { + static isFieldComponent = true; + + static defaultProps = { + onChange: _noop, + }; + + private monacoRef: any = null; + + handleEditorChange = () => { + if (this.monacoRef) { + if (!(this.monacoRef as any).getModelMarkers().find((marker: editor.IMarker) => marker.owner === 'json')) { + this.props.onChange?.((this.monacoRef as any)?.getModels()?.[0]?.getValue()); + } + } + }; + + handleEditorWillMount: EditorWillMount = (editor) => { + this.monacoRef = editor?.editor; + }; + + render() { + const { value } = this.props; + return ( + + ); + } +} + +export const JSFunction = connect()(JSFunctionComp); diff --git a/packages/plugin-datasource-pane/src/form-components/param-value.scss b/packages/plugin-datasource-pane/src/form-components/param-value.scss new file mode 100644 index 000000000..97541db8a --- /dev/null +++ b/packages/plugin-datasource-pane/src/form-components/param-value.scss @@ -0,0 +1,7 @@ +.param-value { + display: flex; + flex-direction: row; + .param-value-type { + margin-right: 4px; + } +} diff --git a/packages/plugin-datasource-pane/src/form-components/param-value.tsx b/packages/plugin-datasource-pane/src/form-components/param-value.tsx new file mode 100644 index 000000000..9ce8f5712 --- /dev/null +++ b/packages/plugin-datasource-pane/src/form-components/param-value.tsx @@ -0,0 +1,150 @@ +import React, { PureComponent, ReactElement, FC } from 'react'; +import { Select, Input, Radio, NumberPicker, Switch } from '@alifd/next'; +import { connect } from '@formily/react-schema-renderer'; +import _isPlainObject from 'lodash/isPlainObject'; +import _isArray from 'lodash/isArray'; +import _isNumber from 'lodash/isNumber'; +import _isString from 'lodash/isString'; +import _isBoolean from 'lodash/isBoolean'; +import _get from 'lodash/get'; +import _tap from 'lodash/tap'; +import { ExpressionSetter } from '@ali/lowcode-editor-setters'; + +import './param-value.scss'; + +const { Group: RadioGroup } = Radio; + +type ParamValueType = 'string' | 'number' | 'boolean' | 'expression'; + +export interface ParamValueProps { + className: string; + value: any; + onChange?: (value: any) => void; + types: ParamValueType[]; +} + +export interface ParamValueState { + type: ParamValueType; +} + +const TYPE_LABEL_MAP = { + string: '字符串', + number: '数字', + boolean: '布尔', + expression: '表达式', +}; + +class ParamValueComp extends PureComponent { + static isFieldComponent = true; + + static defaultProps = { + types: ['string', 'boolean', 'number', 'expression'], + }; + + state: ParamValueState = { + type: 'string', + }; + + constructor(props: ParamValueProps) { + super(props); + this.state.type = this.getTypeFromValue(this.props.value); + } + + // @todo + getTypeFromValue = (value: any) => { + if (_isBoolean(value)) { + return 'boolean'; + } else if (_isNumber(value)) { + return 'number'; + } else if (_isPlainObject(value) && value.type === 'JSFunction') { + return 'expression'; + } + return 'string'; + }; + + // @todo 需要再 bind 一次? + handleChange = (value: any) => { + this.props?.onChange?.(value); + }; + + componentDidUpdate(prevProps: ParamValueProps) { + if (this.props.value !== prevProps.value) { + this.setState({ + type: this.getTypeFromValue(this.props.value), + }); + } + } + + handleTypeChange = (type: string) => { + this.setState( + { + type: type as ParamValueType, + }, + () => { + let nextValue = this.props.value || ''; + const { type } = this.state; + if (type === 'string') { + nextValue = nextValue.toString(); + } else if (type === 'number') { + nextValue = nextValue * 1; + } else if (type === 'boolean') { + nextValue = nextValue === 'true' || nextValue; + } else if (type === 'expression') { + nextValue = ''; + } + this.props.onChange?.(nextValue); + }, + ); + }; + + renderTypeSelect = () => { + const { type } = this.state; + const { types } = this.props; + + if (_isArray(types) && types.length > 2) { + return ( + } + {type === 'boolean' && } + {type === 'number' && } + {type === 'expression' && } + + ); + } +} + +export const ParamValue = connect()(ParamValueComp); diff --git a/packages/plugin-datasource-pane/src/import-plugins/code.scss b/packages/plugin-datasource-pane/src/import-plugins/code.scss new file mode 100644 index 000000000..a420f7a90 --- /dev/null +++ b/packages/plugin-datasource-pane/src/import-plugins/code.scss @@ -0,0 +1,15 @@ +.lowcode-plugin-datasource-import-plugin-code { + height: unquote("calc(100vh - 48px - 48px - 42px)"); + overflow: auto; + .error-msg { + line-height: 24px; + color: #f60; + font-size: 12px; + } + .btns { + margin-top: 8px; + } + .next-btn { + margin-right: 4px; + } +} diff --git a/packages/plugin-datasource-pane/src/import-plugins/code.tsx b/packages/plugin-datasource-pane/src/import-plugins/code.tsx new file mode 100644 index 000000000..e3141dccb --- /dev/null +++ b/packages/plugin-datasource-pane/src/import-plugins/code.tsx @@ -0,0 +1,138 @@ +/* eslint-disable @typescript-eslint/indent */ +// @todo 缩进问题 +/** + * 源码导入插件 + * @todo editor 关联 types,并提供详细的出错信息 + */ +import React, { PureComponent } from 'react'; +import { Button } from '@alifd/next'; +import _noop from 'lodash/noop'; +import _isArray from 'lodash/isArray'; +import _last from 'lodash/last'; +import _isPlainObject from 'lodash/isPlainObject'; +import MonacoEditor, { EditorWillMount } from 'react-monaco-editor'; +import { editor } from 'monaco-editor'; +import { DataSourceConfig } from '@ali/lowcode-types'; +import Ajv from 'ajv'; +import { DataSourcePaneImportPluginComponentProps } from '../types'; + +import './code.scss'; + +export interface DataSourceImportPluginCodeProps extends DataSourcePaneImportPluginComponentProps { + defaultValue?: DataSourceConfig[]; +} + +export interface DataSourceImportPluginCodeState { + code: string; + isCodeValid: boolean; +} + +export class DataSourceImportPluginCode extends PureComponent< + DataSourceImportPluginCodeProps, + DataSourceImportPluginCodeState +> { + static defaultProps = { + defaultValue: [ + { + type: 'http', + id: 'test', + }, + ], + }; + + state = { + code: '', + isCodeValid: true, + }; + + private monacoRef: any; + + constructor(props: DataSourceImportPluginCodeProps) { + super(props); + this.state.code = JSON.stringify(this.deriveValue(this.props.defaultValue)); + this.handleEditorWillMount = this.handleEditorWillMount.bind(this); + this.handleEditorChange = this.handleEditorChange.bind(this); + this.handleComplete = this.handleComplete.bind(this); + } + + deriveValue = (value: any) => { + const { dataSourceTypes } = this.props; + + if (!_isArray(dataSourceTypes) || dataSourceTypes.length === 0) return []; + + let result = value; + if (_isPlainObject(result)) { + // 如果是对象则转化成数组 + result = [result]; + } else if (!_isArray(result)) { + return []; + } + + const ajv = new Ajv(); + + return (result as DataSourceConfig[]).filter((dataSource) => { + if (!dataSource.type) return false; + const dataSourceType = dataSourceTypes.find((type) => type.type === dataSource.type); + if (!dataSourceType) return false; + return ajv.validate(dataSourceType.schema, dataSource); + }); + }; + + handleComplete = () => { + if (this.monacoRef) { + if (!this.monacoRef.getModelMarkers().find((marker: editor.IMarker) => marker.owner === 'json')) { + this.setState({ isCodeValid: true }); + const model: any = _last(this.monacoRef.getModels()); + if (!model) return; + this.props.onImport?.(this.deriveValue(JSON.parse(model.getValue()))); + return; + } + } + this.setState({ isCodeValid: false }); + }; + + handleEditorChange = () => { + if (this.monacoRef) { + if (!this.monacoRef.getModelMarkers().find((marker: editor.IMarker) => marker.owner === 'json')) { + this.setState({ isCodeValid: true }); + } + } + }; + + handleEditorWillMount: EditorWillMount = (editor) => { + this.monacoRef = editor?.editor; + // @todo 格式化一次 + }; + + handleCodeChagne = (code: string) => { + this.setState({ code }); + }; + + render() { + const { onCancel = _noop } = this.props; + const { code, isCodeValid } = this.state; + + // @todo + // formatOnType formatOnPaste + return ( +
+ + {!isCodeValid &&

格式有误

} +

+ + +

+
+ ); + } +} diff --git a/packages/plugin-datasource-pane/src/import-plugins/index.ts b/packages/plugin-datasource-pane/src/import-plugins/index.ts new file mode 100644 index 000000000..d18a4e09a --- /dev/null +++ b/packages/plugin-datasource-pane/src/import-plugins/index.ts @@ -0,0 +1 @@ +export * from './code'; diff --git a/packages/plugin-datasource-pane/src/index.scss b/packages/plugin-datasource-pane/src/index.scss new file mode 100644 index 000000000..81a8d5c7b --- /dev/null +++ b/packages/plugin-datasource-pane/src/index.scss @@ -0,0 +1,97 @@ +.lowcode-plugin-datasource-pane { + margin: 0 8px; + >.next-tabs { + >.next-tabs-bar { + .next-tabs-nav-extra { + .next-btn { + margin-left: 4px; + } + } + } + } +} + +.lowcode-plugin-datasource-pane-list { + margin: 8px; + .next-search { + width: 100%; + .next-search-left { + height: 28px !important; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + .next-before { + height: 28px !important; + .next-select { + height: 28px !important; + } + } + .next-search-input { + height: 28px !important; + input { + height: 28px !important; + } + } + .next-input { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + } + .next-after { + // height: 28px !important; + .next-btn { + height: 30px !important; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + .next-icon:before { + font-size: 12px; + } + } + } + } + .datasource-list { + margin-top: 8px; + height: unquote("calc(100vh - 48px - 48px - 42px - 28px - 8px - 8px)"); + overflow: auto; + .next-virtual-list-wrapper > div > ul > li { + border-top: 1px solid #ddd; + &:first-child { + border-top: none; + } + } + } + .datasource-item { + margin: 8px; + .datasource-item-title { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + height: 28px; + .datasource-item-id { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; + } + .next-btn { + margin-left: 4px; + } + } + .datasource-item-desc { + .next-tag { + margin-right: 4px; + } + } + } +} + +.lowcode-plugin-datasource-form { + height: unquote("calc(100vh - 48px - 48px - 42px)"); + overflow: auto; + .next-form-item { + .next-form-item-control { + + } + } +} diff --git a/packages/plugin-datasource-pane/src/index.tsx b/packages/plugin-datasource-pane/src/index.tsx new file mode 100644 index 000000000..4ee4dc71d --- /dev/null +++ b/packages/plugin-datasource-pane/src/index.tsx @@ -0,0 +1,149 @@ +import React, { PureComponent } from 'react'; +import { PluginProps, DataSource } from '@ali/lowcode-types'; +import _get from 'lodash/get'; +import _set from 'lodash/set'; +import { DataSourcePane } from './pane'; +import { DataSourcePaneImportPlugin, DataSourceType } from './types'; +import { DataSourceImportPluginCode } from './import-plugins'; + +import './index.scss'; + +export { DataSourceImportPluginCode }; + +const PLUGIN_NAME = 'dataSourcePane'; + +export interface DataSourcePaneProps extends PluginProps { + importPlugins: DataSourcePaneImportPlugin[]; + dataSourceTypes?: DataSourceType[]; +} + +export interface DataSourcePaneState { + active: boolean; +} + +const BUILTIN_DATASOURCE_TYPES: DataSourceType[] = [ + { + type: 'fetch', + schema: { + type: 'object', + properties: { + options: { + type: 'object', + properties: {}, + }, + }, + }, + }, + { + type: 'mtop', + schema: { + type: 'object', + properties: { + options: { + type: 'object', + properties: { + uri: { + title: 'api', + }, + v: { + title: 'v', + type: 'string', + }, + appKey: { + title: 'appKey', + type: 'string', + }, + }, + }, + }, + }, + }, + { + type: 'jsonp', + schema: { + type: 'object', + properties: { + options: { + type: 'object', + properties: { + method: { + enum: ['GET'], + }, + }, + }, + }, + }, + }, +]; + +const BUILTIN_IMPORT_PLUGINS: DataSourcePaneImportPlugin[] = [ + { + name: 'default', + title: '源码', + component: DataSourceImportPluginCode, + }, +]; + +export default class DataSourcePanePlugin extends PureComponent { + static displayName = 'DataSourcePanePlugin'; + + static defaultProps = { + dataSourceTypes: [], + importPlugins: [], + }; + + state = { + active: false, + }; + + constructor(props: DataSourcePaneProps) { + super(props); + this.state.active = true; + + const { editor } = this.props; + // @todo pluginName, to unsubscribe + // 第一次 active 事件不会触发监听器 + editor.on('skeleton.panel-dock.active', (pluginName, dock) => { + if (pluginName === PLUGIN_NAME) { + this.setState({ active: true }); + } + }); + editor.on('skeleton.panel-dock.unactive', (pluginName, dock) => { + if (pluginName === PLUGIN_NAME) { + this.setState({ active: false }); + } + }); + } + + handleSchemaChange = (schema: DataSource) => { + const { editor } = this.props; + + // @TODO 姿势是否最优? + if (editor.get('designer')) { + const docSchema = editor.get('designer').project.getSchema(); + _set(docSchema, 'componentsTree[0].dataSource', schema); + editor.get('designer').project.load(docSchema, true); + // console.log('check datasorce save result', editor.get('designer').project.getSchema()); + } + }; + + render() { + const { importPlugins, dataSourceTypes = [], editor } = this.props; + const { active } = this.state; + + if (!active) return null; + + const projectSchema = editor.get('designer').project.getSchema() ?? {}; + + return ( + + ); + } +} + +export * from './types'; diff --git a/packages/plugin-datasource-pane/src/list.tsx b/packages/plugin-datasource-pane/src/list.tsx new file mode 100644 index 000000000..caff9efd1 --- /dev/null +++ b/packages/plugin-datasource-pane/src/list.tsx @@ -0,0 +1,178 @@ +import React, { PureComponent, ReactElement, FC } from 'react'; +import { Button, Search, VirtualList, Tag, Balloon, Table } from '@alifd/next'; +import { DataSourceConfig } from '@ali/lowcode-types'; +import _isPlainObject from 'lodash/isPlainObject'; +import _isNumber from 'lodash/isNumber'; +import _isBoolean from 'lodash/isBoolean'; +import _isNil from 'lodash/isNil'; +import _tap from 'lodash/tap'; +import { DataSourceType } from './types'; + +const { Column: TableCol } = Table; + +function deriveTypeFromValue(val: any) { + if (_isBoolean(val)) return 'bool'; + if (_isNumber(val)) return 'number'; + if (_isPlainObject(val)) return 'obj'; + return 'string'; +} + +export interface DataSourceListProps { + dataSourceTypes: DataSourceType[]; + dataSource: DataSourceConfig[]; + onEditDataSource?: (dataSourceId: string) => void; + onDuplicateDataSource?: (dataSourceId: string) => void; + onRemoveDataSource?: (dataSourceId: string) => void; +} + +export interface DataSourceListState { + filteredType: string; + keyword: string; +} + +type TableRow = { + label: string; + value: any; +}; + +export default class DataSourceList extends PureComponent { + state = { + filteredType: '', + keyword: '', + }; + + handleSearchFilterChange = (filteredType: any) => { + this.setState({ filteredType }); + }; + + handleSearch = (keyword: any) => { + this.setState({ keyword }); + }; + + handleEditDataSource = (id: any) => { + this.props.onEditDataSource?.(id); + }; + + handleDuplicateDataSource = (id: any) => { + this.props.onDuplicateDataSource?.(id); + }; + + handleRemoveDataSource = (id: any) => { + this.props.onRemoveDataSource?.(id); + }; + + deriveListDataSource = () => { + const { filteredType, keyword } = this.state; + const { dataSource, dataSourceTypes } = this.props; + + return ( + dataSource + ?.filter((item) => (filteredType ? item.type === filteredType : true)) + ?.filter((item) => (keyword ? item.id.indexOf(keyword) !== -1 : true)) + ?.map((item) => ( +
  • +
    +
    +
    + {item.id} +
    + {!!dataSourceTypes.find((ds) => ds.type === item.type) && ( + 详情} + align="b" + alignEdge + triggerType="hover" + style={{ width: 300 }} + > + ((acc, cur) => { + // @todo 这里的 ts 处理得不好 + if (_isPlainObject(item.options[cur])) { + Object.keys(item?.options?.[cur] || {}).forEach((curInOption) => { + acc.push({ + label: `${cur}.${curInOption}`, + value: (item?.options?.[cur] as any)?.[curInOption], + }); + }); + } else if (!_isNil(item.options[cur])) { + // @todo 排除 null + acc.push({ + label: cur, + value: item.options[cur], + }); + } + return acc; + }, []), + console.log, + )} + > + + ( +
    + {deriveTypeFromValue(val)} + {val.toString()} +
    + )} + /> +
    +
    + )} + {!!dataSourceTypes.find((ds) => ds.type === item.type) && ( + + )} + {!!dataSourceTypes.find((ds) => ds.type === item.type) && ( + + )} + +
    +
    + {item.type} + {item.isInit ? '自动' : '手动'} +
    +
    +
  • + )) || [] + ); + }; + + render() { + const { dataSourceTypes } = this.props; + const { filteredType } = this.state; + + return ( +
    + ({ + label: type?.type, + value: type?.type, + })), + )} + onFilterChange={this.handleSearchFilterChange} + /> +
    + {this.deriveListDataSource()} +
    +
    + ); + } +} diff --git a/packages/plugin-datasource-pane/src/locale/en-US.json b/packages/plugin-datasource-pane/src/locale/en-US.json new file mode 100644 index 000000000..5b7636ab3 --- /dev/null +++ b/packages/plugin-datasource-pane/src/locale/en-US.json @@ -0,0 +1,3 @@ +{ + "DataSourcePane": "DataSource Pane" +} diff --git a/packages/plugin-datasource-pane/src/locale/index.ts b/packages/plugin-datasource-pane/src/locale/index.ts new file mode 100644 index 000000000..49de985ed --- /dev/null +++ b/packages/plugin-datasource-pane/src/locale/index.ts @@ -0,0 +1,10 @@ +import { createIntl } from '@ali/lowcode-editor-core'; +import enUS from './en-US.json'; +import zhCN from './zh-CN.json'; + +const { intl, intlNode, getLocale, setLocale } = createIntl({ + 'en-US': enUS, + 'zh-CN': zhCN, +}); + +export { intl, intlNode, getLocale, setLocale }; diff --git a/packages/plugin-datasource-pane/src/locale/zh-CN.json b/packages/plugin-datasource-pane/src/locale/zh-CN.json new file mode 100644 index 000000000..dba113fb6 --- /dev/null +++ b/packages/plugin-datasource-pane/src/locale/zh-CN.json @@ -0,0 +1,3 @@ +{ + "DataSourcePane": "数据源面板" +} diff --git a/packages/plugin-datasource-pane/src/pane.tsx b/packages/plugin-datasource-pane/src/pane.tsx new file mode 100644 index 000000000..2604c9f9f --- /dev/null +++ b/packages/plugin-datasource-pane/src/pane.tsx @@ -0,0 +1,375 @@ +/** + * 面板,先通过 Dialog 呈现 + */ +import React, { PureComponent } from 'react'; +import { DataSource, DataSourceConfig } from '@ali/lowcode-types'; +import { Tab, Button, MenuButton, Message, Dialog } from '@alifd/next'; +import _cloneDeep from 'lodash/cloneDeep'; +import _uniqueId from 'lodash/uniqueId'; +import _isArray from 'lodash/isArray'; +import _get from 'lodash/get'; +import List from './list'; +// import { DataSourceImportButton, DataSourceImportPluginCode } from './import'; +import { DataSourceForm } from './datasource-form'; +import { DataSourcePaneImportPlugin, DataSourceType } from './types'; + +const { Item: TabItem } = Tab; +const { Item: MenuButtonItem } = MenuButton; + +const TAB_ITEM_LIST = 'list'; +const TAB_ITEM_IMPORT = 'import'; +const TAB_ITEM_CREATE = 'create'; +const TAB_ITEM_EDIT = 'edit'; + +export interface DataSourcePaneProps { + dataSourceTypes?: DataSourceType[]; + importPlugins?: DataSourcePaneImportPlugin[]; + defaultSchema?: DataSource; + onSchemaChange?: (schema: DataSource) => void; +} + +export interface TabItem { + key: string; + title: string; + closeable: boolean; + data?: any; + content?: any; +} + +export interface DataSourcePaneState { + dataSourceList: DataSourceConfig[]; + tabItems: TabItem[]; + activeTabKey: string; +} + +export class DataSourcePane extends PureComponent { + state: DataSourcePaneState = { + dataSourceList: [...(this.props.defaultSchema?.list || [])], + tabItems: [ + { + key: TAB_ITEM_LIST, + title: '数据源列表', + closeable: false, + }, + ], + activeTabKey: TAB_ITEM_LIST, + }; + + handleDataSourceListChange = (dataSourceList?: DataSourceConfig[]) => { + if (dataSourceList) { + this.setState({ dataSourceList }); + } + this.props.onSchemaChange?.({ + list: this.state.dataSourceList, + }); + }; + + handleImportDataSourceList = (toImport: DataSourceConfig[]) => { + const importDataSourceList = () => { + this.closeTab(TAB_ITEM_IMPORT); + this.setState( + ({ dataSourceList }) => ({ + dataSourceList: dataSourceList.concat(toImport), + }), + () => { + this.handleDataSourceListChange(); + }, + ); + }; + if (!_isArray(toImport) || toImport.length === 0) { + Message.error('没有找到可导入的数据源'); + return; + } + const repeatedDataSourceList = toImport.filter( + (item) => !!this.state.dataSourceList.find((dataSource) => dataSource.id === item.id), + ); + if (repeatedDataSourceList.length > 0) { + Dialog.confirm({ + content: `数据源(${repeatedDataSourceList + .map((item) => item.id) + .join(',')})已存在,如果导入会替换原数据源,是否继续?`, + onOk: () => { + importDataSourceList(); + }, + }); + return; + } + importDataSourceList(); + }; + + handleCreateDataSource = (toCreate: DataSourceConfig) => { + const create = () => { + this.closeTab(TAB_ITEM_CREATE); + this.setState( + ({ dataSourceList }) => ({ + dataSourceList: dataSourceList.concat([ + { + ...toCreate, + }, + ]), + }), + () => { + this.handleDataSourceListChange(); + }, + ); + }; + if (this.state.dataSourceList.find((dataSource) => dataSource.id === toCreate.id)) { + Dialog.confirm({ + content: `数据源(${toCreate.id})已存在,如果导入会替换原数据源,是否继续?`, + onOk: () => { + create(); + }, + }); + return; + } + create(); + }; + + handleUpdateDataSource = (toUpdate: DataSourceConfig) => { + this.closeTab(TAB_ITEM_EDIT); + this.setState( + ({ dataSourceList }) => { + const nextDataSourceList = [...dataSourceList]; + const dataSourceUpdate = nextDataSourceList.find((dataSource) => dataSource.id === toUpdate.id); + if (dataSourceUpdate) { + Object.assign(dataSourceUpdate, toUpdate); + } + return { + dataSourceList: nextDataSourceList, + }; + }, + () => { + this.handleDataSourceListChange(); + }, + ); + }; + + handleRemoveDataSource = (dataSourceId: string) => { + const remove = () => { + this.setState( + ({ dataSourceList }) => ({ + dataSourceList: dataSourceList.filter((item) => item.id !== dataSourceId), + }), + () => { + this.handleDataSourceListChange(); + }, + ); + }; + Dialog.confirm({ + content: `确定要删除吗?`, + onOk: () => { + remove(); + }, + }); + }; + + handleDuplicateDataSource = (dataSourceId: string) => { + const target = this.state.dataSourceList.find((item) => item.id === dataSourceId); + if (!target) return; + const cloned = _cloneDeep(target); + + this.openCreateDataSourceTab({ + ...cloned, + id: _uniqueId(`${cloned.id}_`), + }); + }; + + handleEditDataSource = (dataSourceId: string) => { + const target = this.state.dataSourceList.find((item) => item.id === dataSourceId); + if (!target) return; + const cloned = _cloneDeep(target); + + this.openEditDataSourceTab({ + ...cloned, + }); + }; + + // @todo 没有识别出类型 + handleTabChange = (activeTabKey: any) => { + this.setState({ activeTabKey }); + }; + + openCreateDataSourceTab = (dataSource: DataSourceConfig) => { + const { tabItems } = this.state; + const { dataSourceTypes = [] } = this.props; + + if (!tabItems.find((item) => item.key === TAB_ITEM_CREATE)) { + this.setState(({ tabItems }) => ({ + tabItems: tabItems.concat({ + key: TAB_ITEM_CREATE, + title: `添加数据源`, + closeable: true, + data: { + dataSourceType: dataSourceTypes?.find((type) => type.type === dataSource.type), + }, + }), + })); + this.setState({ activeTabKey: TAB_ITEM_CREATE }); + } else { + Message.notice('当前已有一个新建数据源的 TAB 被大家'); + } + }; + + handleCreateDataSourceBtnClick = (dataSourceType: string) => { + this.openCreateDataSourceTab({ + type: dataSourceType, + } as DataSourceConfig); + }; + + handleCreateDataSourceMenuBtnClick = (dataSourceType: string) => { + this.openCreateDataSourceTab({ + type: dataSourceType, + } as DataSourceConfig); + }; + + openEditDataSourceTab = (dataSource: DataSourceConfig) => { + const { tabItems } = this.state; + const { dataSourceTypes = [] } = this.props; + + if (!tabItems.find((item) => item.key === TAB_ITEM_EDIT)) { + this.setState(({ tabItems }) => ({ + tabItems: tabItems.concat({ + key: TAB_ITEM_EDIT, + title: '修改数据源', + closeable: true, + data: { + dataSource, + dataSourceType: dataSourceTypes?.find((type) => type.type === dataSource.type), + }, + }), + })); + } + this.setState({ activeTabKey: TAB_ITEM_EDIT }); + }; + + openImportDataSourceTab = (selectedImportPluginName: string) => { + const { tabItems } = this.state; + const { importPlugins } = this.props; + + if (!tabItems.find((item) => item.key === `${TAB_ITEM_IMPORT}_${selectedImportPluginName}`)) { + this.setState(({ tabItems }) => ({ + tabItems: tabItems.concat({ + key: TAB_ITEM_IMPORT, + title: `导入数据源-${selectedImportPluginName}`, + closeable: true, + content: _get( + importPlugins?.find((plugin) => selectedImportPluginName === plugin.name), + 'component', + ), + }), + })); + this.setState({ activeTabKey: TAB_ITEM_IMPORT }); + } else { + Message.notice('当前已有一个导入数据源的 TAB'); + } + }; + + closeTab = (tabKey: any) => { + this.setState( + ({ tabItems }) => ({ + tabItems: tabItems.filter((item) => item.key !== tabKey), + }), + () => { + this.setState(({ tabItems }) => ({ + activeTabKey: _get(tabItems, '[0].key'), + })); + }, + ); + }; + + renderTabExtraContent = () => { + const { importPlugins, dataSourceTypes } = this.props; + + // @todo onSelect 不行? + return [ + _isArray(dataSourceTypes) && dataSourceTypes.length > 0 ? ( + + {dataSourceTypes.map((type) => ( + {type.type} + ))} + + ) : _isArray(dataSourceTypes) && dataSourceTypes.length === 1 ? ( + + ) : null, + _isArray(importPlugins) && importPlugins.length > 1 ? ( + + {importPlugins.map((plugin) => ( + {plugin.name} + ))} + + ) : _isArray(importPlugins) && importPlugins.length === 1 ? ( + + ) : null, + ]; + }; + + // 更通用的处理 + renderTabItemContentByKey = (tabItemKey: any, data: any) => { + const { dataSourceList, tabItems } = this.state; + const { dataSourceTypes = [] } = this.props; + + if (tabItemKey === TAB_ITEM_LIST) { + return ( + + ); + } else if (tabItemKey === TAB_ITEM_EDIT) { + const dataSourceType = dataSourceTypes.find((type) => type.type === data?.dataSource.type); + if (dataSourceType) { + return ( + + ); + } + } else if (tabItemKey === TAB_ITEM_CREATE) { + return ( + + ); + } else if (tabItemKey === TAB_ITEM_IMPORT) { + const tabItemData = tabItems.find((tabItem) => tabItem.key === tabItemKey); + if (tabItemData) { + const Content = tabItemData.content; + return ( + + ); + } + } + return null; + }; + + render() { + const { activeTabKey, tabItems } = this.state; + + return ( +
    + + {tabItems.map((item: TabItem) => ( + {this.renderTabItemContentByKey(item.key, item.data)} + ))} + +
    + ); + } +} diff --git a/packages/plugin-datasource-pane/src/types/datasource-type.ts b/packages/plugin-datasource-pane/src/types/datasource-type.ts new file mode 100644 index 000000000..630f9dc29 --- /dev/null +++ b/packages/plugin-datasource-pane/src/types/datasource-type.ts @@ -0,0 +1,6 @@ +import { JSONSchema6 } from 'json-schema'; + +export type DataSourceType = { + type: string; + schema: JSONSchema6; +}; diff --git a/packages/plugin-datasource-pane/src/types/import-plugin.ts b/packages/plugin-datasource-pane/src/types/import-plugin.ts new file mode 100644 index 000000000..074e2f106 --- /dev/null +++ b/packages/plugin-datasource-pane/src/types/import-plugin.ts @@ -0,0 +1,20 @@ +import { DataSourceConfig } from '@ali/lowcode-types'; +import { DataSourceType } from './datasource-type'; + +// 导入插件 +export interface DataSourcePaneImportPlugin { + name: string; + title: string; + component: React.ReactNode; + componentProps?: DataSourcePaneImportPluginCustomProps; +} + +export interface DataSourcePaneImportPluginCustomProps extends DataSourcePaneImportPluginComponentProps { + [customPropName: string]: any; +} + +export interface DataSourcePaneImportPluginComponentProps { + dataSourceTypes: DataSourceType[]; + onImport?: (dataSourceList: DataSourceConfig[]) => void; + onCancel?: () => void; +} diff --git a/packages/plugin-datasource-pane/src/types/index.ts b/packages/plugin-datasource-pane/src/types/index.ts new file mode 100644 index 000000000..f3ea68ac2 --- /dev/null +++ b/packages/plugin-datasource-pane/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './datasource-type'; +export * from './import-plugin'; diff --git a/packages/plugin-datasource-pane/test/foobar.ts b/packages/plugin-datasource-pane/test/foobar.ts new file mode 100644 index 000000000..7a14c4b2d --- /dev/null +++ b/packages/plugin-datasource-pane/test/foobar.ts @@ -0,0 +1,5 @@ +import test from 'ava'; + +test('foobar', t => { + t.pass(); +}); diff --git a/packages/plugin-datasource-pane/tsconfig.json b/packages/plugin-datasource-pane/tsconfig.json new file mode 100644 index 000000000..df5f697e9 --- /dev/null +++ b/packages/plugin-datasource-pane/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "lib": ["esnext", "dom"], + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "./src/" + ] +} diff --git a/packages/plugin-designer/CHANGELOG.md b/packages/plugin-designer/CHANGELOG.md index 57f80fd26..9d5fb421b 100644 --- a/packages/plugin-designer/CHANGELOG.md +++ b/packages/plugin-designer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-designer@1.0.13...@ali/lowcode-plugin-designer@1.0.15) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-designer + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-designer@1.0.12...@ali/lowcode-plugin-designer@1.0.13) (2020-11-02) diff --git a/packages/plugin-designer/package.json b/packages/plugin-designer/package.json index 551c4ae25..a992d32f3 100644 --- a/packages/plugin-designer/package.json +++ b/packages/plugin-designer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-designer", - "version": "1.0.13", + "version": "1.0.15", "description": "alibaba lowcode editor designer plugin", "files": [ "es", @@ -20,8 +20,8 @@ ], "author": "xiayang.xy", "dependencies": { - "@ali/lowcode-designer": "^1.0.13", - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-designer": "^1.0.15", + "@ali/lowcode-editor-core": "^1.0.15", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/plugin-event-bind-dialog/CHANGELOG.md b/packages/plugin-event-bind-dialog/CHANGELOG.md index 42cdcfe34..a8d6047df 100644 --- a/packages/plugin-event-bind-dialog/CHANGELOG.md +++ b/packages/plugin-event-bind-dialog/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-event-bind-dialog@1.0.13...@ali/lowcode-plugin-event-bind-dialog@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-event-bind-dialog + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-event-bind-dialog@1.0.12...@ali/lowcode-plugin-event-bind-dialog@1.0.13) (2020-11-02) diff --git a/packages/plugin-event-bind-dialog/package.json b/packages/plugin-event-bind-dialog/package.json index 4d4bb0704..6b78edd42 100644 --- a/packages/plugin-event-bind-dialog/package.json +++ b/packages/plugin-event-bind-dialog/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-event-bind-dialog", - "version": "1.0.13", + "version": "1.0.14", "description": "alibaba lowcode editor event bind dialog plugin", "files": [ "es", @@ -19,7 +19,7 @@ ], "author": "zude.hzd", "dependencies": { - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-editor-core": "^1.0.15", "@ali/lowcode-types": "^1.0.13", "@alifd/next": "^1.19.16", "react": "^16.8.1", diff --git a/packages/plugin-event-bind-dialog/src/index.tsx b/packages/plugin-event-bind-dialog/src/index.tsx index 978f074e5..7af71729a 100644 --- a/packages/plugin-event-bind-dialog/src/index.tsx +++ b/packages/plugin-event-bind-dialog/src/index.tsx @@ -58,15 +58,11 @@ export default class EventBindDialog extends Component { paramStr: '', }; - openDialog = (bindEventName: string) => { + openDialog = (bindEventName: string, isEdit:boolean) => { this.bindEventName = bindEventName; - this.initEventName(); + this.initEventName(isEdit); - this.setState({ - visiable: true, - selectedEventName: '', - }); }; closeDialog = () => { @@ -77,7 +73,7 @@ export default class EventBindDialog extends Component { componentDidMount() { const { editor, config } = this.props; - editor.on(`${config.pluginKey}.openDialog`, (bindEventName: string, setterName:string, paramStr:string) => { + editor.on(`${config.pluginKey}.openDialog`, (bindEventName: string, setterName:string, paramStr:string, isEdit:boolean) => { console.log(`paramStr:${ paramStr}`); this.setState({ setterName, @@ -95,20 +91,25 @@ export default class EventBindDialog extends Component { } } - this.openDialog(bindEventName); + this.openDialog(bindEventName, isEdit); }); } - initEventName = () => { + initEventName = (isEdit:boolean) => { let eventName = this.bindEventName; - this.eventList.forEach((item) => { - if (item.name === eventName) { - eventName = `${eventName}_new`; - } - }); + + if (!isEdit) { + this.eventList.forEach((item) => { + if (item.name === eventName) { + eventName = `${eventName}_new`; + } + }); + } this.setState({ eventName, + selectedEventName: (isEdit ? eventName : ''), + visiable: true, }); }; @@ -168,6 +169,7 @@ export default class EventBindDialog extends Component { render() { const { selectedEventName, eventName, visiable, paramStr } = this.state; + console.log('selectedEventName:' + selectedEventName); return ( +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.13...@ali/lowcode-plugin-outline-pane@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-outline-pane@1.0.12...@ali/lowcode-plugin-outline-pane@1.0.13) (2020-11-02) diff --git a/packages/plugin-outline-pane/package.json b/packages/plugin-outline-pane/package.json index 426c3cfa6..13e29033b 100644 --- a/packages/plugin-outline-pane/package.json +++ b/packages/plugin-outline-pane/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-outline-pane", - "version": "1.0.13", + "version": "1.0.14", "description": "Outline pane for Ali lowCode engine", "files": [ "es", @@ -14,10 +14,10 @@ "test:snapshot": "ava --update-snapshots" }, "dependencies": { - "@ali/lowcode-designer": "^1.0.13", - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-designer": "^1.0.15", + "@ali/lowcode-editor-core": "^1.0.15", "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "@alifd/next": "^1.19.16", "classnames": "^2.2.6", "react": "^16", diff --git a/packages/plugin-sample-logo/CHANGELOG.md b/packages/plugin-sample-logo/CHANGELOG.md index ed38fecb4..705e3ab40 100644 --- a/packages/plugin-sample-logo/CHANGELOG.md +++ b/packages/plugin-sample-logo/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-sample-logo@1.0.13...@ali/lowcode-plugin-sample-logo@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-sample-logo + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-sample-logo@1.0.12...@ali/lowcode-plugin-sample-logo@1.0.13) (2020-11-02) diff --git a/packages/plugin-sample-logo/package.json b/packages/plugin-sample-logo/package.json index ac008f303..641e0b637 100644 --- a/packages/plugin-sample-logo/package.json +++ b/packages/plugin-sample-logo/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-sample-logo", - "version": "1.0.13", + "version": "1.0.14", "description": "alibaba lowcode editor logo plugin", "files": [ "es/", @@ -20,7 +20,7 @@ ], "author": "xiayang.xy", "dependencies": { - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-editor-core": "^1.0.15", "react": "^16.8.1" }, "devDependencies": { diff --git a/packages/plugin-sample-preview/CHANGELOG.md b/packages/plugin-sample-preview/CHANGELOG.md index 000c418a3..eeb575834 100644 --- a/packages/plugin-sample-preview/CHANGELOG.md +++ b/packages/plugin-sample-preview/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-sample-preview@1.0.13...@ali/lowcode-plugin-sample-preview@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-sample-preview + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-sample-preview@1.0.12...@ali/lowcode-plugin-sample-preview@1.0.13) (2020-11-02) diff --git a/packages/plugin-sample-preview/package.json b/packages/plugin-sample-preview/package.json index da4ae9ffe..59f4880d9 100644 --- a/packages/plugin-sample-preview/package.json +++ b/packages/plugin-sample-preview/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-sample-preview", - "version": "1.0.13", + "version": "1.0.14", "description": "alibaba lowcode editor sample preview plugin", "files": [ "es", @@ -19,11 +19,11 @@ "editor" ], "dependencies": { - "@ali/lowcode-designer": "^1.0.13", - "@ali/lowcode-editor-core": "^1.0.13", - "@ali/lowcode-react-renderer": "^1.0.13", + "@ali/lowcode-designer": "^1.0.15", + "@ali/lowcode-editor-core": "^1.0.15", + "@ali/lowcode-react-renderer": "^1.0.14", "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "@alifd/next": "^1.x", "react": "^16.8.1" }, diff --git a/packages/plugin-source-editor/CHANGELOG.md b/packages/plugin-source-editor/CHANGELOG.md index 7d5ab90a8..2c9fbc5c2 100644 --- a/packages/plugin-source-editor/CHANGELOG.md +++ b/packages/plugin-source-editor/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-source-editor@1.0.12...@ali/lowcode-plugin-source-editor@1.0.13) (2020-11-04) + + +### Bug Fixes + +* 修复编辑器转化bug,增加窗口最大最小化功能 ([05666af](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/05666af)) + + +### Features + +* style 迁移 ([6ce97da](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6ce97da)) + + + + ## [1.0.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-source-editor@1.0.12...@ali/lowcode-plugin-source-editor@1.0.12) (2020-11-02) diff --git a/packages/plugin-source-editor/package.json b/packages/plugin-source-editor/package.json index 3364d505d..2c9733772 100644 --- a/packages/plugin-source-editor/package.json +++ b/packages/plugin-source-editor/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-source-editor", - "version": "1.0.12", + "version": "1.0.13", "description": "alibaba lowcode editor source-editor plugin", "files": [ "es", @@ -19,7 +19,7 @@ ], "author": "zude.hzd", "dependencies": { - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-editor-core": "^1.0.15", "@alifd/next": "^1.19.16", "js-beautify": "^1.10.1", "prettier": "^1.18.2", diff --git a/packages/plugin-source-editor/src/index.tsx b/packages/plugin-source-editor/src/index.tsx index 50e1228a3..d918c7532 100644 --- a/packages/plugin-source-editor/src/index.tsx +++ b/packages/plugin-source-editor/src/index.tsx @@ -102,10 +102,6 @@ export default class SourceEditor extends Component<{ } - componentDidMount() { - // this.editorNode = this.editorJsRef.current; // 记录当前dom节点; - } - /** * 执行编辑器事件 */ diff --git a/packages/plugin-undo-redo/CHANGELOG.md b/packages/plugin-undo-redo/CHANGELOG.md index 51a10da73..623c1f091 100644 --- a/packages/plugin-undo-redo/CHANGELOG.md +++ b/packages/plugin-undo-redo/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-undo-redo@1.0.12...@ali/lowcode-plugin-undo-redo@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-undo-redo + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-undo-redo@1.0.12...@ali/lowcode-plugin-undo-redo@1.0.13) (2020-11-02) diff --git a/packages/plugin-undo-redo/package.json b/packages/plugin-undo-redo/package.json index 1ba3785d3..50a640620 100644 --- a/packages/plugin-undo-redo/package.json +++ b/packages/plugin-undo-redo/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-undo-redo", - "version": "1.0.13", + "version": "1.0.14", "description": "alibaba lowcode editor undo redo plugin", "files": [ "es", @@ -19,11 +19,11 @@ ], "author": "xiayang.xy", "dependencies": { - "@ali/lowcode-designer": "^1.0.13", - "@ali/lowcode-editor-core": "^1.0.13", - "@ali/lowcode-editor-skeleton": "^1.0.13", + "@ali/lowcode-designer": "^1.0.15", + "@ali/lowcode-editor-core": "^1.0.15", + "@ali/lowcode-editor-skeleton": "^1.0.15", "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/plugin-variable-bind-dialog/CHANGELOG.md b/packages/plugin-variable-bind-dialog/CHANGELOG.md index 968b8bf78..d73760274 100644 --- a/packages/plugin-variable-bind-dialog/CHANGELOG.md +++ b/packages/plugin-variable-bind-dialog/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-variable-bind-dialog@1.0.12...@ali/lowcode-plugin-variable-bind-dialog@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-variable-bind-dialog + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-variable-bind-dialog@1.0.12...@ali/lowcode-plugin-variable-bind-dialog@1.0.13) (2020-11-02) diff --git a/packages/plugin-variable-bind-dialog/package.json b/packages/plugin-variable-bind-dialog/package.json index 50dc8a57f..f50e74e0b 100644 --- a/packages/plugin-variable-bind-dialog/package.json +++ b/packages/plugin-variable-bind-dialog/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-variable-bind-dialog", - "version": "1.0.13", + "version": "1.0.14", "description": "alibaba lowcode editor variable bind dialog plugin", "files": [ "es", @@ -19,7 +19,7 @@ ], "author": "zude.hzd", "dependencies": { - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-editor-core": "^1.0.15", "@alifd/next": "^1.19.16", "react": "^16.8.1", "react-dom": "^16.8.1" diff --git a/packages/plugin-zh-en/CHANGELOG.md b/packages/plugin-zh-en/CHANGELOG.md index 8e7507880..349ccbc3a 100644 --- a/packages/plugin-zh-en/CHANGELOG.md +++ b/packages/plugin-zh-en/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-zh-en@1.0.12...@ali/lowcode-plugin-zh-en@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-zh-en + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-plugin-zh-en@1.0.12...@ali/lowcode-plugin-zh-en@1.0.13) (2020-11-02) diff --git a/packages/plugin-zh-en/package.json b/packages/plugin-zh-en/package.json index ea799e936..41e956f2e 100644 --- a/packages/plugin-zh-en/package.json +++ b/packages/plugin-zh-en/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-zh-en", - "version": "1.0.13", + "version": "1.0.14", "description": "alibaba lowcode editor zhong english plugin", "files": [ "es", @@ -14,9 +14,9 @@ "test:snapshot": "ava --update-snapshots" }, "dependencies": { - "@ali/lowcode-editor-core": "^1.0.13", + "@ali/lowcode-editor-core": "^1.0.15", "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/rax-provider/CHANGELOG.md b/packages/rax-provider/CHANGELOG.md index 9e54eb8ce..499259753 100644 --- a/packages/rax-provider/CHANGELOG.md +++ b/packages/rax-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-rax-provider@1.0.12...@ali/lowcode-rax-provider@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-rax-provider + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-rax-provider@1.0.12...@ali/lowcode-rax-provider@1.0.13) (2020-11-02) diff --git a/packages/rax-provider/package.json b/packages/rax-provider/package.json index 36f8e3cd3..85cf13c99 100644 --- a/packages/rax-provider/package.json +++ b/packages/rax-provider/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-rax-provider", - "version": "1.0.13", + "version": "1.0.14", "description": "Rax Provider for Runtime", "files": [ "es", @@ -18,7 +18,7 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-runtime": "^1.0.15", + "@ali/lowcode-runtime": "^1.0.16", "driver-universal": "^3.1.3", "history": "^4.10.1", "rax-use-router": "^3.0.0" diff --git a/packages/rax-render/CHANGELOG.md b/packages/rax-render/CHANGELOG.md index 1c48c850e..5b2e9fb7a 100644 --- a/packages/rax-render/CHANGELOG.md +++ b/packages/rax-render/CHANGELOG.md @@ -3,7 +3,15 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-rax-renderer@1.0.12...@ali/lowcode-rax-renderer@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-rax-renderer + + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-rax-renderer@1.0.12...@ali/lowcode-rax-renderer@1.0.13) (2020-11-02) @@ -11,7 +19,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @ali/lowcode-rax-renderer - + ## [1.0.12](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-rax-renderer@1.0.11...@ali/lowcode-rax-renderer@1.0.12) (2020-10-20) diff --git a/packages/rax-render/package.json b/packages/rax-render/package.json index e4b92a661..f0cad1857 100644 --- a/packages/rax-render/package.json +++ b/packages/rax-render/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-rax-renderer", - "version": "1.0.13", + "version": "1.0.14", "description": "Rax renderer for Ali lowCode engine", "main": "lib/index.js", "module": "lib/index.js", @@ -36,7 +36,8 @@ "@ali/b3-one": "^0.0.17", "@ali/bzb-request": "2.6.1", "@ali/lib-mtop": "^2.5.1", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-datasource-engine": "^1.0.14", + "@ali/lowcode-utils": "^1.0.14", "classnames": "^2.2.6", "debug": "^4.1.1", "events": "^3.0.0", diff --git a/packages/rax-render/src/engine/base.tsx b/packages/rax-render/src/engine/base.tsx index 56ee19ffc..bf9436a6e 100644 --- a/packages/rax-render/src/engine/base.tsx +++ b/packages/rax-render/src/engine/base.tsx @@ -3,6 +3,7 @@ import { Component, createElement } from 'rax'; import PropTypes from 'prop-types'; import Debug from 'debug'; import classnames from 'classnames'; +import { createInterpret } from '@ali/lowcode-datasource-engine'; import DataHelper from '../utils/dataHelper'; import { forEach, @@ -86,31 +87,6 @@ export default class BaseEngine extends Component { console.warn(e); } - reloadDataSource = () => new Promise((resolve, reject) => { - debug('reload data source'); - if (!this.__dataHelper) { - this.__showPlaceholder = false; - return resolve(); - } - this.__dataHelper - .getInitData() - .then((res) => { - this.__showPlaceholder = false; - if (isEmpty(res)) { - this.forceUpdate(); - return resolve(); - } - this.setState(res, resolve); - }) - .catch((err) => { - if (this.__showPlaceholder) { - this.__showPlaceholder = false; - this.forceUpdate(); - } - reject(err); - }); - }); - __setLifeCycleMethods = (method, args) => { const lifeCycleMethods = getValue(this.props.__schema, 'lifeCycles', {}); if (lifeCycleMethods[method]) { @@ -156,17 +132,58 @@ export default class BaseEngine extends Component { __initDataSource = (props = this.props) => { const schema = props.__schema || {}; - const appHelper = props.__appHelper; const dataSource = (schema && schema.dataSource) || {}; - this.__dataHelper = new DataHelper(this, dataSource, appHelper, (config) => this.__parseData(config)); - this.dataSourceMap = this.__dataHelper.dataSourceMap; + // requestHandlersMap 存在才走数据源引擎方案 + if (props.requestHandlersMap) { + const { dataSourceMap, reloadDataSource } = createInterpret(dataSource, this, { + requestHandlersMap: { + mtop: createMtopHandler(), + fetch: createFetchHandler(), + } + }); + this.dataSourceMap = dataSourceMap; + this.reloadDataSource = () => new Promise((resolve, reject) => { + debug('reload data source'); + this.__showPlaceholder = true; + reloadDataSource().then(() => { + this.__showPlaceholder = false; + // @TODO 是否需要 forceUpate + resolve(); + }) + }); + } else { + const appHelper = props.__appHelper; + this.__dataHelper = new DataHelper(this, dataSource, appHelper, (config) => this.__parseData(config)); + this.dataSourceMap = this.__dataHelper.dataSourceMap; + this.reloadDataSource = () => new Promise((resolve, reject) => { + debug('reload data source'); + if (!this.__dataHelper) { + this.__showPlaceholder = false; + return resolve(); + } + this.__dataHelper + .getInitData() + .then((res) => { + this.__showPlaceholder = false; + if (isEmpty(res)) { + this.forceUpdate(); + return resolve(); + } + this.setState(res, resolve); + }) + .catch((err) => { + if (this.__showPlaceholder) { + this.__showPlaceholder = false; + this.forceUpdate(); + } + reject(err); + }); + }); + } // 设置容器组件占位,若设置占位则在初始异步请求完成之前用loading占位且不渲染容器组件内部内容 if (this.__parseData(schema.props && schema.props.autoLoading)) { this.__showPlaceholder = (dataSource.list || []).some((item) => !!this.__parseData(item.isInit)); } - // this.__showPlaceholder = this.__parseData(schema.props && schema.props.autoLoading) && (dataSource.list || []).some( - // (item) => !!this.__parseData(item.isInit), - // ); }; __render = () => { diff --git a/packages/rax-render/src/engine/index.tsx b/packages/rax-render/src/engine/index.tsx index 6215a46ed..c704fc59c 100644 --- a/packages/rax-render/src/engine/index.tsx +++ b/packages/rax-render/src/engine/index.tsx @@ -46,6 +46,8 @@ export default class Engine extends Component { appHelper: PropTypes.object, components: PropTypes.object, componentsMap: PropTypes.object, + // 数据源请求处理 + requestHandlersMap: PropTypes.object, designMode: PropTypes.string, suspended: PropTypes.bool, schema: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), @@ -60,6 +62,8 @@ export default class Engine extends Component { appHelper: null, components: {}, componentsMap: {}, + // 数据源请求处理 + requestHandlersMap: null, designMode: '', suspended: false, schema: {}, diff --git a/packages/rax-simulator-renderer/CHANGELOG.md b/packages/rax-simulator-renderer/CHANGELOG.md index ea11741f0..669384c00 100644 --- a/packages/rax-simulator-renderer/CHANGELOG.md +++ b/packages/rax-simulator-renderer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-rax-simulator-renderer@1.0.12...@ali/lowcode-rax-simulator-renderer@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-rax-simulator-renderer + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-rax-simulator-renderer@1.0.12...@ali/lowcode-rax-simulator-renderer@1.0.13) (2020-11-02) diff --git a/packages/rax-simulator-renderer/package.json b/packages/rax-simulator-renderer/package.json index 67775d8ca..b99981db1 100644 --- a/packages/rax-simulator-renderer/package.json +++ b/packages/rax-simulator-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-rax-simulator-renderer", - "version": "1.0.13", + "version": "1.0.14", "description": "rax simulator renderer for alibaba lowcode designer", "main": "lib/index.js", "module": "es/index.js", @@ -13,10 +13,10 @@ "test:snapshot": "ava --update-snapshots" }, "dependencies": { - "@ali/lowcode-designer": "^1.0.13", - "@ali/lowcode-rax-renderer": "^1.0.13", + "@ali/lowcode-designer": "^1.0.15", + "@ali/lowcode-rax-renderer": "^1.0.14", "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "@ali/recore-rax": "^1.2.4", "@ali/vu-css-style": "^1.0.2", "@recore/obx": "^1.0.8", @@ -55,5 +55,5 @@ "publishConfig": { "registry": "https://registry.npm.alibaba-inc.com" }, - "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-rax-simulator-renderer@1.0.12/build/index.html" + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-rax-simulator-renderer@1.0.13/build/index.html" } diff --git a/packages/rax-simulator-renderer/src/renderer-view.tsx b/packages/rax-simulator-renderer/src/renderer-view.tsx index 413697411..8056a9644 100644 --- a/packages/rax-simulator-renderer/src/renderer-view.tsx +++ b/packages/rax-simulator-renderer/src/renderer-view.tsx @@ -113,6 +113,7 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> { schema={renderer.schema} components={renderer.components} context={renderer.context} + requestHandlersMap={host.requestHandlersMap} device={device} designMode={renderer.designMode} suspended={renderer.suspended} diff --git a/packages/react-provider/CHANGELOG.md b/packages/react-provider/CHANGELOG.md index 3c3749f4b..56fb25643 100644 --- a/packages/react-provider/CHANGELOG.md +++ b/packages/react-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.16](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-react-provider@1.0.14...@ali/lowcode-react-provider@1.0.16) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-react-provider + ## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-react-provider@1.0.14...@ali/lowcode-react-provider@1.0.15) (2020-11-02) diff --git a/packages/react-provider/package.json b/packages/react-provider/package.json index cac897530..2ec467aff 100644 --- a/packages/react-provider/package.json +++ b/packages/react-provider/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-react-provider", - "version": "1.0.15", + "version": "1.0.16", "description": "React Provider for Runtime", "files": [ "es", @@ -25,8 +25,8 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-runtime": "^1.0.15", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-runtime": "^1.0.16", + "@ali/lowcode-utils": "^1.0.14", "@recore/router": "^1.0.11", "react": "^16", "react-dom": "^16" diff --git a/packages/react-renderer/CHANGELOG.md b/packages/react-renderer/CHANGELOG.md index 01927f531..bd0a47cd9 100644 --- a/packages/react-renderer/CHANGELOG.md +++ b/packages/react-renderer/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-react-renderer@1.0.12...@ali/lowcode-react-renderer@1.0.14) (2020-11-04) + + +### Bug Fixes + +* children 在 schema 和 props 中并存的情况处理 ([7b639eb](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/7b639eb)) + + + + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-react-renderer@1.0.12...@ali/lowcode-react-renderer@1.0.13) (2020-11-02) diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json index df3c94f77..e03d5b803 100644 --- a/packages/react-renderer/package.json +++ b/packages/react-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-react-renderer", - "version": "1.0.13", + "version": "1.0.14", "description": "react renderer for ali lowcode engine", "main": "lib/index.js", "module": "es/index.js", @@ -27,6 +27,7 @@ "@ali/b3-one": "^0.0.17", "@ali/bzb-request": "^2.6.0-beta.13", "@ali/lib-mtop": "^2.5.1", + "@ali/lowcode-datasource-engine": "1.0.7-0", "@alifd/next": "^1.19.17", "debug": "^4.1.1", "events": "^3.0.0", @@ -53,5 +54,5 @@ "publishConfig": { "registry": "http://registry.npm.alibaba-inc.com" }, - "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@1.0.12/build/index.html" + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@1.0.13/build/index.html" } diff --git a/packages/react-renderer/src/renderer/base.tsx b/packages/react-renderer/src/renderer/base.tsx index ea685d9d8..a67ccf3fd 100644 --- a/packages/react-renderer/src/renderer/base.tsx +++ b/packages/react-renderer/src/renderer/base.tsx @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import Debug from 'debug'; +import { createInterpret } from '@ali/lowcode-datasource-engine'; import Div from '../components/Div'; import VisualDom from '../components/VisualDom'; import AppContext from '../context/appContext'; @@ -82,31 +83,6 @@ export default class BaseRender extends PureComponent { console.warn(e); } - reloadDataSource = () => new Promise((resolve, reject) => { - debug('reload data source'); - if (!this.__dataHelper) { - this.__showPlaceholder = false; - return resolve(); - } - this.__dataHelper - .getInitData() - .then((res) => { - this.__showPlaceholder = false; - if (isEmpty(res)) { - this.forceUpdate(); - return resolve(); - } - this.setState(res, resolve); - }) - .catch((err) => { - if (this.__showPlaceholder) { - this.__showPlaceholder = false; - this.forceUpdate(); - } - reject(err); - }); - }); - __setLifeCycleMethods = (method, args) => { const lifeCycleMethods = getValue(this.props.__schema, 'lifeCycles', {}); let fn = lifeCycleMethods[method]; @@ -168,10 +144,54 @@ export default class BaseRender extends PureComponent { __initDataSource = (props = this.props) => { const schema = props.__schema || {}; - const appHelper = props.__appHelper; const dataSource = (schema && schema.dataSource) || {}; - this.__dataHelper = new DataHelper(this, dataSource, appHelper, (config) => this.__parseData(config)); - this.dataSourceMap = this.__dataHelper.dataSourceMap; + // requestHandlersMap 存在才走数据源引擎方案 + if (props.requestHandlersMap) { + const { dataSourceMap, reloadDataSource } = createInterpret(dataSource, this, { + requestHandlersMap: { + mtop: createMtopHandler(), + fetch: createFetchHandler(), + } + }); + this.dataSourceMap = dataSourceMap; + this.reloadDataSource = () => new Promise((resolve, reject) => { + debug('reload data source'); + this.__showPlaceholder = true; + reloadDataSource().then(() => { + this.__showPlaceholder = false; + // @TODO 是否需要 forceUpate + resolve(); + }) + }); + } else { + const appHelper = props.__appHelper; + this.__dataHelper = new DataHelper(this, dataSource, appHelper, (config) => this.__parseData(config)); + this.dataSourceMap = this.__dataHelper.dataSourceMap; + this.reloadDataSource = () => new Promise((resolve, reject) => { + debug('reload data source'); + if (!this.__dataHelper) { + this.__showPlaceholder = false; + return resolve(); + } + this.__dataHelper + .getInitData() + .then((res) => { + this.__showPlaceholder = false; + if (isEmpty(res)) { + this.forceUpdate(); + return resolve(); + } + this.setState(res, resolve); + }) + .catch((err) => { + if (this.__showPlaceholder) { + this.__showPlaceholder = false; + this.forceUpdate(); + } + reject(err); + }); + }); + } // 设置容器组件占位,若设置占位则在初始异步请求完成之前用loading占位且不渲染容器组件内部内容 this.__showPlaceholder = this.__parseData(schema.props && schema.props.autoLoading) && (dataSource.list || []).some( (item) => !!this.__parseData(item.isInit), diff --git a/packages/react-simulator-renderer/CHANGELOG.md b/packages/react-simulator-renderer/CHANGELOG.md index 872ac8fe8..afc524c4e 100644 --- a/packages/react-simulator-renderer/CHANGELOG.md +++ b/packages/react-simulator-renderer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-react-simulator-renderer@1.0.12...@ali/lowcode-react-simulator-renderer@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-react-simulator-renderer + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-react-simulator-renderer@1.0.12...@ali/lowcode-react-simulator-renderer@1.0.13) (2020-11-02) diff --git a/packages/react-simulator-renderer/package.json b/packages/react-simulator-renderer/package.json index 41ef42642..6a7c4b8cc 100644 --- a/packages/react-simulator-renderer/package.json +++ b/packages/react-simulator-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-react-simulator-renderer", - "version": "1.0.13", + "version": "1.0.14", "description": "react simulator renderer for alibaba lowcode designer", "main": "lib/index.js", "module": "es/index.js", @@ -16,10 +16,10 @@ "build": "build-scripts build --skip-demo" }, "dependencies": { - "@ali/lowcode-designer": "^1.0.13", - "@ali/lowcode-react-renderer": "^1.0.13", + "@ali/lowcode-designer": "^1.0.15", + "@ali/lowcode-react-renderer": "^1.0.14", "@ali/lowcode-types": "^1.0.13", - "@ali/lowcode-utils": "^1.0.13", + "@ali/lowcode-utils": "^1.0.14", "@ali/vu-css-style": "^1.0.2", "@recore/obx": "^1.0.8", "@recore/obx-react": "^1.0.7", diff --git a/packages/react-simulator-renderer/src/renderer-view.tsx b/packages/react-simulator-renderer/src/renderer-view.tsx index 8d7599ef7..c635cb64b 100644 --- a/packages/react-simulator-renderer/src/renderer-view.tsx +++ b/packages/react-simulator-renderer/src/renderer-view.tsx @@ -105,6 +105,7 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> { components={renderer.components} appHelper={renderer.context} // context={renderer.context} + requestHandlersMap={host.requestHandlersMap} designMode={designMode} device={device} suspended={renderer.suspended} diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts index fface745a..35d53b161 100644 --- a/packages/react-simulator-renderer/src/renderer.ts +++ b/packages/react-simulator-renderer/src/renderer.ts @@ -36,10 +36,8 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer { this.dispose = host.connect(this, () => { // sync layout config - // sync schema this._schema = host.document.export(1); - // todo: split with others, not all should recompute if (this._libraryMap !== host.libraryMap || this._componentsMap !== host.designer.componentsMap) { this._libraryMap = host.libraryMap || {}; @@ -57,6 +55,7 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer { // sync device this._device = host.device; }); + host.componentsConsumer.consume(async (componentsAsset) => { if (componentsAsset) { await this.load(componentsAsset); @@ -142,6 +141,11 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer { return loader.load(asset); } + async loadAsyncLibrary(asyncLibraryMap) { + await loader.loadAsyncLibrary(asyncLibraryMap); + this.buildComponents(); + } + private instancesMap = new Map(); private unmountIntance(id: string, instance: ReactInstance) { diff --git a/packages/runtime/CHANGELOG.md b/packages/runtime/CHANGELOG.md index 204d2fa29..a73cc2a1a 100644 --- a/packages/runtime/CHANGELOG.md +++ b/packages/runtime/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.16](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-runtime@1.0.14...@ali/lowcode-runtime@1.0.16) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-runtime + ## [1.0.15](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-runtime@1.0.14...@ali/lowcode-runtime@1.0.15) (2020-11-02) diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 696fe5a2d..ed5391e1d 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-runtime", - "version": "1.0.15", + "version": "1.0.16", "description": "Runtime for Ali lowCode engine", "files": [ "es", diff --git a/packages/runtime/src/core/provider.ts b/packages/runtime/src/core/provider.ts index 3c24dd7e5..33b2f4df8 100644 --- a/packages/runtime/src/core/provider.ts +++ b/packages/runtime/src/core/provider.ts @@ -250,7 +250,8 @@ export default class Provider { } // 定制构造根组件的逻辑,如切换路由机制 - createApp() { + // eslint-disable-next-line + createApp(params?: any) { throw new Error('Method called "createApp" not implemented.'); } diff --git a/packages/runtime/src/core/runApp.ts b/packages/runtime/src/core/runApp.ts index 0241334fd..604a4b0f6 100644 --- a/packages/runtime/src/core/runApp.ts +++ b/packages/runtime/src/core/runApp.ts @@ -28,13 +28,13 @@ export default function runApp() { if (!provider) { throw new Error('Please register class Provider'); } - provider.onReady(() => { + provider.onReady((params) => { const promise = provider.async(); promise.then((config: IAppConfig) => { if (!config) { return; } - const App = provider.createApp(); + const App = provider.createApp(params); provider.runApp(App, config); }).catch((err: Error) => { console.error(err.message); diff --git a/packages/types/.eslintignore b/packages/types/.eslintignore new file mode 100644 index 000000000..a218a6cce --- /dev/null +++ b/packages/types/.eslintignore @@ -0,0 +1,3 @@ +lib +es +node_modules \ No newline at end of file diff --git a/packages/types/.eslintrc.js b/packages/types/.eslintrc.js new file mode 100644 index 000000000..3c65aade8 --- /dev/null +++ b/packages/types/.eslintrc.js @@ -0,0 +1,9 @@ +module.exports = { + extends: '../../.eslintrc', + rules: { + '@typescript-eslint/no-parameter-properties': 1, + 'no-param-reassign': 0, + '@typescript-eslint/member-ordering': 0, + indent: 0, + }, +}; diff --git a/packages/types/.prettierrc.js b/packages/types/.prettierrc.js new file mode 100644 index 000000000..092b67059 --- /dev/null +++ b/packages/types/.prettierrc.js @@ -0,0 +1,6 @@ +module.exports = { + printWidth: 80, + singleQuote: true, + trailingComma: 'all', + tabSize: 2, +}; diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index dc5e44de5..7d05b9c6f 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-types@1.0.12...@ali/lowcode-types@1.0.13) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-types + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-types@1.0.12...@ali/lowcode-types@1.0.13) (2020-11-02) diff --git a/packages/types/src/data-source-handlers.ts b/packages/types/src/data-source-handlers.ts new file mode 100644 index 000000000..4ee7f6a0b --- /dev/null +++ b/packages/types/src/data-source-handlers.ts @@ -0,0 +1,21 @@ +import { + IDataSourceRuntimeContext, + RuntimeOptionsConfig, +} from './data-source-runtime'; + +export type RequestHandler = ( + options: RuntimeOptionsConfig, + context?: IDataSourceRuntimeContext, +) => Promise; + +export type UrlParamsHandler = ( + context?: IDataSourceRuntimeContext, +) => Promise; + +export type RequestHandlersMap = Record>; + +// 仅在 type=custom 的时候生效的 handler +export type CustomRequestHandler = ( + options: RuntimeOptionsConfig, + context?: IDataSourceRuntimeContext, +) => Promise; diff --git a/packages/types/src/data-source-interpret.ts b/packages/types/src/data-source-interpret.ts new file mode 100644 index 000000000..be28117f7 --- /dev/null +++ b/packages/types/src/data-source-interpret.ts @@ -0,0 +1,41 @@ +import { + CompositeValue, + JSExpression, + JSFunction, + JSONObject, +} from './value-type'; + +/** + * 数据源对象 + * @see https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#XMeF5 + */ +export interface InterpretDataSource { + list: InterpretDataSourceConfig[]; + dataHandler?: JSFunction; +} + +/** + * 数据源对象 + * @see https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#XMeF5 + */ +export interface InterpretDataSourceConfig { + id: string; + isInit?: boolean | JSExpression; + isSync?: boolean | JSExpression; + type?: string; + requestHandler?: JSFunction; + dataHandler?: JSFunction; + errorHandler?: JSFunction; + willFetch?: JSFunction; + shouldFetch?: JSFunction; + options?: { + uri: string | JSExpression; + params?: JSONObject | JSExpression; + method?: string | JSExpression; + isCors?: boolean | JSExpression; + timeout?: number | JSExpression; + headers?: JSONObject | JSExpression; + [option: string]: CompositeValue; + }; + [otherKey: string]: CompositeValue; +} diff --git a/packages/types/src/data-source-runtime.ts b/packages/types/src/data-source-runtime.ts new file mode 100644 index 000000000..878039340 --- /dev/null +++ b/packages/types/src/data-source-runtime.ts @@ -0,0 +1,57 @@ +import { IRuntimeDataSource } from './data-source'; +import { CustomRequestHandler } from './data-source-handlers'; + +// 先定义运行模式的类型 +export interface RuntimeDataSource { + list: RuntimeDataSourceConfig[]; + dataHandler?: (dataSourceMap: DataSourceMap) => void; +} + +export type DataSourceMap = Record; + +export interface RuntimeDataSourceConfig { + id: string; + isInit?: boolean; + isSync?: boolean; + type?: string; + willFetch?: WillFetch; + shouldFetch?: () => boolean; + requestHandler?: CustomRequestHandler; + dataHandler?: DataHandler; + errorHandler?: ErrorHandler; + options?: RuntimeOptions; + [otherKey: string]: unknown; +} + +export type WillFetch = ( + options: RuntimeOptionsConfig, +) => Promise | RuntimeOptionsConfig; + +export type DataHandler = (response: { + data: T; + [index: string]: unknown; +}) => Promise; + +export type ErrorHandler = (err: unknown) => Promise; + +export type RuntimeOptions = () => RuntimeOptionsConfig; + +export interface RuntimeOptionsConfig { + uri: string; + params?: Record; + method?: string; + isCors?: boolean; + timeout?: number; + headers?: Record; + [option: string]: unknown; +} + +// 可以采用 react 的 state,但是需要注意必须提供同步的 setState 功能 +export interface IDataSourceRuntimeContext< + TState extends Record = Record +> { + /** 当前数据源的内容 */ + state: TState; + /** 设置状态(浅合并) */ + setState(state: Partial): void; +} diff --git a/packages/types/src/data-source.ts b/packages/types/src/data-source.ts index 157e65648..f93ca2ded 100644 --- a/packages/types/src/data-source.ts +++ b/packages/types/src/data-source.ts @@ -1,32 +1,65 @@ -import { CompositeValue, JSExpression, JSFunction, JSONObject } from './value-type'; +import { RequestHandlersMap } from './data-source-handlers'; +import { + IDataSourceRuntimeContext, + RuntimeDataSource, +} from './data-source-runtime'; -/** - * 数据源对象 - * @see https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#XMeF5 - */ -export interface DataSourceConfig { - id: string; - isInit?: boolean | JSExpression; - type?: string; - requestHandler?: JSFunction; - dataHandler?: JSFunction; - options?: { - uri: string | JSExpression; - params?: JSONObject | JSExpression; - method?: string | JSExpression; - isCors?: boolean | JSExpression; - timeout?: number | JSExpression; - headers?: JSONObject | JSExpression; - [option: string]: CompositeValue; - }; - [otherKey: string]: CompositeValue; +/** 数据源的状态 */ +export enum RuntimeDataSourceStatus { + /** 初始状态,尚未加载 */ + Initial = 'init', + + /** 正在加载 */ + Loading = 'loading', + + /** 已加载(无错误) */ + Loaded = 'loaded', + + /** 加载出错了 */ + Error = 'error', } /** - * 数据源对象 - * @see https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#XMeF5 + * 运行时的数据源(对外暴露的接口) + * @see https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#Jwgj5 */ -export interface DataSource { - list: DataSourceConfig[]; - dataHandler?: JSFunction; +export interface IRuntimeDataSource { + /** 当前状态(initial/loading/loaded/error) */ + readonly status: RuntimeDataSourceStatus; + + /** 加载成功时的数据 */ + readonly data?: TResultData; + + /** 加载出错的时候的错误信息 */ + readonly error?: Error; + + /** + * 加载数据 (无论是否曾经加载过) + * 注意:若提供 params,则会和默认配置的参数做浅合并;否则会使用默认配置的参数。 + */ + load(params?: TParams): Promise; +} + +/** + * DataSourceEngineFactory + * 用来定义 engine 的工厂函数类型 + */ +export interface IRuntimeDataSourceEngineFactory { + create( + dataSource: RuntimeDataSource, + context: IDataSourceRuntimeContext, + extraConfig?: { + requestHandlersMap: RequestHandlersMap; + [key: string]: any; + }, + ): IDataSourceEngine; +} + +// create 返回的 DataSourceEngine 定义 +export interface IDataSourceEngine { + /** 数据源, key 是数据源的 ID */ + dataSourceMap: Record; + + /** 重新加载所有的数据源 */ + reloadDataSource(): Promise; } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 56ff2826b..6649f4336 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,4 +1,7 @@ export * from './data-source'; +export * from './data-source-handlers'; +export * from './data-source-interpret'; +export * from './data-source-runtime'; export * from './editor'; export * from './field-config'; export * from './i18n'; diff --git a/packages/types/src/value-type.ts b/packages/types/src/value-type.ts index 7aa964620..47904a6b7 100644 --- a/packages/types/src/value-type.ts +++ b/packages/types/src/value-type.ts @@ -11,6 +11,8 @@ export interface JSExpression { * 模拟值 */ mock?: any; + /** 源码 */ + compiled?: string; } // 函数 @@ -39,7 +41,7 @@ export interface JSFunction { } // 函数 -export interface JSFunction{ +export interface JSFunction { type: 'JSFunction'; /** * 函数字符串 @@ -69,14 +71,27 @@ export interface JSBlock { } // JSON 基本类型 -export type JSONValue = boolean | string | number | null | undefined | JSONArray | JSONObject; +export type JSONValue = + | boolean + | string + | number + | null + | undefined + | JSONArray + | JSONObject; export type JSONArray = JSONValue[]; export interface JSONObject { [key: string]: JSONValue; } // 复合类型 -export type CompositeValue = JSONValue | JSExpression | JSFunction | JSSlot | CompositeArray | CompositeObject; +export type CompositeValue = + | JSONValue + | JSExpression + | JSFunction + | JSSlot + | CompositeArray + | CompositeObject; export type CompositeArray = CompositeValue[]; export interface CompositeObject { [key: string]: CompositeValue; diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index 9aacf8bb6..ab54ad6a1 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.14](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-utils@1.0.12...@ali/lowcode-utils@1.0.14) (2020-11-04) + + + + +**Note:** Version bump only for package @ali/lowcode-utils + ## [1.0.13](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/@ali/lowcode-utils@1.0.12...@ali/lowcode-utils@1.0.13) (2020-11-02) diff --git a/packages/utils/package.json b/packages/utils/package.json index a04c9b4ad..9226e1277 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-utils", - "version": "1.0.13", + "version": "1.0.14", "description": "Utils for Ali lowCode engine", "files": [ "es", diff --git a/packages/utils/src/asset.ts b/packages/utils/src/asset.ts index 6534372cd..85328952b 100644 --- a/packages/utils/src/asset.ts +++ b/packages/utils/src/asset.ts @@ -271,4 +271,23 @@ export class AssetLoader { } return isUrl ? load(content) : evaluate(content); } + + private async loadAsyncLibrary(asyncLibraryMap) { + const promiseList = []; const libraryKeyList = []; + for (const key in asyncLibraryMap) { + // 需要异步加载 + if (asyncLibraryMap[key].async) { + promiseList.push(window[asyncLibraryMap[key].library]); + libraryKeyList.push(asyncLibraryMap[key].library); + } + } + await Promise.all(promiseList).then((mods) => { + if (mods.length > 0) { + mods.map((item, index) => { + window[libraryKeyList[index]] = item; + return item; + }); + } + }); + } }