几个小BUG,以及对现在属性为undefined会报RowUnexsited错误的处理
This commit is contained in:
parent
f412afd2a9
commit
90c7db02b7
|
|
@ -1,14 +1,18 @@
|
|||
import { EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
|
||||
import { DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, EntityDict, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
|
||||
import { StorageSchema } from "oak-domain/lib/types/Storage";
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { Checker, CheckerType, Context } from 'oak-domain/lib/types';
|
||||
import { TreeStore } from 'oak-memory-tree-store';
|
||||
import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store';
|
||||
interface CachStoreOperation extends TreeStoreOperateOption {
|
||||
inSync?: boolean;
|
||||
}
|
||||
export declare class CacheStore<ED extends EntityDict & BaseEntityDict, Cxt extends Context<ED>> extends TreeStore<ED, Cxt> {
|
||||
private executor;
|
||||
private getFullDataFn?;
|
||||
private resetInitialDataFn?;
|
||||
constructor(storageSchema: StorageSchema<ED>, contextBuilder: () => (store: CacheStore<ED, Cxt>) => Cxt, getFullDataFn?: () => any, resetInitialDataFn?: () => void);
|
||||
operate<T extends keyof ED, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<OperationResult<ED>>;
|
||||
protected updateAbjointRow<T extends keyof ED>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, option?: CachStoreOperation): Promise<number>;
|
||||
operate<T extends keyof ED, OP extends TreeStoreOperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<OperationResult<ED>>;
|
||||
sync(opRecords: Array<OpRecord<ED>>, context: Cxt): Promise<void>;
|
||||
check<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, checkerTypes?: CheckerType[]): Promise<void>;
|
||||
select<T extends keyof ED, S extends ED[T]['Selection'], OP extends SelectOption>(entity: T, selection: S, context: Cxt, option: OP): Promise<import("oak-domain/lib/types").SelectionResult<ED[T]["Schema"], S["data"]>>;
|
||||
|
|
@ -24,3 +28,4 @@ export declare class CacheStore<ED extends EntityDict & BaseEntityDict, Cxt exte
|
|||
*/
|
||||
resetInitialData(): void;
|
||||
}
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ var tslib_1 = require("tslib");
|
|||
var TriggerExecutor_1 = require("oak-domain/lib/store/TriggerExecutor");
|
||||
var oak_memory_tree_store_1 = require("oak-memory-tree-store");
|
||||
var assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
;
|
||||
var CacheStore = /** @class */ (function (_super) {
|
||||
tslib_1.__extends(CacheStore, _super);
|
||||
function CacheStore(storageSchema, contextBuilder, getFullDataFn, resetInitialDataFn) {
|
||||
|
|
@ -16,6 +17,28 @@ var CacheStore = /** @class */ (function (_super) {
|
|||
_this.resetInitialDataFn = resetInitialDataFn;
|
||||
return _this;
|
||||
}
|
||||
CacheStore.prototype.updateAbjointRow = function (entity, operation, context, option) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var action, data, attributes, key;
|
||||
var _a;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
if (!(option === null || option === void 0 ? void 0 : option.inSync)) {
|
||||
action = operation.action, data = operation.data;
|
||||
if (action === 'create') {
|
||||
attributes = this.getSchema()[entity].attributes;
|
||||
for (key in attributes) {
|
||||
if (data[key] === undefined) {
|
||||
Object.assign(data, (_a = {},
|
||||
_a[key] = null,
|
||||
_a));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [2 /*return*/, _super.prototype.updateAbjointRow.call(this, entity, operation, context, option)];
|
||||
});
|
||||
});
|
||||
};
|
||||
CacheStore.prototype.operate = function (entity, operation, context, option) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var autoCommit, result, err_1;
|
||||
|
|
@ -77,7 +100,9 @@ var CacheStore = /** @class */ (function (_super) {
|
|||
_a.label = 2;
|
||||
case 2:
|
||||
_a.trys.push([2, 4, , 7]);
|
||||
return [4 /*yield*/, _super.prototype.sync.call(this, opRecords, context)];
|
||||
return [4 /*yield*/, _super.prototype.sync.call(this, opRecords, context, {
|
||||
inSync: true,
|
||||
})];
|
||||
case 3:
|
||||
result = _a.sent();
|
||||
return [3 /*break*/, 7];
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import { EntityDict, SelectOption, Context, RowStore, DeduceCreateOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperateOption, SelectionResult, SelectRowShape } from "oak-domain/lib/types";
|
||||
import { TreeStore } from 'oak-memory-tree-store';
|
||||
import { EntityDict, Context, RowStore, DeduceCreateOperation, DeduceRemoveOperation, DeduceUpdateOperation, SelectionResult, SelectRowShape } from "oak-domain/lib/types";
|
||||
import { TreeStore, TreeStoreOperateOption, TreeStoreSelectOption } from 'oak-memory-tree-store';
|
||||
import { StorageSchema, Trigger, Checker } from "oak-domain/lib/types";
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
interface DebugStoreOperateOption extends OperateOption {
|
||||
interface DebugStoreOperateOption extends TreeStoreOperateOption {
|
||||
noLock?: true;
|
||||
}
|
||||
interface DebugStoreSelectOption extends SelectOption {
|
||||
interface DebugStoreSelectOption extends TreeStoreSelectOption {
|
||||
noLock?: true;
|
||||
}
|
||||
export declare class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends Context<ED>> extends TreeStore<ED, Cxt> {
|
||||
private executor;
|
||||
private rwLock;
|
||||
constructor(storageSchema: StorageSchema<ED>, contextBuilder: (cxtString?: string) => (store: RowStore<ED, Cxt>) => Promise<Cxt>);
|
||||
protected updateAbjointRow<T extends keyof ED, OP extends DebugStoreOperateOption>(entity: T, operation: ED[T]['CreateSingle'] | ED[T]['Update'] | ED[T]['Remove'], context: Cxt, option?: OP): Promise<number>;
|
||||
protected cascadeUpdate<T extends keyof ED, OP extends DebugStoreOperateOption>(entity: T, operation: DeduceCreateOperation<ED[T]["Schema"]> | DeduceUpdateOperation<ED[T]["Schema"]> | DeduceRemoveOperation<ED[T]["Schema"]>, context: Cxt, option: OP): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
|
||||
protected cascadeSelect<T extends keyof ED, S extends ED[T]["Selection"], OP extends DebugStoreSelectOption>(entity: T, selection: S, context: Cxt, option: OP): Promise<SelectRowShape<ED[T]['Schema'], S['data']>[]>;
|
||||
operate<T extends keyof ED, OP extends DebugStoreOperateOption>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,26 @@ var DebugStore = /** @class */ (function (_super) {
|
|||
_this.rwLock = new concurrent_1.RWLock();
|
||||
return _this;
|
||||
}
|
||||
DebugStore.prototype.updateAbjointRow = function (entity, operation, context, option) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var action, data, attributes, key;
|
||||
var _a;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
action = operation.action, data = operation.data;
|
||||
if (action === 'create') {
|
||||
attributes = this.getSchema()[entity].attributes;
|
||||
for (key in attributes) {
|
||||
if (data[key] === undefined) {
|
||||
Object.assign(data, (_a = {},
|
||||
_a[key] = null,
|
||||
_a));
|
||||
}
|
||||
}
|
||||
}
|
||||
return [2 /*return*/, _super.prototype.updateAbjointRow.call(this, entity, operation, context, option)];
|
||||
});
|
||||
});
|
||||
};
|
||||
DebugStore.prototype.cascadeUpdate = function (entity, operation, context, option) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var result;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EntityDict, OperateOption, SelectOption, OpRecord, Context, AspectWrapper, SelectionResult, CheckerType } from 'oak-domain/lib/types';
|
||||
import { EntityDict, OperateOption, SelectOption, OpRecord, Context, AspectWrapper, CheckerType } from 'oak-domain/lib/types';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { CommonAspectDict } from 'oak-common-aspect';
|
||||
import { Feature } from '../types/Feature';
|
||||
|
|
@ -36,7 +36,8 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict, Cxt extends C
|
|||
tryRedoOperationsThenSelect<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, opers: Array<{
|
||||
entity: keyof ED;
|
||||
operation: ED[keyof ED]['Operation'];
|
||||
}>): Promise<SelectionResult<ED[T]["Schema"], S["data"]>>;
|
||||
}>): Promise<import("oak-domain/lib/types").SelectRowShape<ED[T]["Schema"], S["data"]>[]>;
|
||||
private getInner;
|
||||
get<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, params?: SelectOption): Promise<import("oak-domain/lib/types").SelectRowShape<ED[T]["Schema"], S["data"]>[]>;
|
||||
judgeRelation(entity: keyof ED, attr: string): string | 0 | 1 | 2 | string[];
|
||||
bindOnSync(callback: (opRecords: OpRecord<ED>[]) => Promise<void>): void;
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ var Cache = /** @class */ (function (_super) {
|
|||
*/
|
||||
Cache.prototype.tryRedoOperationsThenSelect = function (entity, selection, opers) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var result, context, opers_1, opers_1_1, oper, e_2_1, err_3, missedRows;
|
||||
var context, opers_1, opers_1_1, oper, e_2_1, result, err_3;
|
||||
var e_2, _a;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
|
|
@ -269,50 +269,63 @@ var Cache = /** @class */ (function (_super) {
|
|||
(0, selection_1.reinforceSelection)(this.cacheStore.getSchema(), entity, selection);
|
||||
_b.label = 10;
|
||||
case 10:
|
||||
if (!true) return [3 /*break*/, 20];
|
||||
_b.label = 11;
|
||||
_b.trys.push([10, 13, , 15]);
|
||||
return [4 /*yield*/, this.getInner(entity, selection, context)];
|
||||
case 11:
|
||||
_b.trys.push([11, 14, , 19]);
|
||||
result = _b.sent();
|
||||
return [4 /*yield*/, context.rollback()];
|
||||
case 12:
|
||||
_b.sent();
|
||||
return [2 /*return*/, result];
|
||||
case 13:
|
||||
err_3 = _b.sent();
|
||||
return [4 /*yield*/, context.rollback()];
|
||||
case 14:
|
||||
_b.sent();
|
||||
throw err_3;
|
||||
case 15: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
Cache.prototype.getInner = function (entity, selection, context) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var result, err_4, missedRows;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
if (!true) return [3 /*break*/, 8];
|
||||
_a.label = 1;
|
||||
case 1:
|
||||
_a.trys.push([1, 3, , 7]);
|
||||
return [4 /*yield*/, this.cacheStore.select(entity, selection, context, {
|
||||
dontCollect: true,
|
||||
})];
|
||||
case 12:
|
||||
result = _b.sent();
|
||||
return [4 /*yield*/, context.rollback()];
|
||||
case 13:
|
||||
_b.sent();
|
||||
case 2:
|
||||
result = (_a.sent()).result;
|
||||
return [2 /*return*/, result];
|
||||
case 14:
|
||||
err_3 = _b.sent();
|
||||
if (!(err_3 instanceof Exception_1.OakRowUnexistedException)) return [3 /*break*/, 16];
|
||||
missedRows = err_3.getRows();
|
||||
case 3:
|
||||
err_4 = _a.sent();
|
||||
if (!(err_4 instanceof Exception_1.OakRowUnexistedException)) return [3 /*break*/, 5];
|
||||
missedRows = err_4.getRows();
|
||||
return [4 /*yield*/, this.aspectWrapper.exec('fetchRows', missedRows)];
|
||||
case 15:
|
||||
_b.sent();
|
||||
return [3 /*break*/, 18];
|
||||
case 16: return [4 /*yield*/, context.rollback()];
|
||||
case 17:
|
||||
_b.sent();
|
||||
throw err_3;
|
||||
case 18: return [3 /*break*/, 19];
|
||||
case 19: return [3 /*break*/, 10];
|
||||
case 20: return [2 /*return*/];
|
||||
case 4:
|
||||
_a.sent();
|
||||
return [3 /*break*/, 6];
|
||||
case 5: throw err_4;
|
||||
case 6: return [3 /*break*/, 7];
|
||||
case 7: return [3 /*break*/, 0];
|
||||
case 8: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
Cache.prototype.get = function (entity, selection, params) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var context, result;
|
||||
var context;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
context = this.contextBuilder();
|
||||
return [4 /*yield*/, this.cacheStore.select(entity, selection, context, {})];
|
||||
case 1:
|
||||
result = (_a.sent()).result;
|
||||
return [2 /*return*/, result];
|
||||
}
|
||||
context = this.contextBuilder();
|
||||
return [2 /*return*/, this.getInner(entity, selection, context)];
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ declare class ListNode<ED extends EntityDict & BaseEntityDict, T extends keyof E
|
|||
entity: T;
|
||||
})[] | undefined>;
|
||||
getProjection(): Promise<ED[T]['Selection']['data']>;
|
||||
constructSelection(withParent?: true): Promise<{
|
||||
constructSelection(withParent?: true, disableOperation?: boolean): Promise<{
|
||||
data: ED[T]["Selection"]["data"];
|
||||
filter: ED[T]["Selection"]["filter"] | undefined;
|
||||
sorter: DeduceSorterItem<ED[T]["Schema"]>[];
|
||||
|
|
@ -114,7 +114,7 @@ declare class SingleNode<ED extends EntityDict & BaseEntityDict, T extends keyof
|
|||
};
|
||||
addChild(path: string, node: SingleNode<ED, keyof ED, Cxt, AD> | ListNode<ED, keyof ED, Cxt, AD>): void;
|
||||
removeChild(path: string): void;
|
||||
getFreshValue(): Promise<SelectRowShape<ED[T]['Schema'], ED[T]['Selection']['data']> | undefined>;
|
||||
getFreshValue(disableOperation?: boolean): Promise<SelectRowShape<ED[T]['Schema'], ED[T]['Selection']['data']> | undefined>;
|
||||
doBeforeTrigger(): Promise<void>;
|
||||
doAfterTrigger(): Promise<void>;
|
||||
addOperation(oper: Omit<ED[T]['Operation'], 'id'>, beforeExecute?: Operation<ED, T>['beforeExecute'], afterExecute?: Operation<ED, T>['afterExecute']): Promise<void>;
|
||||
|
|
@ -124,7 +124,7 @@ declare class SingleNode<ED extends EntityDict & BaseEntityDict, T extends keyof
|
|||
getProjection(withDecendants?: boolean): Promise<ED[keyof ED]["Selection"]["data"]>;
|
||||
refresh(): Promise<void>;
|
||||
clean(): void;
|
||||
getParentFilter<T2 extends keyof ED>(childNode: Node<ED, keyof ED, Cxt, AD>): Promise<ED[T2]['Selection']['filter'] | undefined>;
|
||||
getParentFilter<T2 extends keyof ED>(childNode: Node<ED, keyof ED, Cxt, AD>, disableOperation?: boolean): Promise<ED[T2]['Selection']['filter'] | undefined>;
|
||||
}
|
||||
declare class VirtualNode {
|
||||
private dirty;
|
||||
|
|
|
|||
|
|
@ -600,7 +600,7 @@ var ListNode = /** @class */ (function (_super) {
|
|||
finally { if (e_9) throw e_9.error; }
|
||||
}
|
||||
if (!needRefresh) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, this.constructSelection(true)];
|
||||
return [4 /*yield*/, this.constructSelection(true, true)];
|
||||
case 1:
|
||||
_a = _c.sent(), filter = _a.filter, sorter = _a.sorter;
|
||||
return [4 /*yield*/, this.cache.get(this.getEntity(), {
|
||||
|
|
@ -895,7 +895,7 @@ var ListNode = /** @class */ (function (_super) {
|
|||
}
|
||||
return [4 /*yield*/, this.cache.tryRedoOperationsThenSelect(this.entity, selection, operations)];
|
||||
case 4:
|
||||
result = (_e.sent()).result;
|
||||
result = _e.sent();
|
||||
return [2 /*return*/, result];
|
||||
case 5: return [2 /*return*/, []];
|
||||
}
|
||||
|
|
@ -1155,7 +1155,7 @@ var ListNode = /** @class */ (function (_super) {
|
|||
});
|
||||
});
|
||||
};
|
||||
ListNode.prototype.constructSelection = function (withParent) {
|
||||
ListNode.prototype.constructSelection = function (withParent, disableOperation) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var _a, filters, sorters, data, validParentFilter, sorterArr, filterArr, filterOfParent, filters2, filter;
|
||||
var _this = this;
|
||||
|
|
@ -1200,7 +1200,7 @@ var ListNode = /** @class */ (function (_super) {
|
|||
filterArr = _b.sent();
|
||||
if (!(withParent && this.parent)) return [3 /*break*/, 5];
|
||||
if (!(this.parent instanceof SingleNode)) return [3 /*break*/, 5];
|
||||
return [4 /*yield*/, this.parent.getParentFilter(this)];
|
||||
return [4 /*yield*/, this.parent.getParentFilter(this, disableOperation)];
|
||||
case 4:
|
||||
filterOfParent = _b.sent();
|
||||
if (filterOfParent) {
|
||||
|
|
@ -1400,7 +1400,7 @@ var SingleNode = /** @class */ (function (_super) {
|
|||
SingleNode.prototype.removeChild = function (path) {
|
||||
(0, lodash_1.unset)(this.children, path);
|
||||
};
|
||||
SingleNode.prototype.getFreshValue = function () {
|
||||
SingleNode.prototype.getFreshValue = function (disableOperation) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var projection, modies, _a, operations, filter, createOper, parent_1, result;
|
||||
var _this = this;
|
||||
|
|
@ -1419,31 +1419,38 @@ var SingleNode = /** @class */ (function (_super) {
|
|||
modies = _a;
|
||||
operations = modies ? (0, modi_1.createOperationsFromModies)(modies) : [];
|
||||
filter = this.id && { id: this.id };
|
||||
if (!filter) {
|
||||
if (!filter && !disableOperation) {
|
||||
createOper = this.operations.find(function (ele) { return ele.oper.action === 'create'; });
|
||||
if (createOper) {
|
||||
(0, assert_1.assert)(createOper.oper.data.id);
|
||||
filter = {
|
||||
id: createOper.oper.data.id
|
||||
};
|
||||
}
|
||||
parent_1 = this.parent;
|
||||
if (parent_1 instanceof ListNode || parent_1 instanceof SingleNode) {
|
||||
filter = parent_1.getParentFilter(this);
|
||||
}
|
||||
}
|
||||
if (!filter) return [3 /*break*/, 5];
|
||||
operations.push.apply(operations, tslib_1.__spreadArray([], tslib_1.__read(this.operations.map(function (ele) { return ({
|
||||
entity: _this.entity,
|
||||
operation: ele.oper,
|
||||
}); })), false));
|
||||
if (!!filter) return [3 /*break*/, 5];
|
||||
parent_1 = this.parent;
|
||||
if (!(parent_1 instanceof ListNode || parent_1 instanceof SingleNode)) return [3 /*break*/, 5];
|
||||
return [4 /*yield*/, parent_1.getParentFilter(this, disableOperation)];
|
||||
case 4:
|
||||
filter = _b.sent();
|
||||
_b.label = 5;
|
||||
case 5:
|
||||
if (!filter) return [3 /*break*/, 7];
|
||||
if (!disableOperation) {
|
||||
operations.push.apply(operations, tslib_1.__spreadArray([], tslib_1.__read(this.operations.map(function (ele) { return ({
|
||||
entity: _this.entity,
|
||||
operation: ele.oper,
|
||||
}); })), false));
|
||||
}
|
||||
return [4 /*yield*/, this.cache.tryRedoOperationsThenSelect(this.entity, {
|
||||
data: projection,
|
||||
filter: filter,
|
||||
}, operations)];
|
||||
case 4:
|
||||
result = (_b.sent()).result;
|
||||
case 6:
|
||||
result = _b.sent();
|
||||
return [2 /*return*/, result[0]];
|
||||
case 5: return [2 /*return*/];
|
||||
case 7: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1863,13 +1870,13 @@ var SingleNode = /** @class */ (function (_super) {
|
|||
this.children[child].clean();
|
||||
}
|
||||
};
|
||||
SingleNode.prototype.getParentFilter = function (childNode) {
|
||||
SingleNode.prototype.getParentFilter = function (childNode, disableOperation) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var value, key, sliceIdx, key2, rel;
|
||||
var _a;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, this.getFreshValue()];
|
||||
case 0: return [4 /*yield*/, this.getFreshValue(disableOperation)];
|
||||
case 1:
|
||||
value = (_b.sent());
|
||||
if (!value) {
|
||||
|
|
@ -1882,28 +1889,34 @@ var SingleNode = /** @class */ (function (_super) {
|
|||
rel = this.judgeRelation(key2);
|
||||
if (rel === 2) {
|
||||
// 基于entity/entityId的多对一
|
||||
return [2 /*return*/, {
|
||||
id: value.entityId,
|
||||
}];
|
||||
if (value.entityId && value.entity === childNode.getEntity()) {
|
||||
return [2 /*return*/, {
|
||||
id: value.entityId,
|
||||
}];
|
||||
}
|
||||
return [2 /*return*/, undefined];
|
||||
}
|
||||
else if (typeof rel === 'string') {
|
||||
return [2 /*return*/, {
|
||||
id: value["".concat(rel, "Id")],
|
||||
}];
|
||||
if (value["".concat(rel, "Id")]) {
|
||||
return [2 /*return*/, {
|
||||
id: value["".concat(rel, "Id")],
|
||||
}];
|
||||
}
|
||||
return [2 /*return*/, undefined];
|
||||
}
|
||||
else {
|
||||
(0, assert_1.assert)(rel instanceof Array);
|
||||
if (rel[1]) {
|
||||
// 基于普通外键的一对多
|
||||
return [2 /*return*/, (_a = {},
|
||||
_a[rel[1]] = this.id,
|
||||
_a[rel[1]] = value.id,
|
||||
_a)];
|
||||
}
|
||||
else {
|
||||
// 基于entity/entityId的一对多
|
||||
return [2 /*return*/, {
|
||||
entity: this.entity,
|
||||
entityId: this.id,
|
||||
entityId: value.id,
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
import { EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
|
||||
import { DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, EntityDict, OperateOption, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity';
|
||||
import { StorageSchema } from "oak-domain/lib/types/Storage";
|
||||
import { TriggerExecutor } from 'oak-domain/lib/store/TriggerExecutor';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { Checker, CheckerType, Context, Trigger } from 'oak-domain/lib/types';
|
||||
import { TreeStore } from 'oak-memory-tree-store';
|
||||
import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store';
|
||||
import assert from 'assert';
|
||||
|
||||
interface CachStoreOperation extends TreeStoreOperateOption {
|
||||
inSync?: boolean;
|
||||
};
|
||||
|
||||
export class CacheStore<
|
||||
ED extends EntityDict & BaseEntityDict,
|
||||
Cxt extends Context<ED>
|
||||
|
|
@ -28,7 +32,25 @@ export class CacheStore<
|
|||
this.resetInitialDataFn = resetInitialDataFn;
|
||||
}
|
||||
|
||||
async operate<T extends keyof ED, OP extends OperateOption>(
|
||||
protected async updateAbjointRow<T extends keyof ED>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, option?: CachStoreOperation): Promise<number> {
|
||||
if (!option?.inSync) {
|
||||
// 如果不是同步,需要补齐所有的null属性
|
||||
const { action, data } = operation;
|
||||
if (action === 'create') {
|
||||
const { attributes } = this.getSchema()[entity];
|
||||
for (const key in attributes) {
|
||||
if (data[key] === undefined) {
|
||||
Object.assign(data, {
|
||||
[key]: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.updateAbjointRow(entity, operation, context, option);
|
||||
}
|
||||
|
||||
async operate<T extends keyof ED, OP extends TreeStoreOperateOption>(
|
||||
entity: T,
|
||||
operation: ED[T]['Operation'],
|
||||
context: Cxt,
|
||||
|
|
@ -67,7 +89,9 @@ export class CacheStore<
|
|||
let result;
|
||||
|
||||
try {
|
||||
result = await super.sync(opRecords, context);
|
||||
result = await super.sync<CachStoreOperation>(opRecords, context, {
|
||||
inSync: true,
|
||||
});
|
||||
} catch (err) {
|
||||
if (autoCommit) {
|
||||
await context.rollback();
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { EntityDict, SelectOption, Context, RowStore, DeduceCreateOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperateOption, SelectionResult, SelectRowShape } from "oak-domain/lib/types";
|
||||
import { TreeStore } from 'oak-memory-tree-store';
|
||||
import { TreeStore, TreeStoreOperateOption, TreeStoreSelectOption } from 'oak-memory-tree-store';
|
||||
import { StorageSchema, Trigger, Checker } from "oak-domain/lib/types";
|
||||
import { TriggerExecutor } from 'oak-domain/lib/store/TriggerExecutor';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { RWLock } from 'oak-domain/lib/utils/concurrent';
|
||||
|
||||
interface DebugStoreOperateOption extends OperateOption {
|
||||
interface DebugStoreOperateOption extends TreeStoreOperateOption {
|
||||
noLock?: true;
|
||||
};
|
||||
|
||||
interface DebugStoreSelectOption extends SelectOption {
|
||||
interface DebugStoreSelectOption extends TreeStoreSelectOption {
|
||||
noLock?: true;
|
||||
};
|
||||
|
||||
|
|
@ -22,6 +22,26 @@ export class DebugStore<ED extends EntityDict & BaseEntityDict, Cxt extends Cont
|
|||
this.rwLock = new RWLock();
|
||||
}
|
||||
|
||||
protected async updateAbjointRow<T extends keyof ED, OP extends DebugStoreOperateOption>(
|
||||
entity: T,
|
||||
operation: ED[T]['CreateSingle'] | ED[T]['Update'] | ED[T]['Remove'],
|
||||
context: Cxt,
|
||||
option?: OP) {
|
||||
// 对于create动作,没有值的属性要置NULL
|
||||
const { action, data } = operation;
|
||||
if (action === 'create') {
|
||||
const { attributes } = this.getSchema()[entity];
|
||||
for (const key in attributes) {
|
||||
if (data[key] === undefined) {
|
||||
Object.assign(data, {
|
||||
[key]: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.updateAbjointRow(entity, operation, context, option);
|
||||
}
|
||||
|
||||
protected async cascadeUpdate<T extends keyof ED, OP extends DebugStoreOperateOption>(entity: T, operation: DeduceCreateOperation<ED[T]["Schema"]> | DeduceUpdateOperation<ED[T]["Schema"]> | DeduceRemoveOperation<ED[T]["Schema"]>, context: Cxt, option: OP) {
|
||||
if (!option.blockTrigger) {
|
||||
await this.executor.preOperation(entity, operation, context, option);
|
||||
|
|
|
|||
|
|
@ -171,7 +171,6 @@ export class Cache<
|
|||
operation: ED[keyof ED]['Operation'];
|
||||
}>
|
||||
) {
|
||||
let result: SelectionResult<ED[T]['Schema'], S['data']>;
|
||||
const context = this.contextBuilder!();
|
||||
await context.begin();
|
||||
for (const oper of opers) {
|
||||
|
|
@ -188,9 +187,21 @@ export class Cache<
|
|||
);
|
||||
}
|
||||
reinforceSelection(this.cacheStore!.getSchema(), entity, selection);
|
||||
try {
|
||||
const result = await this.getInner(entity, selection, context);
|
||||
await context.rollback();
|
||||
return result;
|
||||
}
|
||||
catch (err) {
|
||||
await context.rollback();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
private async getInner<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, context: Cxt) {
|
||||
while (true) {
|
||||
try {
|
||||
result = await this.cacheStore!.select(
|
||||
const { result } = await this.cacheStore!.select(
|
||||
entity,
|
||||
selection,
|
||||
context,
|
||||
|
|
@ -198,15 +209,12 @@ export class Cache<
|
|||
dontCollect: true,
|
||||
}
|
||||
);
|
||||
|
||||
await context.rollback();
|
||||
return result;
|
||||
} catch (err) {
|
||||
if (err instanceof OakRowUnexistedException) {
|
||||
const missedRows = err.getRows();
|
||||
await this.aspectWrapper.exec('fetchRows', missedRows);
|
||||
} else {
|
||||
await context.rollback();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
|
@ -219,13 +227,8 @@ export class Cache<
|
|||
params?: SelectOption
|
||||
) {
|
||||
const context = this.contextBuilder!();
|
||||
const { result } = await this.cacheStore!.select(
|
||||
entity,
|
||||
selection,
|
||||
context,
|
||||
{}
|
||||
);
|
||||
return result;
|
||||
|
||||
return this.getInner(entity, selection, context);
|
||||
}
|
||||
|
||||
judgeRelation(entity: keyof ED, attr: string) {
|
||||
|
|
|
|||
|
|
@ -583,7 +583,8 @@ class ListNode<
|
|||
此时对userRelation的删除动作就会导致user不会被移出list
|
||||
*/
|
||||
if (needRefresh) {
|
||||
const { filter, sorter } = await this.constructSelection(true);
|
||||
// 这里因为operation还没被移除掉(execute还没有结束),所以同步的时候不能计算动态的operation产生的id
|
||||
const { filter, sorter } = await this.constructSelection(true, true);
|
||||
const result = await this.cache.get(this.getEntity(), {
|
||||
data: {
|
||||
id: 1,
|
||||
|
|
@ -815,7 +816,7 @@ class ListNode<
|
|||
});
|
||||
}
|
||||
|
||||
const { result } = await this.cache.tryRedoOperationsThenSelect(this.entity, selection, operations);
|
||||
const result = await this.cache.tryRedoOperationsThenSelect(this.entity, selection, operations);
|
||||
return result;
|
||||
}
|
||||
return [];
|
||||
|
|
@ -936,7 +937,7 @@ class ListNode<
|
|||
return projection;
|
||||
}
|
||||
|
||||
async constructSelection(withParent?: true) {
|
||||
async constructSelection(withParent?: true, disableOperation?: boolean) {
|
||||
const { filters, sorters } = this;
|
||||
const data = await this.getProjection();
|
||||
let validParentFilter = true;
|
||||
|
|
@ -964,7 +965,7 @@ class ListNode<
|
|||
|
||||
if (withParent && this.parent) {
|
||||
if (this.parent instanceof SingleNode) {
|
||||
const filterOfParent = await this.parent.getParentFilter<T>(this);
|
||||
const filterOfParent = await this.parent.getParentFilter<T>(this, disableOperation);
|
||||
if (filterOfParent) {
|
||||
filterArr.push(filterOfParent as any);
|
||||
}
|
||||
|
|
@ -1149,7 +1150,7 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
|
|||
unset(this.children, path);
|
||||
}
|
||||
|
||||
async getFreshValue(): Promise<SelectRowShape<ED[T]['Schema'], ED[T]['Selection']['data']> | undefined> {
|
||||
async getFreshValue(disableOperation?: boolean): Promise<SelectRowShape<ED[T]['Schema'], ED[T]['Selection']['data']> | undefined> {
|
||||
const projection = await this.getProjection(false);
|
||||
|
||||
// 如果本结点是在modi路径上,需要将modi更新之后再得到后项
|
||||
|
|
@ -1159,31 +1160,35 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
|
|||
operation: ED[keyof ED]['Operation'];
|
||||
}> : [];
|
||||
let filter = this.id && { id: this.id } as ED[T]['Selection']['filter'];
|
||||
if (!filter) {
|
||||
if (!filter && !disableOperation) {
|
||||
// 可能是create
|
||||
const createOper = this.operations.find(
|
||||
(ele) => ele.oper.action === 'create'
|
||||
) as { oper: ED[T]['CreateSingle'] };
|
||||
if (createOper) {
|
||||
assert(createOper.oper.data.id);
|
||||
filter = {
|
||||
id: createOper.oper.data.id
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!filter) {
|
||||
// 还可能是来自父级的外键
|
||||
const { parent } = this;
|
||||
if (parent instanceof ListNode || parent instanceof SingleNode) {
|
||||
filter = parent.getParentFilter(this);
|
||||
filter = await parent.getParentFilter(this, disableOperation);
|
||||
}
|
||||
|
||||
}
|
||||
if (filter) {
|
||||
operations.push(...this.operations.map(
|
||||
ele => ({
|
||||
entity: this.entity,
|
||||
operation: ele.oper,
|
||||
})
|
||||
));
|
||||
const { result } = await this.cache.tryRedoOperationsThenSelect(this.entity, {
|
||||
if (!disableOperation) {
|
||||
operations.push(...this.operations.map(
|
||||
ele => ({
|
||||
entity: this.entity,
|
||||
operation: ele.oper,
|
||||
})
|
||||
));
|
||||
}
|
||||
const result = await this.cache.tryRedoOperationsThenSelect(this.entity, {
|
||||
data: projection,
|
||||
filter,
|
||||
}, operations);
|
||||
|
|
@ -1439,8 +1444,8 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
|
|||
}
|
||||
}
|
||||
|
||||
async getParentFilter<T2 extends keyof ED>(childNode: Node<ED, keyof ED, Cxt, AD>): Promise<ED[T2]['Selection']['filter'] | undefined> {
|
||||
const value = (await this.getFreshValue())!;
|
||||
async getParentFilter<T2 extends keyof ED>(childNode: Node<ED, keyof ED, Cxt, AD>, disableOperation?: boolean): Promise<ED[T2]['Selection']['filter'] | undefined> {
|
||||
const value = (await this.getFreshValue(disableOperation))!;
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1452,28 +1457,34 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
|
|||
const rel = this.judgeRelation(key2);
|
||||
if (rel === 2) {
|
||||
// 基于entity/entityId的多对一
|
||||
return {
|
||||
id: value.entityId,
|
||||
};
|
||||
if (value.entityId && value.entity === childNode.getEntity()) {
|
||||
return {
|
||||
id: value.entityId,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
else if (typeof rel === 'string') {
|
||||
return {
|
||||
id: value[`${rel}Id`],
|
||||
};
|
||||
if (value[`${rel}Id`]) {
|
||||
return {
|
||||
id: value[`${rel}Id`],
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
else {
|
||||
assert(rel instanceof Array);
|
||||
if (rel[1]) {
|
||||
// 基于普通外键的一对多
|
||||
return {
|
||||
[rel[1]]: this.id,
|
||||
[rel[1]]: value.id,
|
||||
};
|
||||
}
|
||||
else {
|
||||
// 基于entity/entityId的一对多
|
||||
return {
|
||||
entity: this.entity,
|
||||
entityId: this.id,
|
||||
entityId: value.id,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue