mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
215 lines
5.9 KiB
TypeScript
215 lines
5.9 KiB
TypeScript
/*
|
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
*
|
|
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import EventEmitter from 'events';
|
|
|
|
import { cloneDeep } from 'lodash-es';
|
|
|
|
import type { AppCore, DataSourceSchema, Id, MNode } from '@tmagic/schema';
|
|
import { compiledNode } from '@tmagic/utils';
|
|
|
|
import { DataSource, HttpDataSource } from './data-sources';
|
|
import type { ChangeEvent, DataSourceManagerData, DataSourceManagerOptions } from './types';
|
|
import { compiledNodeField, compliedConditions, compliedIteratorItems } from './utils';
|
|
|
|
class DataSourceManager extends EventEmitter {
|
|
private static dataSourceClassMap = new Map<string, typeof DataSource>();
|
|
|
|
public static register<T extends typeof DataSource = typeof DataSource>(type: string, dataSource: T) {
|
|
DataSourceManager.dataSourceClassMap.set(type, dataSource);
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
*/
|
|
public static registe<T extends typeof DataSource = typeof DataSource>(type: string, dataSource: T) {
|
|
DataSourceManager.register(type, dataSource);
|
|
}
|
|
|
|
public static getDataSourceClass(type: string) {
|
|
return DataSourceManager.dataSourceClassMap.get(type);
|
|
}
|
|
|
|
public app: AppCore;
|
|
|
|
public dataSourceMap = new Map<string, DataSource>();
|
|
|
|
public data: DataSourceManagerData = {};
|
|
public useMock?: boolean = false;
|
|
|
|
constructor({ app, useMock, initialData }: DataSourceManagerOptions) {
|
|
super();
|
|
|
|
this.app = app;
|
|
this.useMock = useMock;
|
|
|
|
if (initialData) {
|
|
this.data = initialData;
|
|
}
|
|
|
|
app.dsl?.dataSources?.forEach((config) => {
|
|
this.addDataSource(config);
|
|
});
|
|
|
|
const dataSourceList = Array.from(this.dataSourceMap);
|
|
Promise.allSettled<Record<string, any>>(dataSourceList.map(([, ds]) => this.init(ds))).then((values) => {
|
|
const data: DataSourceManagerData = {};
|
|
|
|
values.forEach((value, index) => {
|
|
if (value.status === 'fulfilled') {
|
|
const dsId = dataSourceList[index][0];
|
|
data[dsId] = this.data[dsId];
|
|
}
|
|
});
|
|
|
|
this.emit('init', data);
|
|
});
|
|
}
|
|
|
|
public async init(ds: DataSource) {
|
|
if (ds.isInit) {
|
|
return;
|
|
}
|
|
|
|
if (this.app.jsEngine && ds.schema.disabledInitInJsEngine?.includes(this.app.jsEngine)) {
|
|
return;
|
|
}
|
|
|
|
const beforeInit: ((...args: any[]) => any)[] = [];
|
|
const afterInit: ((...args: any[]) => any)[] = [];
|
|
|
|
ds.methods.forEach((method) => {
|
|
if (typeof method.content !== 'function') return;
|
|
if (method.timing === 'beforeInit') {
|
|
beforeInit.push(method.content);
|
|
}
|
|
if (method.timing === 'afterInit') {
|
|
afterInit.push(method.content);
|
|
}
|
|
});
|
|
|
|
for (const method of beforeInit) {
|
|
await method({ params: {}, dataSource: ds, app: this.app });
|
|
}
|
|
|
|
await ds.init();
|
|
|
|
for (const method of afterInit) {
|
|
await method({ params: {}, dataSource: ds, app: this.app });
|
|
}
|
|
}
|
|
|
|
public get(id: string) {
|
|
return this.dataSourceMap.get(id);
|
|
}
|
|
|
|
public async addDataSource(config?: DataSourceSchema) {
|
|
if (!config) return;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
const DataSourceClass = DataSourceManager.dataSourceClassMap.get(config.type) || DataSource;
|
|
|
|
const ds = new DataSourceClass({
|
|
app: this.app,
|
|
schema: config,
|
|
request: this.app.request,
|
|
useMock: this.useMock,
|
|
initialData: this.data[config.id],
|
|
});
|
|
|
|
this.dataSourceMap.set(config.id, ds);
|
|
|
|
this.data[ds.id] = ds.data;
|
|
|
|
ds.on('change', (changeEvent: ChangeEvent) => {
|
|
this.setData(ds, changeEvent);
|
|
});
|
|
}
|
|
|
|
public setData(ds: DataSource, changeEvent: ChangeEvent) {
|
|
this.data[ds.id] = ds.data;
|
|
this.emit('change', ds.id, changeEvent);
|
|
}
|
|
|
|
public removeDataSource(id: string) {
|
|
this.get(id)?.destroy();
|
|
delete this.data[id];
|
|
this.dataSourceMap.delete(id);
|
|
}
|
|
|
|
public updateSchema(schemas: DataSourceSchema[]) {
|
|
schemas.forEach((schema) => {
|
|
const ds = this.get(schema.id);
|
|
if (!ds) {
|
|
return;
|
|
}
|
|
|
|
this.removeDataSource(schema.id);
|
|
|
|
this.addDataSource(schema);
|
|
const newDs = this.get(schema.id);
|
|
if (newDs) {
|
|
this.init(newDs);
|
|
}
|
|
});
|
|
}
|
|
|
|
public compiledNode({ items, ...node }: MNode, sourceId?: Id, deep = false) {
|
|
const newNode = cloneDeep(node);
|
|
|
|
if (items) {
|
|
newNode.items =
|
|
Array.isArray(items) && deep ? items.map((item) => this.compiledNode(item, sourceId, deep)) : items;
|
|
}
|
|
|
|
if (node.condResult === false) return newNode;
|
|
if (node.visible === false) return newNode;
|
|
|
|
return compiledNode(
|
|
(value: any) => compiledNodeField(value, this.data),
|
|
newNode,
|
|
this.app.dsl?.dataSourceDeps || {},
|
|
sourceId,
|
|
);
|
|
}
|
|
|
|
public compliedConds(node: MNode) {
|
|
return compliedConditions(node, this.data);
|
|
}
|
|
|
|
public compliedIteratorItems(itemData: any, items: MNode[], dataSourceField: string[] = []) {
|
|
const [dsId, ...keys] = dataSourceField;
|
|
const ds = this.get(dsId);
|
|
if (!ds) return items;
|
|
return compliedIteratorItems(itemData, items, dsId, keys);
|
|
}
|
|
|
|
public destroy() {
|
|
this.removeAllListeners();
|
|
this.data = {};
|
|
this.dataSourceMap.forEach((ds) => {
|
|
ds.destroy();
|
|
});
|
|
this.dataSourceMap.clear();
|
|
}
|
|
}
|
|
|
|
DataSourceManager.register('http', HttpDataSource as any);
|
|
|
|
export default DataSourceManager;
|