更新了localebuilder中出错处理,在判定attrUpdateMatrix时,增加了对cascade更新条件的判定

This commit is contained in:
Xu Chang 2024-04-11 18:57:31 +08:00
parent 8f433f84bf
commit 68289a281e
7 changed files with 332 additions and 28 deletions

View File

@ -133,9 +133,14 @@ class LocaleBuilder {
this.locales[ns] = [module, position.replace(/\\/g, '/'), language, data];
if (watch) {
fs_1.default.watch(filepath, () => {
const data = this.readLocaleFileContent(filepath);
this.locales[ns] = [module, position.replace(/\\/g, '/'), language, data];
this.outputDataFile();
try {
const data = this.readLocaleFileContent(filepath);
this.locales[ns] = [module, position.replace(/\\/g, '/'), language, data];
this.outputDataFile();
}
catch (err) {
// 啥都不干
}
});
}
}

View File

@ -1,12 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeIntrinsicCheckers = void 0;
const tslib_1 = require("tslib");
const types_1 = require("../types");
const lodash_1 = require("../utils/lodash");
const filter_1 = require("./filter");
const modi_1 = require("./modi");
const checker_1 = require("./checker");
const action_1 = require("../actions/action");
const assert_1 = tslib_1.__importDefault(require("assert"));
function checkUniqueBetweenRows(rows, uniqAttrs) {
// 先检查这些行本身之间有无unique冲突
const dict = {};
@ -241,6 +243,60 @@ function createActionTransformerCheckers(actionDefDict) {
}
return checkers;
}
/**
* 检查一次更新是否有关联通过的可能
* 例如更新A的条件是B = 1此时行上的B并不等于1但由于更新数据是 { B: 1, A: .. }
* 此时如果B更新可以成功则A也可以成功
* @param entity
* @param data
* @param filters
* @param context
*/
function cascadelyCheckUpdateFilters(entity, schema, data, filter, matrix, restAttrs, context) {
const successAttrs = (0, lodash_1.difference)(Object.keys(data), restAttrs);
const successAttrFilter = (0, lodash_1.pick)(data, successAttrs);
/**
* 先找到能直接更新成功的属性
*/
const legalAttrResult = restAttrs.map((attr) => {
const { filter: f } = matrix[attr];
if (!f) {
return true;
}
// 此时看应用了success的attributes更新后能否消除掉f中的部分条件
const result = (0, filter_1.analyzeFilterRelation)(entity, schema, successAttrFilter, f, true);
if (typeof result === 'boolean') {
return result;
}
const { sureAttributes } = result;
const f2 = (0, lodash_1.omit)(f, sureAttributes);
return (0, filter_1.checkFilterContains)(entity, context, f2, filter, true);
});
const checkResult1 = (lar) => {
const legalAttrs = [];
const illegalAttrs = [];
(0, assert_1.default)(lar.length === restAttrs.length);
lar.forEach((ele, idx) => {
if (ele) {
legalAttrs.push(restAttrs[idx]);
}
else {
illegalAttrs.push(restAttrs[idx]);
}
});
if (illegalAttrs.length === 0) {
return;
}
if (legalAttrs.length === 0) {
throw new types_1.OakAttrCantUpdateException(entity, illegalAttrs, '更新的行当前属性不满足约束,请仔细检查数据');
}
return cascadelyCheckUpdateFilters(entity, schema, data, filter, matrix, illegalAttrs, context);
};
if (legalAttrResult.find(ele => ele instanceof Promise)) {
return Promise.all(legalAttrResult).then((lar) => checkResult1(lar));
}
return checkResult1(legalAttrResult);
}
function createAttrUpdateCheckers(schema, attrUpdateMatrix) {
const checkers = [];
for (const entity in attrUpdateMatrix) {
@ -270,19 +326,27 @@ function createAttrUpdateCheckers(schema, attrUpdateMatrix) {
if (!a.includes(action)) {
// 找到不满足的那个attr
const attrsIllegal = attrs.filter((attr) => matrix[attr]?.actions && !matrix[attr]?.actions?.includes(action));
throw new types_1.OakAttrCantUpdateException(entity, attrsIllegal, `${attrsIllegal}不允许被${action}动作更新`);
throw new types_1.OakAttrCantUpdateException(entity, attrsIllegal, `${attrsIllegal.join(',')}不允许被${action}动作更新`);
}
}
if (f) {
const rr = (0, filter_1.contains)(entity, context.getSchema(), data, f);
console.log(rr);
const result = (0, filter_1.checkFilterContains)(entity, context, f, filter, true);
if (result instanceof Promise) {
return result.then((v) => {
if (!v) {
if (attrs.length > 1) {
return cascadelyCheckUpdateFilters(entity, schema, data, filter, matrix, attrs, context);
}
throw new types_1.OakAttrCantUpdateException(entity, attrs, '更新的行当前属性不满足约束,请仔细检查数据');
}
});
}
if (!result) {
if (attrs.length > 1) {
return cascadelyCheckUpdateFilters(entity, schema, data, filter, matrix, attrs, context);
}
throw new types_1.OakAttrCantUpdateException(entity, attrs, '更新的行当前属性不满足约束,请仔细检查数据');
}
}

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

@ -4,6 +4,14 @@ import { AsyncContext } from './AsyncRowStore';
import { SyncContext } from './SyncRowStore';
export declare function translateCreateDataToFilter<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(schema: StorageSchema<ED>, entity: T, data: ED[T]['CreateSingle']['data'], allowUnrecoganized: boolean): ED[T]["Selection"]["filter"];
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;
type DeducedFilter<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
entity: T;
filter: ED[T]['Selection']['filter'];
};
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 } }
@ -35,6 +43,44 @@ export declare function combineFilters<ED extends EntityDict & BaseEntityDict, T
* @attention: 1), 2)
*/
export declare function judgeValueRelation(value1: any, value2: any, contained: boolean): boolean | undefined;
/**
* filter对compared查询的各个条件进行逐项分析
* @param entity
* @param schema
* @param filter
* @param compared
* @param contained
* @returns
* sureAttributes中包含被判定肯定相容或肯定不相斥的属性
* uncertainAttributes中包含的是无法判定结果的属性
* totalAndDeducedFilters包含的是判定过程中推论的相容的充分条件and关系
* totalOrDeducedFilters包含的是判定过程中推论的相斥的充分条件or关系
*/
export declare function analyzeFilterRelation<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(entity: T, schema: StorageSchema<ED>, filter: NonNullable<ED[T]['Selection']['filter']>, compared: NonNullable<ED[T]['Selection']['filter']>, contained: boolean): boolean | {
totalAndDeducedFilters: (DeducedFilterCombination<ED> | DeducedFilter<ED, T>)[];
totalOrDeducedFilters: (DeducedFilterCombination<ED> | DeducedFilter<ED, T>)[];
uncertainAttributes: string[];
sureAttributes: string[];
};
/**
*
* filter是否包含contained中的查询条件filter查询的结果一定是contained查询结果的子集
* filter = {
* a: 1
* b: 2,
* c: 3,
* },
* conditionalFilter = {
* a: 1
* }
*
* @param entity
* @param schema
* @param filter
* @param contained
* @returns
*/
export declare function contains<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(entity: T, schema: StorageSchema<ED>, filter: ED[T]['Selection']['filter'], contained: ED[T]['Selection']['filter']): boolean | DeducedFilterCombination<ED>;
/**
* filter中判断是否有确定的id对象id
* @param filter
@ -84,3 +130,4 @@ export declare function checkFilterRepel<ED extends EntityDict & BaseEntityDict,
* @param filter
*/
export declare function translateFilterToObjectPredicate(filter: Record<string, any>): {};
export {};

View File

@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.translateFilterToObjectPredicate = exports.checkFilterRepel = exports.checkFilterContains = exports.makeTreeDescendantFilter = exports.makeTreeAncestorFilter = exports.same = exports.getRelevantIds = exports.judgeValueRelation = exports.combineFilters = exports.translateCreateDataToFilter = void 0;
exports.translateFilterToObjectPredicate = exports.checkFilterRepel = exports.checkFilterContains = exports.makeTreeDescendantFilter = exports.makeTreeAncestorFilter = exports.same = exports.getRelevantIds = exports.contains = exports.analyzeFilterRelation = exports.judgeValueRelation = exports.combineFilters = exports.translateCreateDataToFilter = void 0;
const tslib_1 = require("tslib");
const assert_1 = tslib_1.__importDefault(require("assert"));
const types_1 = require("../types");
@ -43,9 +43,24 @@ function addFilterSegment(entity, schema, ...filters) {
if (!filter[attr]) {
filter[attr] = value;
}
// 优化一种情况,就是两个都等值且相等
// 优化两个都等值且相等
else if (filter[attr] === value) {
}
// value定义的查询被当前查询包含
else if (contains(entity, schema, {
[attr]: value,
}, {
[attr]: filter[attr],
}) === true) {
filter[attr] = value;
}
// 当前查询被value所定义的查询包含
else if (contains(entity, schema, {
[attr]: filter[attr],
}, {
[attr]: value
}) == true) {
}
else {
addIntoAnd({
[attr]: value,
@ -1041,15 +1056,20 @@ function judgeFilterSingleAttrRelation(entity, schema, attr, filter, compared, c
// 到这里说明无法直接判断此attr上的相容或者相斥也无法把判定推断到更深层的算子之上
return;
}
/** filtercompared
/**
* 根据filter对compared查询的各个条件进行逐项分析
* @param entity
* @param schema
* @param filter
* @param compared
* @param contained: true代表判定filter包容compared(filter的查询结果是compared查询结果的子集), false代表判定filter与compared相斥filter的查询结果与compared没有交集
* @returns 返回true说明肯定相容相斥返回false说明无法判定相容相斥返回DeducedFilterCombination说明需要进一步判断此推断的条件
* @param contained
* @returns
* sureAttributes中包含被判定肯定相容或肯定不相斥的属性不用再继续判定了
* uncertainAttributes中包含的是无法判定结果的属性
* totalAndDeducedFilters包含的是判定过程中推论的相容的充分条件and关系
* totalOrDeducedFilters包含的是判定过程中推论的相斥的充分条件or关系
*/
function judgeFilterRelation(entity, schema, filter, compared, contained) {
function analyzeFilterRelation(entity, schema, filter, compared, contained) {
const totalAndDeducedFilters = [];
const totalOrDeducedFilters = [];
const uncertainAttributes = [];
@ -1239,6 +1259,28 @@ function judgeFilterRelation(entity, schema, filter, compared, contained) {
}
}
}
return {
totalAndDeducedFilters,
totalOrDeducedFilters,
uncertainAttributes,
sureAttributes,
};
}
exports.analyzeFilterRelation = analyzeFilterRelation;
/** filtercompared
* @param entity
* @param schema
* @param filter
* @param compared
* @param contained: true代表判定filter包容compared(filter的查询结果是compared查询结果的子集), false代表判定filter与compared相斥filter的查询结果与compared没有交集
* @returns 返回true说明肯定相容相斥返回false说明无法判定相容相斥返回DeducedFilterCombination说明需要进一步判断此推断的条件
*/
function judgeFilterRelation(entity, schema, filter, compared, contained) {
const result = analyzeFilterRelation(entity, schema, filter, compared, contained);
if (typeof result === 'boolean') {
return result;
}
const { sureAttributes, uncertainAttributes, totalAndDeducedFilters, totalOrDeducedFilters, } = result;
if (contained) {
if (sureAttributes.length === Object.keys(compared).length) {
return true;
@ -1296,6 +1338,7 @@ function contains(entity, schema, filter, contained) {
return judgeFilterRelation(entity, schema, filter, contained, true);
// return false;
}
exports.contains = contains;
/**
* 判断filter1和filter2是否相斥即filter1和filter2查询的结果一定没有交集
* filter1 = {

View File

@ -219,9 +219,14 @@ export default class LocaleBuilder {
if (watch) {
fs.watch(filepath, () => {
const data = this.readLocaleFileContent(filepath);
this.locales[ns] = [module, position.replace(/\\/g, '/'), language, data];
this.outputDataFile();
try {
const data = this.readLocaleFileContent(filepath);
this.locales[ns] = [module, position.replace(/\\/g, '/'), language, data];
this.outputDataFile();
}
catch(err) {
// 啥都不干
}
});
}
}

View File

@ -1,12 +1,13 @@
import { ActionDefDict, Checker, EntityDict, StorageSchema, RowChecker, OakUniqueViolationException, CHECKER_MAX_PRIORITY, AttrUpdateMatrix, LogicalChecker, OakAttrCantUpdateException } from "../types";
import { SyncContext } from "./SyncRowStore";
import { AsyncContext } from "./AsyncRowStore";
import { pick, intersection, difference } from '../utils/lodash';
import { checkFilterContains, combineFilters } from "./filter";
import { pick, intersection, difference, omit } from '../utils/lodash';
import { analyzeFilterRelation, checkFilterContains, combineFilters, contains } from "./filter";
import { EntityDict as BaseEntityDict } from '../base-app-domain/EntityDict';
import { createModiRelatedCheckers } from "./modi";
import { createCreateCheckers, createRemoveCheckers } from "./checker";
import { readOnlyActions } from "../actions/action";
import assert from 'assert';
function checkUniqueBetweenRows(rows: Record<string, any>[], uniqAttrs: string[]) {
@ -270,7 +271,7 @@ function createActionTransformerCheckers<ED extends EntityDict & BaseEntityDict,
}
);
}
else if (data){
else if (data) {
if (!(data as Readonly<ED[keyof ED]['CreateSingle']['data']>)[attr]) {
Object.assign(data, {
[attr]: is,
@ -286,6 +287,82 @@ function createActionTransformerCheckers<ED extends EntityDict & BaseEntityDict,
return checkers;
}
/**
*
* A的条件是B = 1B并不等于1 { B: 1, A: .. }
* B更新可以成功则A也可以成功
* @param entity
* @param data
* @param filters
* @param context
*/
function cascadelyCheckUpdateFilters<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>>(
entity: T,
schema: StorageSchema<ED>,
data: ED[T]['Update']['data'],
filter: ED[T]['Update']['filter'],
matrix: NonNullable<AttrUpdateMatrix<ED>[T]>,
restAttrs: string[],
context: Cxt | FrontCxt
): void | Promise<void> {
const successAttrs = difference(Object.keys(data), restAttrs);
const successAttrFilter = pick(data, successAttrs);
/**
*
*/
const legalAttrResult = restAttrs.map(
(attr) => {
const { filter: f } = matrix[attr]!;
if (!f) {
return true;
}
// 此时看应用了success的attributes更新后能否消除掉f中的部分条件
const result = analyzeFilterRelation(entity, schema, successAttrFilter, f, true);
if (typeof result === 'boolean') {
return result;
}
const { sureAttributes } = result;
const f2 = omit(f, sureAttributes);
return checkFilterContains<ED, keyof ED, Cxt | FrontCxt>(entity, context, f2, filter, true);
}
);
const checkResult1 = (lar: boolean[]) => {
const legalAttrs: string[] = [];
const illegalAttrs: string[] = [];
assert(lar.length === restAttrs.length);
lar.forEach(
(ele, idx) => {
if (ele) {
legalAttrs.push(restAttrs[idx]);
}
else {
illegalAttrs.push(restAttrs[idx]);
}
}
);
if (illegalAttrs.length === 0) {
return;
}
if (legalAttrs.length === 0) {
throw new OakAttrCantUpdateException(entity as keyof ED, illegalAttrs, '更新的行当前属性不满足约束,请仔细检查数据');
}
return cascadelyCheckUpdateFilters(entity, schema, data, filter, matrix, illegalAttrs, context);
};
if (legalAttrResult.find(ele => ele instanceof Promise)) {
return Promise.all(legalAttrResult).then(
(lar) => checkResult1(lar)
);
}
return checkResult1(legalAttrResult as boolean[]);
}
function createAttrUpdateCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>>(
schema: StorageSchema<ED>,
attrUpdateMatrix: AttrUpdateMatrix<ED>
@ -323,21 +400,31 @@ function createAttrUpdateCheckers<ED extends EntityDict & BaseEntityDict, Cxt ex
const attrsIllegal = attrs.filter(
(attr) => matrix[attr]?.actions && !matrix[attr]?.actions?.includes(action!)
);
throw new OakAttrCantUpdateException(entity, attrsIllegal, `${attrsIllegal}不允许被${action}动作更新`);
throw new OakAttrCantUpdateException(entity, attrsIllegal, `${attrsIllegal.join(',')}不允许被${action}动作更新`);
}
}
if (f) {
const result = checkFilterContains<ED, keyof ED, Cxt>(entity, context as any, f, filter, true);
const rr = contains<ED, keyof ED>(entity, context.getSchema(), data, f);
console.log(rr);
const result = checkFilterContains<ED, keyof ED, Cxt | FrontCxt>(entity, context, f, filter, true);
if (result instanceof Promise) {
return result.then(
(v) => {
if (!v) {
if (attrs.length > 1) {
return cascadelyCheckUpdateFilters(entity, schema, data as ED[keyof ED]['Update']['data'],
filter, matrix, attrs, context);
}
throw new OakAttrCantUpdateException(entity, attrs, '更新的行当前属性不满足约束,请仔细检查数据');
}
}
);
}
if (!result) {
if (attrs.length > 1) {
return cascadelyCheckUpdateFilters(entity, schema, data as ED[keyof ED]['Update']['data'],
filter, matrix, attrs, context);
}
throw new OakAttrCantUpdateException(entity, attrs, '更新的行当前属性不满足约束,请仔细检查数据');
}
}

View File

@ -6,7 +6,7 @@ import { AsyncContext } from './AsyncRowStore';
import { judgeRelation } from './relation';
import { SyncContext } from './SyncRowStore';
export function translateCreateDataToFilter<ED extends EntityDict & BaseEntityDict, T extends keyof ED> (
export function translateCreateDataToFilter<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(
schema: StorageSchema<ED>,
entity: T,
data: ED[T]['CreateSingle']['data'],
@ -53,9 +53,24 @@ function addFilterSegment<ED extends EntityDict & BaseEntityDict, T extends keyo
if (!filter[attr]) {
filter[attr] = value;
}
// 优化一种情况,就是两个都等值且相等
// 优化两个都等值且相等
else if (filter[attr] === value) {
}
// value定义的查询被当前查询包含
else if (contains(entity, schema, {
[attr]: value,
}, {
[attr]: filter[attr],
}) === true) {
filter[attr] = value;
}
// 当前查询被value所定义的查询包含
else if (contains(entity, schema, {
[attr]: filter[attr],
}, {
[attr]: value
}) == true) {
}
else {
addIntoAnd({
@ -1194,21 +1209,25 @@ function judgeFilterSingleAttrRelation<ED extends EntityDict & BaseEntityDict, T
return;
}
/** filtercompared
/**
* filter对compared查询的各个条件进行逐项分析
* @param entity
* @param schema
* @param filter
* @param compared
* @param contained: true代表判定filter包容compared(filter的查询结果是compared查询结果的子集), false代表判定filter与compared相斥filter的查询结果与compared没有交集
* @returns true说明肯定相容false说明无法判定相容DeducedFilterCombination说明需要进一步判断此推断的条件
* @param compared
* @param contained
* @returns
* sureAttributes中包含被判定肯定相容或肯定不相斥的属性
* uncertainAttributes中包含的是无法判定结果的属性
* totalAndDeducedFilters包含的是判定过程中推论的相容的充分条件and关系
* totalOrDeducedFilters包含的是判定过程中推论的相斥的充分条件or关系
*/
function judgeFilterRelation<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(
export function analyzeFilterRelation<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(
entity: T,
schema: StorageSchema<ED>,
filter: NonNullable<ED[T]['Selection']['filter']>,
compared: NonNullable<ED[T]['Selection']['filter']>,
contained: boolean): boolean | DeducedFilterCombination<ED> {
contained: boolean) {
const totalAndDeducedFilters: (DeducedFilterCombination<ED> | DeducedFilter<ED, T>)[] = [];
const totalOrDeducedFilters: (DeducedFilterCombination<ED> | DeducedFilter<ED, T>)[] = [];
const uncertainAttributes: string[] = [];
@ -1405,6 +1424,40 @@ function judgeFilterRelation<ED extends EntityDict & BaseEntityDict, T extends k
}
}
return {
totalAndDeducedFilters,
totalOrDeducedFilters,
uncertainAttributes,
sureAttributes,
};
}
/** filtercompared
* @param entity
* @param schema
* @param filter
* @param compared
* @param contained: true代表判定filter包容compared(filter的查询结果是compared查询结果的子集), false代表判定filter与compared相斥filter的查询结果与compared没有交集
* @returns true说明肯定相容false说明无法判定相容DeducedFilterCombination说明需要进一步判断此推断的条件
*/
function judgeFilterRelation<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(
entity: T,
schema: StorageSchema<ED>,
filter: NonNullable<ED[T]['Selection']['filter']>,
compared: NonNullable<ED[T]['Selection']['filter']>,
contained: boolean): boolean | DeducedFilterCombination<ED> {
const result = analyzeFilterRelation(entity, schema, filter, compared, contained);
if (typeof result === 'boolean') {
return result;
}
const {
sureAttributes,
uncertainAttributes,
totalAndDeducedFilters,
totalOrDeducedFilters,
} = result;
if (contained) {
if (sureAttributes.length === Object.keys(compared).length) {
return true;
@ -1457,7 +1510,7 @@ function judgeFilterRelation<ED extends EntityDict & BaseEntityDict, T extends k
* @param contained
* @returns
*/
function contains<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(
export function contains<ED extends EntityDict & BaseEntityDict, T extends keyof ED>(
entity: T,
schema: StorageSchema<ED>,
filter: ED[T]['Selection']['filter'],