import type { DynamicModule, Provider } from '@nestjs/common'; import { Module } from '@nestjs/common'; import type { Constructor } from '@rsdk/common'; import { RsdkMetadata } from '@rsdk/metadata'; import { METRIC_RSDK_METADATA_SCOPE } from './metadata/constants'; import type { MetricRsdkMetadata } from './metadata'; import { MetricStorage } from './metric.storage'; import { MetricInitializingModule } from './metric-initializing.module'; import type { AnyMetric, MetricsModuleOptions } from './types'; /** * Metrics module exposes metrics interface (depending on transport) * and gives convenient ways to register and inject custom prom-client * metrics. * * @willsoto/nestjs-prometheus was an alternative, but it didn't really * fit */ @Module({}) export class MetricsModule { // TODO По идее от этого нужно избавиться, но тут нужно будет делать @InjectMetric(SomeMetric) там где сейчас используется someMetric: SomeMetric // Но стоит понимать что смысла в этом нет от слова совсем // Так как даже если там окажутся чужие метрики, но приложение не будет их использовать, то для приложения ничего не произойдёт // Но даже если представить что существует коллизия с использованием одного и того же инстанса, то во-первых // Это дыра в архитектуре потому что мы ссылаемся на один и тот же класс, то рассчитываем получить один и тот же инстанс, а так как это метрика то она должна быть общая на процесс, иначе не имеет смысла // Во-вторых если запускаем n > 1 приложения в одном и том же процессе, то снимать метрики с этих приложений как минимум... странно? // private static metrics = new Map, Provider>(); /** * Adds metrics controller provided as options * * @returns dynamic module */ static forRoot(options: MetricsModuleOptions): DynamicModule { return { global: true, controllers: [...options.controllers], module: MetricsModule, providers: [ { provide: MetricStorage, useValue: new MetricStorage(), }, ], exports: [MetricStorage], imports: [MetricInitializingModule], }; } /** * Use this to inject specified metric into target module * * @param first metric classe * @param other Not empty List of metric classes * @returns dynamic module */ static forFeature( first: Constructor, ...other: Constructor[] ): DynamicModule { const providers: Provider[] = []; for (const ctor of [first, ...other]) { const [resource] = RsdkMetadata.get( ctor, METRIC_RSDK_METADATA_SCOPE, ) ?? []; const metadata = resource.value.metadata; providers.push({ inject: [MetricStorage], provide: ctor, useFactory: (metricStorage: MetricStorage) => { const existing = metricStorage.metrics.get(ctor) ?? new ctor(metadata); metricStorage.metrics.set(ctor, existing); return existing; }, }); } return { module: MetricsModule, exports: providers, providers, }; } }