@elsikora/nestjs-crud-automator
Version:
A library for automating the creation of CRUD operations in NestJS.
119 lines (116 loc) • 6.51 kB
JavaScript
import { AsyncLocalStorage } from 'node:async_hooks';
import { ApiControllerGeneratedScopeWhereContract } from './scope-where-contract.class.js';
import { ApiControllerGeneratedSecuritySnapshot } from './security-snapshot.class.js';
import '../../../../enum/decorator/api/action.enum.js';
import '../../../../enum/decorator/api/authentication-type.enum.js';
import '../../../../enum/decorator/api/controller/get-list/query/filter/missing-behavior.enum.js';
import '../../../../enum/decorator/api/controller/get-list/query/pagination-mode.enum.js';
import '../../../../enum/decorator/api/controller/get-list/query/unlisted-fields.enum.js';
import '../../../../enum/decorator/api/controller/relation-reference-shape.enum.js';
import '../../../../enum/decorator/api/controller/request/target.enum.js';
import '../../../../enum/decorator/api/controller/request/transformer-type.enum.js';
import '../../../../enum/decorator/api/controller/response-target.enum.js';
import '../../../../enum/decorator/api/dto-type.enum.js';
import '../../../../enum/decorator/api/function/context-storage-kind.enum.js';
import '../../../../enum/decorator/api/function/subscriber-transaction-expectation.enum.js';
import '../../../../enum/decorator/api/function/transaction/event-status.enum.js';
import '../../../../enum/decorator/api/function/transaction/failure-stage.enum.js';
import '../../../../enum/decorator/api/function/transaction/mode.enum.js';
import '../../../../enum/decorator/api/function/transaction/outcome.enum.js';
import '../../../../enum/decorator/api/function/transaction/owner-kind.enum.js';
import '../../../../enum/decorator/api/function/transaction/trace-type.enum.js';
import { EApiFunctionType } from '../../../../enum/decorator/api/function/type.enum.js';
import '../../../../enum/decorator/api/on-type.enum.js';
import '../../../../enum/decorator/api/property/data-type.enum.js';
import '../../../../enum/decorator/api/property/date/identifier.enum.js';
import '../../../../enum/decorator/api/property/date/type.enum.js';
import '../../../../enum/decorator/api/property/desribe-type.enum.js';
import '../../../../enum/decorator/api/property/number-type.enum.js';
import '../../../../enum/decorator/api/property/string-type.enum.js';
import '../../../../enum/decorator/api/route/subscriber-authorization-expectation.enum.js';
import '../../../../enum/decorator/api/route/type.enum.js';
import { AuthorizationScopeMergeWhere } from '../../../../utility/authorization/scope-merge-where.utility.js';
import { ErrorException } from '../../../../utility/error/exception.utility.js';
/**
* Carries the final generated-route read predicate to exactly one decorated service call.
* This is intentionally internal: direct service calls retain their ordinary subscriber contract.
*/
class ApiControllerGeneratedReadScopeStorage {
static STORAGE = new AsyncLocalStorage();
static claim(functionType, input) {
const entry = this.STORAGE.getStore();
if (entry?.functionType !== functionType || entry.input !== input) {
return undefined;
}
if (entry.isClaimed) {
throw ErrorException("Generated mandatory scope must be claimed exactly once");
}
entry.isClaimed = true;
return entry.where;
}
static isWriteHydration(functionType, input) {
const entry = this.STORAGE.getStore();
return Boolean(entry?.isWriteHydration && entry.functionType === functionType && entry.input === input && entry.isClaimed);
}
static protect(properties, mandatoryWhere) {
const whereDescriptor = Object.getOwnPropertyDescriptor(properties, "where");
if (whereDescriptor && (!whereDescriptor.enumerable || !("value" in whereDescriptor))) {
throw ErrorException("Generated read function subscriber WHERE must be an enumerable data property");
}
const protectedProperties = Object.create(Object.getPrototypeOf(properties));
for (const key of Reflect.ownKeys(properties)) {
if (key === "cache" || key === "where") {
continue;
}
const descriptor = Object.getOwnPropertyDescriptor(properties, key);
if (descriptor) {
Object.defineProperty(protectedProperties, key, descriptor);
}
}
Object.defineProperty(protectedProperties, "cache", {
// eslint-disable-next-line @elsikora/typescript/naming-convention
configurable: true,
// eslint-disable-next-line @elsikora/typescript/naming-convention
enumerable: true,
// eslint-disable-next-line @elsikora/typescript/naming-convention
value: false,
// eslint-disable-next-line @elsikora/typescript/naming-convention
writable: true,
});
Object.defineProperty(protectedProperties, "where", {
// eslint-disable-next-line @elsikora/typescript/naming-convention
configurable: true,
// eslint-disable-next-line @elsikora/typescript/naming-convention
enumerable: true,
value: ApiControllerGeneratedScopeWhereContract.merge(whereDescriptor && "value" in whereDescriptor ? whereDescriptor.value : undefined, mandatoryWhere),
// eslint-disable-next-line @elsikora/typescript/naming-convention
writable: true,
});
return protectedProperties;
}
static run(functionType, input, where, callback) {
return this.runWithEntry(functionType, input, where, callback, false);
}
static runWriteHydration(input, where, callback) {
return this.runWithEntry(EApiFunctionType.GET, input, where, callback, true);
}
static runWithEntry(functionType, input, where, callback, isWriteHydration) {
const normalizedWhere = AuthorizationScopeMergeWhere(undefined, ApiControllerGeneratedSecuritySnapshot.detach(where));
const entry = {
functionType,
input,
isClaimed: false,
isWriteHydration,
where: normalizedWhere,
};
return this.STORAGE.run(entry, async () => {
const result = await callback();
if (!entry.isClaimed) {
throw ErrorException("Generated service function did not claim its mandatory scope");
}
return result;
});
}
}
export { ApiControllerGeneratedReadScopeStorage };
//# sourceMappingURL=read-scope-storage.class.js.map