import { Container, Service } from 'typedi'; import type { Variables } from '@/databases/entities/variables'; import { generateNanoId } from '@/databases/utils/generators'; import { canCreateNewVariable } from './environment-helpers'; import { CacheService } from '@/services/cache/cache.service'; import { VariablesRepository } from '@/databases/repositories/variables.repository'; import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error'; import { VariableValidationError } from '@/errors/variable-validation.error'; import { EventService } from '@/events/event.service'; @Service() export class VariablesService { constructor( protected cacheService: CacheService, protected variablesRepository: VariablesRepository, private readonly eventService: EventService, ) {} async getAllCached(): Promise { const variables = await this.cacheService.get('variables', { async refreshFn() { return await Container.get(VariablesService).findAll(); }, }); return (variables as Array>).map((v) => this.variablesRepository.create(v)); } async getCount(): Promise { return (await this.getAllCached()).length; } async getCached(id: string): Promise { const variables = await this.getAllCached(); const foundVariable = variables.find((variable) => variable.id === id); if (!foundVariable) { return null; } return this.variablesRepository.create(foundVariable as Partial); } async delete(id: string): Promise { await this.variablesRepository.delete(id); await this.updateCache(); } async updateCache(): Promise { // TODO: log update cache metric const variables = await this.findAll(); await this.cacheService.set('variables', variables); } async findAll(): Promise { return await this.variablesRepository.find(); } validateVariable(variable: Omit): void { if (variable.key.length > 50) { throw new VariableValidationError('key cannot be longer than 50 characters'); } if (variable.key.replace(/[A-Za-z0-9_]/g, '').length !== 0) { throw new VariableValidationError('key can only contain characters A-Za-z0-9_'); } if (variable.value?.length > 255) { throw new VariableValidationError('value cannot be longer than 255 characters'); } } async create(variable: Omit): Promise { if (!canCreateNewVariable(await this.getCount())) { throw new VariableCountLimitReachedError('Variables limit reached'); } this.validateVariable(variable); this.eventService.emit('variable-created'); const saveResult = await this.variablesRepository.save( { ...variable, id: generateNanoId(), }, { transaction: false }, ); await this.updateCache(); return saveResult; } async update(id: string, variable: Omit): Promise { this.validateVariable(variable); await this.variablesRepository.update(id, variable); await this.updateCache(); return (await this.getCached(id))!; } }