chore: main datasource api (#3880)

* chore: main datasource api

* chore: test

* chore: console.log

* chore: middleware order

* chore: test

* chore: middleware order

* chore: test

* chore: middleware options
This commit is contained in:
ChengLei Shao 2024-03-31 16:22:45 +08:00 committed by GitHub
parent a93d2ddab2
commit 79f14d9024
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 64 additions and 57 deletions

View File

@ -335,7 +335,7 @@ export class ACL extends EventEmitter {
can: ctx.can({ resource: resourceName, action: actionName }),
};
return compose(acl.middlewares.nodes)(ctx, next);
return await compose(acl.middlewares.nodes)(ctx, next);
};
}

View File

@ -24,17 +24,16 @@ export class DataSourceManager {
middleware() {
return async (ctx, next) => {
const name = ctx.get('x-data-source');
if (name) {
if (this.dataSources.has(name)) {
const ds = this.dataSources.get(name);
ctx.dataSource = ds;
return ds.middleware(this.middlewares)(ctx, next);
} else {
ctx.throw(`data source ${name} does not exist`);
}
const name = ctx.get('x-data-source') || 'main';
if (!this.dataSources.has(name)) {
ctx.throw(`data source ${name} does not exist`);
}
await next();
const ds = this.dataSources.get(name);
ctx.dataSource = ds;
return ds.middleware(this.middlewares)(ctx, next);
};
}
}

View File

@ -62,13 +62,19 @@ export abstract class DataSource extends EventEmitter {
if (this.resourceManager.isDefined(resourceName)) {
return next();
}
// 如果经过加载后是已经定义的表
if (!this.collectionManager.hasCollection(resourceName)) {
const splitResult = resourceName.split('.');
const collectionName = splitResult[0];
if (!this.collectionManager.hasCollection(collectionName)) {
return next();
}
this.resourceManager.define({
name: resourceName,
});
return next();
};
}
@ -86,7 +92,7 @@ export abstract class DataSource extends EventEmitter {
return this.collectionManager.getRepository(resourceName, resourceOf);
};
return compose([this.collectionToResourceMiddleware(), this.resourceManager.restApiMiddleware()])(ctx, next);
return compose([this.collectionToResourceMiddleware(), this.resourceManager.middleware()])(ctx, next);
};
}

View File

@ -1073,13 +1073,6 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
this._dataSourceManager.use(this._authManager.middleware(), { tag: 'auth' });
this._dataSourceManager.use(validateFilterParams, { tag: 'validate-filter-params', before: ['auth'] });
this.resourcer.use(this._authManager.middleware(), { tag: 'auth' });
this.resourcer.use(validateFilterParams, { tag: 'validate-filter-params', before: ['auth'] });
if (this.options.acl !== false) {
this.resourcer.use(this.acl.middleware(), { tag: 'acl', after: ['auth'] });
}
this._locales = new Locale(createAppProxy(this));
if (options.perfHooks) {
@ -1103,10 +1096,12 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
database: this.createDatabase(options),
acl: createACL(),
resourceManager: createResourcer(options),
useACL: options.acl,
});
this._dataSourceManager = new DataSourceManager();
// can not use await here
this.dataSourceManager.dataSources.set('main', mainDataSourceInstance);
}

View File

@ -9,7 +9,6 @@ export default (app: Application) => {
.option('--quickstart')
.action(async (...cliArgs) => {
const [options] = cliArgs;
console.log('start options', options);
if (options.quickstart) {
if (await app.isInstalled()) {
await app.upgrade();

View File

@ -81,16 +81,7 @@ export function registerMiddlewares(app: Application, options: ApplicationOption
app.use(dataWrapping(), { tag: 'dataWrapping', after: 'i18n' });
}
app.resourcer.use(parseVariables, {
tag: 'parseVariables',
after: 'acl',
});
app.resourcer.use(dateTemplate, { tag: 'dateTemplate', after: 'acl' });
app.use(db2resource, { tag: 'db2resource', after: 'dataWrapping' });
app.use(app.resourcer.restApiMiddleware({ skipIfDataSourceExists: true }), { tag: 'restApi', after: 'db2resource' });
app.use(app.dataSourceManager.middleware(), { tag: 'dataSource', after: 'restApi' });
app.use(app.dataSourceManager.middleware(), { tag: 'dataSource', after: 'dataWrapping' });
}
export const createAppProxy = (app: Application) => {

View File

@ -1,4 +1,6 @@
import { DataSourceOptions, SequelizeDataSource } from '@nocobase/data-source-manager';
import { db2resource, parseVariables } from './middlewares';
import { dateTemplate } from './middlewares/data-template';
export class MainDataSource extends SequelizeDataSource {
init(options: DataSourceOptions = {}) {
@ -15,5 +17,16 @@ export class MainDataSource extends SequelizeDataSource {
},
},
});
if (options.useACL !== false) {
this.resourceManager.use(this.acl.middleware(), { group: 'acl', after: 'auth' });
}
this.resourceManager.use(parseVariables, {
group: 'parseVariables',
after: 'acl',
});
this.resourceManager.use(dateTemplate, { group: 'dateTemplate', after: 'acl' });
}
}

View File

@ -9,10 +9,6 @@ export function dataWrapping() {
return;
}
// if (!ctx?.action?.params) {
// return;
// }
if (ctx.body instanceof stream.Readable) {
return;
}
@ -59,6 +55,8 @@ export function dataWrapping() {
if (ctx.body && ctx.state.messages?.length) {
ctx.body.messages = ctx.state.messages;
}
ctx.dataWrapped = true;
};
}

View File

@ -2,10 +2,6 @@ import Database from '@nocobase/database';
import { getNameByParams, parseRequest, ResourcerContext, ResourceType } from '@nocobase/resourcer';
export async function db2resource(ctx: ResourcerContext & { db: Database }, next: () => Promise<any>) {
const dataSource = ctx.get('x-data-source');
if (dataSource) {
return next();
}
const resourcer = ctx.resourcer;
const database = ctx.db;
@ -33,13 +29,9 @@ export async function db2resource(ctx: ResourcerContext & { db: Database }, next
const splitResult = resourceName.split('.');
let collectionName = splitResult[0];
const collectionName = splitResult[0];
const fieldName = splitResult[1];
if (collectionName.includes('@')) {
collectionName = collectionName.split('@')[1];
}
// 如果经过加载后是已经定义的表
if (!database.hasCollection(collectionName)) {
return next();

View File

@ -188,6 +188,7 @@ describe('destroy action with acl', () => {
},
{
before: 'acl',
after: 'auth',
},
);
@ -224,6 +225,7 @@ describe('destroy action with acl', () => {
},
{
before: 'acl',
after: 'auth',
},
);
@ -282,6 +284,7 @@ describe('destroy action with acl', () => {
},
{
before: 'acl',
after: 'auth',
},
);

View File

@ -88,6 +88,7 @@ describe('list action with acl', () => {
},
{
before: 'acl',
after: 'auth',
},
);
@ -132,6 +133,7 @@ describe('list action with acl', () => {
},
{
before: 'acl',
after: 'auth',
},
);
@ -172,6 +174,7 @@ describe('list action with acl', () => {
},
{
before: 'acl',
after: 'auth',
},
);
@ -214,6 +217,7 @@ describe('list action with acl', () => {
},
{
before: 'acl',
after: 'auth',
},
);

View File

@ -692,7 +692,7 @@ export class PluginACL extends Plugin {
ctx.logger.error(error);
}
},
{ after: 'restApi', group: 'after' },
{ after: 'dataSource', group: 'with-acl-meta' },
);
}

View File

@ -21,17 +21,24 @@ export class PluginAPIKeysServer extends Plugin {
}
async load() {
this.app.resourcer.use(async (ctx, next) => {
const { resourceName, actionName } = ctx.action;
if (resourceName === this.resourceName && ['list', 'destroy'].includes(actionName)) {
ctx.action.mergeParams({
filter: {
createdById: ctx.auth.user.id,
},
});
}
await next();
});
this.app.resourcer.use(
async (ctx, next) => {
const { resourceName, actionName } = ctx.action;
if (resourceName === this.resourceName && ['list', 'destroy'].includes(actionName)) {
ctx.action.mergeParams({
filter: {
createdById: ctx.auth.user.id,
},
});
}
await next();
},
{
group: 'apiKeys',
before: 'acl',
after: 'auth',
},
);
}
}