334 lines
14 KiB
JavaScript
334 lines
14 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.analyzeActionDefDict = exports.getFullProjection = void 0;
|
||
var tslib_1 = require("tslib");
|
||
var types_1 = require("../types");
|
||
var lodash_1 = require("../utils/lodash");
|
||
var filter_1 = require("./filter");
|
||
function getFullProjection(entity, schema) {
|
||
var attributes = schema[entity].attributes;
|
||
var projection = {
|
||
id: 1,
|
||
$$createAt$$: 1,
|
||
$$updateAt$$: 1,
|
||
$$deleteAt$$: 1,
|
||
};
|
||
Object.keys(attributes).forEach(function (k) {
|
||
var _a;
|
||
return Object.assign(projection, (_a = {},
|
||
_a[k] = 1,
|
||
_a));
|
||
});
|
||
return projection;
|
||
}
|
||
exports.getFullProjection = getFullProjection;
|
||
function makeIntrinsicWatchers(schema) {
|
||
var watchers = [];
|
||
for (var entity in schema) {
|
||
var attributes = schema[entity].attributes;
|
||
var expiresAt = attributes.expiresAt, expired = attributes.expired;
|
||
if (expiresAt && expiresAt.type === 'datetime' && expired && expired.type === 'boolean') {
|
||
// 如果有定义expiresAt和expired,则自动生成一个检查的watcher
|
||
watchers.push({
|
||
entity: entity,
|
||
name: "\u5BF9\u8C61".concat(entity, "\u4E0A\u7684\u8FC7\u671F\u81EA\u52A8watcher"),
|
||
filter: function () {
|
||
return {
|
||
expired: false,
|
||
expiresAt: {
|
||
$lte: Date.now(),
|
||
},
|
||
};
|
||
},
|
||
action: 'update',
|
||
actionData: {
|
||
expired: true,
|
||
},
|
||
});
|
||
}
|
||
}
|
||
return watchers;
|
||
}
|
||
function checkUniqueBetweenRows(rows, uniqAttrs) {
|
||
// 先检查这些行本身之间有无unique冲突
|
||
var uniqRows = (0, lodash_1.uniqBy)(rows, function (d) {
|
||
var e_1, _a;
|
||
var s = '';
|
||
try {
|
||
for (var uniqAttrs_1 = tslib_1.__values(uniqAttrs), uniqAttrs_1_1 = uniqAttrs_1.next(); !uniqAttrs_1_1.done; uniqAttrs_1_1 = uniqAttrs_1.next()) {
|
||
var a = uniqAttrs_1_1.value;
|
||
if (d[a] === null || d[a] === undefined) {
|
||
s + d.id;
|
||
}
|
||
else {
|
||
s + "-".concat(d[a]);
|
||
}
|
||
}
|
||
}
|
||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||
finally {
|
||
try {
|
||
if (uniqAttrs_1_1 && !uniqAttrs_1_1.done && (_a = uniqAttrs_1.return)) _a.call(uniqAttrs_1);
|
||
}
|
||
finally { if (e_1) throw e_1.error; }
|
||
}
|
||
return s;
|
||
});
|
||
if (uniqRows.length < rows.length) {
|
||
throw new types_1.OakUniqueViolationException([{
|
||
attrs: uniqAttrs,
|
||
}]);
|
||
}
|
||
}
|
||
function checkCountLessThan(count, uniqAttrs, than, id) {
|
||
if (than === void 0) { than = 0; }
|
||
if (count instanceof Promise) {
|
||
return count.then(function (count2) {
|
||
if (count2 > than) {
|
||
throw new types_1.OakUniqueViolationException([{
|
||
id: id,
|
||
attrs: uniqAttrs,
|
||
}]);
|
||
}
|
||
});
|
||
}
|
||
if (count > than) {
|
||
throw new types_1.OakUniqueViolationException([{
|
||
id: id,
|
||
attrs: uniqAttrs,
|
||
}]);
|
||
}
|
||
}
|
||
function checkUnique(entity, row, context, uniqAttrs, extraFilter) {
|
||
var filter = (0, lodash_1.pick)(row, uniqAttrs);
|
||
for (var a in filter) {
|
||
if (filter[a] === null || filter[a] === undefined) {
|
||
delete filter[a];
|
||
}
|
||
}
|
||
if (Object.keys(filter).length < uniqAttrs.length) {
|
||
// 说明有null值,不需要检查约束
|
||
return;
|
||
}
|
||
var filter2 = extraFilter ? (0, filter_1.addFilterSegment)(filter, extraFilter) : filter;
|
||
var count = context.count(entity, { filter: filter2 }, { dontCollect: true });
|
||
return checkCountLessThan(count, uniqAttrs, 0, row.id);
|
||
}
|
||
function analyzeActionDefDict(schema, actionDefDict) {
|
||
var _a;
|
||
var checkers = [];
|
||
var triggers = [];
|
||
// action状态转换矩阵相应的checker
|
||
for (var entity in actionDefDict) {
|
||
var _loop_1 = function (attr) {
|
||
var def = actionDefDict[entity][attr];
|
||
var _b = def, stm = _b.stm, is = _b.is;
|
||
var _loop_3 = function (action) {
|
||
var _c, _d;
|
||
var actionStm = stm[action];
|
||
var conditionalFilter = typeof actionStm[0] === 'string' ? (_c = {},
|
||
_c[attr] = actionStm[0],
|
||
_c) : (_d = {},
|
||
_d[attr] = {
|
||
$in: actionStm[0],
|
||
},
|
||
_d);
|
||
checkers.push({
|
||
action: action,
|
||
type: 'row',
|
||
entity: entity,
|
||
filter: conditionalFilter,
|
||
errMsg: '',
|
||
});
|
||
checkers.push({
|
||
action: action,
|
||
type: 'data',
|
||
entity: entity,
|
||
priority: 10,
|
||
checker: function (data) {
|
||
var _a;
|
||
Object.assign(data, (_a = {},
|
||
_a[attr] = stm[action][1],
|
||
_a));
|
||
}
|
||
});
|
||
};
|
||
for (var action in stm) {
|
||
_loop_3(action);
|
||
}
|
||
if (is) {
|
||
checkers.push({
|
||
action: 'create',
|
||
type: 'data',
|
||
entity: entity,
|
||
priority: 10,
|
||
checker: function (data) {
|
||
var _a;
|
||
if (data instanceof Array) {
|
||
data.forEach(function (ele) {
|
||
var _a;
|
||
if (!ele[attr]) {
|
||
Object.assign(ele, (_a = {},
|
||
_a[attr] = is,
|
||
_a));
|
||
}
|
||
});
|
||
}
|
||
else {
|
||
if (!data[attr]) {
|
||
Object.assign(data, (_a = {},
|
||
_a[attr] = is,
|
||
_a));
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
};
|
||
for (var attr in actionDefDict[entity]) {
|
||
_loop_1(attr);
|
||
}
|
||
}
|
||
var _loop_2 = function (entity) {
|
||
var e_2, _e;
|
||
var indexes = schema[entity].indexes;
|
||
if (indexes) {
|
||
var _loop_4 = function (index) {
|
||
if ((_a = index.config) === null || _a === void 0 ? void 0 : _a.unique) {
|
||
var attributes = index.attributes;
|
||
var uniqAttrs_2 = attributes.map(function (ele) { return ele.name; });
|
||
checkers.push({
|
||
entity: entity,
|
||
action: 'create',
|
||
type: 'logical',
|
||
priority: 20,
|
||
checker: function (operation, context) {
|
||
var data = operation.data;
|
||
if (data instanceof Array) {
|
||
checkUniqueBetweenRows(data, uniqAttrs_2);
|
||
var checkResult = data.map(function (ele) { return checkUnique(entity, ele, context, uniqAttrs_2); });
|
||
if (checkResult[0] instanceof Promise) {
|
||
return Promise.all(checkResult).then(function () { return undefined; });
|
||
}
|
||
}
|
||
else {
|
||
return checkUnique(entity, data, context, uniqAttrs_2);
|
||
}
|
||
}
|
||
}, {
|
||
entity: entity,
|
||
action: 'update',
|
||
type: 'logical',
|
||
priority: 20,
|
||
checker: function (operation, context) {
|
||
var e_3, _a, e_4, _b, _c;
|
||
var _d = operation, data = _d.data, operationFilter = _d.filter;
|
||
var attrs = Object.keys(data);
|
||
var refAttrs = (0, lodash_1.intersection)(attrs, uniqAttrs_2);
|
||
if (refAttrs.length === 0) {
|
||
// 如果本次更新和unique约束的属性之间没有交集则直接返回
|
||
return;
|
||
}
|
||
try {
|
||
for (var refAttrs_1 = (e_3 = void 0, tslib_1.__values(refAttrs)), refAttrs_1_1 = refAttrs_1.next(); !refAttrs_1_1.done; refAttrs_1_1 = refAttrs_1.next()) {
|
||
var attr = refAttrs_1_1.value;
|
||
// 如果有更新为null值,不用再检查约束
|
||
if (data[attr] === null || data[attr] === undefined) {
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
||
finally {
|
||
try {
|
||
if (refAttrs_1_1 && !refAttrs_1_1.done && (_a = refAttrs_1.return)) _a.call(refAttrs_1);
|
||
}
|
||
finally { if (e_3) throw e_3.error; }
|
||
}
|
||
if (refAttrs.length === uniqAttrs_2.length) {
|
||
// 如果更新了全部属性,直接检查
|
||
var filter = (0, lodash_1.pick)(data, refAttrs);
|
||
// 在这些行以外的行不和更新后的键值冲突
|
||
var count = context.count(entity, {
|
||
filter: (0, filter_1.addFilterSegment)([filter, {
|
||
$not: operationFilter,
|
||
}]),
|
||
}, { dontCollect: true });
|
||
var checkCount = checkCountLessThan(count, uniqAttrs_2);
|
||
// 更新的行只能有一行
|
||
var rowCount = context.count(entity, {
|
||
filter: operationFilter,
|
||
}, { dontCollect: true });
|
||
var checkRowCount = checkCountLessThan(rowCount, uniqAttrs_2, 1);
|
||
// 如果更新的行数为零似乎也可以,但这应该不可能出现吧,by Xc 20230131
|
||
if (checkRowCount instanceof Promise) {
|
||
return Promise.all([checkCount, checkRowCount]).then(function () { return undefined; });
|
||
}
|
||
}
|
||
// 否则需要结合本行现有的属性来进行检查
|
||
var projection = { id: 1 };
|
||
try {
|
||
for (var uniqAttrs_3 = (e_4 = void 0, tslib_1.__values(uniqAttrs_2)), uniqAttrs_3_1 = uniqAttrs_3.next(); !uniqAttrs_3_1.done; uniqAttrs_3_1 = uniqAttrs_3.next()) {
|
||
var attr = uniqAttrs_3_1.value;
|
||
Object.assign(projection, (_c = {},
|
||
_c[attr] = 1,
|
||
_c));
|
||
}
|
||
}
|
||
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
||
finally {
|
||
try {
|
||
if (uniqAttrs_3_1 && !uniqAttrs_3_1.done && (_b = uniqAttrs_3.return)) _b.call(uniqAttrs_3);
|
||
}
|
||
finally { if (e_4) throw e_4.error; }
|
||
}
|
||
var checkWithRows = function (rows2) {
|
||
var rows22 = rows2.map(function (ele) { return Object.assign(ele, data); });
|
||
// 先检查这些行本身之间是否冲突
|
||
checkUniqueBetweenRows(rows22, uniqAttrs_2);
|
||
var checkResults = rows22.map(function (row) { return checkUnique(entity, row, context, uniqAttrs_2, {
|
||
$not: operationFilter
|
||
}); });
|
||
if (checkResults[0] instanceof Promise) {
|
||
return Promise.all(checkResults).then(function () { return undefined; });
|
||
}
|
||
};
|
||
var currentRows = context.select(entity, {
|
||
data: projection,
|
||
filter: operationFilter,
|
||
}, { dontCollect: true });
|
||
if (currentRows instanceof Promise) {
|
||
return currentRows.then(function (row2) { return checkWithRows(row2); });
|
||
}
|
||
return checkWithRows(currentRows);
|
||
}
|
||
});
|
||
}
|
||
};
|
||
try {
|
||
for (var indexes_1 = (e_2 = void 0, tslib_1.__values(indexes)), indexes_1_1 = indexes_1.next(); !indexes_1_1.done; indexes_1_1 = indexes_1.next()) {
|
||
var index = indexes_1_1.value;
|
||
_loop_4(index);
|
||
}
|
||
}
|
||
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
||
finally {
|
||
try {
|
||
if (indexes_1_1 && !indexes_1_1.done && (_e = indexes_1.return)) _e.call(indexes_1);
|
||
}
|
||
finally { if (e_2) throw e_2.error; }
|
||
}
|
||
}
|
||
};
|
||
// unique索引相应的checker
|
||
for (var entity in schema) {
|
||
_loop_2(entity);
|
||
}
|
||
return {
|
||
triggers: triggers,
|
||
checkers: checkers,
|
||
watchers: makeIntrinsicWatchers(schema),
|
||
};
|
||
}
|
||
exports.analyzeActionDefDict = analyzeActionDefDict;
|