重构了actionAuth和relation判定的数据结构和逻辑
This commit is contained in:
parent
8282f37cd8
commit
8bc0f3af4e
|
|
@ -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;
|
||||
} | {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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') {
|
||||
|
|
|
|||
|
|
@ -38,14 +38,23 @@ export declare class RelationAuth<ED extends EntityDict & BaseEntityDict> {
|
|||
*/
|
||||
private destructOperation;
|
||||
/**
|
||||
* 定位到了当前用户所有可能的actionAuth,再用以判定对应的entity是不是满足当前的查询约束
|
||||
* 定位到了当前用户所有可能的actionAuth,对单条actionAuth加以判断,找到可以满足当前操作的actionAuth
|
||||
* @param entity
|
||||
* @param filter
|
||||
* @param actionAuths
|
||||
* @param context
|
||||
* @return string代表用户获得授权的relationId,空字符串代表通过userId赋权,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;
|
||||
}[];
|
||||
}[]>[];
|
||||
}>;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
});
|
||||
}); })];
|
||||
});
|
||||
});
|
||||
}))];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}, {})];
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
|
|
|||
1274
lib/store/filter.js
1274
lib/store/filter.js
File diff suppressed because it is too large
Load Diff
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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/**/*",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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]),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>{
|
|||
}
|
||||
|
||||
/**
|
||||
* 定位到了当前用户所有可能的actionAuth,再用以判定对应的entity是不是满足当前的查询约束
|
||||
* 定位到了当前用户所有可能的actionAuth,对单条actionAuth加以判断,找到可以满足当前操作的actionAuth
|
||||
* @param entity
|
||||
* @param filter
|
||||
* @param actionAuths
|
||||
* @param context
|
||||
* @return string代表用户获得授权的relationId,空字符串代表通过userId赋权,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 });
|
||||
|
||||
/**
|
||||
* 返回的结果中,第一层为leafNode,必须全通过,第二层为单个leafNode上的deduce,通过一个就可以,第三层为所有可能的actionAuth,通过一个就可以
|
||||
* 返回的结果中,第一层为leafNode,必须全通过,第二层为单个leafNode上的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,
|
||||
}[];
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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'>, {});
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
1253
src/store/filter.ts
1253
src/store/filter.ts
File diff suppressed because it is too large
Load Diff
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
Loading…
Reference in New Issue