Merge branch 'dev'

This commit is contained in:
Xu Chang 2024-03-29 18:31:40 +08:00
commit a31f5a2015
16 changed files with 68 additions and 56 deletions

View File

@ -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;
} }

View File

@ -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) {

View File

@ -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];

View File

@ -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;

View File

@ -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$$ = {

View File

@ -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));

View File

@ -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在逻辑上相容或者相斥

View File

@ -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)) {

View File

@ -1,4 +1,3 @@
/// <reference types="node" />
/** /**
* assert打包体积过大 * assert打包体积过大
*/ */

View File

@ -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;
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "oak-domain", "name": "oak-domain",
"version": "4.4.0", "version": "4.5.0",
"author": { "author": {
"name": "XuChang" "name": "XuChang"
}, },

View File

@ -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;

View File

@ -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;

View File

@ -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$$ = {

View File

@ -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));

View File

@ -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!)) {