重构了actionAuth和relation判定的数据结构和逻辑

This commit is contained in:
Xu Chang 2023-08-09 18:55:57 +08:00
parent 8282f37cd8
commit 8bc0f3af4e
26 changed files with 2499 additions and 1198 deletions

View File

@ -10,7 +10,6 @@ declare type Actions = string[];
declare type Paths = string[];
export declare type OpSchema = EntityShape & {
relationId?: ForeignKey<"relation"> | null;
path: String<256>;
paths: Paths;
destEntity: String<32>;
deActions: Actions;
@ -18,7 +17,6 @@ export declare type OpSchema = EntityShape & {
export declare type OpAttr = keyof OpSchema;
export declare type Schema = EntityShape & {
relationId?: ForeignKey<"relation"> | null;
path: String<256>;
paths: Paths;
destEntity: String<32>;
deActions: Actions;
@ -37,7 +35,6 @@ declare type AttrFilter = {
$$updateAt$$: Q_DateValue;
relationId: Q_StringValue;
relation: Relation.Filter;
path: Q_StringValue;
paths: JsonFilter<Paths>;
destEntity: Q_StringValue;
deActions: JsonFilter<Actions>;
@ -54,7 +51,6 @@ export declare type Projection = {
$$seq$$?: number;
relationId?: number;
relation?: Relation.Projection;
path?: number;
paths?: number | JsonProjection<Paths>;
destEntity?: number;
deActions?: number | JsonProjection<Actions>;
@ -89,8 +85,6 @@ export declare type SortAttr = {
relationId: number;
} | {
relation: Relation.SortAttr;
} | {
path: number;
} | {
paths: number;
} | {

View File

@ -8,13 +8,6 @@ exports.desc = {
type: "ref",
ref: "relation"
},
path: {
notNull: true,
type: "varchar",
params: {
length: 256
}
},
paths: {
notNull: true,
type: "object"
@ -35,7 +28,7 @@ exports.desc = {
actions: action_1.genericActions,
indexes: [
{
name: 'index_entity_relation_path',
name: 'index_entity_relation',
attributes: [
{
name: 'destEntity'
@ -43,9 +36,6 @@ exports.desc = {
{
name: "relationId"
},
{
name: 'path'
},
],
config: {
unique: true

View File

@ -5,7 +5,6 @@ declare type Actions = string[];
declare type Paths = string[];
export interface Schema extends EntityShape {
relation?: Relation;
path: String<256>;
paths: Paths;
destEntity: String<32>;
deActions: Actions;

View File

@ -7,7 +7,6 @@ var entityDesc = {
name: '用户授权',
attr: {
relation: '关系',
path: '路径',
paths: '路径',
destEntity: '目标对象',
deActions: '目标对象动作',
@ -16,7 +15,7 @@ var entityDesc = {
},
indexes: [
{
name: 'index_entity_relation_path',
name: 'index_entity_relation',
attributes: [
{
name: 'destEntity',
@ -24,9 +23,6 @@ var entityDesc = {
{
name: 'relation',
},
{
name: 'path',
},
],
config: {
unique: true,

View File

@ -39,7 +39,7 @@ var CascadeStore = /** @class */ (function (_super) {
id: 1,
deActions: 1,
destEntity: 1,
path: 1,
paths: 1,
relationId: 1,
},
filter: {
@ -299,7 +299,7 @@ var CascadeStore = /** @class */ (function (_super) {
id: 1,
deActions: 1,
destEntity: 1,
path: 1,
paths: 1,
relationId: 1,
},
}
@ -565,7 +565,7 @@ var CascadeStore = /** @class */ (function (_super) {
var _a, _b;
var aggrResult = aggregateFn.call(_this, entity2_1, {
data: subProjection_1,
filter: (0, filter_1.combineFilters)([(_a = {},
filter: (0, filter_1.combineFilters)(entity2_1, _this.getSchema(), [(_a = {},
_a[foreignKey_1] = row.id,
_a), subFilter_1]),
sorter: subSorter_1,
@ -619,7 +619,7 @@ var CascadeStore = /** @class */ (function (_super) {
if (ids.length > 0) {
var subRows = cascadeSelectFn.call(_this, entity2_1, {
data: subProjection_1,
filter: (0, filter_1.combineFilters)([(_a = {},
filter: (0, filter_1.combineFilters)(entity2_1, _this.getSchema(), [(_a = {},
_a[foreignKey_1] = {
$in: ids,
},
@ -645,7 +645,7 @@ var CascadeStore = /** @class */ (function (_super) {
var _a;
var aggrResult = aggregateFn.call(_this, entity2_1, {
data: subProjection_1,
filter: (0, filter_1.combineFilters)([{
filter: (0, filter_1.combineFilters)(entity2_1, _this.getSchema(), [{
entity: entity,
entityId: row.id,
}, subFilter_1]),
@ -699,7 +699,7 @@ var CascadeStore = /** @class */ (function (_super) {
if (ids.length > 0) {
var subRows = cascadeSelectFn.call(_this, entity2_1, {
data: subProjection_1,
filter: (0, filter_1.combineFilters)([{
filter: (0, filter_1.combineFilters)(entity2_1, _this.getSchema(), [{
entity: entity,
entityId: {
$in: ids,
@ -808,10 +808,9 @@ var CascadeStore = /** @class */ (function (_super) {
else {
// A中data的entityId作为B中filter的主键
Object.assign(operationMto_1, {
filter: (0, filter_1.addFilterSegment)({
id: fkId,
}),
filterMto: filterMto,
filter: (0, filter_1.combineFilters)(attr, this_2.getSchema(), [{
id: fkId,
}, filterMto]),
});
}
}
@ -824,24 +823,24 @@ var CascadeStore = /** @class */ (function (_super) {
}
else if (filter.entity === attr && filter.entityId) {
Object.assign(operationMto_1, {
filter: (0, filter_1.addFilterSegment)({
id: filter.entityId,
}, filterMto),
filter: (0, filter_1.combineFilters)(attr, this_2.getSchema(), [{
id: filter.entityId,
}, filterMto]),
});
}
else if (filter[attr]) {
Object.assign(operationMto_1, {
filter: (0, filter_1.addFilterSegment)(filter[attr], filterMto),
filter: (0, filter_1.combineFilters)(attr, this_2.getSchema(), [filter[attr], filterMto]),
});
}
else {
// A中data的entityId作为B中filter的主键
Object.assign(operationMto_1, {
filter: (0, filter_1.addFilterSegment)((_b = {},
_b["".concat(entity, "$entity")] = {
filter: filter,
},
_b), filterMto),
filter: (0, filter_1.combineFilters)(attr, this_2.getSchema(), [(_b = {},
_b["".concat(entity, "$entity")] = {
filter: filter,
},
_b), filterMto]),
});
}
}
@ -866,9 +865,9 @@ var CascadeStore = /** @class */ (function (_super) {
else {
// A中data的entityId作为B中filter的主键
Object.assign(operationMto_2, {
filter: (0, filter_1.addFilterSegment)(filterMto || {}, {
id: fkId,
}),
filter: (0, filter_1.combineFilters)(relation, this_2.getSchema(), [filterMto, {
id: fkId,
}]),
});
}
}
@ -880,22 +879,22 @@ var CascadeStore = /** @class */ (function (_super) {
}
else if (filter["".concat(attr, "Id")]) {
Object.assign(operationMto_2, {
filter: (0, filter_1.addFilterSegment)(filterMto || {}, {
id: filter["".concat(attr, "Id")],
}),
filter: (0, filter_1.combineFilters)(relation, this_2.getSchema(), [filterMto, {
id: filter["".concat(attr, "Id")],
}]),
});
}
else if (filter[attr]) {
Object.assign(operationMto_2, {
filter: (0, filter_1.addFilterSegment)(filterMto || {}, filter[attr]),
filter: (0, filter_1.combineFilters)(relation, this_2.getSchema(), [filterMto, filter[attr]]),
});
}
else {
// A中data的attrId作为B中filter的主键
Object.assign(operationMto_2, {
filter: (0, filter_1.addFilterSegment)(filterMto || {}, (_d = {},
_d["".concat(entity, "$").concat(attr)] = filter,
_d)),
filter: (0, filter_1.combineFilters)(relation, this_2.getSchema(), [filterMto, (_d = {},
_d["".concat(entity, "$").concat(attr)] = filter,
_d)]),
});
}
}
@ -948,28 +947,28 @@ var CascadeStore = /** @class */ (function (_super) {
if (filter) {
if (filter.id && Object.keys(filter).length === 1) {
Object.assign(otm, {
filter: (0, filter_1.addFilterSegment)({
entity: entity,
entityId: filter.id,
}, filterOtm),
filter: (0, filter_1.combineFilters)(entityOtm_1, _this.getSchema(), [{
entity: entity,
entityId: filter.id,
}, filterOtm]),
});
}
else {
Object.assign(otm, {
filter: (0, filter_1.addFilterSegment)((_a = {},
_a[entity] = filter,
_a), filterOtm),
filter: (0, filter_1.combineFilters)(entityOtm_1, _this.getSchema(), [(_a = {},
_a[entity] = filter,
_a), filterOtm]),
});
}
}
else {
Object.assign(otm, {
filter: (0, filter_1.addFilterSegment)({
entity: entity,
entityId: {
$exists: true,
}
}, filterOtm)
filter: (0, filter_1.combineFilters)(entityOtm_1, _this.getSchema(), [{
entity: entity,
entityId: {
$exists: true,
}
}, filterOtm])
});
}
if (action === 'remove' && actionOtm === 'update') {
@ -1022,26 +1021,26 @@ var CascadeStore = /** @class */ (function (_super) {
if (filter) {
if (filter.id && Object.keys(filter).length === 1) {
Object.assign(otm, {
filter: (0, filter_1.addFilterSegment)((_d = {},
_d[foreignKey_2] = filter.id,
_d), filterOtm),
filter: (0, filter_1.combineFilters)(entityOtm_1, _this.getSchema(), [(_d = {},
_d[foreignKey_2] = filter.id,
_d), filterOtm]),
});
}
else {
Object.assign(otm, {
filter: (0, filter_1.addFilterSegment)((_e = {},
_e[foreignKey_2.slice(0, foreignKey_2.length - 2)] = filter,
_e), filterOtm),
filter: (0, filter_1.combineFilters)(entityOtm_1, _this.getSchema(), [(_e = {},
_e[foreignKey_2.slice(0, foreignKey_2.length - 2)] = filter,
_e), filterOtm]),
});
}
}
else {
Object.assign(otm, {
filter: (0, filter_1.addFilterSegment)((_f = {},
_f[foreignKey_2] = {
$exists: true,
},
_f), filterOtm),
filter: (0, filter_1.combineFilters)(entityOtm_1, _this.getSchema(), [(_f = {},
_f[foreignKey_2] = {
$exists: true,
},
_f), filterOtm]),
});
}
if (action === 'remove' && actionOtm === 'update') {

View File

@ -38,14 +38,23 @@ export declare class RelationAuth<ED extends EntityDict & BaseEntityDict> {
*/
private destructOperation;
/**
* actionAuthentity是不是满足当前的查询约束
* actionAuthactionAuth加以判断actionAuth
* @param entity
* @param filter
* @param actionAuths
* @param context
* @return string代表用户获得授权的relationIduserId赋权false代表失败
*/
private checkSingleOperation;
private filterActionAuths;
/**
* $or的查询actionAuth无法满足
* @param entity
* @param filter
* @param actionAuths
* @param context
* @param actions
*/
private checkActionAuthInGroup;
private checkSelection;
private findActionAuthsOnNode;
private checkOperationTree;
@ -68,6 +77,7 @@ export declare class RelationAuth<ED extends EntityDict & BaseEntityDict> {
* entity进行actions操作权限的userRelation关系
* @param params
* @param context
* todo paths改成复数以后这里还未充分测试过
*/
export declare function getUserRelationsByActions<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(params: {
entity: T;
@ -76,9 +86,9 @@ export declare function getUserRelationsByActions<ED extends EntityDict & BaseEn
overlap?: boolean;
}, context: Cxt): Promise<{
userRelations: ED["userRelation"]["Schema"][];
userEntities: {
userEntities: Promise<{
entity: keyof ED;
entityId: string;
userId: string;
}[];
}[]>[];
}>;

View File

@ -11,6 +11,7 @@ var SyncRowStore_1 = require("./SyncRowStore");
var action_1 = require("../actions/action");
var lodash_1 = require("../utils/lodash");
var env_1 = require("../compiler/env");
var path_1 = tslib_1.__importDefault(require("path"));
var RelationAuth = /** @class */ (function () {
function RelationAuth(schema, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities) {
this.actionCascadePathGraph = actionCascadePathGraph;
@ -303,7 +304,7 @@ var RelationAuth = /** @class */ (function () {
hasOneToMany = true;
destructInner(e, {
data: data[attr].data,
filter: (0, filter_1.combineFilters)([(_a = {},
filter: (0, filter_1.combineFilters)(e, _this.schema, [(_a = {},
_a[foreignKey.slice(0, foreignKey.length - 2)] = filter,
_a), data[attr].filter || {}]),
});
@ -313,7 +314,7 @@ var RelationAuth = /** @class */ (function () {
hasOneToMany = true;
destructInner(e, {
data: data[attr].data,
filter: (0, filter_1.combineFilters)([(_b = {},
filter: (0, filter_1.combineFilters)(e, _this.schema, [(_b = {},
_b[entity2] = filter,
_b), data[attr].filter || {}]),
});
@ -462,38 +463,37 @@ var RelationAuth = /** @class */ (function () {
return destructInner(entity2, (0, lodash_1.cloneDeep)(operation2));
};
/**
* 定位到了当前用户所有可能的actionAuth再用以判定对应的entity是不是满足当前的查询约束
* 定位到了当前用户所有可能的actionAuth对单条actionAuth加以判断找到可以满足当前操作的actionAuth
* @param entity
* @param filter
* @param actionAuths
* @param context
* @return string代表用户获得授权的relationId空字符串代表通过userId赋权false代表失败
*/
RelationAuth.prototype.checkSingleOperation = function (entity, filter, actionAuths, context, actions) {
var legalAuths = actionAuths.filter(function (ele) { return ele.destEntity === entity && (0, lodash_1.intersection)(ele.deActions, actions).length > 0; } // 这里只要overlap就可以了
);
return legalAuths.map(function (ele) {
var path = ele.path, relation = ele.relation, relationId = ele.relationId;
RelationAuth.prototype.filterActionAuths = function (entity, filter, actionAuths, context, actions) {
return actionAuths.map(function (ele) {
var paths = ele.paths, relation = ele.relation, relationId = ele.relationId;
if (relationId) {
(0, assert_1.default)(relation);
var userRelations = relation.userRelation$relation;
if (userRelations.length > 0) {
var entityIds = (0, lodash_1.uniq)(userRelations.map(function (ele) { return ele.entityId; }));
var contained_1 = {};
var idFilter = entityIds.length > 1 ? {
var idFilter_1 = entityIds.length > 1 ? {
$in: entityIds,
} : entityIds[0];
(0, assert_1.default)(idFilter);
if (path) {
(0, lodash_1.set)(contained_1, path, {
id: idFilter,
});
}
else {
Object.assign(contained_1, {
id: idFilter
});
}
(0, assert_1.default)(idFilter_1);
var pathFilters = paths.map(function (path) {
if (path) {
return (0, lodash_1.set)({}, path, {
id: idFilter_1,
});
}
return {
id: idFilter_1,
};
});
// 这里是或关系,只要对象落在任意一条路径上就可以
var contained_1 = (0, filter_1.combineFilters)(entity, context.getSchema(), pathFilters, true);
var contains_1 = (0, filter_1.checkFilterContains)(entity, context, contained_1, filter, true);
if (contains_1 instanceof Promise) {
return contains_1.then(function (c) {
@ -512,7 +512,7 @@ var RelationAuth = /** @class */ (function () {
}
// 说明是通过userId关联
var contained = {};
(0, lodash_1.set)(contained, "".concat(path, ".id"), context.getCurrentUserId());
(0, lodash_1.set)(contained, "".concat(path_1.default, ".id"), context.getCurrentUserId());
var contains = (0, filter_1.checkFilterContains)(entity, context, contained, filter, true);
if (contains instanceof Promise) {
return contains.then(function (c) {
@ -527,6 +527,46 @@ var RelationAuth = /** @class */ (function () {
}
});
};
/**
* 对于有些特殊的查询带很多$or的查询多发生在系统级别单个actionAuth无法满足需要共同加以判定
* @param entity
* @param filter
* @param actionAuths
* @param context
* @param actions
*/
RelationAuth.prototype.checkActionAuthInGroup = function (entity, filter, actionAuths, context) {
var filters = actionAuths.map(function (ele) {
var paths = ele.paths, relation = ele.relation, relationId = ele.relationId;
if (relationId) {
(0, assert_1.default)(relation);
var userRelations = relation.userRelation$relation;
(0, assert_1.default)(userRelations.length > 0);
var entityIds = (0, lodash_1.uniq)(userRelations.map(function (ele) { return ele.entityId; }));
var idFilter_2 = entityIds.length > 1 ? {
$in: entityIds,
} : entityIds[0];
(0, assert_1.default)(idFilter_2);
var pathFilters = paths.map(function (path) {
if (path) {
return (0, lodash_1.set)({}, path, {
id: idFilter_2,
});
}
return {
id: idFilter_2
};
});
return pathFilters;
}
// 说明是通过userId关联
var contained = {};
(0, lodash_1.set)(contained, "".concat(path_1.default, ".id"), context.getCurrentUserId());
return contained;
});
var groupFilter = (0, filter_1.combineFilters)(entity, this.schema, filters.flat(), true);
return (0, filter_1.checkFilterContains)(entity, context, groupFilter, filter, true);
};
RelationAuth.prototype.checkSelection = function (entity, selection, context) {
var _this = this;
var leafSelections = this.destructSelection(entity, selection);
@ -561,7 +601,7 @@ var RelationAuth = /** @class */ (function () {
var actionAuths = context.select('actionAuth', {
data: {
id: 1,
path: 1,
paths: 1,
destEntity: 1,
deActions: 1,
relation: {
@ -585,34 +625,57 @@ var RelationAuth = /** @class */ (function () {
},
destEntity: {
$in: allEntities,
}
},
$or: [
{
relation: {
userRelation$relation: {
userId: context.getCurrentUserId(),
},
}
},
{
relationId: {
$exists: false,
},
}
]
}
}, { dontCollect: true });
/**
* 返回的结果中第一层为leafNode必须全通过第二层为单个leafNode上的deduce通过一个就可以第三层为所有可能的actionAuth通过一个就可以
* 返回的结果中第一层为leafNode必须全通过第二层为单个leafNode上的deduce通过一个就可以
* @param result
* @returns
*/
var checkResult = function (result) {
var r = !result.find(function (ele) {
var eleFlated = ele.flat();
return !eleFlated.find(function (ele2) { return !!ele2; });
});
if (!r && process.env.NODE_ENV === 'development') {
dlSelections.forEach(function (ele, idx) {
var r2 = result[idx].flat();
if (!r2.find(function (ele2) { return !!ele; })) {
console.warn('对象的select权限被否决请检查', ele);
var e_3, _a;
var idx = 0;
try {
for (var result_1 = tslib_1.__values(result), result_1_1 = result_1.next(); !result_1_1.done; result_1_1 = result_1.next()) {
var r1 = result_1_1.value;
var r2 = r1.find(function (ele) { return ele === true; });
if (!r2) {
if (process.env.NODE_ENV === 'development') {
console.warn('对象的select权限被否决请检查', dlSelections[idx]);
}
return false;
}
});
}
}
return r;
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (result_1_1 && !result_1_1.done && (_a = result_1.return)) _a.call(result_1);
}
finally { if (e_3) throw e_3.error; }
}
return true;
};
if (actionAuths instanceof Promise) {
(0, assert_1.default)(context instanceof AsyncRowStore_1.AsyncContext);
return actionAuths.then(function (aas) { return Promise.all(dlSelections.map(function (ele) { return Promise.all(ele.map(function (ele2) { return Promise.all(_this.checkSingleOperation(ele2.entity, ele2.filter, aas, context, ['select'])); })); })).then(function (result) { return checkResult(result); }); });
return actionAuths.then(function (aas) { return Promise.all(dlSelections.map(function (ele) { return Promise.all(ele.map(function (ele2) { return _this.checkActionAuthInGroup(ele2.entity, ele2.filter, aas, context); })); })).then(function (result) { return checkResult(result); }); });
}
return checkResult(dlSelections.map(function (ele) { return ele.map(function (ele2) { return _this.checkSingleOperation(ele2.entity, ele2.filter, actionAuths, context, ['select']); }); }));
return checkResult(dlSelections.map(function (ele) { return ele.map(function (ele2) { return _this.checkActionAuthInGroup(ele2.entity, ele2.filter, actionAuths, context); }); }));
};
if (deducedLeafSelections[0] instanceof Promise) {
return Promise.all(deducedLeafSelections)
@ -630,11 +693,12 @@ var RelationAuth = /** @class */ (function () {
var deducedEntityFilters2 = this.getDeducedEntityFilters(entity, filter, [action], context);
var dealWithDeducedEntityFilters = function (deducedEntityFilters) {
var allEntities = deducedEntityFilters.map(function (ele) { return ele.entity; });
var allActions = (0, lodash_1.uniq)(deducedEntityFilters.map(function (ele) { return ele.actions; }).flat());
// todo 这里其实可以在查询条件里通过userRelation过滤一次但问题不大
var actionAuths = context.select('actionAuth', {
data: {
id: 1,
path: 1,
paths: 1,
destEntity: 1,
deActions: 1,
relation: {
@ -655,7 +719,24 @@ var RelationAuth = /** @class */ (function () {
filter: {
destEntity: {
$in: allEntities,
}
},
deActions: {
$overlaps: allActions,
},
$or: [
{
relation: {
userRelation$relation: {
userId: context.getCurrentUserId(),
},
}
},
{
relationId: {
$exists: false,
},
}
]
}
}, { dontCollect: true });
var getActionAuths = function (result) {
@ -672,7 +753,11 @@ var RelationAuth = /** @class */ (function () {
var ars = actionAuths.filter(function (ar) { return !!userRelations.find(function (ur) { return ur.relationId === ar.relationId; }); });
if (ars.length > 0) {
// 这里能找到actionAuth其必然是本对象上的授权
(0, assert_1.default)(!ars.find(function (ele) { return ele.path !== '' || ele.destEntity !== entity; }));
// 下面这个看不懂,跑到了再说
// assert(!ars.find(
// ele => ele.path !== '' || ele.destEntity !== entity
// ));
console.log('跑到你要看的代码了');
return ars;
}
}
@ -683,7 +768,11 @@ var RelationAuth = /** @class */ (function () {
if (created) {
return created;
}
return Promise.all(deducedEntityFilters.map(function (ele) { return Promise.all(_this.checkSingleOperation(ele.entity, ele.filter, ars, context, ele.actions)); })).then(function (result) { return getActionAuths(result); });
return Promise.all(deducedEntityFilters.map(function (ele) {
var ars2 = ars.filter(function (ele2) { return ele2.destEntity === ele.entity && (0, lodash_1.intersection)(ele2.deActions, ele.actions).length > 0; } // 这里只要overlap就可以了
);
return Promise.all(_this.filterActionAuths(ele.entity, ele.filter, ars2, context, ele.actions));
})).then(function (result) { return getActionAuths(result); });
});
}
(0, assert_1.default)(context instanceof SyncRowStore_1.SyncContext);
@ -691,7 +780,11 @@ var RelationAuth = /** @class */ (function () {
if (created) {
return created;
}
return getActionAuths(deducedEntityFilters.map(function (ele) { return _this.checkSingleOperation(ele.entity, ele.filter, actionAuths, context, ele.actions); }));
return getActionAuths(deducedEntityFilters.map(function (ele) {
var ars2 = actionAuths.filter(function (ele2) { return ele2.destEntity === ele.entity && (0, lodash_1.intersection)(ele2.deActions, ele.actions).length > 0; } // 这里只要overlap就可以了
);
return _this.filterActionAuths(ele.entity, ele.filter, ars2, context, ele.actions);
}));
};
if (deducedEntityFilters2 instanceof Promise) {
return deducedEntityFilters2.then(function (def2) { return dealWithDeducedEntityFilters(def2); });
@ -742,14 +835,16 @@ var RelationAuth = /** @class */ (function () {
if (child instanceof Array) {
var childActions_1 = child.map(function (ele) { return ele.action; });
var childLegalAuths_1 = selfLegalPaths.map(function (ele) {
var path = ele.path, relationId = ele.relationId;
var path2 = path ? "".concat(pathToParent, ".").concat(path) : pathToParent;
var paths = ele.paths, relationId = ele.relationId;
var paths2 = paths.map(function (path) { return path ? "".concat(pathToParent, ".").concat(path) : pathToParent; });
return context.select('actionAuth', {
data: {
id: 1,
},
filter: {
path: path2,
paths: {
$overlaps: paths2,
},
destEntity: childEntity,
deActions: {
$overlaps: childActions_1,
@ -766,14 +861,16 @@ var RelationAuth = /** @class */ (function () {
return child.map(function (c) { return checkChildNode(childLegalAuths_1, c); });
}
var childLegalAuths = realLegalPaths.map(function (ele) {
var path = ele.path, relationId = ele.relationId;
var path2 = path ? "".concat(pathToParent, ".").concat(path) : pathToParent;
var paths = ele.paths, relationId = ele.relationId;
var paths2 = paths.map(function (path) { return path ? "".concat(pathToParent, ".").concat(path) : pathToParent; });
return context.select('actionAuth', {
data: {
id: 1,
},
filter: {
path: path2,
paths: {
$overlaps: paths2,
},
destEntity: childEntity,
deActions: {
$overlaps: child.action,
@ -896,6 +993,7 @@ exports.RelationAuth = RelationAuth;
* 获取有对entity进行actions操作权限的userRelation关系
* @param params
* @param context
* todo paths改成复数以后这里还未充分测试过
*/
function getUserRelationsByActions(params, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
@ -925,7 +1023,7 @@ function getUserRelationsByActions(params, context) {
return [4 /*yield*/, context.select('actionAuth', {
data: {
id: 1,
path: 1,
paths: 1,
relationId: 1,
relation: {
id: 1,
@ -937,7 +1035,7 @@ function getUserRelationsByActions(params, context) {
case 1:
actionAuths = _b.sent();
getUserRelations = function (urAuths) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var makeRelationIterator, urAuthDict, urAuthGroups, userRelations;
var makeRelationIterator, urAuthDict2, userRelations;
var _this = this;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
@ -1018,31 +1116,36 @@ function getUserRelationsByActions(params, context) {
};
return makeIter(entity, 0);
};
urAuthDict = (0, lodash_1.groupBy)(urAuths, 'path');
urAuthGroups = Object.keys(urAuthDict).map(function (ele) { return ({
path: ele,
relationIds: urAuthDict[ele].map(function (ele) { return ele.relationId; })
}); });
return [4 /*yield*/, Promise.all(urAuthGroups.map(function (_a) {
var path = _a.path, relationIds = _a.relationIds;
return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _b, projection, getData, rows, urs;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
_b = makeRelationIterator(path, relationIds), projection = _b.projection, getData = _b.getData;
return [4 /*yield*/, context.select(entity, {
data: projection,
filter: filter,
}, { dontCollect: true })];
case 1:
rows = _c.sent();
urs = rows.map(function (ele) { return getData(ele); }).flat().filter(function (ele) { return !!ele; });
return [2 /*return*/, urs];
}
});
urAuthDict2 = {};
urAuths.forEach(function (auth) {
var paths = auth.paths, relationId = auth.relationId;
paths.forEach(function (path) {
if (!urAuthDict2[path]) {
urAuthDict2[path] = [relationId];
}
else if (!urAuthDict2[path].includes(relationId)) {
urAuthDict2[path].push(relationId);
}
});
});
return [4 /*yield*/, Promise.all(Object.keys(urAuthDict2).map(function (path) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var relationIds, _a, projection, getData, rows, urs;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
relationIds = urAuthDict2[path];
_a = makeRelationIterator(path, relationIds), projection = _a.projection, getData = _a.getData;
return [4 /*yield*/, context.select(entity, {
data: projection,
filter: filter,
}, { dontCollect: true })];
case 1:
rows = _b.sent();
urs = rows.map(function (ele) { return getData(ele); }).flat().filter(function (ele) { return !!ele; });
return [2 /*return*/, urs];
}
});
}))];
}); }))];
case 1:
userRelations = _a.sent();
return [2 /*return*/, userRelations.flat()];
@ -1143,22 +1246,27 @@ function getUserRelationsByActions(params, context) {
return makeIter(entity, 0);
};
return [4 /*yield*/, Promise.all(directAuths.map(function (_a) {
var path = _a.path;
var paths = _a.paths;
return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _b, getData, projection, rows, userEntities;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
_b = makeRelationIterator(path), getData = _b.getData, projection = _b.projection;
return [4 /*yield*/, context.select(entity, {
data: projection,
filter: filter,
}, { dontCollect: true })];
case 1:
rows = _c.sent();
userEntities = rows.map(function (ele) { return getData(ele); }).flat().filter(function (ele) { return !!ele; });
return [2 /*return*/, userEntities];
}
var _this = this;
return tslib_1.__generator(this, function (_b) {
return [2 /*return*/, paths.map(function (path) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _a, getData, projection, rows, userEntities;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = makeRelationIterator(path), getData = _a.getData, projection = _a.projection;
return [4 /*yield*/, context.select(entity, {
data: projection,
filter: filter,
}, { dontCollect: true })];
case 1:
rows = _b.sent();
userEntities = rows.map(function (ele) { return getData(ele); }).flat().filter(function (ele) { return !!ele; });
return [2 /*return*/, userEntities];
}
});
}); })];
});
});
}))];

View File

@ -153,20 +153,20 @@ var TriggerExecutor = /** @class */ (function () {
_c.label = 2;
case 2:
filter = operation.filter;
filter2 = (0, filter_1.addFilterSegment)({
$or: [
{
$$triggerData$$: {
$exists: true,
filter2 = (0, filter_1.combineFilters)(entity, context.getSchema(), [{
$or: [
{
$$triggerData$$: {
$exists: true,
},
},
},
{
$$triggerTimestamp$$: {
$exists: true,
},
}
],
}, filter);
{
$$triggerTimestamp$$: {
$exists: true,
},
}
],
}, filter]);
return [4 /*yield*/, context.count(entity, {
filter: filter2
}, {})];

View File

@ -127,7 +127,7 @@ function checkUnique(entity, row, context, uniqAttrs, extraFilter) {
// 说明有null值不需要检查约束
return;
}
var filter2 = extraFilter ? (0, filter_1.addFilterSegment)(filter, extraFilter) : filter;
var filter2 = extraFilter ? (0, filter_1.combineFilters)(entity, context.getSchema(), [filter, extraFilter]) : filter;
var count = context.count(entity, { filter: filter2 }, { dontCollect: true });
return checkCountLessThan(count, uniqAttrs, 0, row.id);
}
@ -267,7 +267,7 @@ function makeIntrinsicCTWs(schema, actionDefDict) {
var filter = (0, lodash_1.pick)(data, refAttrs);
// 在这些行以外的行不和更新后的键值冲突
var count = context.count(entity, {
filter: (0, filter_1.addFilterSegment)([filter, {
filter: (0, filter_1.combineFilters)(entity, context.getSchema(), [filter, {
$not: operationFilter,
}]),
}, { dontCollect: true });

View File

@ -64,7 +64,7 @@ function translateCheckerInAsyncContext(checker) {
case 3:
filter2 = _b;
if (!['select', 'count', 'stat'].includes(action)) return [3 /*break*/, 4];
operation.filter = (0, filter_1.addFilterSegment)(operationFilter || {}, filter2);
operation.filter = (0, filter_1.combineFilters)(entity, context.getSchema(), [operationFilter, filter2]);
return [2 /*return*/, 0];
case 4: return [4 /*yield*/, (0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter || {}, true)];
case 5:
@ -134,7 +134,7 @@ function translateCheckerInAsyncContext(checker) {
return [2 /*return*/, 0];
}
if (['select', 'count', 'stat'].includes(action)) {
operation.filter = (0, filter_1.addFilterSegment)(filter || {}, result);
operation.filter = (0, filter_1.combineFilters)(entity, context.getSchema(), [filter, result]);
return [2 /*return*/, 0];
}
(0, assert_1.default)(filter);

13
lib/store/filter.d.ts vendored
View File

@ -2,9 +2,7 @@ import { EntityDict as BaseEntityDict, StorageSchema } from '../types';
import { EntityDict } from "../base-app-domain";
import { AsyncContext } from './AsyncRowStore';
import { SyncContext } from './SyncRowStore';
export declare function addFilterSegment<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(...filters: ED[T]['Selection']['filter'][]): ED[T]["Selection"]["filter"] | undefined;
export declare function unionFilterSegment<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(...filters: ED[T]['Selection']['filter'][]): ED[T]["Selection"]["filter"];
export declare function combineFilters<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(filters: Array<ED[T]['Selection']['filter']>, union?: true): ED[T]["Selection"]["filter"] | undefined;
export declare function combineFilters<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(entity: T, schema: StorageSchema<ED>, filters: Array<ED[T]['Selection']['filter']>, union?: true): ED[T]["Selection"]["filter"] | undefined;
declare type DeducedFilter<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
entity: T;
filter: ED[T]['Selection']['filter'];
@ -13,6 +11,11 @@ declare type DeducedFilterCombination<ED extends EntityDict & BaseEntityDict> =
$or?: (DeducedFilterCombination<ED> | DeducedFilter<ED, keyof ED>)[];
$and?: (DeducedFilterCombination<ED> | DeducedFilter<ED, keyof ED>)[];
};
/**
* //filter在逻辑上相容或者相斥
* { a: 1 } { a: { $ne: 1 } }
* 使访
*/
/**
* value1表达的单个属性查询与同属性上value2表达的查询是包容还是相斥
* value1所表达的查询结果一定被value2表达的查询结果所包含
@ -35,9 +38,10 @@ declare type DeducedFilterCombination<ED extends EntityDict & BaseEntityDict> =
*
* @param value1
* @param value2
* @return true代表肯定相容/false代表肯定不相容/undefined代表不能确定
* @attention: 1), 2)
*/
export declare function judgeValueRelation(value1: any, value2: any, contained: boolean): boolean;
export declare function judgeValueRelation(value1: any, value2: any, contained: boolean): boolean | undefined;
/**
*
* filter是否包含contained中的查询条件filter查询的结果一定是contained查询结果的子集
@ -116,5 +120,4 @@ export declare function checkDeduceFilters<ED extends EntityDict & BaseEntityDic
*/
export declare function checkFilterContains<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(entity: T, context: Cxt, contained: ED[T]['Selection']['filter'], filter?: ED[T]['Selection']['filter'], dataCompare?: true): boolean | Promise<boolean>;
export declare function checkFilterRepel<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends SyncContext<ED> | AsyncContext<ED>>(entity: T, context: Cxt, filter1: ED[T]['Selection']['filter'], filter2: ED[T]['Selection']['filter'], dataCompare?: true): boolean | Promise<boolean>;
export declare function getCascadeEntityFilter<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(filter: NonNullable<ED[T]['Selection']['filter']>, attr: keyof NonNullable<ED[T]['Selection']['filter']>): ED[keyof ED]['Selection']['filter'];
export {};

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,7 @@ function vaccumOper(option, context) {
entity: 'operEntity',
aliveLine: aliveLine + 10000,
filter: {
oper: (0, filter_1.combineFilters)([operFilter, {
oper: (0, filter_1.combineFilters)('operEntity', context.getSchema(), [operFilter, {
$$createAt$$: {
$lt: aliveLine,
}

View File

@ -34,7 +34,7 @@ function vaccumEntities(option, context) {
},
};
if (filter) {
filter2 = (0, filter_1.combineFilters)([filter2, filter]);
filter2 = (0, filter_1.combineFilters)(entity, context.getSchema(), [filter2, filter]);
}
if (!(backupDir && process.env.OAK_PLATFORM === 'server')) return [3 /*break*/, 4];
zip = option.zip;
@ -65,7 +65,7 @@ function vaccumEntities(option, context) {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
filter3 = (0, filter_1.combineFilters)([filter2, {
filter3 = (0, filter_1.combineFilters)(entity, context.getSchema(), [filter2, {
$$createAt$$: {
$gt: minCreateAt,
},

View File

@ -8,7 +8,7 @@
"make:domain": "ts-node scripts/make.ts",
"build": "tsc",
"prebuild": "npm run make:domain",
"test": "ts-node test/test.ts"
"test": "ts-node test/testFilter2.ts"
},
"files": [
"lib/**/*",

View File

@ -8,7 +8,6 @@ type Paths = string[];
export interface Schema extends EntityShape {
relation?: Relation;
path: String<256>;
paths: Paths;
destEntity: String<32>;
deActions: Actions;
@ -20,7 +19,6 @@ const entityDesc: EntityDesc<Schema> = {
name: '用户授权',
attr: {
relation: '关系',
path: '路径',
paths: '路径',
destEntity: '目标对象',
deActions: '目标对象动作',
@ -29,7 +27,7 @@ const entityDesc: EntityDesc<Schema> = {
},
indexes: [
{
name: 'index_entity_relation_path',
name: 'index_entity_relation',
attributes: [
{
name: 'destEntity',
@ -37,9 +35,6 @@ const entityDesc: EntityDesc<Schema> = {
{
name: 'relation',
},
{
name: 'path',
},
],
config: {
unique: true,

View File

@ -6,7 +6,7 @@ import {
import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { OperationRewriter, RowStore, SelectionRewriter } from '../types/RowStore';
import { StorageSchema } from '../types/Storage';
import { addFilterSegment, combineFilters } from "./filter";
import { combineFilters } from "./filter";
import { judgeRelation } from "./relation";
import { EXPRESSION_PREFIX, getAttrRefInExpression, OakRowUnexistedException } from "../types";
import { unset, uniq, cloneDeep, pick } from '../utils/lodash';
@ -46,7 +46,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
id: 1,
deActions: 1,
destEntity: 1,
path: 1,
paths: 1,
relationId: 1,
},
filter: {
@ -309,7 +309,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
id: 1,
deActions: 1,
destEntity: 1,
path: 1,
paths: 1,
relationId: 1,
},
}
@ -667,7 +667,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
(row) => {
const aggrResult = aggregateFn.call(this, entity2, {
data: subProjection,
filter: combineFilters<ED, T>([{
filter: combineFilters<ED, keyof ED>(entity2, this.getSchema(), [{
[foreignKey]: row.id,
}, subFilter]),
sorter: subSorter,
@ -730,7 +730,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
if (ids.length > 0) {
const subRows = cascadeSelectFn.call(this, entity2, {
data: subProjection,
filter: combineFilters<ED, T>([{
filter: combineFilters<ED, keyof ED>(entity2, this.getSchema(), [{
[foreignKey]: {
$in: ids,
}
@ -760,7 +760,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
(row) => {
const aggrResult = aggregateFn.call(this, entity2, {
data: subProjection,
filter: combineFilters<ED, T>([{
filter: combineFilters<ED, keyof ED>(entity2, this.getSchema(), [{
entity,
entityId: row.id,
}, subFilter]),
@ -823,7 +823,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
if (ids.length > 0) {
const subRows = cascadeSelectFn.call(this, entity2, {
data: subProjection,
filter: combineFilters<ED, T>([{
filter: combineFilters<ED, keyof ED>(entity2, this.getSchema(), [{
entity,
entityId: {
$in: ids,
@ -944,9 +944,9 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
else {
// A中data的entityId作为B中filter的主键
Object.assign(operationMto, {
filter: addFilterSegment({
filter: combineFilters(attr, this.getSchema(), [{
id: fkId,
}), filterMto,
}, filterMto]),
});
}
}
@ -959,24 +959,24 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
}
else if (filter!.entity === attr && filter!.entityId) {
Object.assign(operationMto, {
filter: addFilterSegment({
filter: combineFilters(attr, this.getSchema(), [{
id: filter!.entityId,
}, filterMto),
}, filterMto]),
});
}
else if (filter![attr]) {
Object.assign(operationMto, {
filter: addFilterSegment(filter![attr], filterMto),
filter: combineFilters(attr, this.getSchema(), [filter![attr], filterMto]),
});
}
else {
// A中data的entityId作为B中filter的主键
Object.assign(operationMto, {
filter: addFilterSegment({
filter: combineFilters(attr, this.getSchema(), [{
[`${entity as string}$entity`]: {
filter,
}
}, filterMto),
}, filterMto]),
});
}
}
@ -1005,9 +1005,9 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
else {
// A中data的entityId作为B中filter的主键
Object.assign(operationMto, {
filter: addFilterSegment(filterMto || {}, {
filter: combineFilters(relation, this.getSchema(), [filterMto, {
id: fkId,
}),
}]),
});
}
}
@ -1019,22 +1019,22 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
}
else if (filter![`${attr}Id`]) {
Object.assign(operationMto, {
filter: addFilterSegment(filterMto || {}, {
filter: combineFilters(relation, this.getSchema(), [filterMto, {
id: filter![`${attr}Id`],
}),
}]),
});
}
else if (filter![attr]) {
Object.assign(operationMto, {
filter: addFilterSegment(filterMto || {}, filter![attr]),
filter: combineFilters(relation, this.getSchema(), [filterMto, filter![attr]]),
});
}
else {
// A中data的attrId作为B中filter的主键
Object.assign(operationMto, {
filter: addFilterSegment(filterMto || {}, {
filter: combineFilters(relation, this.getSchema(), [filterMto, {
[`${entity as string}$${attr}`]: filter
}),
}]),
});
}
}
@ -1093,28 +1093,28 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
if (filter) {
if (filter.id && Object.keys(filter).length === 1) {
Object.assign(otm, {
filter: addFilterSegment({
filter: combineFilters(entityOtm, this.getSchema(), [{
entity,
entityId: filter.id,
}, filterOtm),
}, filterOtm]),
});
}
else {
Object.assign(otm, {
filter: addFilterSegment({
filter: combineFilters(entityOtm, this.getSchema(), [{
[entity]: filter,
}, filterOtm),
}, filterOtm]),
});
}
}
else {
Object.assign(otm, {
filter: addFilterSegment({
filter: combineFilters(entityOtm, this.getSchema(), [{
entity,
entityId: {
$exists: true,
}
}, filterOtm)
}, filterOtm])
});
}
@ -1166,26 +1166,26 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
if (filter) {
if (filter.id && Object.keys(filter).length === 1) {
Object.assign(otm, {
filter: addFilterSegment({
filter: combineFilters(entityOtm, this.getSchema(), [{
[foreignKey]: filter.id,
}, filterOtm),
}, filterOtm]),
});
}
else {
Object.assign(otm, {
filter: addFilterSegment({
filter: combineFilters(entityOtm, this.getSchema(), [{
[foreignKey.slice(0, foreignKey.length - 2)]: filter,
}, filterOtm),
}, filterOtm]),
});
}
}
else {
Object.assign(otm, {
filter: addFilterSegment({
filter: combineFilters(entityOtm, this.getSchema(), [{
[foreignKey]: {
$exists: true,
},
}, filterOtm),
}, filterOtm]),
});
}

View File

@ -3,12 +3,13 @@ import { EntityDict } from "../base-app-domain";
import { OakException, OakUniqueViolationException, OakUnloggedInException, OakUserUnpermittedException, StorageSchema } from "../types";
import { AuthCascadePath, EntityDict as BaseEntityDict, AuthDeduceRelationMap } from "../types/Entity";
import { AsyncContext } from "./AsyncRowStore";
import { addFilterSegment, checkFilterContains, combineFilters } from "./filter";
import { checkFilterContains, combineFilters } from "./filter";
import { judgeRelation } from "./relation";
import { SyncContext } from "./SyncRowStore";
import { readOnlyActions } from '../actions/action';
import { difference, intersection, set, uniq, cloneDeep, groupBy } from '../utils/lodash';
import { SYSTEM_RESERVE_ENTITIES } from "../compiler/env";
import path from "path";
type OperationTree<ED extends EntityDict & BaseEntityDict> = {
@ -371,7 +372,7 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
hasOneToMany = true;
destructInner(e, {
data: data[attr].data,
filter: combineFilters([{
filter: combineFilters(e, this.schema, [{
[foreignKey.slice(0, foreignKey.length - 2)]: filter,
}, data[attr].filter || {}]),
} as any);
@ -381,7 +382,7 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
hasOneToMany = true;
destructInner(e, {
data: data[attr].data,
filter: combineFilters([{
filter: combineFilters(e, this.schema, [{
[entity2]: filter,
}, data[attr].filter || {}]),
} as any);
@ -541,48 +542,47 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
}
/**
* actionAuthentity是不是满足当前的查询约束
* actionAuthactionAuth加以判断actionAuth
* @param entity
* @param filter
* @param actionAuths
* @param context
* @return string代表用户获得授权的relationIduserId赋权false代表失败
*/
private checkSingleOperation<T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>>(
private filterActionAuths<T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>>(
entity: T,
filter: ED[T]['Selection']['filter'],
actionAuths: ED['actionAuth']['Schema'][],
context: Cxt,
actions: ED[T]['Action'][],
) {
const legalAuths = actionAuths.filter(
ele => ele.destEntity === entity && intersection(ele.deActions, actions).length > 0 // 这里只要overlap就可以了
);
return legalAuths.map(
return actionAuths.map(
(ele) => {
const { path, relation, relationId } = ele;
const { paths, relation, relationId } = ele;
if (relationId) {
assert(relation);
const { userRelation$relation: userRelations } = relation;
if (userRelations!.length > 0) {
const entityIds = uniq(userRelations!.map(ele => ele.entityId));
const contained = {};
const idFilter = entityIds.length > 1 ? {
$in: entityIds,
} : entityIds[0];
assert(idFilter);
if (path) {
set(contained, path, {
id: idFilter,
});
}
else {
Object.assign(contained, {
id: idFilter
});
}
const pathFilters = paths.map(
(path) => {
if (path) {
return set({}, path, {
id: idFilter,
});
}
return {
id: idFilter,
};
}
);
// 这里是或关系,只要对象落在任意一条路径上就可以
const contained = combineFilters(entity, context.getSchema(), pathFilters, true);
const contains = checkFilterContains(entity, context, contained, filter, true)
if (contains instanceof Promise) {
return contains.then(
@ -624,6 +624,58 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
);
}
/**
* $or的查询actionAuth无法满足
* @param entity
* @param filter
* @param actionAuths
* @param context
* @param actions
*/
private checkActionAuthInGroup<T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>>(
entity: T,
filter: ED[T]['Selection']['filter'],
actionAuths: ED['actionAuth']['Schema'][],
context: Cxt,
) {
const filters = actionAuths.map(
(ele) => {
const { paths, relation, relationId } = ele;
if (relationId) {
assert(relation);
const { userRelation$relation: userRelations } = relation;
assert(userRelations!.length > 0);
const entityIds = uniq(userRelations!.map(ele => ele.entityId));
const idFilter = entityIds.length > 1 ? {
$in: entityIds,
} : entityIds[0];
assert(idFilter);
const pathFilters = paths.map(
(path) => {
if (path) {
return set({}, path, {
id: idFilter,
});
}
return {
id: idFilter
};
}
)
return pathFilters;
}
// 说明是通过userId关联
const contained = {};
set(contained, `${path}.id`, context.getCurrentUserId());
return contained;
}
);
const groupFilter = combineFilters(entity, this.schema, filters.flat(), true);
return checkFilterContains(entity, context, groupFilter, filter, true);
}
private checkSelection<T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>>(
entity: T,
@ -675,7 +727,7 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
const actionAuths = context.select('actionAuth', {
data: {
id: 1,
path: 1,
paths: 1,
destEntity: 1,
deActions: 1,
relation: {
@ -699,43 +751,49 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
},
destEntity: {
$in: allEntities as string[],
}
},
$or: [
{
relation: {
userRelation$relation: {
userId: context.getCurrentUserId(),
},
}
},
{
relationId: {
$exists: false,
},
}
]
}
}, { dontCollect: true });
/**
* leafNodeleafNode上的deduceactionAuth
* leafNodeleafNode上的deduce
* @param result
* @returns
*/
const checkResult = (result: (ED['actionAuth']['Schema'] | undefined)[][][]) => {
const r = !result.find(
(ele) => {
const eleFlated = ele.flat();
return !eleFlated.find(
ele2 => !!ele2
);
}
);
if (!r && process.env.NODE_ENV === 'development') {
dlSelections.forEach(
(ele, idx) => {
const r2 = result[idx].flat();
if (!r2.find(ele2 => !!ele)) {
console.warn('对象的select权限被否决请检查', ele);
}
const checkResult = (result: boolean[][]) => {
let idx = 0;
for (const r1 of result) {
const r2 = r1.find(ele => ele === true);
if (!r2) {
if (process.env.NODE_ENV === 'development') {
console.warn('对象的select权限被否决请检查', dlSelections[idx]);
}
)
return false;
}
}
return r;
}
return true;
};
if (actionAuths instanceof Promise) {
assert(context instanceof AsyncContext);
return actionAuths.then(
(aas) => Promise.all(dlSelections.map(
(ele) => Promise.all(ele.map(
(ele2) => Promise.all(this.checkSingleOperation(ele2.entity, ele2.filter, aas as ED['actionAuth']['Schema'][], context, ['select']))
(ele2) => this.checkActionAuthInGroup(ele2.entity, ele2.filter, aas as ED['actionAuth']['Schema'][], context)
))
)).then(
(result) => checkResult(result)
@ -745,7 +803,7 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
return checkResult(
dlSelections.map(
ele => ele.map(
ele2 => (this.checkSingleOperation(ele2.entity, ele2.filter, actionAuths as ED['actionAuth']['Schema'][], context, ['select']) as (ED['actionAuth']['Schema'] | undefined)[])
ele2 => (this.checkActionAuthInGroup(ele2.entity, ele2.filter, actionAuths as ED['actionAuth']['Schema'][], context) as boolean)
)
)
);
@ -780,12 +838,13 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
actions: ED[keyof ED]['Action'][];
}[]) => {
const allEntities: (keyof ED)[] = deducedEntityFilters.map(ele => ele.entity);
const allActions = uniq(deducedEntityFilters.map(ele => ele.actions).flat());
// todo 这里其实可以在查询条件里通过userRelation过滤一次但问题不大
const actionAuths = context.select('actionAuth', {
data: {
id: 1,
path: 1,
paths: 1,
destEntity: 1,
deActions: 1,
relation: {
@ -806,7 +865,24 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
filter: {
destEntity: {
$in: allEntities as string[],
}
},
deActions: {
$overlaps: allActions,
},
$or: [
{
relation: {
userRelation$relation: {
userId: context.getCurrentUserId(),
},
}
},
{
relationId: {
$exists: false,
},
}
]
}
}, { dontCollect: true });
@ -834,13 +910,15 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
if (ars.length > 0) {
// 这里能找到actionAuth其必然是本对象上的授权
assert(!ars.find(
ele => ele.path !== '' || ele.destEntity !== entity
));
// 下面这个看不懂,跑到了再说
// assert(!ars.find(
// ele => ele.path !== '' || ele.destEntity !== entity
// ));
console.log('跑到你要看的代码了');
return ars;
}
}
}
};
if (actionAuths instanceof Promise) {
return actionAuths.then(
@ -851,15 +929,20 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
}
return Promise.all(
deducedEntityFilters.map(
ele => Promise.all(
this.checkSingleOperation(
ele.entity,
ele.filter,
ars as ED['actionAuth']['Schema'][],
context,
ele.actions
)
)
ele => {
const ars2 = ars.filter(
ele2 => ele2.destEntity === ele.entity && intersection(ele2.deActions, ele.actions).length > 0 // 这里只要overlap就可以了
);
return Promise.all(
this.filterActionAuths(
ele.entity,
ele.filter,
ars2 as ED['actionAuth']['Schema'][],
context,
ele.actions
)
);
}
)
).then(
(result) => getActionAuths(result)
@ -875,13 +958,18 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
}
return getActionAuths(
deducedEntityFilters.map(
ele => (this.checkSingleOperation(
ele.entity,
ele.filter,
actionAuths as ED['actionAuth']['Schema'][],
context,
ele.actions
) as (ED['actionAuth']['Schema'] | undefined)[])
ele => {
const ars2 = actionAuths.filter(
ele2 => ele2.destEntity === ele.entity && intersection(ele2.deActions, ele.actions).length > 0 // 这里只要overlap就可以了
);
return this.filterActionAuths(
ele.entity,
ele.filter,
ars2 as ED['actionAuth']['Schema'][],
context,
ele.actions
) as (ED['actionAuth']['Schema'] | undefined)[];
}
)
);
};
@ -948,14 +1036,18 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
const childActions = child.map(ele => ele.action);
const childLegalAuths = selfLegalPaths.map(
(ele) => {
const { path, relationId } = ele;
const path2 = path ? `${pathToParent}.${path}` : pathToParent;
const { paths, relationId } = ele;
const paths2 = paths.map(
(path) => path ? `${pathToParent}.${path}` : pathToParent
);
return context.select('actionAuth', {
data: {
id: 1,
},
filter: {
path: path2,
paths: {
$overlaps: paths2,
},
destEntity: childEntity as string,
deActions: {
$overlaps: childActions,
@ -981,14 +1073,18 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
const childLegalAuths = realLegalPaths.map(
(ele) => {
const { path, relationId } = ele;
const path2 = path ? `${pathToParent}.${path}` : pathToParent;
const { paths, relationId } = ele;
const paths2 = paths.map(
(path) => path ? `${pathToParent}.${path}` : pathToParent
);
return context.select('actionAuth', {
data: {
id: 1,
},
filter: {
path: path2,
paths: {
$overlaps: paths2,
},
destEntity: childEntity as string,
deActions: {
$overlaps: child.action,
@ -1142,6 +1238,7 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict>{
* entity进行actions操作权限的userRelation关系
* @param params
* @param context
* todo paths改成复数以后这里还未充分测试过
*/
export async function getUserRelationsByActions<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(params: {
entity: T;
@ -1171,7 +1268,7 @@ export async function getUserRelationsByActions<ED extends EntityDict & BaseEnti
const actionAuths = await context.select('actionAuth', {
data: {
id: 1,
path: 1,
paths: 1,
relationId: 1,
relation: {
id: 1,
@ -1261,16 +1358,26 @@ export async function getUserRelationsByActions<ED extends EntityDict & BaseEnti
};
// 相同的path可以groupBy掉
const urAuthDict = groupBy(urAuths, 'path');
const urAuthGroups = Object.keys(urAuthDict).map(
ele => ({
path: ele,
relationIds: urAuthDict[ele].map(ele => ele.relationId!)
})
);
const urAuthDict2: Record<string, string[]> = {};
urAuths.forEach(
(auth) => {
const { paths, relationId } = auth;
paths!.forEach(
(path) => {
if (!urAuthDict2[path]) {
urAuthDict2[path] = [relationId!];
}
else if (!urAuthDict2[path].includes(relationId!)) {
urAuthDict2[path].push(relationId!);
}
}
)
}
)
const userRelations = await Promise.all(urAuthGroups.map(
async ({ path, relationIds }) => {
const userRelations = await Promise.all(Object.keys(urAuthDict2).map(
async (path) => {
const relationIds = urAuthDict2[path];
const { projection, getData } = makeRelationIterator(path, relationIds);
const rows = await context.select(entity, {
data: projection,
@ -1374,19 +1481,23 @@ export async function getUserRelationsByActions<ED extends EntityDict & BaseEnti
};
const userEntities = await Promise.all(
directAuths.map(
async ({ path }) => {
const { getData, projection } = makeRelationIterator(path!);
async ({ paths }) => {
return paths!.map(
async (path) => {
const { getData, projection } = makeRelationIterator(path!);
const rows = await context.select(entity, {
data: projection,
filter,
}, { dontCollect: true });
const userEntities = rows.map(ele => getData(ele)).flat().filter(ele => !!ele);
return userEntities as {
entity: keyof ED,
entityId: string,
userId: string,
}[];
const rows = await context.select(entity, {
data: projection,
filter,
}, { dontCollect: true });
const userEntities = rows.map(ele => getData(ele)).flat().filter(ele => !!ele);
return userEntities as {
entity: keyof ED,
entityId: string,
userId: string,
}[];
}
);
}
)
);

View File

@ -1,6 +1,6 @@
import assert from 'assert';
import { pull, unset } from "../utils/lodash";
import { addFilterSegment, checkFilterRepel } from "../store/filter";
import { checkFilterRepel, combineFilters } from "../store/filter";
import { EntityDict, OperateOption, SelectOption, TriggerDataAttribute, TriggerTimestampAttribute } from "../types/Entity";
import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { Logger } from "../types/Logger";
@ -173,7 +173,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
default: {
const { filter } = operation;
// 此时要保证更新或者删除的行上没有跨事务约束
const filter2 = addFilterSegment({
const filter2 = combineFilters(entity, context.getSchema(), [{
$or: [
{
$$triggerData$$: {
@ -186,7 +186,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
},
}
],
}, filter);
}, filter]);
const count = await context.count(entity, {
filter: filter2
} as Omit<ED[T]['Selection'], 'action' | 'sorter' | 'data'>, {});

View File

@ -2,7 +2,7 @@ import { ActionDictOfEntityDict, BBWatcher, Checker, EntityDict, StorageSchema,
import { SyncContext } from "./SyncRowStore";
import { AsyncContext } from "./AsyncRowStore";
import { uniqBy, pick, intersection } from '../utils/lodash';
import { addFilterSegment } from "./filter";
import { combineFilters } from "./filter";
import { createDynamicCheckers } from '../checkers';
import { createDynamicTriggers } from '../triggers';
import { EntityDict as BaseEntityDict } from '../base-app-domain/EntityDict';
@ -118,7 +118,7 @@ function checkUnique<ED extends EntityDict& BaseEntityDict, Cxt extends SyncCont
// 说明有null值不需要检查约束
return;
}
const filter2 = extraFilter ? addFilterSegment(filter, extraFilter) : filter;
const filter2 = extraFilter ? combineFilters(entity, context.getSchema(), [filter, extraFilter]) : filter;
const count = context.count(entity, { filter: filter2 }, { dontCollect: true });
return checkCountLessThan(count, uniqAttrs, 0, row.id)
}
@ -250,7 +250,7 @@ export function makeIntrinsicCTWs<ED extends EntityDict & BaseEntityDict, Cxt ex
// 在这些行以外的行不和更新后的键值冲突
const count = context.count(entity, {
filter: addFilterSegment([filter, {
filter: combineFilters(entity, context.getSchema(), [filter, {
$not: operationFilter,
}]),
}, { dontCollect: true });

View File

@ -1,5 +1,5 @@
import assert from 'assert';
import { addFilterSegment, checkFilterContains, combineFilters } from "../store/filter";
import { checkFilterContains, combineFilters } from "../store/filter";
import { OakAttrNotNullException, OakInputIllegalException, OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
import {
AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn,
@ -50,7 +50,7 @@ export function translateCheckerInAsyncContext<
const { filter: operationFilter, action } = operation;
const filter2 = typeof filter === 'function' ? await (filter as Function)(operation, context, option) : filter;
if (['select', 'count', 'stat'].includes(action)) {
operation.filter = addFilterSegment(operationFilter || {}, filter2);
operation.filter = combineFilters(entity, context.getSchema(), [operationFilter, filter2]);
return 0;
}
else {
@ -108,7 +108,7 @@ export function translateCheckerInAsyncContext<
return 0;
}
if (['select', 'count', 'stat'].includes(action)) {
operation.filter = addFilterSegment(filter || {}, result);
operation.filter = combineFilters(entity, context.getSchema(), [filter, result]);
return 0;
}
assert(filter);

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,7 @@ export async function vaccumOper<ED extends EntityDict & BaseEntityDict, Cxt ext
entity: 'operEntity',
aliveLine: aliveLine + 10000,
filter: {
oper: combineFilters([operFilter, {
oper: combineFilters('operEntity', context.getSchema(), [operFilter, {
$$createAt$$: {
$lt: aliveLine,
}

View File

@ -35,7 +35,7 @@ export async function vaccumEntities<ED extends EntityDict & BaseEntityDict, Cxt
},
};
if (filter) {
filter2 = combineFilters([filter2, filter]);
filter2 = combineFilters(entity, context.getSchema(), [filter2, filter]);
}
if (backupDir && process.env.OAK_PLATFORM === 'server') {
// 使用mysqldump将待删除的数据备份出来
@ -65,7 +65,7 @@ export async function vaccumEntities<ED extends EntityDict & BaseEntityDict, Cxt
let count = 0;
const appendData = async (minCreateAt: number): Promise<void> => {
const filter3 = combineFilters([filter2, {
const filter3 = combineFilters(entity, context.getSchema(), [filter2, {
$$createAt$$: {
$gt: minCreateAt,
},

View File

@ -2,7 +2,7 @@ import assert from 'assert';
import { judgeValueRelation, contains, repel } from '../src/store/filter';
import { storageSchema } from '../src/base-app-domain';
/*
assert(judgeValueRelation({ $gte: 2 }, { $gt: 2 }, false) === false);
assert(judgeValueRelation({ $gte: 'ddddd'}, { $lte: 'dddde'}, false) === false);
assert(judgeValueRelation({ $eq: 2}, {$lt: 1}, true) === false);
@ -14,197 +14,210 @@ assert(judgeValueRelation({$in: [2, 3, 4]}, { $in: [ 2, 3, 4, 5]}, true) === tru
assert(judgeValueRelation({ $in: [2, 3, 4]}, {$exists: true}, true) === true);
assert(judgeValueRelation({ $in: [2, 3, 4]}, {$exists: false}, true) === false);
assert(judgeValueRelation({ $in: [2, 3, 4]}, {$exists: false}, false) === true);
assert(judgeValueRelation({ $nin: [2, 3, 4]}, {$exists: true}, true) === false);
assert(judgeValueRelation({ $nin: [2, 3, 4]}, {$exists: false}, true) === false);
assert(judgeValueRelation({ $nin: [2, 3, 4]}, {$exists: true}, true) === undefined);
assert(judgeValueRelation({ $nin: [2, 3, 4]}, {$exists: false}, true) === undefined);
assert(judgeValueRelation({ $nin: [2, 3, 4]}, {$exists: false}, false) === false);
assert(judgeValueRelation({ $startsWith: 'xuchang'}, {$startsWith: 'xuc'}, true) === true);
assert(judgeValueRelation({ $startsWith: 'xuchang'}, {$startsWith: 'xuchang2'}, true) === false);
assert(judgeValueRelation({ $startsWith: 'xuchang'}, {$startsWith: 'xuchang2'}, true) === undefined);
assert(judgeValueRelation({ $startsWith: 'xuchang'}, {$startsWith: 'xu2c'}, false) === true);
assert(contains('operEntity', storageSchema, {
entity: 'ddd',
entityId: 'abc'
id: 'ddd',
$$seq$$: 'abc'
}, {
entity: 'ddd',
id: 'ddd',
}) === true);
assert(contains('modi', storageSchema, {
entity: 'ddd',
entityId: 'abc'
id: 'ddd',
$$seq$$: 'abc'
}, {
entity: {
id: {
$startsWith: 'dd',
},
entityId: 'abc',
$$seq$$: 'abc',
}) === true);
assert(contains('modi', storageSchema, {
entity: 'ddd',
entityId: 'abc'
assert(typeof contains('modi', storageSchema, {
id: 'ddd',
$$seq$$: 'abc'
}, {
entity: {
id: {
$startsWith: 'dd',
},
entityId: 'abc',
$$seq$$: 'abc',
action: 'create',
}) === false);
}) === 'object');
assert(repel('modi', storageSchema, {
entity: 'ddd',
entityId: 'abc'
assert(typeof repel('modi', storageSchema, {
id: 'ddd',
$$seq$$: 'abc'
}, {
entity: {
id: {
$startsWith: 'dd',
},
entityId: 'abc',
$$seq$$: 'abc',
action: 'create',
}) === false);
}) === 'object');
assert(repel('modi', storageSchema, {
entity: 'ddd',
entityId: 'abc'
id: 'ddd',
$$seq$$: 'abc'
}, {
entity: {
id: {
$startsWith: 'zzz',
},
entityId: 'abc',
$$seq$$: 'abc',
action: 'create',
}) === true);
assert(repel('modi', storageSchema, {
entity: 'ddd',
entityId: 'abc'
id: 'ddd',
$$seq$$: 'abc'
}, {
$not: {
entity: 'ddd',
id: 'ddd',
}
}) === true);
assert(repel('modi', storageSchema, {
let rrr = repel('modi', storageSchema, {
$not: {
entity: 'ddd',
entityId: 'abc',
id: 'ddd',
$$seq$$: 'abc',
},
}, {
$not: {
entity: 'ddd',
id: 'ddd',
}
}) === false);
});
assert(typeof rrr === 'object');
assert(contains('modi', storageSchema, {
rrr = contains('modi', storageSchema, {
$not: {
entity: 'ddd',
entityId: 'abc',
id: 'ddd',
$$seq$$: 'abc',
},
}, {
$not: {
entity: 'ddd',
id: 'ddd',
}
}) === true);
});
assert(typeof rrr === 'object');
assert(contains('modi', storageSchema, {
rrr = contains('modi', storageSchema, {
$not: {
entity: 'ddd',
entityId: 'abc',
id: 'ddd',
$$seq$$: 'abc',
},
}, {
$not: {
entity: 'ddd',
entityId: 'ccc',
id: 'ddd',
$$seq$$: 'ccc',
}
}) === true);
});
assert(typeof rrr === 'object');
assert(repel('modi', storageSchema, {
rrr = contains('modi', storageSchema, {
$not: {
entity: 'ddd',
entityId: 'abc',
id: 'ddd',
},
}, {
entity: 'ddd',
}) === true);
$not: {
id: 'ddd',
$$seq$$: 'abc',
}
});
assert(rrr === true);
assert(repel('modi', storageSchema, {
entityId: '888'
rrr = repel('modi', storageSchema, {
$not: {
id: 'ddd',
$$seq$$: 'abc',
},
}, {
entity: 'ddd',
}) === false);
id: 'ddd',
});
assert(typeof rrr === 'object');
assert(contains('modi', storageSchema, {
entityId: '888'
rrr = repel('modi', storageSchema, {
$$seq$$: '888'
}, {
entity: 'ddd',
}) === false);
*/
id: 'ddd',
});
assert(typeof rrr === 'object');
rrr =contains('modi', storageSchema, {
$$seq$$: '888'
}, {
id: 'ddd',
});
assert(typeof rrr === 'object');
// 有id的情况应当构造出查询条件给上层肉眼看下结果先
let r = contains('modi', storageSchema, {
id: 'bbccdd',
}, {
entity: 'dddd',
id: 'dddd',
});
// {"$and":[{"entity":"modi","filter":{"id":"bbccdd","$not":{"entity":"dddd"}}}]}
console.log(1, JSON.stringify(r));
assert(r === false);
r = repel('modiEntity', storageSchema, {
modiId: 'cdcd',
}, {
modi: {
entity: 'dddd',
id: 'dddd',
}
});
// {"$or":[{"$or":[{"$or":[{"entity":"modi","filter":{"id":"cdcd","entity":"dddd"}}]}]}]}
console.log(2, JSON.stringify(r));
assert(r === true);
r = contains('modiEntity', storageSchema, {
modi: {
id: 'cdcde',
entity: 'dddd',
$$seq$$: 'dddd',
action: 'create',
}
}, {
modiId: 'cdcd',
});
// {"$and":[{"$or":[{"$and":[{"entity":"modi","filter":{"id":"cdcde","entity":"dddd","action":"create","$not":{"id":"cdcd"}}}]}]}]}
console.log(3, JSON.stringify(r));
assert(r === false);
r = contains('modiEntity', storageSchema, {
modi: {
id: 'cdcde',
entity: 'dddd',
$$seq$$: 'dddd',
action: 'create',
},
$$seq$$: 'xc',
}, {
modiId: 'cdcd',
modiId: 'cdcde',
$$seq$$: {
$startsWith: 'xcc',
},
});
// false。如果把$startsWith: 'xcc'改成$startsWith: 'x',则和上面的返回结果一样
console.log(4, JSON.stringify(r));
assert(r === false);
r = contains('modiEntity', storageSchema, {
entity: 'user',
entityId: 'xc',
id: 'user',
$$seq$$: 'xc',
}, {
user: {
name: 'xcxc',
}
});
// {"$and":[{"$or":[{"$and":[{"entity":"user","filter":{"id":"xc","$not":{"name":"xcxc"}}}]}]}]}
// {"$and":[{"$or":[{"$and":[{"id":"user","filter":{"id":"xc","$not":{"name":"xcxc"}}}]}]}]}
console.log(5, JSON.stringify(r));
r = contains('modiEntity', storageSchema, {
entity: 'user',
entityId: 'xc',
id: 'user',
$$seq$$: 'xc',
modi: {
id: 'cdcde',
entity: 'dddd',
$$seq$$: 'dddd',
action: 'create',
},
}, {
@ -213,6 +226,23 @@ r = contains('modiEntity', storageSchema, {
},
modiId: 'cdcd',
});
// {"$and":[{"$or":[{"$and":[{"entity":"user","filter":{"id":"xc","$not":{"name":"xcxc"}}}]}]},{"$or":[{"$and":[{"entity":"modi","filter":{"id":"cdcde","entity":"dddd","action":"create","$not":{"id":"cdcd"}}}]}]}]}
console.log(6, JSON.stringify(r));
assert(r === false);
r = contains('modiEntity', storageSchema, {
id: 'user',
$$seq$$: 'xc',
modi: {
id: 'cdcd',
$$seq$$: 'dddd',
action: 'create',
},
}, {
user: {
name: 'xcxc',
},
modiId: 'cdcd',
});
// 这个查询应当可以过滤掉modiId: 'cdcd'的条件
console.log(8, r);

81
test/testFilter2.ts Normal file
View File

@ -0,0 +1,81 @@
import assert from 'assert';
import { combineFilters } from '../src/store/filter';
import { storageSchema, EntityDict } from '../src/base-app-domain';
let r = combineFilters<EntityDict, 'user'>('user', storageSchema, [
{
ref: {
nickname: 'xc',
},
},
{
ref: {
name: 'xc2',
}
}
]);
console.log(JSON.stringify(r));
r = combineFilters<EntityDict, 'user'>('user', storageSchema, [
{
ref: {
nickname: 'xc',
},
},
{
ref: {
name: 'xc2',
}
}
], true);
console.log(JSON.stringify(r));
r = combineFilters<EntityDict, 'user'>('user', storageSchema, [
{
$or: [
{
password: '1234',
},
{
ref: {
nickname: 'xc',
},
}
]
},
{
ref: {
name: 'xc2',
}
}
], true);
console.log(JSON.stringify(r));
r = combineFilters<EntityDict, 'user'>('user', storageSchema, [
{
$or: [
{
password: '1234',
},
{
ref: {
nickname: 'xc',
},
}
]
},
{
$or: [
{
ref: {
name: 'xc2',
}
},
{
password: 'dddd',
}
]
}
], true);
console.log(JSON.stringify(r));