Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(application-system): Add notifications support to pruning #17029

Merged
merged 14 commits into from
Dec 3, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import { environment } from '../../../../environments'
import { SequelizeConfigService } from '../../../sequelizeConfig.service'
import { ApplicationChargeModule } from '../charge/application-charge.module'
import { ApplicationLifeCycleService } from './application-lifecycle.service'
import {
UserNotificationClientConfig,
UserNotificationEagerClientModule,
} from '@island.is/clients/user-notification'

@Module({
imports: [
Expand All @@ -26,10 +30,16 @@ import { ApplicationLifeCycleService } from './application-lifecycle.service'
LoggingModule,
ApplicationChargeModule,
ApplicationFilesModule,
UserNotificationEagerClientModule,
AuditModule.forRoot(environment.audit),
ConfigModule.forRoot({
isGlobal: true,
load: [signingModuleConfig, ApplicationFilesConfig, FileStorageConfig],
load: [
UserNotificationClientConfig,
signingModuleConfig,
ApplicationFilesConfig,
FileStorageConfig,
],
}),
],
providers: [ApplicationLifeCycleService],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,34 @@ import { LOGGER_PROVIDER } from '@island.is/logging'
import type { Logger } from '@island.is/logging'
import { ApplicationChargeService } from '../charge/application-charge.service'
import { FileService } from '@island.is/application/api/files'
import {
CreateHnippNotificationDto,
NotificationsApi,
} from '@island.is/clients/user-notification'
import { getApplicationTemplateByTypeId } from '@island.is/application/template-loader'
import {
ApplicationWithAttachments,
PruningApplication,
} from '@island.is/application/types'

export interface ApplicationPruning {
pruned: boolean
application: Pick<
Application,
'id' | 'attachments' | 'answers' | 'externalData' | 'typeId' | 'state'
>
application: PruningApplication
failedAttachments: object
}

@Injectable()
export class ApplicationLifeCycleService {
private processingApplications: ApplicationPruning[] = []
private pruneNotifications = new Map<string, CreateHnippNotificationDto>()

constructor(
@Inject(LOGGER_PROVIDER)
private logger: Logger,
private applicationService: ApplicationService,
private fileService: FileService,
private applicationChargeService: ApplicationChargeService,
private readonly notificationApi: NotificationsApi,
) {
this.logger = logger.child({ context: 'ApplicationLifeCycleService' })
}
Expand All @@ -38,7 +46,7 @@ export class ApplicationLifeCycleService {
await this.pruneAttachments()
await this.pruneApplicationCharge()
await this.pruneApplicationData()
this.reportResults()
await this.reportResults()
this.logger.info(`Application pruning done.`)
}

Expand All @@ -48,10 +56,7 @@ export class ApplicationLifeCycleService {

private async fetchApplicationsToBePruned() {
const applications =
(await this.applicationService.findAllDueToBePruned()) as Pick<
Application,
'id' | 'attachments' | 'answers' | 'externalData' | 'typeId' | 'state'
>[]
(await this.applicationService.findAllDueToBePruned()) as PruningApplication[]

this.logger.info(`Found ${applications.length} applications to be pruned.`)

Expand All @@ -62,6 +67,13 @@ export class ApplicationLifeCycleService {
failedAttachments: {},
}
})

for (const { application } of this.processingApplications) {
const notification = await this.preparePrunedNotification(application)
if (notification) {
this.pruneNotifications.set(application.id, notification)
}
}
norda-gunni marked this conversation as resolved.
Show resolved Hide resolved
}

private async pruneAttachments() {
Expand Down Expand Up @@ -106,7 +118,7 @@ export class ApplicationLifeCycleService {
},
)

prune.application = updatedApplication
prune.application = updatedApplication as PruningApplication
} catch (error) {
prune.pruned = false
this.logger.error(
Expand All @@ -117,7 +129,7 @@ export class ApplicationLifeCycleService {
}
}

private reportResults() {
private async reportResults() {
const failed = this.processingApplications.filter(
(application) => !application.pruned,
)
Expand All @@ -126,6 +138,72 @@ export class ApplicationLifeCycleService {
(application) => application.pruned,
)

for (const { application } of success) {
const notification = this.pruneNotifications.get(application.id)
if (notification) {
await this.sendPrunedNotification(notification, application.id)
}
}

this.logger.info(`Successful: ${success.length}, Failed: ${failed.length}`)
}

private async preparePrunedNotification(
application: PruningApplication,
): Promise<CreateHnippNotificationDto | null> {
const template = await getApplicationTemplateByTypeId(application.typeId)
const stateConfig = template.stateMachineConfig.states[application.state]
const lifeCycle = stateConfig.meta?.lifecycle
if (lifeCycle && lifeCycle.shouldBePruned && lifeCycle.pruneMessage) {
try {
const pruneMessage =
typeof lifeCycle.pruneMessage === 'function'
? lifeCycle.pruneMessage(application as ApplicationWithAttachments)
: lifeCycle.pruneMessage
const notification = {
recipient: application.applicant,
templateId: pruneMessage.notificationTemplateId,
args: [
{
key: 'externalBody',
value: pruneMessage.externalBody || '',
},
{
key: 'internalBody',
value: pruneMessage.internalBody || '',
},
],
}
return notification
} catch (error) {
this.logger.error(
`Failed to prepare pruning notification for application ${application.id}`,
error,
)
return null
}
}
return null
}

private async sendPrunedNotification(
notification: CreateHnippNotificationDto,
applicationId: string,
) {
try {
const response =
await this.notificationApi.notificationsControllerCreateHnippNotification(
{
createHnippNotificationDto: notification,
},
)
this.logger.info(
`Prune notification sent with response: ${JSON.stringify(response)}`,
)
} catch (error) {
this.logger.error(
`Failed to send pruning notification with error: ${error} for application ${applicationId}`,
)
}
}
}
Loading
Loading