Merge branch 'release'

This commit is contained in:
Xu Chang 2024-02-29 22:07:24 +08:00
commit 9b068a142d
12 changed files with 131 additions and 94 deletions

View File

@ -1308,6 +1308,7 @@ class CascadeStore extends RowStore_1.RowStore {
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity) && ids.length > 0) {
// 按照框架要求生成Oper和OperEntity这两个内置的对象
(0, assert_1.default)(operId);
const operatorId = context.getCurrentUserId(true);
const createOper = {
id: 'dummy',
action: 'create',
@ -1317,6 +1318,7 @@ class CascadeStore extends RowStore_1.RowStore {
data,
targetEntity: entity,
bornAt,
operatorId,
operEntity$oper: {
id: 'dummy',
action: 'create',

View File

@ -152,7 +152,7 @@ function translateCheckerInSyncContext(checker) {
if ((0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter, true)) {
return;
}
const e = new Exception_1.OakRowInconsistencyException(undefined, errMsg);
const e = new Exception_1.OakRowInconsistencyException(undefined, errMsg || 'row checker condition illegal');
throw e;
};
return {
@ -402,7 +402,7 @@ function checkAttributeLegal(schema, entity, data) {
case 'enum': {
(0, assert_1.default)(enumeration);
if (!enumeration.includes(data[attr])) {
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not in enumberation');
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not in enumeration');
}
break;
}

View File

@ -10,9 +10,10 @@ export interface Connector<ED extends EntityDict, FrontCxt extends SyncContext<E
message?: string | null;
}>;
getRouter: () => string;
parseRequestHeaders: (headers: IncomingHttpHeaders) => {
parseRequest: (headers: IncomingHttpHeaders, body?: any, files?: any) => {
contextString?: string;
aspectName: string;
data?: any;
};
serializeResult: (result: any, opRecords: OpRecord<ED>[], headers: IncomingHttpHeaders, body: any, message?: string) => Promise<{
body: any;

9
lib/types/Sync.d.ts vendored
View File

@ -32,14 +32,13 @@ export interface PushEntityDef<ED extends EntityDict & BaseEntityDict, T extends
recursive?: boolean;
relationName?: string;
actions?: ED[T]['Action'][];
/**
*
*/
onSynchronized?: (result: {
action: ED[T]['Action'];
data: ED[T]['Operation']['data'];
result: Array<{
userId: string;
rowIds: string[];
error?: Error;
}>;
rowIds: string[];
}, context: Cxt) => Promise<void>;
}
export interface SyncRemoteConfigBase<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> {

View File

@ -20,6 +20,22 @@ export declare class SimpleConnector<ED extends EntityDict, FrontCxt extends Syn
private option;
private makeException;
constructor(option: ServerOption, makeException: (exceptionData: any) => OakException<ED>);
protected makeHeadersAndBody(name: string, data: any, context?: FrontCxt): Promise<{
headers: Record<string, string>;
body: FormData;
} | {
headers: HeadersInit;
body: string;
}>;
protected parseAspectResult(response: Response): Promise<{
result: any;
opRecords: any;
message: string | null;
} | {
result: ArrayBuffer;
message: string | null;
opRecords?: undefined;
}>;
callAspect(name: string, params: any, context?: FrontCxt): Promise<{
result: any;
opRecords: any;
@ -37,9 +53,10 @@ export declare class SimpleConnector<ED extends EntityDict, FrontCxt extends Syn
path: any;
}>;
getEndpointRouter(): string;
parseRequestHeaders(headers: IncomingHttpHeaders): {
parseRequest(headers: IncomingHttpHeaders, body?: any, files?: any): {
contextString: string | undefined;
aspectName: string;
data: any;
};
serializeResult(result: any, opRecords: OpRecord<ED>[], headers: IncomingHttpHeaders, body: any, message?: string): Promise<{
body: any;

View File

@ -6,20 +6,6 @@ const assert_1 = tslib_1.__importDefault(require("assert"));
const stream_1 = require("stream");
const url_1 = tslib_1.__importDefault(require("url"));
const types_1 = require("../types");
function makeContentTypeAndBody(data) {
if (process.env.OAK_PLATFORM !== 'wechatMp') {
if (data instanceof FormData) {
return {
// contentType: 'multipart/form-data',
body: data,
};
}
}
return {
contentType: 'application/json',
body: JSON.stringify(data),
};
}
class SimpleConnector {
static ASPECT_ROUTER = '/aspect';
static BRIDGE_ROUTER = '/bridge';
@ -47,26 +33,30 @@ class SimpleConnector {
this.serverSubscribePointUrl = `${serverUrl}${SimpleConnector.SUBSCRIBE_POINT_ROUTER}`;
this.makeException = makeException;
}
async callAspect(name, params, context) {
async makeHeadersAndBody(name, data, context) {
const cxtStr = context ? await context.toString() : '{}';
const { contentType, body } = makeContentTypeAndBody(params);
let response;
try {
response = await global.fetch(this.serverAspectUrl, {
method: 'POST',
headers: Object.assign({
'oak-cxt': cxtStr,
'oak-aspect': name,
}, contentType && {
'Content-Type': contentType,
}),
body,
});
}
catch (err) {
// fetch返回异常一定是网络异常
throw new types_1.OakNetworkException(`请求[${this.serverAspectUrl}],发生网络异常`);
const headers = {
'oak-cxt': cxtStr,
'oak-aspect': name,
};
if (process.env.OAK_PLATFORM !== 'wechatMp') {
if (data instanceof FormData) {
return {
headers,
body: data,
};
}
}
return {
headers: {
'Content-Type': 'application/json',
...headers,
},
body: JSON.stringify(data),
};
}
;
async parseAspectResult(response) {
if (response.status > 299) {
const err = new types_1.OakServerProxyException(`网络请求返回status是${response.status}`);
throw err;
@ -98,6 +88,22 @@ class SimpleConnector {
throw new Error(`尚不支持的content-type类型${responseType}`);
}
}
async callAspect(name, params, context) {
const { headers, body } = await this.makeHeadersAndBody(name, params, context);
let response;
try {
response = await global.fetch(this.serverAspectUrl, {
method: 'POST',
headers,
body,
});
}
catch (err) {
// fetch返回异常一定是网络异常
throw new types_1.OakNetworkException(`请求[${this.serverAspectUrl}],发生网络异常`);
}
return this.parseAspectResult(response);
}
getRouter() {
return SimpleConnector.ASPECT_ROUTER;
}
@ -142,13 +148,18 @@ class SimpleConnector {
getEndpointRouter() {
return SimpleConnector.ENDPOINT_ROUTER;
}
parseRequestHeaders(headers) {
parseRequest(headers, body, files) {
const { 'oak-cxt': oakCxtStr, 'oak-aspect': aspectName } = headers;
(0, assert_1.default)(typeof oakCxtStr === 'string' || oakCxtStr === undefined);
(0, assert_1.default)(typeof aspectName === 'string');
return {
contextString: oakCxtStr,
aspectName,
/* data: !files ? body : {
data: body,
files,
}, */ // 下个版本再改
data: files ? Object.assign({}, body, files) : body,
};
}
async serializeResult(result, opRecords, headers, body, message) {

View File

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

View File

@ -1586,6 +1586,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity as string) && ids.length > 0) {
// 按照框架要求生成Oper和OperEntity这两个内置的对象
assert(operId);
const operatorId = context.getCurrentUserId(true);
const createOper: CreateSingleOperOperation = {
id: 'dummy',
action: 'create',
@ -1595,6 +1596,7 @@ export abstract class CascadeStore<ED extends EntityDict & BaseEntityDict> exten
data,
targetEntity: entity as string,
bornAt,
operatorId,
operEntity$oper: {
id: 'dummy',
action: 'create',

View File

@ -176,7 +176,7 @@ export function translateCheckerInSyncContext<
if (checkFilterContains<ED, T, Cxt>(entity, context, filter2, operationFilter, true)) {
return;
}
const e = new OakRowInconsistencyException(undefined, errMsg);
const e = new OakRowInconsistencyException(undefined, errMsg || 'row checker condition illegal');
throw e;
};
return {
@ -446,7 +446,7 @@ function checkAttributeLegal<ED extends EntityDict & BaseEntityDict>(
case 'enum': {
assert(enumeration);
if (!enumeration.includes((data as ED[keyof ED]['CreateSingle']['data'])[attr])) {
throw new OakInputIllegalException(entity, [attr], 'not in enumberation');
throw new OakInputIllegalException(entity, [attr], 'not in enumeration');
}
break;
}

View File

@ -1,6 +1,4 @@
import { IncomingHttpHeaders } from "http";
import { RowStore } from ".";
import { AsyncContext, AsyncRowStore } from "../store/AsyncRowStore";
import { SyncContext } from "../store/SyncRowStore";
import { EntityDict, OpRecord } from "./Entity";
import { OakException } from "./Exception";
@ -18,9 +16,10 @@ export interface Connector<ED extends EntityDict, FrontCxt extends SyncContext<E
getRouter: () => string;
parseRequestHeaders: (headers: IncomingHttpHeaders) => {
parseRequest: (headers: IncomingHttpHeaders, body?: any, files?: any) => {
contextString?: string;
aspectName: string;
data?: any;
};
serializeResult: (

View File

@ -41,16 +41,13 @@ export interface PushEntityDef<ED extends EntityDict & BaseEntityDict, T extends
relationName?: string; // 要同步的user与根对象的relation名称为空说明是userId)
actions?: ED[T]['Action'][];
// 同步结果回调一行可能要向多个syncEntity上去同步因此返回结果是一个数组(表示向某个userId上同步了rowIds相关的数据如果有失败则返回error)
// 如果不定义,则认为同步一定会成功。若失败则会反复同步直到成功为止
/**
*
*/
onSynchronized?: (result: {
action: ED[T]['Action'],
data: ED[T]['Operation']['data'];
result: Array<{
userId: string;
rowIds: string[];
error?: Error;
}>
rowIds: string[];
}, context: Cxt) => Promise<void>,
};

View File

@ -5,22 +5,6 @@ import URL from 'url';
import { SyncContext } from '../store/SyncRowStore';
import { Connector, EntityDict, OakException, OakNetworkException, OakServerProxyException, OpRecord } from "../types";
function makeContentTypeAndBody(data: any) {
if (process.env.OAK_PLATFORM !== 'wechatMp') {
if (data instanceof FormData) {
return {
// contentType: 'multipart/form-data',
body: data,
};
}
}
return {
contentType: 'application/json',
body: JSON.stringify(data),
};
}
type ServerOption = {
protocol: string;
hostname: string;
@ -62,29 +46,31 @@ export class SimpleConnector<ED extends EntityDict, FrontCxt extends SyncContext
this.makeException = makeException;
}
async callAspect(name: string, params: any, context?: FrontCxt) {
protected async makeHeadersAndBody(name: string, data: any, context?: FrontCxt) {
const cxtStr = context ? await context.toString() : '{}';
const { contentType, body } = makeContentTypeAndBody(params);
let response: Response;
try {
response = await global.fetch(this.serverAspectUrl, {
method: 'POST',
headers: Object.assign(
{
'oak-cxt': cxtStr,
'oak-aspect': name as string,
},
contentType && {
'Content-Type': contentType as string,
}
) as RequestInit['headers'],
body,
});
} catch (err) {
// fetch返回异常一定是网络异常
throw new OakNetworkException(`请求[${this.serverAspectUrl}],发生网络异常`);
const headers: HeadersInit = {
'oak-cxt': cxtStr,
'oak-aspect': name,
};
if (process.env.OAK_PLATFORM !== 'wechatMp') {
if (data instanceof FormData) {
return {
headers,
body: data,
};
}
}
return {
headers: {
'Content-Type': 'application/json',
...headers,
} as HeadersInit,
body: JSON.stringify(data),
};
};
protected async parseAspectResult(response: Response) {
if (response.status > 299) {
const err = new OakServerProxyException(
`网络请求返回status是${response.status}`
@ -120,6 +106,24 @@ export class SimpleConnector<ED extends EntityDict, FrontCxt extends SyncContext
} else {
throw new Error(`尚不支持的content-type类型${responseType}`);
}
}
async callAspect(name: string, params: any, context?: FrontCxt) {
const { headers, body } = await this.makeHeadersAndBody(name, params, context);
let response: Response;
try {
response = await global.fetch(this.serverAspectUrl, {
method: 'POST',
headers,
body,
});
} catch (err) {
// fetch返回异常一定是网络异常
throw new OakNetworkException(`请求[${this.serverAspectUrl}],发生网络异常`);
}
return this.parseAspectResult(response);
}
getRouter(): string {
@ -177,13 +181,18 @@ export class SimpleConnector<ED extends EntityDict, FrontCxt extends SyncContext
return SimpleConnector.ENDPOINT_ROUTER;
}
parseRequestHeaders(headers: IncomingHttpHeaders) {
parseRequest(headers: IncomingHttpHeaders, body?: any, files?: any) {
const { 'oak-cxt': oakCxtStr, 'oak-aspect': aspectName } = headers;
assert(typeof oakCxtStr === 'string' || oakCxtStr === undefined);
assert(typeof aspectName === 'string');
return {
contextString: oakCxtStr,
aspectName,
/* data: !files ? body : {
data: body,
files,
}, */ //
data: files ? Object.assign({}, body, files) : body,
};
}
@ -244,7 +253,7 @@ export class SimpleConnector<ED extends EntityDict, FrontCxt extends SyncContext
return `${this.serverBridgeUrl}?url=${encodeUrl}`;
}
parseBridgeRequestQuery(urlParams: string): {
url: string;
headers?: Record<string, string> | undefined;