merge
This commit is contained in:
commit
6e5d7d5bfe
|
|
@ -7,6 +7,7 @@ export declare abstract class AsyncContext<ED extends EntityDict> implements Con
|
||||||
opRecords: OpRecord<ED>[];
|
opRecords: OpRecord<ED>[];
|
||||||
private scene?;
|
private scene?;
|
||||||
private headers?;
|
private headers?;
|
||||||
|
private message?;
|
||||||
events: {
|
events: {
|
||||||
commit: Array<() => Promise<void>>;
|
commit: Array<() => Promise<void>>;
|
||||||
rollback: Array<() => Promise<void>>;
|
rollback: Array<() => Promise<void>>;
|
||||||
|
|
@ -32,6 +33,8 @@ export declare abstract class AsyncContext<ED extends EntityDict> implements Con
|
||||||
mergeMultipleResults(toBeMerged: OperationResult<ED>[]): OperationResult<ED>;
|
mergeMultipleResults(toBeMerged: OperationResult<ED>[]): OperationResult<ED>;
|
||||||
getCurrentTxnId(): string | undefined;
|
getCurrentTxnId(): string | undefined;
|
||||||
getSchema(): import("../types").StorageSchema<ED>;
|
getSchema(): import("../types").StorageSchema<ED>;
|
||||||
|
setMessage(message: string): void;
|
||||||
|
getMessage(): string | undefined;
|
||||||
abstract isRoot(): boolean;
|
abstract isRoot(): boolean;
|
||||||
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;
|
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;
|
||||||
abstract toString(): string;
|
abstract toString(): string;
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,12 @@ var AsyncContext = /** @class */ (function () {
|
||||||
AsyncContext.prototype.getSchema = function () {
|
AsyncContext.prototype.getSchema = function () {
|
||||||
return this.rowStore.getSchema();
|
return this.rowStore.getSchema();
|
||||||
};
|
};
|
||||||
|
AsyncContext.prototype.setMessage = function (message) {
|
||||||
|
this.message = message;
|
||||||
|
};
|
||||||
|
AsyncContext.prototype.getMessage = function () {
|
||||||
|
return this.message;
|
||||||
|
};
|
||||||
return AsyncContext;
|
return AsyncContext;
|
||||||
}());
|
}());
|
||||||
exports.AsyncContext = AsyncContext;
|
exports.AsyncContext = AsyncContext;
|
||||||
|
|
|
||||||
|
|
@ -543,7 +543,7 @@ var CascadeStore = /** @class */ (function (_super) {
|
||||||
var _g = tslib_1.__read(relation, 2), entityOtm_1 = _g[0], foreignKey_2 = _g[1];
|
var _g = tslib_1.__read(relation, 2), entityOtm_1 = _g[0], foreignKey_2 = _g[1];
|
||||||
var otmOperations = data[attr];
|
var otmOperations = data[attr];
|
||||||
var dealWithOneToMany = function (otm) {
|
var dealWithOneToMany = function (otm) {
|
||||||
var _a, _b, _c, _d, _e;
|
var _a, _b, _c, _d, _e, _f;
|
||||||
var actionOtm = otm.action, dataOtm = otm.data, filterOtm = otm.filter;
|
var actionOtm = otm.action, dataOtm = otm.data, filterOtm = otm.filter;
|
||||||
if (!foreignKey_2) {
|
if (!foreignKey_2) {
|
||||||
// 基于entity/entityId的one-to-many
|
// 基于entity/entityId的one-to-many
|
||||||
|
|
@ -581,13 +581,25 @@ var CascadeStore = /** @class */ (function (_super) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作
|
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||||
// 这个倒是好像不可能出现create/update的一对多,如果遇到了再完善
|
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||||
Object.assign(otm, {
|
if (filter) {
|
||||||
filter: (0, filter_1.addFilterSegment)((_a = {},
|
if (filter.id && Object.keys(filter).length === 1) {
|
||||||
_a[entity] = filter,
|
Object.assign(otm, {
|
||||||
_a), filterOtm),
|
filter: (0, filter_1.addFilterSegment)({
|
||||||
});
|
entity: entity,
|
||||||
|
entityId: filter.id,
|
||||||
|
}, filterOtm),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Object.assign(otm, {
|
||||||
|
filter: (0, filter_1.addFilterSegment)((_a = {},
|
||||||
|
_a[entity] = filter,
|
||||||
|
_a), filterOtm),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
if (action === 'remove' && actionOtm === 'update') {
|
if (action === 'remove' && actionOtm === 'update') {
|
||||||
Object.assign(dataOtm, {
|
Object.assign(dataOtm, {
|
||||||
entity: null,
|
entity: null,
|
||||||
|
|
@ -634,20 +646,34 @@ var CascadeStore = /** @class */ (function (_super) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||||
Object.assign(otm, {
|
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||||
filter: (0, filter_1.addFilterSegment)((_d = {},
|
// 绝大多数情况都是id,但也有可能update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
||||||
_d[foreignKey_2.slice(0, foreignKey_2.length - 2)] = filter,
|
if (filter) {
|
||||||
_d), filterOtm),
|
if (filter.id && Object.keys(filter).length === 1) {
|
||||||
});
|
Object.assign(otm, {
|
||||||
|
filter: (0, filter_1.addFilterSegment)((_d = {},
|
||||||
|
_d[foreignKey_2] = filter.id,
|
||||||
|
_d), filterOtm),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Object.assign(otm, {
|
||||||
|
filter: (0, filter_1.addFilterSegment)((_e = {},
|
||||||
|
_e[foreignKey_2.slice(0, foreignKey_2.length - 2)] = filter,
|
||||||
|
_e), filterOtm),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
if (action === 'remove' && actionOtm === 'update') {
|
if (action === 'remove' && actionOtm === 'update') {
|
||||||
Object.assign(dataOtm, (_e = {},
|
Object.assign(dataOtm, (_f = {},
|
||||||
_e[foreignKey_2] = null,
|
_f[foreignKey_2] = null,
|
||||||
_e));
|
_f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
beforeFns.push(function () { return cascadeUpdate.call(_this, entityOtm_1, otm, context, option2); });
|
// 一对多的依赖应该后建,否则中间会出现空指针,导致checker等出错
|
||||||
|
afterFns.push(function () { return cascadeUpdate.call(_this, entityOtm_1, otm, context, option2); });
|
||||||
};
|
};
|
||||||
if (otmOperations instanceof Array) {
|
if (otmOperations instanceof Array) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,8 @@ function createModiRelatedTriggers(schema) {
|
||||||
var _loop_2 = function (entity) {
|
var _loop_2 = function (entity) {
|
||||||
var inModi = schema[entity].inModi;
|
var inModi = schema[entity].inModi;
|
||||||
if (inModi) {
|
if (inModi) {
|
||||||
// 当关联modi的对象被删除时,对应的modi也删除
|
// 当关联modi的对象被删除时,对应的modi也删除。这里似乎只需要删除掉活跃对象?因为oper不能删除,所以oper和modi是必须要支持对deleted对象的容错?
|
||||||
|
// 这里没有想清楚,by Xc 20230209
|
||||||
triggers.push({
|
triggers.push({
|
||||||
name: "\u5F53\u5220\u9664".concat(entity, "\u5BF9\u8C61\u65F6\uFF0C\u5220\u9664\u76F8\u5173\u8054\u7684modi\u7684modiEntity"),
|
name: "\u5F53\u5220\u9664".concat(entity, "\u5BF9\u8C61\u65F6\uFF0C\u5220\u9664\u76F8\u5173\u8054\u7684modi\u7684modiEntity"),
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
|
|
@ -163,43 +164,42 @@ function createModiRelatedTriggers(schema) {
|
||||||
fn: function (_a, context, option) {
|
fn: function (_a, context, option) {
|
||||||
var operation = _a.operation;
|
var operation = _a.operation;
|
||||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||||
var data, id, _b, _c, _d, _e, _f, _g;
|
var filter, _b, _c, _d, _e, _f, _g;
|
||||||
var _h, _j;
|
var _h, _j, _k, _l;
|
||||||
return tslib_1.__generator(this, function (_k) {
|
return tslib_1.__generator(this, function (_m) {
|
||||||
switch (_k.label) {
|
switch (_m.label) {
|
||||||
case 0:
|
case 0:
|
||||||
data = operation.data;
|
filter = operation.filter;
|
||||||
id = data.id;
|
|
||||||
_c = (_b = context).operate;
|
_c = (_b = context).operate;
|
||||||
_d = ['modiEntity'];
|
_d = ['modiEntity'];
|
||||||
_h = {};
|
_h = {};
|
||||||
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
|
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
|
||||||
case 1: return [4 /*yield*/, _c.apply(_b, _d.concat([(_h.id = _k.sent(),
|
case 1: return [4 /*yield*/, _c.apply(_b, _d.concat([(_h.id = _m.sent(),
|
||||||
_h.action = 'remove',
|
_h.action = 'remove',
|
||||||
_h.data = {},
|
_h.data = {},
|
||||||
_h.filter = {
|
_h.filter = {
|
||||||
modi: {
|
modi: (_j = {},
|
||||||
entity: entity,
|
_j[entity] = filter,
|
||||||
entityId: id,
|
_j.iState = 'active',
|
||||||
},
|
_j),
|
||||||
},
|
},
|
||||||
_h), { dontCollect: true }]))];
|
_h), { dontCollect: true }]))];
|
||||||
case 2:
|
case 2:
|
||||||
_k.sent();
|
_m.sent();
|
||||||
_f = (_e = context).operate;
|
_f = (_e = context).operate;
|
||||||
_g = ['modi'];
|
_g = ['modi'];
|
||||||
_j = {};
|
_k = {};
|
||||||
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
|
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
|
||||||
case 3: return [4 /*yield*/, _f.apply(_e, _g.concat([(_j.id = _k.sent(),
|
case 3: return [4 /*yield*/, _f.apply(_e, _g.concat([(_k.id = _m.sent(),
|
||||||
_j.action = 'remove',
|
_k.action = 'remove',
|
||||||
_j.data = {},
|
_k.data = {},
|
||||||
_j.filter = {
|
_k.filter = (_l = {},
|
||||||
entity: entity,
|
_l[entity] = filter,
|
||||||
entityId: id,
|
_l.iState = 'active',
|
||||||
},
|
_l),
|
||||||
_j), { dontCollect: true }]))];
|
_k), { dontCollect: true }]))];
|
||||||
case 4:
|
case 4:
|
||||||
_k.sent();
|
_m.sent();
|
||||||
return [2 /*return*/, 0];
|
return [2 /*return*/, 0];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ function reinforceSelection(schema, entity, selection) {
|
||||||
var projectionNodeDict = {};
|
var projectionNodeDict = {};
|
||||||
var checkProjectionNode = function (entity2, projectionNode) {
|
var checkProjectionNode = function (entity2, projectionNode) {
|
||||||
var _a, _b, _c;
|
var _a, _b, _c;
|
||||||
var necessaryAttrs = ['id', '$$createAt$$']; // 因有的页面依赖于其他页面的数据,因此默认的createAt的filter不一定会加上,为了保险起见在这里统一加上
|
var necessaryAttrs = ['id', '$$createAt$$']; // 有的页面依赖于其它页面取数据,有时两个页面的filter的差异会导致有一个加createAt,有一个不加,此时可能产生前台取数据不完整的异常。先统一加上
|
||||||
for (var attr in projectionNode) {
|
for (var attr in projectionNode) {
|
||||||
if (attr === '#id') {
|
if (attr === '#id') {
|
||||||
(0, assert_1.default)(!projectionNodeDict[projectionNode[attr]], "projection\u4E2D\u7ED3\u70B9\u7684id\u6709\u91CD\u590D, ".concat(projectionNode[attr]));
|
(0, assert_1.default)(!projectionNodeDict[projectionNode[attr]], "projection\u4E2D\u7ED3\u70B9\u7684id\u6709\u91CD\u590D, ".concat(projectionNode[attr]));
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ export interface Aspect<ED extends EntityDict, Cxt extends AsyncContext<ED>> {
|
||||||
export interface AspectWrapper<ED extends EntityDict, Cxt extends AsyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>> {
|
export interface AspectWrapper<ED extends EntityDict, Cxt extends AsyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>> {
|
||||||
exec: <T extends keyof AD>(name: T, params: Parameters<AD[T]>[0]) => Promise<{
|
exec: <T extends keyof AD>(name: T, params: Parameters<AD[T]>[0]) => Promise<{
|
||||||
result: Awaited<ReturnType<AD[T]>>;
|
result: Awaited<ReturnType<AD[T]>>;
|
||||||
opRecords: OpRecord<ED>[];
|
opRecords?: OpRecord<ED>[];
|
||||||
|
message?: string | null;
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ export declare type AuthDef<ED extends EntityDict, T extends keyof ED> = {
|
||||||
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
|
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
|
||||||
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
|
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
|
||||||
cascadeRemove?: {
|
cascadeRemove?: {
|
||||||
[E in (keyof ED | '@entity')]?: ActionOnRemove;
|
[E in (keyof ED | keyof ED[T]['Schema'] | '@entity')]?: ActionOnRemove;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export declare type AuthDefDict<ED extends EntityDict> = {
|
export declare type AuthDefDict<ED extends EntityDict> = {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import { OakException } from "./Exception";
|
||||||
export declare abstract class Connector<ED extends EntityDict, BackCxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>> {
|
export declare abstract class Connector<ED extends EntityDict, BackCxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>> {
|
||||||
abstract callAspect(name: string, params: any, context: FrontCxt): Promise<{
|
abstract callAspect(name: string, params: any, context: FrontCxt): Promise<{
|
||||||
result: any;
|
result: any;
|
||||||
opRecords: OpRecord<ED>[];
|
opRecords?: OpRecord<ED>[];
|
||||||
|
message?: string | null;
|
||||||
}>;
|
}>;
|
||||||
abstract getRouter(): string;
|
abstract getRouter(): string;
|
||||||
abstract parseRequest(headers: IncomingHttpHeaders, body: any, store: AsyncRowStore<ED, BackCxt>): Promise<{
|
abstract parseRequest(headers: IncomingHttpHeaders, body: any, store: AsyncRowStore<ED, BackCxt>): Promise<{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { IncomingHttpHeaders } from "http";
|
import { IncomingHttpHeaders } from "http";
|
||||||
import { AsyncContext, AsyncRowStore } from '../store/AsyncRowStore';
|
import { AsyncContext, AsyncRowStore } from '../store/AsyncRowStore';
|
||||||
import { SyncContext } from '../store/SyncRowStore';
|
import { SyncContext } from '../store/SyncRowStore';
|
||||||
import { Connector, EntityDict, OakException, OpRecord } from "../types";
|
import { Connector, EntityDict, OakException } from "../types";
|
||||||
export declare class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>> extends Connector<ED, BackCxt, FrontCxt> {
|
export declare class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>> extends Connector<ED, BackCxt, FrontCxt> {
|
||||||
static ROUTER: string;
|
static ROUTER: string;
|
||||||
private serverUrl;
|
private serverUrl;
|
||||||
|
|
@ -11,7 +11,12 @@ export declare class SimpleConnector<ED extends EntityDict, BackCxt extends Asyn
|
||||||
constructor(serverUrl: string, makeException: (exceptionData: any) => OakException<ED>, contextBuilder: (str: string | undefined) => (store: AsyncRowStore<ED, BackCxt>) => Promise<BackCxt>);
|
constructor(serverUrl: string, makeException: (exceptionData: any) => OakException<ED>, contextBuilder: (str: string | undefined) => (store: AsyncRowStore<ED, BackCxt>) => Promise<BackCxt>);
|
||||||
callAspect(name: string, params: any, context: FrontCxt): Promise<{
|
callAspect(name: string, params: any, context: FrontCxt): Promise<{
|
||||||
result: any;
|
result: any;
|
||||||
opRecords: OpRecord<ED>[];
|
opRecords: any;
|
||||||
|
message: string | null;
|
||||||
|
} | {
|
||||||
|
result: ArrayBuffer;
|
||||||
|
message: string | null;
|
||||||
|
opRecords?: undefined;
|
||||||
}>;
|
}>;
|
||||||
getRouter(): string;
|
getRouter(): string;
|
||||||
parseRequest(headers: IncomingHttpHeaders, body: any, store: AsyncRowStore<ED, BackCxt>): Promise<{
|
parseRequest(headers: IncomingHttpHeaders, body: any, store: AsyncRowStore<ED, BackCxt>): Promise<{
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ var SimpleConnector = /** @class */ (function (_super) {
|
||||||
}
|
}
|
||||||
SimpleConnector.prototype.callAspect = function (name, params, context) {
|
SimpleConnector.prototype.callAspect = function (name, params, context) {
|
||||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||||
var cxtStr, _a, contentType, body, response, err, _b, exception, result, opRecords;
|
var cxtStr, _a, contentType, body, response, err, message, responseType, _b, exception, result, opRecords, result;
|
||||||
return tslib_1.__generator(this, function (_c) {
|
return tslib_1.__generator(this, function (_c) {
|
||||||
switch (_c.label) {
|
switch (_c.label) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
@ -52,6 +52,9 @@ var SimpleConnector = /** @class */ (function (_super) {
|
||||||
err = new types_1.OakExternalException("\u7F51\u7EDC\u8BF7\u6C42\u8FD4\u56DE\u5F02\u5E38\uFF0Cstatus\u662F".concat(response.status));
|
err = new types_1.OakExternalException("\u7F51\u7EDC\u8BF7\u6C42\u8FD4\u56DE\u5F02\u5E38\uFF0Cstatus\u662F".concat(response.status));
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
message = response.headers.get('oak-message');
|
||||||
|
responseType = response.headers.get('Content-Type');
|
||||||
|
if (!(responseType === null || responseType === void 0 ? void 0 : responseType.toLocaleLowerCase().match(/application\/json/i))) return [3 /*break*/, 3];
|
||||||
return [4 /*yield*/, response.json()];
|
return [4 /*yield*/, response.json()];
|
||||||
case 2:
|
case 2:
|
||||||
_b = _c.sent(), exception = _b.exception, result = _b.result, opRecords = _b.opRecords;
|
_b = _c.sent(), exception = _b.exception, result = _b.result, opRecords = _b.opRecords;
|
||||||
|
|
@ -61,7 +64,18 @@ var SimpleConnector = /** @class */ (function (_super) {
|
||||||
return [2 /*return*/, {
|
return [2 /*return*/, {
|
||||||
result: result,
|
result: result,
|
||||||
opRecords: opRecords,
|
opRecords: opRecords,
|
||||||
|
message: message,
|
||||||
}];
|
}];
|
||||||
|
case 3:
|
||||||
|
if (!(responseType === null || responseType === void 0 ? void 0 : responseType.toLocaleLowerCase().match(/application\/octet-stream/i))) return [3 /*break*/, 5];
|
||||||
|
return [4 /*yield*/, response.arrayBuffer()];
|
||||||
|
case 4:
|
||||||
|
result = _c.sent();
|
||||||
|
return [2 /*return*/, {
|
||||||
|
result: result,
|
||||||
|
message: message,
|
||||||
|
}];
|
||||||
|
case 5: throw new Error("\u5C1A\u4E0D\u652F\u6301\u7684content-type\u7C7B\u578B".concat(responseType));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -102,6 +116,9 @@ var SimpleConnector = /** @class */ (function (_super) {
|
||||||
result: result,
|
result: result,
|
||||||
opRecords: context.opRecords,
|
opRecords: context.opRecords,
|
||||||
},
|
},
|
||||||
|
headers: {
|
||||||
|
'oak-message': context.getMessage(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
SimpleConnector.prototype.serializeException = function (exception, headers, body) {
|
SimpleConnector.prototype.serializeException = function (exception, headers, body) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export declare function composeUrl(url: string, params: Record<string, any>): string;
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.composeUrl = void 0;
|
||||||
|
var url_1 = require("url");
|
||||||
|
function composeUrl(url, params) {
|
||||||
|
var urlSp = new url_1.URLSearchParams(params);
|
||||||
|
if (url.includes('?')) {
|
||||||
|
return "".concat(url, "&").concat(urlSp);
|
||||||
|
}
|
||||||
|
return "".concat(url, "?").concat(urlSp);
|
||||||
|
}
|
||||||
|
exports.composeUrl = composeUrl;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "oak-domain",
|
"name": "oak-domain",
|
||||||
"version": "2.6.1",
|
"version": "2.6.2",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "XuChang"
|
"name": "XuChang"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
|
||||||
opRecords: OpRecord<ED>[];
|
opRecords: OpRecord<ED>[];
|
||||||
private scene?: string;
|
private scene?: string;
|
||||||
private headers?: IncomingHttpHeaders;
|
private headers?: IncomingHttpHeaders;
|
||||||
|
private message?: string;
|
||||||
events: {
|
events: {
|
||||||
commit: Array<() => Promise<void>>;
|
commit: Array<() => Promise<void>>;
|
||||||
rollback: Array<() => Promise<void>>;
|
rollback: Array<() => Promise<void>>;
|
||||||
|
|
@ -128,6 +129,14 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
|
||||||
return this.rowStore.getSchema();
|
return this.rowStore.getSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMessage(message: string) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMessage() {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
abstract isRoot(): boolean;
|
abstract isRoot(): boolean;
|
||||||
|
|
||||||
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;
|
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -151,7 +151,8 @@ export function createModiRelatedTriggers<ED extends EntityDict & BaseEntityDict
|
||||||
for (const entity in schema) {
|
for (const entity in schema) {
|
||||||
const { inModi } = schema[entity];
|
const { inModi } = schema[entity];
|
||||||
if (inModi) {
|
if (inModi) {
|
||||||
// 当关联modi的对象被删除时,对应的modi也删除
|
// 当关联modi的对象被删除时,对应的modi也删除。这里似乎只需要删除掉活跃对象?因为oper不能删除,所以oper和modi是必须要支持对deleted对象的容错?
|
||||||
|
// 这里没有想清楚,by Xc 20230209
|
||||||
triggers.push({
|
triggers.push({
|
||||||
name: `当删除${entity}对象时,删除相关联的modi的modiEntity`,
|
name: `当删除${entity}对象时,删除相关联的modi的modiEntity`,
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
|
|
@ -159,16 +160,15 @@ export function createModiRelatedTriggers<ED extends EntityDict & BaseEntityDict
|
||||||
when: 'after',
|
when: 'after',
|
||||||
priority: REMOVE_CASCADE_PRIORITY,
|
priority: REMOVE_CASCADE_PRIORITY,
|
||||||
fn: async ({ operation }, context, option) => {
|
fn: async ({ operation }, context, option) => {
|
||||||
const { data } = operation;
|
const { filter } = operation;
|
||||||
const { id } = data;
|
|
||||||
await context.operate('modiEntity', {
|
await context.operate('modiEntity', {
|
||||||
id: await generateNewIdAsync(),
|
id: await generateNewIdAsync(),
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
data: {},
|
data: {},
|
||||||
filter: {
|
filter: {
|
||||||
modi: {
|
modi: {
|
||||||
entity,
|
[entity]: filter,
|
||||||
entityId: id,
|
iState: 'active',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
|
|
@ -177,8 +177,8 @@ export function createModiRelatedTriggers<ED extends EntityDict & BaseEntityDict
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
data: {},
|
data: {},
|
||||||
filter: {
|
filter: {
|
||||||
entity,
|
[entity]: filter,
|
||||||
entityId: id,
|
iState: 'active',
|
||||||
}
|
}
|
||||||
}, { dontCollect: true });
|
}, { dontCollect: true });
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ export function reinforceSelection<ED extends EntityDict>(schema: StorageSchema<
|
||||||
const toBeAssignNode2: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
|
const toBeAssignNode2: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
|
||||||
const projectionNodeDict: Record<string, ED[keyof ED]['Selection']['data']> = {};
|
const projectionNodeDict: Record<string, ED[keyof ED]['Selection']['data']> = {};
|
||||||
const checkProjectionNode = (entity2: keyof ED, projectionNode: ED[keyof ED]['Selection']['data']) => {
|
const checkProjectionNode = (entity2: keyof ED, projectionNode: ED[keyof ED]['Selection']['data']) => {
|
||||||
const necessaryAttrs: string[] = ['id', '$$createAt$$']; // 因有的页面依赖于其他页面的数据,因此默认的createAt的filter不一定会加上,为了保险起见在这里统一加上
|
const necessaryAttrs: string[] = ['id', '$$createAt$$']; // 有的页面依赖于其它页面取数据,有时两个页面的filter的差异会导致有一个加createAt,有一个不加,此时可能产生前台取数据不完整的异常。先统一加上
|
||||||
for (const attr in projectionNode) {
|
for (const attr in projectionNode) {
|
||||||
if (attr === '#id') {
|
if (attr === '#id') {
|
||||||
assert(!projectionNodeDict[projectionNode[attr]!], `projection中结点的id有重复, ${projectionNode[attr]}`);
|
assert(!projectionNodeDict[projectionNode[attr]!], `projection中结点的id有重复, ${projectionNode[attr]}`);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ export interface Aspect<ED extends EntityDict, Cxt extends AsyncContext<ED>>{
|
||||||
export interface AspectWrapper<ED extends EntityDict, Cxt extends AsyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>>{
|
export interface AspectWrapper<ED extends EntityDict, Cxt extends AsyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>>{
|
||||||
exec: <T extends keyof AD>(name: T, params: Parameters<AD[T]>[0]) => Promise<{
|
exec: <T extends keyof AD>(name: T, params: Parameters<AD[T]>[0]) => Promise<{
|
||||||
result: Awaited<ReturnType<AD[T]>>;
|
result: Awaited<ReturnType<AD[T]>>;
|
||||||
opRecords: OpRecord<ED>[];
|
opRecords?: OpRecord<ED>[];
|
||||||
|
message?: string | null;
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
@ -89,7 +89,7 @@ export type AuthDef<ED extends EntityDict, T extends keyof ED> = {
|
||||||
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
|
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
|
||||||
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
|
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
|
||||||
cascadeRemove?: {
|
cascadeRemove?: {
|
||||||
[E in (keyof ED | '@entity')]?: ActionOnRemove;
|
[E in (keyof ED | keyof ED[T]['Schema'] | '@entity')]?: ActionOnRemove;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import { OakException } from "./Exception";
|
||||||
export abstract class Connector<ED extends EntityDict, BackCxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>> {
|
export abstract class Connector<ED extends EntityDict, BackCxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>> {
|
||||||
abstract callAspect(name: string, params: any, context: FrontCxt): Promise<{
|
abstract callAspect(name: string, params: any, context: FrontCxt): Promise<{
|
||||||
result: any;
|
result: any;
|
||||||
opRecords: OpRecord<ED>[];
|
opRecords?: OpRecord<ED>[];
|
||||||
|
message?: string | null;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
abstract getRouter(): string;
|
abstract getRouter(): string;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext
|
||||||
this.contextBuilder = contextBuilder;
|
this.contextBuilder = contextBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
async callAspect(name: string, params: any, context: FrontCxt): Promise<{ result: any; opRecords: OpRecord<ED>[]; }> {
|
async callAspect(name: string, params: any, context: FrontCxt) {
|
||||||
const cxtStr = context.toString();
|
const cxtStr = context.toString();
|
||||||
|
|
||||||
const { contentType, body } = makeContentTypeAndBody(params);
|
const { contentType, body } = makeContentTypeAndBody(params);
|
||||||
|
|
@ -53,20 +53,34 @@ export class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo 处理各种返回的格式
|
const message = response.headers.get('oak-message');
|
||||||
const {
|
const responseType = response.headers.get('Content-Type');
|
||||||
exception,
|
if (responseType?.toLocaleLowerCase().match(/application\/json/i)) {
|
||||||
result,
|
const {
|
||||||
opRecords
|
exception,
|
||||||
} = await response.json();
|
result,
|
||||||
|
opRecords,
|
||||||
|
} = await response.json();
|
||||||
|
|
||||||
if (exception) {
|
if (exception) {
|
||||||
throw this.makeException(exception);
|
throw this.makeException(exception);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
opRecords,
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (responseType?.toLocaleLowerCase().match(/application\/octet-stream/i)) {
|
||||||
|
const result = await response.arrayBuffer();
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error(`尚不支持的content-type类型${responseType}`);
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
result,
|
|
||||||
opRecords,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getRouter(): string {
|
getRouter(): string {
|
||||||
|
|
@ -97,6 +111,9 @@ export class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext
|
||||||
result,
|
result,
|
||||||
opRecords: context.opRecords,
|
opRecords: context.opRecords,
|
||||||
},
|
},
|
||||||
|
headers: {
|
||||||
|
'oak-message': context.getMessage(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
|
export function composeUrl(url: string, params: Record<string, any>) {
|
||||||
|
const urlSp = new URLSearchParams(params);
|
||||||
|
if (url.includes('?')) {
|
||||||
|
return `${url}&${urlSp}`;
|
||||||
|
}
|
||||||
|
return `${url}?${urlSp}`;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue