bootstrap 接口慢问题 #2714

This commit is contained in:
baozhoutao 2022-01-21 14:13:14 +08:00
parent 01b8c2b08b
commit 47eaaf2c1e
11 changed files with 256 additions and 96 deletions

View File

@ -1,7 +1,7 @@
import steedosI18n = require("@steedos/i18n");
import { getPlugins } from '../';
import { requireAuthentication } from './auth'
import { getObject, getObjectLayouts, getLayout, getAppConfigs, getAssignedMenus, getAssignedApps } from '@steedos/objectql'
import { getObject, getObjectLayouts, getLayout, getAppConfigs, getAssignedMenus, getAssignedApps, FieldPermission } from '@steedos/objectql'
require("@steedos/license");
const Fiber = require('fibers')
const clone = require("clone");
@ -166,10 +166,8 @@ export async function getSpaceBootStrap(req, res) {
}
let space = Creator.Collections["spaces"].findOne({ _id: spaceId }, { fields: { name: 1 } })
//TODO 无需再从getAllPermissions中获取用户的对象权限
let result = Creator.getAllPermissions(spaceId, userId);
let lng = _getLocale(db.users.findOne(userId, { fields: { locale: 1 } }))
steedosI18n.translationObjects(lng, result.objects);
result.user = userSession
@ -183,9 +181,7 @@ export async function getSpaceBootStrap(req, res) {
// result.apps = clone(Creator.Apps)
result.apps = await getAppConfigs(spaceId);
result.assigned_apps = await getAssignedApps(userSession);
let datasources = Creator.steedosSchema.getDataSources();
// for (const datasourceName in datasources) {
// if(datasourceName != 'default'){
// let datasource = datasources[datasourceName];
@ -200,6 +196,15 @@ export async function getSpaceBootStrap(req, res) {
// }
// }
// }
const spaceProcessDefinition = await getObject("process_definition").find({ filters: [['space', '=', spaceId], ['active', '=', true]] })
const spaceObjectsProcessDefinition = _.groupBy(spaceProcessDefinition, 'object_name');
const dbListViews = await getObject("object_listviews").directFind({ filters: [['space', '=', userSession.spaceId], [['owner', '=', userSession.userId], 'or', ['shared', '=', true]]] })
const dbObjectsListViews = _.groupBy(dbListViews, 'object_name');
const layouts = await getObjectLayouts(userSession.profile, spaceId)
const objectsLayouts = _.groupBy(layouts, 'object_name');
const objectsFieldsPermissionGroupRole = await FieldPermission.getObjectsFieldsPermissionGroupRole();
for (const datasourceName in datasources) {
let datasource = datasources[datasourceName];
const datasourceObjects = await datasource.getObjects();
@ -207,7 +212,13 @@ export async function getSpaceBootStrap(req, res) {
const objectConfig = object.metadata;
if (!result.objects[objectConfig.name] || objectConfig.name.endsWith("__c")) {
try {
const userObjectConfig = await getObject(objectConfig.name).getRecordView(userSession);
const userObjectConfig = await getObject(objectConfig.name).getRecordView(userSession, {
objectConfig: objectConfig,
layouts: objectsLayouts[objectConfig.name] || [],
spaceProcessDefinition: spaceObjectsProcessDefinition[objectConfig.name] || [],
dbListViews: dbObjectsListViews[objectConfig.name] || [],
rolesFieldsPermission: objectsFieldsPermissionGroupRole[objectConfig.name] || []
});
let _objectConfig = null;
if (userObjectConfig) {
_objectConfig = userObjectConfig
@ -217,8 +228,12 @@ export async function getSpaceBootStrap(req, res) {
const _obj = Creator.convertObject(_objectConfig, spaceId)
_obj.name = objectConfig.name
_obj.database_name = datasourceName
_obj.permissions = await getObject(objectConfig.name).getUserObjectPermission(userSession)
steedosI18n.translationObject(lng, _obj.name, _obj);
if (userObjectConfig) {
_obj.permission = userObjectConfig.permissions
} else {
_obj.permissions = await getObject(objectConfig.name).getUserObjectPermission(userSession)
steedosI18n.translationObject(lng, _obj.name, _obj);
}
result.objects[_obj.name] = _obj
} catch (error) {
console.error(error.message)
@ -279,25 +294,23 @@ export async function getSpaceBootStrap(req, res) {
result.dashboards = _Dashboards
result.plugins = getPlugins ? getPlugins() : null
await getUserObjects(userId, spaceId, result.objects);
// TODO object layout 是否需要控制审批记录显示?
let spaceProcessDefinition = await getObject("process_definition").find({filters: [['space', '=', spaceId], ['active', '=', true]]})
_.each(spaceProcessDefinition, function(item){
if(result.objects[item.object_name]){
result.objects[item.object_name].enable_process = true
}
})
for (const key in result.objects) {
if (Object.prototype.hasOwnProperty.call(result.objects, key)) {
const objectConfig = result.objects[key];
try {
const object = getObject(key);
objectConfig.details = await object.getDetailsInfo();
objectConfig.masters = await object.getMastersInfo();
objectConfig.lookup_details = await object.getLookupDetailsInfo();
const relationsInfo = await object.getRelationsInfo();
objectConfig.details = relationsInfo.details;
objectConfig.masters = relationsInfo.masters;
objectConfig.lookup_details = relationsInfo.lookup_details;
_.each(objectConfig.triggers, function(trigger, key){
if(trigger?.on != 'client'){
delete objectConfig.triggers[key];
@ -311,8 +324,6 @@ export async function getSpaceBootStrap(req, res) {
}
}
}
return res.status(200).send(result);
}).run();
}
@ -332,9 +343,10 @@ async function getObjectConfig(objectName, spaceId, userSession) {
objectConfig.name = objectName
objectConfig.datasource = object.datasource.name;
objectConfig.permissions = await object.getUserObjectPermission(userSession);
objectConfig.details = await object.getDetailsInfo();
objectConfig.masters = await object.getMastersInfo();
objectConfig.lookup_details = await object.getLookupDetailsInfo();
const relationsInfo = await object.getRelationsInfo();
objectConfig.details = relationsInfo.details;
objectConfig.masters = relationsInfo.masters;
objectConfig.lookup_details = relationsInfo.lookup_details;
return objectConfig;
} catch (error) {
console.warn('not load object', objectName)
@ -381,64 +393,77 @@ export async function getSpaceObjectBootStrap(req, res) {
let userSession = req.user;
let userId = userSession.userId;
let urlParams = req.params;
const objectName = urlParams.objectName
let spaceId = req.headers['x-space-id'] || urlParams.spaceId
if (!userSession) {
return res.status(500).send();
}
let _object = Creator.getCollection('objects').findOne({ name: objectName }) || {}
let lng = _getLocale(db.users.findOne(userId, { fields: { locale: 1 } }))
let objectConfig: any = {}
if (_.isEmpty(_object)) {
objectConfig = Creator.getAllPermissions(spaceId, userId).objects[objectName];
if (_.isEmpty(objectConfig)) {
objectConfig = await getObjectConfig(objectName, spaceId, userSession);
}else{
const object = getObject(objectName);
objectConfig.details = await object.getDetailsInfo();
objectConfig.masters = await object.getMastersInfo();
objectConfig.lookup_details = await object.getLookupDetailsInfo();
}
} else {
if (userSession.is_space_admin || _object.in_development == '0' && _object.is_enable) {
if (_object.datasource == 'meteor') {
objectConfig = Creator.getAllPermissions(spaceId, userId).objects[objectName]
const object = getObject(objectName);
objectConfig.details = await object.getDetailsInfo();
objectConfig.masters = await object.getMastersInfo();
objectConfig.lookup_details = await object.getLookupDetailsInfo();
const objectNames = urlParams.objectNames
const objects = {};
if (objectNames) {
const objectNamesArray = objectNames.split(',');
let dbObjects = Creator.getCollection('objects').find({ name: { $in: objectNamesArray } }).fetch() || []
let creatorObjects = Creator.getAllPermissions(spaceId, userId).objects;
let lng = _getLocale(db.users.findOne(userId, { fields: { locale: 1 } }))
for (const objectName of objectNamesArray) {
let _object = _.find(dbObjects, function (item) { return item.name === objectName }) || {}
let objectConfig: any = {}
if (_.isEmpty(_object)) {
objectConfig = creatorObjects[objectName];
if (_.isEmpty(objectConfig)) {
objectConfig = await getObjectConfig(objectName, spaceId, userSession);
} else {
const object = getObject(objectName);
const relationsInfo = await object.getRelationsInfo();
objectConfig.details = relationsInfo.details;
objectConfig.masters = relationsInfo.masters;
objectConfig.lookup_details = relationsInfo.lookup_details;
}
} else {
objectConfig = await getObjectConfig(objectName, spaceId, userSession);
if (userSession.is_space_admin || _object.in_development == '0' && _object.is_enable) {
if (_object.datasource == 'meteor') {
objectConfig = creatorObjects[objectName]
const object = getObject(objectName);
const relationsInfo = await object.getRelationsInfo();
objectConfig.details = relationsInfo.details;
objectConfig.masters = relationsInfo.masters;
objectConfig.lookup_details = relationsInfo.lookup_details;
} else {
objectConfig = await getObjectConfig(objectName, spaceId, userSession);
}
}
}
if (objectConfig) {
delete objectConfig.db
// objectConfig.list_views = await getUserObjectListViews(userId, spaceId, objectName)
steedosI18n.translationObject(lng, objectConfig.name, objectConfig)
objectConfig = await getUserObject(userId, spaceId, objectConfig)
// TODO object layout 是否需要控制审批记录显示?
let spaceProcessDefinition = await getObject("process_definition").directFind({ filters: [['space', '=', spaceId], ['object_name', '=', objectName], ['active', '=', true]] })
if (spaceProcessDefinition.length > 0) {
objectConfig.enable_process = true
}
_.each(objectConfig.triggers, function (trigger, key) {
if (trigger?.on != 'client') {
delete objectConfig.triggers[key];
}
})
delete objectConfig.listeners
delete objectConfig.__filename
delete objectConfig.extend
objects[objectConfig.name] = objectConfig;
}
}
}
if (objectConfig) {
delete objectConfig.db
// objectConfig.list_views = await getUserObjectListViews(userId, spaceId, objectName)
steedosI18n.translationObject(lng, objectConfig.name, objectConfig)
objectConfig = await getUserObject(userId, spaceId, objectConfig)
// TODO object layout 是否需要控制审批记录显示?
let spaceProcessDefinition = await getObject("process_definition").directFind({ filters: [['space', '=', spaceId], ['object_name', '=', objectName], ['active', '=', true]] })
if (spaceProcessDefinition.length > 0) {
objectConfig.enable_process = true
}
_.each(objectConfig.triggers, function(trigger, key){
if(trigger?.on != 'client'){
delete objectConfig.triggers[key];
}
})
delete objectConfig.listeners
delete objectConfig.__filename
delete objectConfig.extend
}
return res.status(200).send(objectConfig || {});
return res.status(200).send({
objects: objects
});
}).run();
}
bootStrapExpress.use('/api/bootstrap/:spaceId/:objectName', requireAuthentication, getSpaceObjectBootStrap)
bootStrapExpress.use('/api/bootstrap/:spaceId/:objectNames', requireAuthentication, getSpaceObjectBootStrap)
bootStrapExpress.use('/api/bootstrap/:spaceId/', requireAuthentication, getSpaceBootStrap)

View File

@ -148,8 +148,8 @@ function getObjectServiceMethodsSchema() {
}
},
getRecordView: {
async handler(userSession) {
return await this.object.getRecordView(userSession);
async handler(userSession, context?) {
return await this.object.getRecordView(userSession, context);
}
},
createDefaulRecordView: {
@ -472,7 +472,8 @@ function getObjectServiceActionsSchema() {
},
async handler(ctx) {
const userSession = ctx.meta.user;
return await this.getRecordView(userSession);
const { context } = ctx.params;
return await this.getRecordView(userSession, context);
}
},
createDefaulRecordView: {

View File

@ -159,6 +159,10 @@ class ObjectServiceDispatcher {
return await this.callMetadataObjectServiceAction(`getLookupDetailsInfo`, {objectApiName: this.objectApiName});
}
async getRelationsInfo() {
return await this.callMetadataObjectServiceAction(`getRelationsInfo`, { objectApiName: this.objectApiName });
}
async getDetailPaths(){
return await this.callMetadataObjectServiceAction(`getDetailPaths`, {objectApiName: this.objectApiName});
}
@ -179,8 +183,8 @@ class ObjectServiceDispatcher {
return await this.callAction(`getRecordPermissions`, {record, userSession});
}
async getRecordView(userSession){
return await this.callAction(`getRecordView`, {userSession});
async getRecordView(userSession, context?) {
return await this.callAction(`getRecordView`, { userSession, context });
}
async createDefaulRecordView(userSession){

View File

@ -23,4 +23,20 @@ export class FieldPermission {
})
return result;
}
static async getObjectsFieldsPermissionGroupRole() {
const permissions = await this.getObjectFieldsPermission('*', '*');
const result = {};
_.each(permissions, (permission) => {
if (!result[permission.object_name]) {
result[permission.object_name] = {};
}
const { permission_set_id, field, editable, readable } = permission.metadata;
if (!result[permission.object_name][permission_set_id]) {
result[permission.object_name][permission_set_id] = [];
}
result[permission.object_name][permission_set_id].push({ field: field, read: readable, edit: editable })
})
return result;
}
}

View File

@ -7,6 +7,7 @@ export * from "./dashboard";
export * from "./object";
export * from "./connection";
export * from "./object_layouts";
export * from "./field_permission";
export { SteedosFieldType, SteedosFieldTypeConfig } from "./field";
export { SteedosListenerConfig } from './listeners'
export { SteedosTriggerType } from './trigger'

View File

@ -609,7 +609,7 @@ export class SteedosObjectType extends SteedosObjectProperties {
return globalPermission
}
async getUserObjectPermission(userSession: SteedosUserSession) {
async getUserObjectPermission(userSession: SteedosUserSession, rolesFieldsPermission?: any) {
if (!userSession) {
throw new Error('userSession is required')
@ -649,7 +649,9 @@ export class SteedosObjectType extends SteedosObjectProperties {
throw new Error('not find user permission');
}
const rolesFieldsPermission = await FieldPermission.getObjectFieldsPermissionGroupRole(this.name);
if (!rolesFieldsPermission) {
rolesFieldsPermission = await FieldPermission.getObjectFieldsPermissionGroupRole(this.name);
}
roles.forEach((role) => {
let rolePermission = objectRolesPermission[role];
@ -926,6 +928,13 @@ export class SteedosObjectType extends SteedosObjectProperties {
return await this.callMetadataObjectServiceAction(`getLookupDetailsInfo`, {objectApiName: this.name});
}
/**
* getDetailsInfogetMastersInfogetLookupDetailsInfo 3访
*/
async getRelationsInfo() {
return await this.callMetadataObjectServiceAction(`getRelationsInfo`, { objectApiName: this.name });
}
async getRecordPermissions(record, userSession){
const permissions = await this.getUserObjectPermission(userSession);
const { userId, company_ids: user_company_ids } = userSession;
@ -985,22 +994,39 @@ export class SteedosObjectType extends SteedosObjectProperties {
return permissions
}
async getRecordView(userSession){
async getRecordView(userSession, context?: any) {
let objectConfig;
let layouts;
let spaceProcessDefinition;
let dbListViews;
let rolesFieldsPermission;
if (context) {
objectConfig = context.objectConfig;
layouts = context.layouts;
spaceProcessDefinition = context.spaceProcessDefinition;
dbListViews = context.dbListViews;
rolesFieldsPermission = context.rolesFieldsPermission;
}
const lng = userSession.language;
const objectMetadataConfig: any = await this.callMetadataObjectServiceAction('get', {objectApiName: this.name});
let objectConfig = objectMetadataConfig.metadata;
if (!objectConfig) {
const objectMetadataConfig: any = await this.callMetadataObjectServiceAction('get', { objectApiName: this.name });
objectConfig = objectMetadataConfig.metadata;
}
objectConfig.name = this.name
objectConfig.datasource = this.datasource.name;
objectConfig.permissions = await this.getUserObjectPermission(userSession);
objectConfig.details = await this.getDetailsInfo();
objectConfig.masters = await this.getMastersInfo();
objectConfig.lookup_details = await this.getLookupDetailsInfo();
objectConfig.permissions = await this.getUserObjectPermission(userSession, rolesFieldsPermission);
const relationsInfo = await this.getRelationsInfo();
objectConfig.details = relationsInfo.details;
objectConfig.masters = relationsInfo.masters;
objectConfig.lookup_details = relationsInfo.lookup_details;
delete objectConfig.db
translationObject(lng, objectConfig.name, objectConfig, true);
const layouts = await getObjectLayouts(userSession.profile, userSession.spaceId, this.name);
if (!layouts) {
layouts = await getObjectLayouts(userSession.profile, userSession.spaceId, this.name);
}
if(layouts && layouts.length > 0){
const layout = layouts[0];
let _fields = {};
@ -1101,7 +1127,9 @@ export class SteedosObjectType extends SteedosObjectProperties {
objectConfig.fields = userObjectFields
// TODO object layout 是否需要控制审批记录显示?
let spaceProcessDefinition = await getObject("process_definition").directFind({ filters: [['space', '=', userSession.spaceId], ['object_name', '=', this.name], ['active', '=', true]] })
if (!spaceProcessDefinition) {
spaceProcessDefinition = await getObject("process_definition").directFind({ filters: [['space', '=', userSession.spaceId], ['object_name', '=', this.name], ['active', '=', true]] })
}
if (spaceProcessDefinition.length > 0) {
objectConfig.enable_process = true
}
@ -1113,8 +1141,9 @@ export class SteedosObjectType extends SteedosObjectProperties {
delete objectConfig.triggers[key];
}
})
const dbListViews = await getObject("object_listviews").find({ filters: [['space', '=', userSession.spaceId], ['object_name', '=', this.name], [['owner', '=', userSession.userId], 'or', ['shared', '=', true]]] })
if (!dbListViews) {
dbListViews = await getObject("object_listviews").directFind({ filters: [['space', '=', userSession.spaceId], ['object_name', '=', this.name], [['owner', '=', userSession.userId], 'or', ['shared', '=', true]]] })
}
objectConfig.list_views = Object.assign({}, objectConfig.list_views)
_.each(dbListViews, function(dbListView){
delete dbListView.created;

View File

@ -83,6 +83,12 @@ export class LookupActionHandler {
})
return _.uniq(detailName);
}
getDetailsInfoKey(objectApiName: string) {
return this.getDetailKey(objectApiName, "*");
}
async getDetailsInfo(objectApiName: string){
const detailsInfo = [];
let records = (await this.broker.call('metadata.filter', { key: this.getDetailKey(objectApiName, "*") }, { meta: {} })) || {};

View File

@ -215,6 +215,9 @@ export class MasterDetailActionHandler{
return _.uniq(mastersName);
}
getMastersInfoKey(objectApiName: string) {
return this.getMasterKey(objectApiName, "*");
}
async getMastersInfo(objectApiName: string){
const mastersInfo = [];
let records = (await this.broker.call('metadata.filter', { key: this.getMasterKey(objectApiName, "*") }, { meta: {} })) || {};
@ -257,6 +260,10 @@ export class MasterDetailActionHandler{
return _.uniq(detailsName);
}
getDetailsInfoKey(objectApiName: string) {
return this.getDetailKey(objectApiName, "*", "*");
}
async getDetailsInfo(objectApiName: string){
const detailsInfo = [];
let records = (await this.broker.call('metadata.filter', { key: this.getDetailKey(objectApiName, "*", "*") }, { meta: {} })) || {};

View File

@ -168,6 +168,21 @@ module.exports = {
return await this.lookupActionHandler.getDetailsInfo(objectApiName);
}
},
getRelationsInfo: {
async handler(ctx) {
const { objectApiName } = ctx.params;
const detailsInfoKey = this.masterDetailActionHandler.getDetailsInfoKey(objectApiName);
const mastersInfo = this.masterDetailActionHandler.getMastersInfoKey(objectApiName);
const lookupDetailsInfo = this.lookupActionHandler.getDetailsInfoKey(objectApiName);
const results = await ctx.broker.call('metadata.mget', { keys: [detailsInfoKey, mastersInfo, lookupDetailsInfo] });
return {
details: results[0],
masters: results[1],
lookup_details: results[2],
}
}
},
getMasterPaths: {
async handler(ctx) {
const { objectApiName } = ctx.params;

View File

@ -6,6 +6,33 @@ import * as _ from 'underscore';
let savePackageServicesTimeoutID = null;
const useScan = false;
async function redisScanKeys(redisClient, match, count = 100): Promise<Array<string>> {
if (!useScan) {
return await redisClient.keys(match);
} else {
return await new Promise((resolve, reject) => {
var stream = redisClient.scanStream({
// only returns keys following the pattern of `user:*`
match: match,
// returns approximately 100 elements per call
count: count
});
var keys = [];
stream.on('data', function (resultKeys) {
for (var i = 0; i < resultKeys.length; i++) {
keys.push(resultKeys[i]);
}
});
stream.on('end', function () {
resolve(keys)
});
})
}
}
//////////////// mock for redis cacher ////////////////
// let mockCacherData: string[] = [];
// let mock_prefix = "";
@ -134,17 +161,33 @@ async function addServiceMetadata(ctx) {
// });
}
async function mget(ctx, keys) {
if (!keys || keys.length == 0) {
return [];
}
const values = await ctx.broker.cacher.client.mget(...keys);
const results = [];
_.map(values, (item) => {
try {
if (item) {
results.push(JSON.parse(item));
} else {
results.push(item);
}
} catch (error) {
results.push(item);
}
})
return results;
}
// 这里需要更改
async function query(ctx, queryKey) {
try {
const keyPrefix = ctx.broker.cacher?.prefix || "";
const keys = await ctx.broker.cacher.client.keys(`${keyPrefix}${queryKey}`); //TODO 此功能仅支持redis cache
const keys = await redisScanKeys(ctx.broker.cacher.client, `${keyPrefix}${queryKey}`) //ctx.broker.cacher.client.keys(`${keyPrefix}${queryKey}`); //TODO 此功能仅支持redis cache
// REPLACE: const keys = await mockCacherKeys(ctx, `${keyPrefix}${queryKey}`) //TODO 此功能仅支持redis cache
const values = [];
for (const key of keys) {
values.push(await ctx.broker.cacher.get(getKey(key, keyPrefix)));
// REPLACE: values.push(await mockCacherGet(ctx, getKey(key, keyPrefix)));
}
const values = _.compact(await mget(ctx, keys));
return values;
} catch (error) {
// console.error(`error`, error)
@ -243,7 +286,7 @@ async function clearPackageServicesMetadatas(ctx, offlinePackageServices) {
async function getMetadataServices(broker) {
const queryKey = `${METADATA_SERVICES_PERFIX}.*`;
const keyPrefix = broker.cacher?.prefix || "";
const keys = await broker.cacher.client.keys(`${keyPrefix}${queryKey}`); //TODO 此功能仅支持redis cache
const keys = await redisScanKeys(broker.cacher.client, `${keyPrefix}${queryKey}`) // broker.cacher.client.keys(`${keyPrefix}${queryKey}`); //TODO 此功能仅支持redis cache
// REPLACE: const keys = await mockCacherKeys({broker,}, `${keyPrefix}${queryKey}`) //TODO 此功能仅支持redis cache
const values = [];
for (const key of keys) {
@ -280,6 +323,14 @@ export const ActionHandlers = {
}
// REPLACE: return await mockCacherGet(ctx, ctx.params.key)
},
async mget(ctx: any): Promise<any> {
try {
return await mget(ctx, ctx.params.keys);
} catch (error) {
}
// REPLACE: return await mockCacherGet(ctx, ctx.params.key)
},
async filter(ctx: any): Promise<Array<any>> {
return await query(ctx, ctx.params.key);
// const keyPrefix = ctx.broker.cacher.prefix
@ -300,7 +351,7 @@ export const ActionHandlers = {
async fuzzyDelete(ctx: any){
const { key } = ctx.params
const keyPrefix = ctx.broker.cacher?.prefix || "";
const keys = await ctx.broker.cacher.client.keys(`${keyPrefix}${key}`);
const keys = await redisScanKeys(ctx.broker.cacher.client, `${keyPrefix}${key}`) // await ctx.broker.cacher.client.keys(`${keyPrefix}${key}`);
for (const _key of keys) {
await ctx.broker.cacher.del(getKey(_key, keyPrefix));
}

View File

@ -48,6 +48,11 @@ module.exports = {
return await ActionHandlers.get(ctx);
}
},
mget: {
async handler(ctx) {
return await ActionHandlers.mget(ctx);
}
},
filter: {
async handler(ctx) {
return await ActionHandlers.filter(ctx);