import { BadRequestException, ForbiddenException, Injectable, NotFoundException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { ServicePolicy } from '../authorization/policies/service.policy'; import { AuthUser } from '../auth/interfaces/auth-user.interface'; import { SmsTemplatesService } from '../sms-templates/sms-templates.service'; import { SMS_SCENARIO_MAP, SmsScenarioDefinition, } from './constants/sms-scenarios.constants'; import { PreviewSmsScenarioDto, UpdateSmsScenarioSettingDto, } from './dto/update-sms-scenario-setting.dto'; import { SmsScenarioSetting } from './entities/sms-scenario-setting.entity'; import { SmsScenarioKey } from './enums/sms-scenario-key.enum'; import { SmsDispatchService } from './sms-dispatch.service'; import { isCampaignSmsScenarioKey } from './utils/is-campaign-sms-scenario.util'; export interface SmsScenarioSettingResponse { key: SmsScenarioKey; body: string | null; templateId: string | null; templateTitle: string | null; isActive: boolean; definition: SmsScenarioDefinition; preview: string; } @Injectable() export class SmsSettingsService { constructor( @InjectRepository(SmsScenarioSetting) private readonly settingsRepository: Repository, private readonly smsDispatchService: SmsDispatchService, private readonly smsTemplatesService: SmsTemplatesService, private readonly servicePolicy: ServicePolicy, ) {} getScenarioDefinitions(): SmsScenarioDefinition[] { return this.smsDispatchService .getScenarioDefinitions() .filter((definition) => !isCampaignSmsScenarioKey(definition.key)); } async findAll(user: AuthUser): Promise { this.ensureCanManage(user); const settings = await this.smsDispatchService.findAllSettings(); return Promise.all( settings .filter((setting) => !isCampaignSmsScenarioKey(setting.key)) .map((setting) => this.toResponse(setting)), ); } async update( key: SmsScenarioKey, updateDto: UpdateSmsScenarioSettingDto, user: AuthUser, ): Promise { this.ensureCanManage(user); if (!SMS_SCENARIO_MAP.has(key)) { throw new NotFoundException(`SMS scenario "${key}" not found`); } const setting = await this.smsDispatchService.findSettingByKey(key); if (updateDto.templateId) { await this.smsTemplatesService.findOne(updateDto.templateId); setting.templateId = updateDto.templateId; setting.body = null; } else if (updateDto.templateId === null) { setting.templateId = null; } if (updateDto.body !== undefined) { setting.body = updateDto.body; if (updateDto.body) { setting.templateId = null; } } if (updateDto.isActive !== undefined) { setting.isActive = updateDto.isActive; } if (!setting.templateId && !setting.body) { throw new BadRequestException( 'Either body text or a template must be configured', ); } const saved = await this.settingsRepository.save(setting); return this.toResponse(saved); } async preview( dto: PreviewSmsScenarioDto, user: AuthUser, ): Promise<{ preview: string }> { this.ensureCanManage(user); const preview = await this.smsDispatchService.previewMessage(dto); return { preview }; } private async toResponse( setting: SmsScenarioSetting, ): Promise { const definition = SMS_SCENARIO_MAP.get(setting.key); if (!definition) { throw new NotFoundException(`SMS scenario "${setting.key}" not found`); } let templateTitle: string | null = null; if (setting.templateId) { const template = await this.smsTemplatesService.findOne( setting.templateId, ); templateTitle = template.title; } const preview = await this.smsDispatchService.previewMessage({ key: setting.key, body: setting.body ?? undefined, templateId: setting.templateId, }); return { key: setting.key, body: setting.body, templateId: setting.templateId, templateTitle, isActive: setting.isActive, definition, preview, }; } private ensureCanManage(user: AuthUser): void { if (!this.servicePolicy.canManage(user)) { throw new ForbiddenException(); } } }