Merge branch 'release'
This commit is contained in:
commit
5ec7de7563
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -30,3 +30,4 @@ export declare function createAuthCheckers<ED extends EntityDict & BaseEntityDic
|
|||
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
||||
*/
|
||||
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>[];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
/**
|
||||
* 用户权限不够时抛的异常
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "oak-domain",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.8",
|
||||
"author": {
|
||||
"name": "XuChang"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!)) {
|
||||
|
|
|
|||
|
|
@ -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}」行数据更改`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -777,10 +778,10 @@ export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt e
|
|||
allAuthItem.push(authItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果不指定relation,则使用所有的authItem的or组合
|
||||
Object.assign(raFilterMakerDict, {
|
||||
'@@all': translateActionAuthFilterMaker(schema, allAuthItem, userEntityName, entity),
|
||||
'@@all': translateActionAuthFilterMaker(schema, allAuthItem, userEntityName, entity),
|
||||
});
|
||||
|
||||
const entityIdAttr = `${entity}Id`;
|
||||
|
|
@ -1167,5 +1168,144 @@ 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;
|
||||
}
|
||||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`);
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export interface EntityShape {
|
|||
$$deleteAt$$?: number | Date | null;
|
||||
}
|
||||
|
||||
interface GeneralEntityShape extends EntityShape {
|
||||
export interface GeneralEntityShape extends EntityShape {
|
||||
[K: string]: any;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, '属性不能为空');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue