UNPKG

@elsikora/nestjs-crud-automator

Version:

A library for automating the creation of CRUD operations in NestJS.

144 lines (141 loc) 5.86 kB
'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