@elsikora/nestjs-crud-automator
Version:
A library for automating the creation of CRUD operations in NestJS.
144 lines (141 loc) • 5.86 kB
JavaScript
'use strict';
var tslib_es6 = require('../../../../external/tslib/tslib.es6.js');
var common = require('@nestjs/common');
var logger_utility = require('../../../../utility/logger.utility.js');
const iamQueryPlannerLogger = logger_utility.LoggerUtility.getLogger("ApiAuthorizationIamQueryPlanner");
exports.ApiAuthorizationIamQueryPlanner = class ApiAuthorizationIamQueryPlanner {
plan(options) {
iamQueryPlannerLogger.verbose(`Planning IAM query scope for resource type "${options.resourceDefinition.resourceType}" using ${options.statements.length} statements.`);
const branches = [];
for (const statement of options.statements) {
const compiledStatement = this.compileStatementWhere({
condition: statement.Condition,
principal: options.principal,
requestMetadata: options.requestMetadata,
resource: options.resource,
resourceDefinition: options.resourceDefinition,
});
if (!compiledStatement.isCompilable) {
iamQueryPlannerLogger.verbose(`IAM query scope is not fully compilable for resource type "${options.resourceDefinition.resourceType}".`);
return { isFullyCompilable: false };
}
if (!compiledStatement.where) {
continue;
}
if (Object.keys(compiledStatement.where).length === 0) {
return { isFullyCompilable: true, scope: undefined };
}
branches.push(compiledStatement.where);
}
if (branches.length === 0) {
iamQueryPlannerLogger.verbose(`IAM query scope for resource type "${options.resourceDefinition.resourceType}" resolved without additional where branches.`);
return { isFullyCompilable: true, scope: undefined };
}
iamQueryPlannerLogger.verbose(`IAM query scope for resource type "${options.resourceDefinition.resourceType}" resolved with ${branches.length} where branches.`);
return {
isFullyCompilable: true,
scope: {
where: branches.length === 1 ? branches[0] : branches,
},
};
}
assignWhereValue(where, queryPath, value) {
const pathSegments = queryPath.split(".").filter(Boolean);
if (pathSegments.length === 0) {
return;
}
let currentValue = where;
for (const segment of pathSegments.slice(0, -1)) {
if (!this.isRecord(currentValue[segment])) {
currentValue[segment] = {};
}
currentValue = currentValue[segment];
}
const lastSegment = pathSegments.at(-1);
if (!lastSegment) {
return;
}
currentValue[lastSegment] = value;
}
compileStatementWhere(options) {
if (!options.condition) {
return {
isCompilable: true,
};
}
const where = {};
let hasResourceCondition = false;
for (const [operator, operands] of Object.entries(options.condition)) {
for (const [path, rawValue] of Object.entries(operands)) {
if (!path.startsWith("resource.")) {
continue;
}
hasResourceCondition = true;
if (operator !== "StringEquals" && operator !== "StringEqualsIfExists") {
return { isCompilable: false };
}
const fieldDefinition = options.resourceDefinition.fields.find((field) => field.path === path);
if (!fieldDefinition?.isFilterable) {
return { isCompilable: false };
}
const resolvedValue = this.resolveConditionValue(rawValue, {
principal: options.principal,
request: options.requestMetadata,
resource: options.resource,
});
if (resolvedValue === undefined || this.isComplexValue(resolvedValue)) {
return { isCompilable: false };
}
this.assignWhereValue(where, fieldDefinition.queryPath, resolvedValue);
}
}
return {
isCompilable: true,
where: hasResourceCondition ? where : undefined,
};
}
isComplexValue(value) {
return typeof value === "object" && value !== null;
}
isRecord(value) {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
resolveConditionValue(value, context) {
if (typeof value !== "string") {
return value;
}
const matcher = /^\$\{(.+)\}$/.exec(value);
if (!matcher) {
return value;
}
const matchedPath = matcher[1];
if (!matchedPath) {
return undefined;
}
return this.resolvePathValue({
principal: context.principal,
request: {
body: context.request.body,
headers: context.request.headers,
ip: context.request.ip,
parameters: context.request.parameters,
query: context.request.query,
},
resource: context.resource,
}, matchedPath);
}
resolvePathValue(source, path) {
let currentValue = source;
for (const segment of path.split(".").filter(Boolean)) {
if (!this.isRecord(currentValue)) {
return undefined;
}
currentValue = currentValue[segment];
}
return currentValue;
}
};
exports.ApiAuthorizationIamQueryPlanner = tslib_es6.__decorate([
common.Injectable()
], exports.ApiAuthorizationIamQueryPlanner);
//# sourceMappingURL=query-planner.class.js.map