Merge branch 'dev'
This commit is contained in:
commit
a31f5a2015
|
|
@ -50,7 +50,7 @@ export declare abstract class AsyncContext<ED extends EntityDict> implements Con
|
||||||
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;
|
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;
|
||||||
abstract setCurrentUserId(userId: string | undefined): void;
|
abstract setCurrentUserId(userId: string | undefined): void;
|
||||||
abstract toString(): Promise<string>;
|
abstract toString(): Promise<string>;
|
||||||
abstract initialize(data: any, later?: boolean): Promise<void>;
|
abstract initialize(data?: any, later?: boolean): Promise<void>;
|
||||||
abstract allowUserUpdate(): boolean;
|
abstract allowUserUpdate(): boolean;
|
||||||
abstract openRootMode(): () => void;
|
abstract openRootMode(): () => void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ function createUniqueCheckers(schema) {
|
||||||
entity,
|
entity,
|
||||||
action: 'create',
|
action: 'create',
|
||||||
type: 'logicalData',
|
type: 'logicalData',
|
||||||
priority: types_1.CHECKER_MAX_PRIORITY, // 优先级要放在最低,所有前置的checker/trigger将数据完整之后再在这里检测
|
priority: types_1.CHECKER_MAX_PRIORITY,
|
||||||
checker: (operation, context) => {
|
checker: (operation, context) => {
|
||||||
const { data } = operation;
|
const { data } = operation;
|
||||||
if (data instanceof Array) {
|
if (data instanceof Array) {
|
||||||
|
|
@ -93,9 +93,9 @@ function createUniqueCheckers(schema) {
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
entity,
|
entity,
|
||||||
action: 'update', // 只检查update,其它状态转换的action应该不会涉及unique约束的属性
|
action: 'update',
|
||||||
type: 'logicalData',
|
type: 'logicalData',
|
||||||
priority: types_1.CHECKER_MAX_PRIORITY, // 优先级要放在最低,所有前置的checker/trigger将数据完整之后再在这里检测
|
priority: types_1.CHECKER_MAX_PRIORITY,
|
||||||
checker: (operation, context) => {
|
checker: (operation, context) => {
|
||||||
const { data, filter: operationFilter } = operation;
|
const { data, filter: operationFilter } = operation;
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
@ -213,7 +213,7 @@ function createActionTransformerCheckers(actionDefDict) {
|
||||||
action: 'create',
|
action: 'create',
|
||||||
type: 'logicalData',
|
type: 'logicalData',
|
||||||
entity,
|
entity,
|
||||||
priority: 10, // 优先级要高,先于真正的data检查进行
|
priority: 10,
|
||||||
checker: (operation) => {
|
checker: (operation) => {
|
||||||
const { data } = operation;
|
const { data } = operation;
|
||||||
if (data instanceof Array) {
|
if (data instanceof Array) {
|
||||||
|
|
|
||||||
|
|
@ -441,10 +441,10 @@ class RelationAuth {
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const makeCreateFilter = (entity, operation) => {
|
const makeCreateFilter = (entity, operation) => {
|
||||||
const { data, filter } = operation;
|
const { data, filter, bornAt } = operation;
|
||||||
(0, assert_1.default)(!(data instanceof Array));
|
(0, assert_1.default)(!(data instanceof Array));
|
||||||
if (data) {
|
if (data) {
|
||||||
return (0, filter_1.translateCreateDataToFilter)(this.schema, entity, data);
|
return (0, filter_1.translateCreateDataToFilter)(this.schema, entity, data, !!bornAt);
|
||||||
}
|
}
|
||||||
return filter;
|
return filter;
|
||||||
};
|
};
|
||||||
|
|
@ -498,7 +498,7 @@ class RelationAuth {
|
||||||
}
|
}
|
||||||
(0, assert_1.default)(!(data instanceof Array));
|
(0, assert_1.default)(!(data instanceof Array));
|
||||||
for (const attr in data) {
|
for (const attr in data) {
|
||||||
const rel = (0, relation_1.judgeRelation)(this.schema, entity, attr);
|
const rel = (0, relation_1.judgeRelation)(this.schema, entity, attr, !!operation.bornAt);
|
||||||
if (rel === 2 && !isModiUpdate) {
|
if (rel === 2 && !isModiUpdate) {
|
||||||
(0, assert_1.default)(root === me && !hasParent, 'cascadeUpdate必须是树结构,避免森林');
|
(0, assert_1.default)(root === me && !hasParent, 'cascadeUpdate必须是树结构,避免森林');
|
||||||
const mtoOperation = data[attr];
|
const mtoOperation = data[attr];
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export declare class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt
|
||||||
private logger;
|
private logger;
|
||||||
private contextBuilder;
|
private contextBuilder;
|
||||||
private onVolatileTrigger;
|
private onVolatileTrigger;
|
||||||
constructor(contextBuilder: (cxtString?: string) => Promise<Cxt>, logger?: Logger, onVolatileTrigger?: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>);
|
constructor(contextBuilder: () => Cxt, logger?: Logger, onVolatileTrigger?: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>);
|
||||||
setOnVolatileTrigger(onVolatileTrigger: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>): void;
|
setOnVolatileTrigger(onVolatileTrigger: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>): void;
|
||||||
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>, schema: StorageSchema<ED>): void;
|
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>, schema: StorageSchema<ED>): void;
|
||||||
registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>): void;
|
registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>): void;
|
||||||
|
|
|
||||||
|
|
@ -39,15 +39,21 @@ class TriggerExecutor {
|
||||||
this.volatileEntities = [];
|
this.volatileEntities = [];
|
||||||
this.counter = 0;
|
this.counter = 0;
|
||||||
this.onVolatileTrigger = onVolatileTrigger || (async (entity, trigger, ids, cxtStr, option) => {
|
this.onVolatileTrigger = onVolatileTrigger || (async (entity, trigger, ids, cxtStr, option) => {
|
||||||
const context = await this.contextBuilder(cxtStr);
|
const context = this.contextBuilder();
|
||||||
await context.begin();
|
if (!context.getCurrentTxnId()) {
|
||||||
|
await context.begin();
|
||||||
|
}
|
||||||
|
await context.initialize(JSON.parse(cxtStr));
|
||||||
try {
|
try {
|
||||||
await this.execVolatileTrigger(entity, trigger.name, ids, context, option);
|
await this.execVolatileTrigger(entity, trigger.name, ids, context, option);
|
||||||
await context.commit();
|
await context.commit();
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
await context.rollback();
|
await context.rollback();
|
||||||
this.logger.error('error on volatile trigger', entity, trigger.name, ids.join(','), err);
|
if (!(err instanceof types_1.OakMakeSureByMySelfException)) {
|
||||||
|
this.logger.error('error on volatile trigger', entity, trigger.name, ids.join(','), err);
|
||||||
|
}
|
||||||
|
// throw err;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -271,8 +277,8 @@ class TriggerExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const number = trigger.fn({ operation: operation }, context, option);
|
const number = trigger.fn({ operation: operation }, context, option);
|
||||||
if (number > 0) {
|
if (number > 0 && process.env.NODE_ENV === 'development') {
|
||||||
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
this.logger.info(`前触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(0, assert_1.default)(commitTriggers.length === 0, `前台不应有commitTrigger`);
|
(0, assert_1.default)(commitTriggers.length === 0, `前台不应有commitTrigger`);
|
||||||
|
|
@ -294,8 +300,8 @@ class TriggerExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const number = await trigger.fn({ operation: operation }, context, option);
|
const number = await trigger.fn({ operation: operation }, context, option);
|
||||||
if (number > 0) {
|
if (number > 0 && process.env.NODE_ENV === 'development') {
|
||||||
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
this.logger.info(`前触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
||||||
}
|
}
|
||||||
return execPreTrigger(idx + 1);
|
return execPreTrigger(idx + 1);
|
||||||
};
|
};
|
||||||
|
|
@ -382,8 +388,8 @@ class TriggerExecutor {
|
||||||
operation: operation,
|
operation: operation,
|
||||||
result: result,
|
result: result,
|
||||||
}, context, option);
|
}, context, option);
|
||||||
if (number > 0) {
|
if (number > 0 && process.env.NODE_ENV === 'development') {
|
||||||
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
this.logger.info(`后触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -398,8 +404,8 @@ class TriggerExecutor {
|
||||||
operation: operation,
|
operation: operation,
|
||||||
result: result,
|
result: result,
|
||||||
}, context, option);
|
}, context, option);
|
||||||
if (number > 0) {
|
if (number > 0 && process.env.NODE_ENV === 'development') {
|
||||||
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
this.logger.info(`后触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
||||||
}
|
}
|
||||||
return execPostTrigger(idx + 1);
|
return execPostTrigger(idx + 1);
|
||||||
};
|
};
|
||||||
|
|
@ -441,7 +447,7 @@ class TriggerExecutor {
|
||||||
$lt: timestamp,
|
$lt: timestamp,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const context = await this.contextBuilder();
|
const context = this.contextBuilder();
|
||||||
if (context.clusterInfo?.usingCluster) {
|
if (context.clusterInfo?.usingCluster) {
|
||||||
const { instanceCount, instanceId } = context.clusterInfo;
|
const { instanceCount, instanceId } = context.clusterInfo;
|
||||||
filter.$$seq$$ = {
|
filter.$$seq$$ = {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ function translateCheckerInAsyncContext(checker, schema) {
|
||||||
case 'row': {
|
case 'row': {
|
||||||
const { filter, errMsg, inconsistentRows } = checker;
|
const { filter, errMsg, inconsistentRows } = checker;
|
||||||
const fn = (async ({ operation }, context, option) => {
|
const fn = (async ({ operation }, context, option) => {
|
||||||
const { filter: operationFilter, data, action } = operation;
|
const { filter: operationFilter, data, action, bornAt } = operation;
|
||||||
const filter2 = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
const filter2 = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
||||||
if (['select', 'count', 'stat'].includes(action)) {
|
if (['select', 'count', 'stat'].includes(action)) {
|
||||||
operation.filter = (0, filter_1.combineFilters)(entity, context.getSchema(), [operationFilter, filter2]);
|
operation.filter = (0, filter_1.combineFilters)(entity, context.getSchema(), [operationFilter, filter2]);
|
||||||
|
|
@ -88,11 +88,11 @@ function translateCheckerInAsyncContext(checker, schema) {
|
||||||
(0, assert_1.default)(data);
|
(0, assert_1.default)(data);
|
||||||
if (data instanceof Array) {
|
if (data instanceof Array) {
|
||||||
for (const d of data) {
|
for (const d of data) {
|
||||||
await checkSingle((0, filter_1.translateCreateDataToFilter)(schema, entity, d));
|
await checkSingle((0, filter_1.translateCreateDataToFilter)(schema, entity, d, !!bornAt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await checkSingle((0, filter_1.translateCreateDataToFilter)(schema, entity, data));
|
await checkSingle((0, filter_1.translateCreateDataToFilter)(schema, entity, data, !!bornAt));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +139,7 @@ function translateCheckerInSyncContext(checker, schema) {
|
||||||
case 'row': {
|
case 'row': {
|
||||||
const { filter, errMsg, entity } = checker;
|
const { filter, errMsg, entity } = checker;
|
||||||
const fn = (operation, context, option) => {
|
const fn = (operation, context, option) => {
|
||||||
const { filter: operationFilter, data, action } = operation;
|
const { filter: operationFilter, data, action, bornAt } = operation;
|
||||||
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||||
let operationFilter2 = operationFilter;
|
let operationFilter2 = operationFilter;
|
||||||
if (action === 'create') {
|
if (action === 'create') {
|
||||||
|
|
@ -147,7 +147,7 @@ function translateCheckerInSyncContext(checker, schema) {
|
||||||
// 前端的策略是,有data用data,无data用filter
|
// 前端的策略是,有data用data,无data用filter
|
||||||
// 目前前端应该不可能制造出来createMultiple
|
// 目前前端应该不可能制造出来createMultiple
|
||||||
(0, assert_1.default)(!(data instanceof Array));
|
(0, assert_1.default)(!(data instanceof Array));
|
||||||
operationFilter2 = (0, filter_1.translateCreateDataToFilter)(schema, entity, data);
|
operationFilter2 = (0, filter_1.translateCreateDataToFilter)(schema, entity, data, !!bornAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(0, assert_1.default)(!(filter2 instanceof Promise));
|
(0, assert_1.default)(!(filter2 instanceof Promise));
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { EntityDict as BaseEntityDict, StorageSchema } from '../types';
|
||||||
import { EntityDict } from "../base-app-domain";
|
import { EntityDict } from "../base-app-domain";
|
||||||
import { AsyncContext } from './AsyncRowStore';
|
import { AsyncContext } from './AsyncRowStore';
|
||||||
import { SyncContext } from './SyncRowStore';
|
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']): ED[T]["Selection"]["filter"];
|
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;
|
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;
|
||||||
/**
|
/**
|
||||||
* 在以下判断相容或相斥的过程中,相容/相斥的事实标准是:满足两个条件的查询集合是否被包容/互斥,但如果两个filter在逻辑上相容或者相斥,在事实上不一定相容或者相斥
|
* 在以下判断相容或相斥的过程中,相容/相斥的事实标准是:满足两个条件的查询集合是否被包容/互斥,但如果两个filter在逻辑上相容或者相斥,在事实上不一定相容或者相斥
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||||
const types_1 = require("../types");
|
const types_1 = require("../types");
|
||||||
const lodash_1 = require("../utils/lodash");
|
const lodash_1 = require("../utils/lodash");
|
||||||
const relation_1 = require("./relation");
|
const relation_1 = require("./relation");
|
||||||
function translateCreateDataToFilter(schema, entity, data) {
|
function translateCreateDataToFilter(schema, entity, data, allowUnrecoganized) {
|
||||||
const data2 = {};
|
const data2 = {};
|
||||||
for (const attr in data) {
|
for (const attr in data) {
|
||||||
const rel = (0, relation_1.judgeRelation)(schema, entity, attr);
|
const rel = (0, relation_1.judgeRelation)(schema, entity, attr, allowUnrecoganized);
|
||||||
if (rel === 1) {
|
if (rel === 1) {
|
||||||
// 只需要记住id和各种外键属性,不这样处理有些古怪的属性比如coordinate,其作为createdata和作为filter并不同构
|
// 只需要记住id和各种外键属性,不这样处理有些古怪的属性比如coordinate,其作为createdata和作为filter并不同构
|
||||||
if (!['geometry', 'geography', 'st_geometry', 'st_point'].includes(schema[entity].attributes[attr]?.type)) {
|
if (!['geometry', 'geography', 'st_geometry', 'st_point'].includes(schema[entity].attributes[attr]?.type)) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
/// <reference types="node" />
|
|
||||||
/**
|
/**
|
||||||
* 防止assert打包体积过大,从这里引用
|
* 防止assert打包体积过大,从这里引用
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ function destructRelationPath(schema, entity, path, relationFilter, recursive) {
|
||||||
},
|
},
|
||||||
filter: relationFilter,
|
filter: relationFilter,
|
||||||
} // as ED['userRelation']['Selection']
|
} // as ED['userRelation']['Selection']
|
||||||
}, // as ED[keyof ED]['Selection']['data'],
|
},
|
||||||
getData: (d) => {
|
getData: (d) => {
|
||||||
return d.userRelation$entity;
|
return d.userRelation$entity;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "oak-domain",
|
"name": "oak-domain",
|
||||||
"version": "4.4.0",
|
"version": "4.5.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "XuChang"
|
"name": "XuChang"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
|
||||||
|
|
||||||
// 此接口将字符串parse成对象再进行初始化
|
// 此接口将字符串parse成对象再进行初始化
|
||||||
// later表示允许延时状态,上下文要处理在时间维度上可能的异常(比如用户token已经注销等)
|
// later表示允许延时状态,上下文要处理在时间维度上可能的异常(比如用户token已经注销等)
|
||||||
abstract initialize(data: any, later?: boolean): Promise<void>;
|
abstract initialize(data?: any, later?: boolean): Promise<void>;
|
||||||
|
|
||||||
abstract allowUserUpdate(): boolean;
|
abstract allowUserUpdate(): boolean;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -566,10 +566,10 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict> {
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const makeCreateFilter = <T2 extends keyof ED>(entity: T2, operation: Omit<ED[T2]['CreateSingle'], 'id'>) => {
|
const makeCreateFilter = <T2 extends keyof ED>(entity: T2, operation: Omit<ED[T2]['CreateSingle'], 'id'>) => {
|
||||||
const { data, filter } = operation;
|
const { data, filter, bornAt } = operation;
|
||||||
assert(!(data instanceof Array));
|
assert(!(data instanceof Array));
|
||||||
if (data) {
|
if (data) {
|
||||||
return translateCreateDataToFilter(this.schema, entity, data);
|
return translateCreateDataToFilter(this.schema, entity, data, !!bornAt);
|
||||||
}
|
}
|
||||||
return filter;
|
return filter;
|
||||||
};
|
};
|
||||||
|
|
@ -636,7 +636,7 @@ export class RelationAuth<ED extends EntityDict & BaseEntityDict> {
|
||||||
assert(!(data instanceof Array));
|
assert(!(data instanceof Array));
|
||||||
|
|
||||||
for (const attr in data) {
|
for (const attr in data) {
|
||||||
const rel = judgeRelation(this.schema, entity, attr);
|
const rel = judgeRelation(this.schema, entity, attr, !!operation.bornAt);
|
||||||
if (rel === 2 && !isModiUpdate) {
|
if (rel === 2 && !isModiUpdate) {
|
||||||
assert(root === me && !hasParent, 'cascadeUpdate必须是树结构,避免森林');
|
assert(root === me && !hasParent, 'cascadeUpdate必须是树结构,避免森林');
|
||||||
const mtoOperation = data[attr] as any;
|
const mtoOperation = data[attr] as any;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
|
||||||
private volatileEntities: Array<keyof ED>;
|
private volatileEntities: Array<keyof ED>;
|
||||||
|
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
private contextBuilder: (cxtString?: string) => Promise<Cxt>;
|
private contextBuilder: () => Cxt;
|
||||||
private onVolatileTrigger: <T extends keyof ED>(
|
private onVolatileTrigger: <T extends keyof ED>(
|
||||||
entity: T,
|
entity: T,
|
||||||
trigger: VolatileTrigger<ED, T, Cxt>,
|
trigger: VolatileTrigger<ED, T, Cxt>,
|
||||||
|
|
@ -48,7 +48,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
contextBuilder: (cxtString?: string) => Promise<Cxt>,
|
contextBuilder: () => Cxt,
|
||||||
logger: Logger = console,
|
logger: Logger = console,
|
||||||
onVolatileTrigger?: <T extends keyof ED>(
|
onVolatileTrigger?: <T extends keyof ED>(
|
||||||
entity: T,
|
entity: T,
|
||||||
|
|
@ -64,15 +64,21 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
|
||||||
this.volatileEntities = [];
|
this.volatileEntities = [];
|
||||||
this.counter = 0;
|
this.counter = 0;
|
||||||
this.onVolatileTrigger = onVolatileTrigger || (async (entity, trigger, ids, cxtStr, option) => {
|
this.onVolatileTrigger = onVolatileTrigger || (async (entity, trigger, ids, cxtStr, option) => {
|
||||||
const context = await this.contextBuilder(cxtStr);
|
const context = this.contextBuilder();
|
||||||
await context.begin();
|
if (!context.getCurrentTxnId()) {
|
||||||
|
await context.begin();
|
||||||
|
}
|
||||||
|
await context.initialize(JSON.parse(cxtStr));
|
||||||
try {
|
try {
|
||||||
await this.execVolatileTrigger(entity, trigger.name, ids, context, option);
|
await this.execVolatileTrigger(entity, trigger.name, ids, context, option);
|
||||||
await context.commit();
|
await context.commit();
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
await context.rollback();
|
await context.rollback();
|
||||||
this.logger.error('error on volatile trigger', entity, trigger.name, ids.join(','), err);
|
if (!(err instanceof OakMakeSureByMySelfException)) {
|
||||||
|
this.logger.error('error on volatile trigger', entity, trigger.name, ids.join(','), err);
|
||||||
|
}
|
||||||
|
// throw err;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -353,8 +359,8 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const number = (trigger as CreateTriggerInTxn<ED, T, Cxt>).fn({ operation: operation as ED[T]['Create'] }, context, option as OperateOption);
|
const number = (trigger as CreateTriggerInTxn<ED, T, Cxt>).fn({ operation: operation as ED[T]['Create'] }, context, option as OperateOption);
|
||||||
if (number as number > 0) {
|
if (number as number > 0 && process.env.NODE_ENV === 'development') {
|
||||||
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
this.logger.info(`前触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(commitTriggers.length === 0, `前台不应有commitTrigger`);
|
assert(commitTriggers.length === 0, `前台不应有commitTrigger`);
|
||||||
|
|
@ -376,8 +382,8 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const number = await (trigger as CreateTriggerInTxn<ED, T, Cxt>).fn({ operation: operation as ED[T]['Create'] }, context, option as OperateOption);
|
const number = await (trigger as CreateTriggerInTxn<ED, T, Cxt>).fn({ operation: operation as ED[T]['Create'] }, context, option as OperateOption);
|
||||||
if (number as number > 0) {
|
if (number as number > 0 && process.env.NODE_ENV === 'development') {
|
||||||
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
this.logger.info(`前触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
||||||
}
|
}
|
||||||
return execPreTrigger(idx + 1);
|
return execPreTrigger(idx + 1);
|
||||||
};
|
};
|
||||||
|
|
@ -489,8 +495,8 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
|
||||||
operation: operation as ED[T]['Selection'],
|
operation: operation as ED[T]['Selection'],
|
||||||
result: result!,
|
result: result!,
|
||||||
}, context, option as SelectOption);
|
}, context, option as SelectOption);
|
||||||
if (number as number > 0) {
|
if (number as number > 0 && process.env.NODE_ENV === 'development') {
|
||||||
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
this.logger.info(`后触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -505,8 +511,8 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
|
||||||
operation: operation as ED[T]['Selection'],
|
operation: operation as ED[T]['Selection'],
|
||||||
result: result!,
|
result: result!,
|
||||||
}, context, option as SelectOption);
|
}, context, option as SelectOption);
|
||||||
if (number as number > 0) {
|
if (number as number > 0 && process.env.NODE_ENV === 'development') {
|
||||||
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
this.logger.info(`后触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
|
||||||
}
|
}
|
||||||
return execPostTrigger(idx + 1);
|
return execPostTrigger(idx + 1);
|
||||||
};
|
};
|
||||||
|
|
@ -551,7 +557,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict, Cxt extends
|
||||||
$lt: timestamp,
|
$lt: timestamp,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const context = await this.contextBuilder();
|
const context = this.contextBuilder();
|
||||||
if (context.clusterInfo?.usingCluster) {
|
if (context.clusterInfo?.usingCluster) {
|
||||||
const { instanceCount, instanceId } = context.clusterInfo!;
|
const { instanceCount, instanceId } = context.clusterInfo!;
|
||||||
filter.$$seq$$ = {
|
filter.$$seq$$ = {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ export function translateCheckerInAsyncContext<
|
||||||
case 'row': {
|
case 'row': {
|
||||||
const { filter, errMsg, inconsistentRows } = checker;
|
const { filter, errMsg, inconsistentRows } = checker;
|
||||||
const fn = (async ({ operation }, context, option) => {
|
const fn = (async ({ operation }, context, option) => {
|
||||||
const { filter: operationFilter, data, action } = operation;
|
const { filter: operationFilter, data, action, bornAt } = operation;
|
||||||
const filter2 = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
const filter2 = typeof filter === 'function' ? await filter(operation, context, option) : filter;
|
||||||
if (['select', 'count', 'stat'].includes(action)) {
|
if (['select', 'count', 'stat'].includes(action)) {
|
||||||
operation.filter = combineFilters(entity, context.getSchema(), [operationFilter, filter2]);
|
operation.filter = combineFilters(entity, context.getSchema(), [operationFilter, filter2]);
|
||||||
|
|
@ -102,11 +102,11 @@ export function translateCheckerInAsyncContext<
|
||||||
assert(data);
|
assert(data);
|
||||||
if (data instanceof Array) {
|
if (data instanceof Array) {
|
||||||
for (const d of <ED[T]['CreateMulti']['data']>data) {
|
for (const d of <ED[T]['CreateMulti']['data']>data) {
|
||||||
await checkSingle(translateCreateDataToFilter(schema, entity, d))
|
await checkSingle(translateCreateDataToFilter(schema, entity, d, !!bornAt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await checkSingle(translateCreateDataToFilter(schema, entity, <ED[T]['CreateSingle']['data']><unknown>data))
|
await checkSingle(translateCreateDataToFilter(schema, entity, <ED[T]['CreateSingle']['data']><unknown>data, !!bornAt))
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +160,7 @@ export function translateCheckerInSyncContext<
|
||||||
case 'row': {
|
case 'row': {
|
||||||
const { filter, errMsg, entity } = checker;
|
const { filter, errMsg, entity } = checker;
|
||||||
const fn = (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => {
|
const fn = (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => {
|
||||||
const { filter: operationFilter, data, action } = operation;
|
const { filter: operationFilter, data, action, bornAt } = operation;
|
||||||
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
const filter2 = typeof filter === 'function' ? filter(operation, context, option) : filter;
|
||||||
let operationFilter2 = operationFilter;
|
let operationFilter2 = operationFilter;
|
||||||
if (action === 'create') {
|
if (action === 'create') {
|
||||||
|
|
@ -168,7 +168,7 @@ export function translateCheckerInSyncContext<
|
||||||
// 前端的策略是,有data用data,无data用filter
|
// 前端的策略是,有data用data,无data用filter
|
||||||
// 目前前端应该不可能制造出来createMultiple
|
// 目前前端应该不可能制造出来createMultiple
|
||||||
assert(!(data instanceof Array));
|
assert(!(data instanceof Array));
|
||||||
operationFilter2 = translateCreateDataToFilter(schema, entity, data as ED[T]['CreateSingle']['data']);
|
operationFilter2 = translateCreateDataToFilter(schema, entity, data as ED[T]['CreateSingle']['data'], !!bornAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(!(filter2 instanceof Promise));
|
assert(!(filter2 instanceof Promise));
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,11 @@ export function translateCreateDataToFilter<ED extends EntityDict & BaseEntityDi
|
||||||
schema: StorageSchema<ED>,
|
schema: StorageSchema<ED>,
|
||||||
entity: T,
|
entity: T,
|
||||||
data: ED[T]['CreateSingle']['data'],
|
data: ED[T]['CreateSingle']['data'],
|
||||||
|
allowUnrecoganized: boolean,
|
||||||
) {
|
) {
|
||||||
const data2: ED[T]['Selection']['filter'] = {};
|
const data2: ED[T]['Selection']['filter'] = {};
|
||||||
for (const attr in data) {
|
for (const attr in data) {
|
||||||
const rel = judgeRelation(schema, entity, attr);
|
const rel = judgeRelation(schema, entity, attr, allowUnrecoganized);
|
||||||
if (rel === 1) {
|
if (rel === 1) {
|
||||||
// 只需要记住id和各种外键属性,不这样处理有些古怪的属性比如coordinate,其作为createdata和作为filter并不同构
|
// 只需要记住id和各种外键属性,不这样处理有些古怪的属性比如coordinate,其作为createdata和作为filter并不同构
|
||||||
if (!['geometry', 'geography', 'st_geometry', 'st_point'].includes(schema[entity].attributes[attr as any]?.type!)) {
|
if (!['geometry', 'geography', 'st_geometry', 'st_point'].includes(schema[entity].attributes[attr as any]?.type!)) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue