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>[];
|
||||
private scene?;
|
||||
private headers?;
|
||||
private message?;
|
||||
events: {
|
||||
commit: 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>;
|
||||
getCurrentTxnId(): string | undefined;
|
||||
getSchema(): import("../types").StorageSchema<ED>;
|
||||
setMessage(message: string): void;
|
||||
getMessage(): string | undefined;
|
||||
abstract isRoot(): boolean;
|
||||
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;
|
||||
abstract toString(): string;
|
||||
|
|
|
|||
|
|
@ -176,6 +176,12 @@ var AsyncContext = /** @class */ (function () {
|
|||
AsyncContext.prototype.getSchema = function () {
|
||||
return this.rowStore.getSchema();
|
||||
};
|
||||
AsyncContext.prototype.setMessage = function (message) {
|
||||
this.message = message;
|
||||
};
|
||||
AsyncContext.prototype.getMessage = function () {
|
||||
return this.message;
|
||||
};
|
||||
return 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 otmOperations = data[attr];
|
||||
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;
|
||||
if (!foreignKey_2) {
|
||||
// 基于entity/entityId的one-to-many
|
||||
|
|
@ -581,13 +581,25 @@ var CascadeStore = /** @class */ (function (_super) {
|
|||
}
|
||||
}
|
||||
else {
|
||||
// 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作
|
||||
// 这个倒是好像不可能出现create/update的一对多,如果遇到了再完善
|
||||
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||
if (filter) {
|
||||
if (filter.id && Object.keys(filter).length === 1) {
|
||||
Object.assign(otm, {
|
||||
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') {
|
||||
Object.assign(dataOtm, {
|
||||
entity: null,
|
||||
|
|
@ -634,20 +646,34 @@ var CascadeStore = /** @class */ (function (_super) {
|
|||
}
|
||||
}
|
||||
else {
|
||||
// update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
||||
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||
// 绝大多数情况都是id,但也有可能update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
||||
if (filter) {
|
||||
if (filter.id && Object.keys(filter).length === 1) {
|
||||
Object.assign(otm, {
|
||||
filter: (0, filter_1.addFilterSegment)((_d = {},
|
||||
_d[foreignKey_2.slice(0, foreignKey_2.length - 2)] = filter,
|
||||
_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') {
|
||||
Object.assign(dataOtm, (_e = {},
|
||||
_e[foreignKey_2] = null,
|
||||
_e));
|
||||
Object.assign(dataOtm, (_f = {},
|
||||
_f[foreignKey_2] = null,
|
||||
_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) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -153,7 +153,8 @@ function createModiRelatedTriggers(schema) {
|
|||
var _loop_2 = function (entity) {
|
||||
var inModi = schema[entity].inModi;
|
||||
if (inModi) {
|
||||
// 当关联modi的对象被删除时,对应的modi也删除
|
||||
// 当关联modi的对象被删除时,对应的modi也删除。这里似乎只需要删除掉活跃对象?因为oper不能删除,所以oper和modi是必须要支持对deleted对象的容错?
|
||||
// 这里没有想清楚,by Xc 20230209
|
||||
triggers.push({
|
||||
name: "\u5F53\u5220\u9664".concat(entity, "\u5BF9\u8C61\u65F6\uFF0C\u5220\u9664\u76F8\u5173\u8054\u7684modi\u7684modiEntity"),
|
||||
action: 'remove',
|
||||
|
|
@ -163,43 +164,42 @@ function createModiRelatedTriggers(schema) {
|
|||
fn: function (_a, context, option) {
|
||||
var operation = _a.operation;
|
||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var data, id, _b, _c, _d, _e, _f, _g;
|
||||
var _h, _j;
|
||||
return tslib_1.__generator(this, function (_k) {
|
||||
switch (_k.label) {
|
||||
var filter, _b, _c, _d, _e, _f, _g;
|
||||
var _h, _j, _k, _l;
|
||||
return tslib_1.__generator(this, function (_m) {
|
||||
switch (_m.label) {
|
||||
case 0:
|
||||
data = operation.data;
|
||||
id = data.id;
|
||||
filter = operation.filter;
|
||||
_c = (_b = context).operate;
|
||||
_d = ['modiEntity'];
|
||||
_h = {};
|
||||
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.data = {},
|
||||
_h.filter = {
|
||||
modi: {
|
||||
entity: entity,
|
||||
entityId: id,
|
||||
},
|
||||
modi: (_j = {},
|
||||
_j[entity] = filter,
|
||||
_j.iState = 'active',
|
||||
_j),
|
||||
},
|
||||
_h), { dontCollect: true }]))];
|
||||
case 2:
|
||||
_k.sent();
|
||||
_m.sent();
|
||||
_f = (_e = context).operate;
|
||||
_g = ['modi'];
|
||||
_j = {};
|
||||
_k = {};
|
||||
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
|
||||
case 3: return [4 /*yield*/, _f.apply(_e, _g.concat([(_j.id = _k.sent(),
|
||||
_j.action = 'remove',
|
||||
_j.data = {},
|
||||
_j.filter = {
|
||||
entity: entity,
|
||||
entityId: id,
|
||||
},
|
||||
_j), { dontCollect: true }]))];
|
||||
case 3: return [4 /*yield*/, _f.apply(_e, _g.concat([(_k.id = _m.sent(),
|
||||
_k.action = 'remove',
|
||||
_k.data = {},
|
||||
_k.filter = (_l = {},
|
||||
_l[entity] = filter,
|
||||
_l.iState = 'active',
|
||||
_l),
|
||||
_k), { dontCollect: true }]))];
|
||||
case 4:
|
||||
_k.sent();
|
||||
_m.sent();
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ function reinforceSelection(schema, entity, selection) {
|
|||
var projectionNodeDict = {};
|
||||
var checkProjectionNode = function (entity2, projectionNode) {
|
||||
var _a, _b, _c;
|
||||
var necessaryAttrs = ['id', '$$createAt$$']; // 因有的页面依赖于其他页面的数据,因此默认的createAt的filter不一定会加上,为了保险起见在这里统一加上
|
||||
var necessaryAttrs = ['id', '$$createAt$$']; // 有的页面依赖于其它页面取数据,有时两个页面的filter的差异会导致有一个加createAt,有一个不加,此时可能产生前台取数据不完整的异常。先统一加上
|
||||
for (var attr in projectionNode) {
|
||||
if (attr === '#id') {
|
||||
(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>>> {
|
||||
exec: <T extends keyof AD>(name: T, params: Parameters<AD[T]>[0]) => Promise<{
|
||||
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']>>;
|
||||
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
|
||||
cascadeRemove?: {
|
||||
[E in (keyof ED | '@entity')]?: ActionOnRemove;
|
||||
[E in (keyof ED | keyof ED[T]['Schema'] | '@entity')]?: ActionOnRemove;
|
||||
};
|
||||
};
|
||||
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>> {
|
||||
abstract callAspect(name: string, params: any, context: FrontCxt): Promise<{
|
||||
result: any;
|
||||
opRecords: OpRecord<ED>[];
|
||||
opRecords?: OpRecord<ED>[];
|
||||
message?: string | null;
|
||||
}>;
|
||||
abstract getRouter(): string;
|
||||
abstract parseRequest(headers: IncomingHttpHeaders, body: any, store: AsyncRowStore<ED, BackCxt>): Promise<{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { IncomingHttpHeaders } from "http";
|
||||
import { AsyncContext, AsyncRowStore } from '../store/AsyncRowStore';
|
||||
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> {
|
||||
static ROUTER: string;
|
||||
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>);
|
||||
callAspect(name: string, params: any, context: FrontCxt): Promise<{
|
||||
result: any;
|
||||
opRecords: OpRecord<ED>[];
|
||||
opRecords: any;
|
||||
message: string | null;
|
||||
} | {
|
||||
result: ArrayBuffer;
|
||||
message: string | null;
|
||||
opRecords?: undefined;
|
||||
}>;
|
||||
getRouter(): string;
|
||||
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) {
|
||||
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) {
|
||||
switch (_c.label) {
|
||||
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));
|
||||
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()];
|
||||
case 2:
|
||||
_b = _c.sent(), exception = _b.exception, result = _b.result, opRecords = _b.opRecords;
|
||||
|
|
@ -61,7 +64,18 @@ var SimpleConnector = /** @class */ (function (_super) {
|
|||
return [2 /*return*/, {
|
||||
result: result,
|
||||
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,
|
||||
opRecords: context.opRecords,
|
||||
},
|
||||
headers: {
|
||||
'oak-message': context.getMessage(),
|
||||
},
|
||||
};
|
||||
};
|
||||
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",
|
||||
"version": "2.6.1",
|
||||
"version": "2.6.2",
|
||||
"author": {
|
||||
"name": "XuChang"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
|
|||
opRecords: OpRecord<ED>[];
|
||||
private scene?: string;
|
||||
private headers?: IncomingHttpHeaders;
|
||||
private message?: string;
|
||||
events: {
|
||||
commit: Array<() => Promise<void>>;
|
||||
rollback: Array<() => Promise<void>>;
|
||||
|
|
@ -128,6 +129,14 @@ export abstract class AsyncContext<ED extends EntityDict> implements Context {
|
|||
return this.rowStore.getSchema();
|
||||
}
|
||||
|
||||
setMessage(message: string) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
abstract isRoot(): boolean;
|
||||
|
||||
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;
|
||||
|
|
|
|||
|
|
@ -726,13 +726,25 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
}
|
||||
}
|
||||
else {
|
||||
// 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作
|
||||
// 这个倒是好像不可能出现create/update的一对多,如果遇到了再完善
|
||||
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||
if (filter) {
|
||||
if (filter.id && Object.keys(filter).length === 1) {
|
||||
Object.assign(otm, {
|
||||
filter: addFilterSegment({
|
||||
entity,
|
||||
entityId: filter.id,
|
||||
}, filterOtm),
|
||||
});
|
||||
}
|
||||
else {
|
||||
Object.assign(otm, {
|
||||
filter: addFilterSegment({
|
||||
[entity]: filter,
|
||||
}, filterOtm),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (action === 'remove' && actionOtm === 'update') {
|
||||
Object.assign(dataOtm, {
|
||||
entity: null,
|
||||
|
|
@ -777,12 +789,25 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
}
|
||||
}
|
||||
else {
|
||||
// update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
||||
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
||||
// 除了性能原因之外,还因为会制造出user: { id: xxx }这样的查询,general中不允许这样查询的出现
|
||||
// 绝大多数情况都是id,但也有可能update可能出现上层filter不是根据id的(userEntityGrant的过期触发的wechatQrCode的过期,见general中的userEntityGrant的trigger)
|
||||
if (filter) {
|
||||
if (filter.id && Object.keys(filter).length === 1) {
|
||||
Object.assign(otm, {
|
||||
filter: addFilterSegment({
|
||||
[foreignKey]: filter.id,
|
||||
}, filterOtm),
|
||||
});
|
||||
}
|
||||
else {
|
||||
Object.assign(otm, {
|
||||
filter: addFilterSegment({
|
||||
[foreignKey.slice(0, foreignKey.length - 2)]: filter,
|
||||
}, filterOtm),
|
||||
});
|
||||
}
|
||||
}
|
||||
if (action === 'remove' && actionOtm === 'update') {
|
||||
Object.assign(dataOtm, {
|
||||
[foreignKey]: null,
|
||||
|
|
@ -791,7 +816,8 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
|
|||
}
|
||||
}
|
||||
|
||||
beforeFns.push(() => cascadeUpdate.call(this, entityOtm!, otm, context, option2));
|
||||
// 一对多的依赖应该后建,否则中间会出现空指针,导致checker等出错
|
||||
afterFns.push(() => cascadeUpdate.call(this, entityOtm!, otm, context, option2));
|
||||
};
|
||||
|
||||
if (otmOperations instanceof Array) {
|
||||
|
|
|
|||
|
|
@ -151,7 +151,8 @@ export function createModiRelatedTriggers<ED extends EntityDict & BaseEntityDict
|
|||
for (const entity in schema) {
|
||||
const { inModi } = schema[entity];
|
||||
if (inModi) {
|
||||
// 当关联modi的对象被删除时,对应的modi也删除
|
||||
// 当关联modi的对象被删除时,对应的modi也删除。这里似乎只需要删除掉活跃对象?因为oper不能删除,所以oper和modi是必须要支持对deleted对象的容错?
|
||||
// 这里没有想清楚,by Xc 20230209
|
||||
triggers.push({
|
||||
name: `当删除${entity}对象时,删除相关联的modi的modiEntity`,
|
||||
action: 'remove',
|
||||
|
|
@ -159,16 +160,15 @@ export function createModiRelatedTriggers<ED extends EntityDict & BaseEntityDict
|
|||
when: 'after',
|
||||
priority: REMOVE_CASCADE_PRIORITY,
|
||||
fn: async ({ operation }, context, option) => {
|
||||
const { data } = operation;
|
||||
const { id } = data;
|
||||
const { filter } = operation;
|
||||
await context.operate('modiEntity', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'remove',
|
||||
data: {},
|
||||
filter: {
|
||||
modi: {
|
||||
entity,
|
||||
entityId: id,
|
||||
[entity]: filter,
|
||||
iState: 'active',
|
||||
},
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
|
|
@ -177,8 +177,8 @@ export function createModiRelatedTriggers<ED extends EntityDict & BaseEntityDict
|
|||
action: 'remove',
|
||||
data: {},
|
||||
filter: {
|
||||
entity,
|
||||
entityId: id,
|
||||
[entity]: filter,
|
||||
iState: 'active',
|
||||
}
|
||||
}, { dontCollect: true });
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ export function reinforceSelection<ED extends EntityDict>(schema: StorageSchema<
|
|||
const toBeAssignNode2: Record<string, string[]> = {}; // 用来记录在表达式中涉及到的结点
|
||||
const projectionNodeDict: Record<string, 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) {
|
||||
if (attr === '#id') {
|
||||
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>>>{
|
||||
exec: <T extends keyof AD>(name: T, params: Parameters<AD[T]>[0]) => Promise<{
|
||||
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']>>;
|
||||
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
|
||||
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>> {
|
||||
abstract callAspect(name: string, params: any, context: FrontCxt): Promise<{
|
||||
result: any;
|
||||
opRecords: OpRecord<ED>[];
|
||||
opRecords?: OpRecord<ED>[];
|
||||
message?: string | null;
|
||||
}>;
|
||||
|
||||
abstract getRouter(): string;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext
|
|||
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 { contentType, body } = makeContentTypeAndBody(params);
|
||||
|
|
@ -53,11 +53,13 @@ export class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext
|
|||
throw err;
|
||||
}
|
||||
|
||||
// todo 处理各种返回的格式
|
||||
const message = response.headers.get('oak-message');
|
||||
const responseType = response.headers.get('Content-Type');
|
||||
if (responseType?.toLocaleLowerCase().match(/application\/json/i)) {
|
||||
const {
|
||||
exception,
|
||||
result,
|
||||
opRecords
|
||||
opRecords,
|
||||
} = await response.json();
|
||||
|
||||
if (exception) {
|
||||
|
|
@ -66,8 +68,20 @@ export class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext
|
|||
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}`);
|
||||
}
|
||||
}
|
||||
|
||||
getRouter(): string {
|
||||
return SimpleConnector.ROUTER;
|
||||
|
|
@ -97,6 +111,9 @@ export class SimpleConnector<ED extends EntityDict, BackCxt extends AsyncContext
|
|||
result,
|
||||
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