Merge branch 'release'

This commit is contained in:
Xu Chang 2023-04-14 18:00:44 +08:00
commit 5ec7de7563
27 changed files with 389 additions and 30 deletions

View File

@ -5,30 +5,35 @@ var Action_1 = require("./Action");
exports.desc = {
attributes: {
targetEntity: {
notNull: true,
type: "varchar",
params: {
length: 32
}
},
entity: {
notNull: true,
type: "varchar",
params: {
length: 32
}
},
entityId: {
notNull: true,
type: "varchar",
params: {
length: 64
}
},
action: {
notNull: true,
type: "varchar",
params: {
length: 16
}
},
data: {
notNull: true,
type: "object"
},
filter: {

View File

@ -5,10 +5,12 @@ var action_1 = require("../../actions/action");
exports.desc = {
attributes: {
modiId: {
notNull: true,
type: "ref",
ref: "modi"
},
entity: {
notNull: true,
type: "varchar",
params: {
length: 32
@ -16,6 +18,7 @@ exports.desc = {
ref: ["user", "userEntityGrant"]
},
entityId: {
notNull: true,
type: "varchar",
params: {
length: 64

View File

@ -5,12 +5,14 @@ var action_1 = require("../../actions/action");
exports.desc = {
attributes: {
action: {
notNull: true,
type: "varchar",
params: {
length: 16
}
},
data: {
notNull: true,
type: "object"
},
filter: {
@ -24,6 +26,7 @@ exports.desc = {
ref: "user"
},
targetEntity: {
notNull: true,
type: "varchar",
params: {
length: 32

View File

@ -5,10 +5,12 @@ var action_1 = require("../../actions/action");
exports.desc = {
attributes: {
operId: {
notNull: true,
type: "ref",
ref: "oper"
},
entity: {
notNull: true,
type: "varchar",
params: {
length: 32
@ -16,6 +18,7 @@ exports.desc = {
ref: ["user", "userEntityGrant"]
},
entityId: {
notNull: true,
type: "varchar",
params: {
length: 64

View File

@ -5,18 +5,21 @@ var action_1 = require("../../actions/action");
exports.desc = {
attributes: {
entity: {
notNull: true,
type: "varchar",
params: {
length: 32
}
},
entityId: {
notNull: true,
type: "varchar",
params: {
length: 64
}
},
relation: {
notNull: true,
type: "varchar",
params: {
length: 32

View File

@ -8,6 +8,7 @@ function createDynamicCheckers(schema, authDict) {
var checkers = [];
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false));
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createRemoveCheckers)(schema, authDict)), false));
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createCreateCheckers)(schema)), false));
if (authDict) {
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createAuthCheckers)(schema, authDict)), false));
}

View File

@ -2917,7 +2917,10 @@ function constructAttributes(entity) {
var result = [];
schemaAttrs.forEach(function (attr) {
var attrAssignments = [];
var name = attr.name, type = attr.type;
var name = attr.name, type = attr.type, allowNull = attr.questionToken;
if (!allowNull) {
attrAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("notNull"), factory.createTrue()));
}
var name2 = name;
if (ts.isTypeReferenceNode(type)) {
var typeName = type.typeName, typeArguments = type.typeArguments;

View File

@ -30,3 +30,4 @@ export declare function createAuthCheckers<ED extends EntityDict & BaseEntityDic
* 使trigger来处理其相关联的外键对象trigger写作beforechecker之前执行
*/
export declare function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];
export declare function createCreateCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[];

View File

@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createRemoveCheckers = exports.createAuthCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
exports.createCreateCheckers = exports.createRemoveCheckers = exports.createAuthCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
var tslib_1 = require("tslib");
var assert_1 = tslib_1.__importDefault(require("assert"));
var filter_1 = require("../store/filter");
@ -11,6 +11,7 @@ var string_1 = require("../utils/string");
var lodash_1 = require("../utils/lodash");
var relation_1 = require("./relation");
var uuid_1 = require("../utils/uuid");
var action_1 = require("../actions/action");
/**
*
* @param checker 要翻译的checker
@ -1216,3 +1217,155 @@ function createRemoveCheckers(schema, authDict) {
return checkers;
}
exports.createRemoveCheckers = createRemoveCheckers;
function checkAttributeLegal(schema, entity, data) {
var _a;
var attributes = schema[entity].attributes;
for (var attr in data) {
if (attributes[attr]) {
var _b = attributes[attr], type = _b.type, params = _b.params, defaultValue = _b.default, enumeration = _b.enumeration, notNull = _b.notNull;
if (data[attr] === null || data[attr] === undefined) {
if (notNull && defaultValue === undefined) {
throw new Exception_1.OakAttrNotNullException(entity, [attr]);
}
if (defaultValue !== undefined) {
Object.assign(data, (_a = {},
_a[attr] = defaultValue,
_a));
}
continue;
}
switch (type) {
case 'char':
case 'varchar': {
if (typeof data[attr] !== 'string') {
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not a string');
}
var length_1 = params.length;
if (length_1 && data[attr].length > length_1) {
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too long');
}
break;
}
case 'int':
case 'smallint':
case 'tinyint':
case 'bigint':
case 'decimal':
case 'money': {
if (typeof data[attr] !== 'number') {
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not a number');
}
var _c = params || {}, min = _c.min, max = _c.max;
if (typeof min === 'number' && data[attr] < min) {
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too small');
}
if (typeof max === 'number' && data[attr] > max) {
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too big');
}
break;
}
case 'enum': {
(0, assert_1.default)(enumeration);
if (!enumeration.includes(data[attr])) {
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not in enumberation');
}
break;
}
}
}
else {
// 这里似乎还有一种update中带cascade remove的case等遇到再说貌似cascadeUpdate没有处理完整这种情况 by Xc
if (typeof data[attr] === 'object' && data[attr].action === 'remove') {
console.warn('cascade remove可能是未处理的边界请注意');
}
}
}
}
function createCreateCheckers(schema) {
var checkers = [];
var _loop_9 = function (entity) {
var _a = schema[entity], attributes = _a.attributes, actions = _a.actions;
var notNullAttrs = Object.keys(attributes).filter(function (ele) { return attributes[ele].notNull; });
var updateActions = (0, lodash_1.difference)(actions, action_1.excludeUpdateActions);
checkers.push({
entity: entity,
type: 'data',
action: 'create',
checker: function (data) {
var checkData = function (data2) {
var e_9, _a, e_10, _b;
var illegalNullAttrs = (0, lodash_1.difference)(notNullAttrs, Object.keys(data2));
if (illegalNullAttrs.length > 0) {
try {
// 要处理多对一的cascade create
for (var illegalNullAttrs_1 = (e_9 = void 0, tslib_1.__values(illegalNullAttrs)), illegalNullAttrs_1_1 = illegalNullAttrs_1.next(); !illegalNullAttrs_1_1.done; illegalNullAttrs_1_1 = illegalNullAttrs_1.next()) {
var attr = illegalNullAttrs_1_1.value;
if (attr === 'entityId') {
if (illegalNullAttrs.includes('entity')) {
continue;
}
}
else if (attr === 'entity' && attributes[attr].type === 'ref') {
var hasCascadeCreate = false;
try {
for (var _c = (e_10 = void 0, tslib_1.__values(attributes[attr].ref)), _d = _c.next(); !_d.done; _d = _c.next()) {
var ref = _d.value;
if (data2[ref] && data2[ref].action === 'create') {
hasCascadeCreate = true;
break;
}
}
}
catch (e_10_1) { e_10 = { error: e_10_1 }; }
finally {
try {
if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
}
finally { if (e_10) throw e_10.error; }
}
if (hasCascadeCreate) {
continue;
}
}
else if (attributes[attr].type === 'ref') {
var ref = attributes[attr].ref;
if (data2[ref] && data2[ref].action === 'create') {
continue;
}
}
// 到这里说明确实是有not null的属性没有赋值
throw new Exception_1.OakAttrNotNullException(entity, illegalNullAttrs);
}
}
catch (e_9_1) { e_9 = { error: e_9_1 }; }
finally {
try {
if (illegalNullAttrs_1_1 && !illegalNullAttrs_1_1.done && (_a = illegalNullAttrs_1.return)) _a.call(illegalNullAttrs_1);
}
finally { if (e_9) throw e_9.error; }
}
}
checkAttributeLegal(schema, entity, data2);
};
if (data instanceof Array) {
data.forEach(function (ele) { return checkData(ele); });
}
else {
checkData(data);
}
}
}, {
entity: entity,
type: 'data',
action: updateActions,
checker: function (data) {
checkAttributeLegal(schema, entity, data);
}
});
};
for (var entity in schema) {
_loop_9(entity);
}
return checkers;
}
exports.createCreateCheckers = createCreateCheckers;

View File

@ -151,8 +151,8 @@ function createModiRelatedTriggers(schema) {
var _this = this;
var triggers = [];
var _loop_2 = function (entity) {
var inModi = schema[entity].inModi;
if (inModi) {
var toModi = schema[entity].toModi;
if (toModi) {
// 当关联modi的对象被删除时对应的modi也删除。这里似乎只需要删除掉活跃对象因为oper不能删除所以oper和modi是必须要支持对deleted对象的容错
// 这里没有想清楚by Xc 20230209
triggers.push({

View File

@ -55,6 +55,7 @@ function judgeRelation(schema, entity, attr) {
else if (attributes.hasOwnProperty('entity')
&& attributes.hasOwnProperty('entityId')
&& schema.hasOwnProperty(attr)) {
(0, assert_1.default)(attributes.entity.ref.includes(attr), '不应当出现的case');
// 反向指针的外键
return 2;
}

View File

@ -5,7 +5,6 @@ var tslib_1 = require("tslib");
var dayjs_1 = tslib_1.__importDefault(require("dayjs"));
var fs_1 = require("fs");
var filter_1 = require("../store/filter");
var node_zlib_1 = require("node:zlib");
var stream_1 = require("stream");
var uuid_1 = require("../utils/uuid");
/**
@ -23,7 +22,7 @@ function vaccumEntities(option, context) {
case 0:
entities = option.entities, backupDir = option.backupDir;
_loop_1 = function (ele) {
var entity, filter, aliveLine, filter2, zip, now, backFile, fd_1, attributes_1, projection_1, attr, count_1, appendData_1, gzip_1, source_1, destination_1, _c, _d, _e;
var entity, filter, aliveLine, filter2, zip, now, backFile, fd_1, attributes_1, projection_1, attr, count_1, appendData_1, createGzip, gzip_1, source_1, destination_1, _c, _d, _e;
var _f, _g;
return tslib_1.__generator(this, function (_h) {
switch (_h.label) {
@ -37,7 +36,7 @@ function vaccumEntities(option, context) {
if (filter) {
filter2 = (0, filter_1.combineFilters)([filter2, filter]);
}
if (!backupDir) return [3 /*break*/, 4];
if (!(backupDir && process.env.OAK_PLATFORM === 'server')) return [3 /*break*/, 4];
zip = option.zip;
now = (0, dayjs_1.default)();
backFile = "".concat(backupDir, "/").concat(entity, "-").concat(now.format('YYYY-MM-DD HH:mm:ss'), ".csv");
@ -107,7 +106,8 @@ function vaccumEntities(option, context) {
return [3 /*break*/, 4];
case 2:
if (!zip) return [3 /*break*/, 4];
gzip_1 = (0, node_zlib_1.createGzip)();
createGzip = require('zlib').createGzip;
gzip_1 = createGzip();
source_1 = (0, fs_1.createReadStream)(backFile);
destination_1 = (0, fs_1.createWriteStream)("".concat(backFile, ".zip"));
return [4 /*yield*/, new Promise(function (resolve, reject) {

View File

@ -65,7 +65,7 @@ export interface EntityShape {
$$updateAt$$: number | Date;
$$deleteAt$$?: number | Date | null;
}
interface GeneralEntityShape extends EntityShape {
export interface GeneralEntityShape extends EntityShape {
[K: string]: any;
}
export declare type MakeAction<A extends string> = A;

View File

@ -55,12 +55,15 @@ export declare class OakRowInconsistencyException<ED extends EntityDict> extends
export declare class OakInputIllegalException<ED extends EntityDict> extends OakUserException<ED> {
private attributes;
private entity;
constructor(entity: string, attributes: string[], message?: string);
getEntity(): string;
constructor(entity: keyof ED, attributes: string[], message?: string);
getEntity(): keyof ED;
getAttributes(): string[];
addAttributesPrefix(prefix: string): void;
toString(): string;
}
export declare class OakAttrNotNullException<ED extends EntityDict> extends OakInputIllegalException<ED> {
constructor(entity: keyof ED, attributes: string[], message?: string);
}
/**
*
*/

View File

@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeException = exports.OakPreConditionUnsetException = exports.OakDeadlock = exports.OakCongruentRowExists = exports.OakRowLockedException = exports.OakUnloggedInException = exports.OakUserUnpermittedException = exports.OakInputIllegalException = exports.OakRowInconsistencyException = exports.OakUserException = exports.OakExternalException = exports.OakRowUnexistedException = exports.OakOperExistedException = exports.OakImportDataParseException = exports.OakUniqueViolationException = exports.OakDataException = exports.OakException = void 0;
exports.makeException = exports.OakPreConditionUnsetException = exports.OakDeadlock = exports.OakCongruentRowExists = exports.OakRowLockedException = exports.OakUnloggedInException = exports.OakUserUnpermittedException = exports.OakAttrNotNullException = exports.OakInputIllegalException = exports.OakRowInconsistencyException = exports.OakUserException = exports.OakExternalException = exports.OakRowUnexistedException = exports.OakOperExistedException = exports.OakImportDataParseException = exports.OakUniqueViolationException = exports.OakDataException = exports.OakException = void 0;
var tslib_1 = require("tslib");
var assert_1 = tslib_1.__importDefault(require("assert"));
var OakException = /** @class */ (function (_super) {
@ -179,6 +179,15 @@ var OakInputIllegalException = /** @class */ (function (_super) {
}(OakUserException));
exports.OakInputIllegalException = OakInputIllegalException;
;
// 属性为空
var OakAttrNotNullException = /** @class */ (function (_super) {
tslib_1.__extends(OakAttrNotNullException, _super);
function OakAttrNotNullException(entity, attributes, message) {
return _super.call(this, entity, attributes, message || '属性不允许为空') || this;
}
return OakAttrNotNullException;
}(OakInputIllegalException));
exports.OakAttrNotNullException = OakAttrNotNullException;
/**
* 用户权限不够时抛的异常
*/
@ -344,6 +353,11 @@ function makeException(data) {
e.setOpRecords(data.opRecords);
return e;
}
case 'OakAttrNotNullException': {
var e = new OakAttrNotNullException(data.entity, data.attributes, data.message);
e.setOpRecords(data.opRecords);
return e;
}
default:
return;
}

View File

@ -114,7 +114,7 @@ function checkAttributesNotNull(entity, data, attributes, allowEmpty) {
}
});
if (attrs.length > 0) {
throw new types_1.OakInputIllegalException(entity, attrs, '属性不能为空');
throw new types_1.OakAttrNotNullException(entity, attrs, '属性不能为空');
}
}
exports.checkAttributesNotNull = checkAttributesNotNull;

View File

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

View File

@ -1,6 +1,6 @@
import { EntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore';
import { createAuthCheckers, createRemoveCheckers } from '../store/checker';
import { createAuthCheckers, createRemoveCheckers, createCreateCheckers } from '../store/checker';
import { createModiRelatedCheckers } from '../store/modi';
import { SyncContext } from '../store/SyncRowStore';
import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDef, AuthDefDict } from '../types';
@ -9,6 +9,7 @@ export function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cx
const checkers: Checker<ED, keyof ED, Cxt>[] = [];
checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema));
checkers.push(...createRemoveCheckers<ED, Cxt>(schema, authDict));
checkers.push(...createCreateCheckers<ED, Cxt>(schema));
if (authDict) {
checkers.push(...createAuthCheckers<ED, Cxt>(schema, authDict));
}

View File

@ -5268,7 +5268,15 @@ function constructAttributes(entity: string): ts.PropertyAssignment[] {
schemaAttrs.forEach(
(attr) => {
const attrAssignments: ts.PropertyAssignment[] = [];
const { name, type } = attr;
const { name, type, questionToken: allowNull } = attr;
if (!allowNull) {
attrAssignments.push(
factory.createPropertyAssignment(
factory.createIdentifier("notNull"),
factory.createTrue(),
),
);
}
let name2 = name;
if (ts.isTypeReferenceNode(type!)) {

View File

@ -241,7 +241,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
}
}
const number = (trigger as CreateTrigger<ED, T, Cxt>).fn({ operation: operation as ED[T]['Create'] }, context, option as OperateOption);
if (number > 0) {
if (number as number > 0) {
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
}
}
@ -371,7 +371,7 @@ export class TriggerExecutor<ED extends EntityDict & BaseEntityDict> {
operation: operation as ED[T]['Selection'],
result: result!,
}, context, option as SelectOption);
if (number > 0) {
if (number as number > 0) {
this.logger.info(`触发器「${trigger.name}」成功触发了「${number}」行数据更改`);
}
}

View File

@ -1,6 +1,6 @@
import assert from 'assert';
import { addFilterSegment, checkFilterContains, combineFilters } from "../store/filter";
import { OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
import { OakAttrNotNullException, OakInputIllegalException, OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
import {
AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn,
EntityDict, OperateOption, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn, RelationHierarchy, SelectOpResult, REMOVE_CASCADE_PRIORITY, RefOrExpression, SyncOrAsync
@ -13,6 +13,7 @@ import { firstLetterUpperCase } from '../utils/string';
import { union, uniq, difference } from '../utils/lodash';
import { judgeRelation } from './relation';
import { generateNewId } from '../utils/uuid';
import { excludeUpdateActions } from '../actions/action';
/**
*
@ -780,7 +781,7 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
// 如果不指定relation则使用所有的authItem的or组合
Object.assign(raFilterMakerDict, {
'@@all': translateActionAuthFilterMaker(schema, allAuthItem, userEntityName, entity),
'@@all': translateActionAuthFilterMaker(schema, allAuthItem, userEntityName, entity),
});
const entityIdAttr = `${entity}Id`;
@ -1169,3 +1170,142 @@ export function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt
return checkers;
}
function checkAttributeLegal<ED extends EntityDict & BaseEntityDict>(
schema: StorageSchema<ED>,
entity: keyof ED,
data: ED[keyof ED]['Update']['data'] | ED[keyof ED]['CreateSingle']['data']) {
const { attributes } = schema[entity];
for (const attr in data) {
if (attributes[attr as string]) {
const { type, params, default: defaultValue, enumeration, notNull } = attributes[attr as string];
if (data[attr] === null || data[attr] === undefined) {
if (notNull && defaultValue === undefined) {
throw new OakAttrNotNullException(entity, [attr]);
}
if (defaultValue !== undefined) {
Object.assign(data, {
[attr]: defaultValue,
});
}
continue;
}
switch (type) {
case 'char':
case 'varchar': {
if (typeof (data as ED[keyof ED]['CreateSingle']['data'])[attr] !== 'string') {
throw new OakInputIllegalException(entity, [attr], 'not a string');
}
const { length } = params!;
if (length && (data as ED[keyof ED]['CreateSingle']['data'])[attr]!.length > length) {
throw new OakInputIllegalException(entity, [attr], 'too long');
}
break;
}
case 'int':
case 'smallint':
case 'tinyint':
case 'bigint':
case 'decimal':
case 'money': {
if (typeof (data as ED[keyof ED]['CreateSingle']['data'])[attr] !== 'number') {
throw new OakInputIllegalException(entity, [attr], 'not a number');
}
const { min, max } = params || {};
if (typeof min === 'number' && (data as ED[keyof ED]['CreateSingle']['data'])[attr] < min) {
throw new OakInputIllegalException(entity, [attr], 'too small');
}
if (typeof max === 'number' && (data as ED[keyof ED]['CreateSingle']['data'])[attr] > max) {
throw new OakInputIllegalException(entity, [attr], 'too big');
}
break;
}
case 'enum': {
assert(enumeration);
if (!enumeration.includes((data as ED[keyof ED]['CreateSingle']['data'])[attr])) {
throw new OakInputIllegalException(entity, [attr], 'not in enumberation');
}
break;
}
}
}
else {
// 这里似乎还有一种update中带cascade remove的case等遇到再说貌似cascadeUpdate没有处理完整这种情况 by Xc
if (typeof data[attr] === 'object' && data[attr].action === 'remove') {
console.warn('cascade remove可能是未处理的边界请注意');
}
}
}
}
export function createCreateCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>) {
const checkers: Checker<ED, keyof ED, Cxt>[] = [];
for (const entity in schema) {
const { attributes, actions } = schema[entity];
const notNullAttrs = Object.keys(attributes).filter(
ele => attributes[ele].notNull
);
const updateActions = difference(actions, excludeUpdateActions);
checkers.push({
entity,
type: 'data',
action: 'create' as ED[keyof ED]['Action'],
checker: (data) => {
const checkData = (data2: ED[keyof ED]['CreateSingle']['data']) => {
const illegalNullAttrs = difference(notNullAttrs, Object.keys(data2));
if (illegalNullAttrs.length > 0) {
// 要处理多对一的cascade create
for (const attr of illegalNullAttrs) {
if (attr === 'entityId') {
if (illegalNullAttrs.includes('entity')) {
continue;
}
}
else if (attr === 'entity' && attributes[attr].type === 'ref') {
let hasCascadeCreate = false;
for (const ref of attributes[attr].ref as string[]) {
if (data2[ref] && data2[ref].action === 'create') {
hasCascadeCreate = true;
break;
}
}
if (hasCascadeCreate) {
continue;
}
}
else if (attributes[attr].type === 'ref') {
const ref = attributes[attr].ref as string;
if (data2[ref] && data2[ref].action === 'create') {
continue;
}
}
// 到这里说明确实是有not null的属性没有赋值
throw new OakAttrNotNullException(entity, illegalNullAttrs);
}
}
checkAttributeLegal(schema, entity, data2);
};
if (data instanceof Array) {
data.forEach(
ele => checkData(ele)
);
}
else {
checkData(data as ED[keyof ED]['CreateSingle']['data']);
}
}
}, {
entity,
type: 'data',
action: updateActions as ED[keyof ED]['Action'][],
checker: (data) => {
checkAttributeLegal(schema, entity, data);
}
})
}
return checkers;
}

View File

@ -149,8 +149,8 @@ export function createModiRelatedTriggers<ED extends EntityDict & BaseEntityDict
const triggers: Trigger<ED, keyof ED, Cxt>[] = [];
for (const entity in schema) {
const { inModi } = schema[entity];
if (inModi) {
const { toModi } = schema[entity];
if (toModi) {
// 当关联modi的对象被删除时对应的modi也删除。这里似乎只需要删除掉活跃对象因为oper不能删除所以oper和modi是必须要支持对deleted对象的容错
// 这里没有想清楚by Xc 20230209
triggers.push({

View File

@ -59,6 +59,7 @@ export function judgeRelation<ED extends {
else if (attributes.hasOwnProperty('entity')
&& attributes.hasOwnProperty('entityId')
&& schema.hasOwnProperty(attr)) {
assert(attributes.entity.ref!.includes(attr), '不应当出现的case');
// 反向指针的外键
return 2;
}

View File

@ -4,7 +4,6 @@ import { EntityDict } from '../types/Entity';
import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore';
import { combineFilters } from '../store/filter';
import { createGzip } from 'node:zlib';
import { pipeline } from 'stream';
import { generateNewIdAsync } from '../utils/uuid';
@ -38,7 +37,7 @@ export async function vaccumEntities<ED extends EntityDict & BaseEntityDict, Cxt
if (filter) {
filter2 = combineFilters([filter2, filter]);
}
if (backupDir) {
if (backupDir && process.env.OAK_PLATFORM === 'server') {
// 使用mysqldump将待删除的数据备份出来
const { zip: zip } = option;
const now = dayJs();
@ -105,6 +104,7 @@ export async function vaccumEntities<ED extends EntityDict & BaseEntityDict, Cxt
rmSync(backFile);
}
else if (zip) {
const { createGzip } = require('zlib');
const gzip = createGzip();
const source = createReadStream(backFile);
const destination = createWriteStream(`${backFile}.zip`);

View File

@ -81,7 +81,7 @@ export interface EntityShape {
$$deleteAt$$?: number | Date | null;
}
interface GeneralEntityShape extends EntityShape {
export interface GeneralEntityShape extends EntityShape {
[K: string]: any;
}

View File

@ -138,8 +138,8 @@ export class OakRowInconsistencyException<ED extends EntityDict> extends OakUser
*/
export class OakInputIllegalException<ED extends EntityDict> extends OakUserException<ED> {
private attributes: string[];
private entity: string;
constructor(entity: string, attributes: string[], message?: string) {
private entity: keyof ED;
constructor(entity: keyof ED, attributes: string[], message?: string) {
super(message);
this.entity = entity;
this.attributes = attributes;
@ -169,6 +169,13 @@ export class OakInputIllegalException<ED extends EntityDict> extends OakUserExce
}
};
// 属性为空
export class OakAttrNotNullException<ED extends EntityDict> extends OakInputIllegalException<ED> {
constructor(entity: keyof ED, attributes: string[], message?: string) {
super(entity, attributes, message || '属性不允许为空');
}
}
/**
*
*/
@ -329,6 +336,15 @@ export function makeException<ED extends EntityDict>(data: {
e.setOpRecords(data.opRecords);
return e;
}
case 'OakAttrNotNullException': {
const e = new OakAttrNotNullException(
data.entity,
data.attributes,
data.message
);
e.setOpRecords(data.opRecords);
return e;
}
default:
return;
}

View File

@ -3,7 +3,7 @@
*/
'use strict';
import { EntityDict, OakInputIllegalException } from "../types";
import { EntityDict, OakAttrNotNullException, OakInputIllegalException } from "../types";
type ValidatorFunction = (text: string, size?:number) => string|boolean;
type ValidatorMoneyFunction = (text: string, zero?:boolean) => string|boolean;
@ -129,7 +129,7 @@ export function checkAttributesNotNull<ED extends EntityDict, T extends keyof En
) as string[];
if (attrs.length > 0) {
throw new OakInputIllegalException(entity as string, attrs, '属性不能为空');
throw new OakAttrNotNullException(entity as string, attrs, '属性不能为空');
}
};