Files
Automata/packages/@n8n/config/src/decorators.ts
Iván Ovejero 95b85dd5c1 refactor(core): Port nodes config (no-changelog) (#10140)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
2024-07-23 13:32:50 +02:00

83 lines
2.7 KiB
TypeScript

import 'reflect-metadata';
import { readFileSync } from 'fs';
import { Container, Service } from 'typedi';
// eslint-disable-next-line @typescript-eslint/ban-types
type Class = Function;
type Constructable<T = unknown> = new (rawValue: string) => T;
type PropertyKey = string | symbol;
interface PropertyMetadata {
type: unknown;
envName?: string;
}
const globalMetadata = new Map<Class, Map<PropertyKey, PropertyMetadata>>();
export const Config: ClassDecorator = (ConfigClass: Class) => {
const factory = function () {
const config = new (ConfigClass as new () => Record<PropertyKey, unknown>)();
const classMetadata = globalMetadata.get(ConfigClass);
if (!classMetadata) {
// eslint-disable-next-line n8n-local-rules/no-plain-errors
throw new Error('Invalid config class: ' + ConfigClass.name);
}
for (const [key, { type, envName }] of classMetadata) {
if (typeof type === 'function' && globalMetadata.has(type)) {
config[key] = Container.get(type);
} else if (envName) {
let value: unknown = process.env[envName];
// Read the value from a file, if "_FILE" environment variable is defined
const filePath = process.env[`${envName}_FILE`];
if (filePath) {
value = readFileSync(filePath, 'utf8');
}
if (type === Number) {
value = Number(value);
if (isNaN(value as number)) {
// TODO: add a warning
value = undefined;
}
} else if (type === Boolean) {
if (value !== 'true' && value !== 'false') {
// TODO: add a warning
value = undefined;
} else {
value = value === 'true';
}
} else if (type !== String && type !== Object) {
value = new (type as Constructable)(value as string);
}
if (value !== undefined) {
config[key] = value;
}
}
}
return config;
};
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Service({ factory })(ConfigClass);
};
export const Nested: PropertyDecorator = (target: object, key: PropertyKey) => {
const ConfigClass = target.constructor;
const classMetadata = globalMetadata.get(ConfigClass) ?? new Map<PropertyKey, PropertyMetadata>();
const type = Reflect.getMetadata('design:type', target, key) as unknown;
classMetadata.set(key, { type });
globalMetadata.set(ConfigClass, classMetadata);
};
export const Env =
(envName: string): PropertyDecorator =>
(target: object, key: PropertyKey) => {
const ConfigClass = target.constructor;
const classMetadata =
globalMetadata.get(ConfigClass) ?? new Map<PropertyKey, PropertyMetadata>();
const type = Reflect.getMetadata('design:type', target, key) as unknown;
classMetadata.set(key, { type, envName });
globalMetadata.set(ConfigClass, classMetadata);
};