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(apiv2,snu-lib): add async tasks capabilities #4543

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions apiv2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ En cours:
}
```

- [ ] Gestion des tâches asynchrones
- [x] Collection
- [x] Queue
- [x] Consumer
- [x] Producer
- [x] Module job admin dédié
- [x] Module task dédié



\


Expand Down
3 changes: 2 additions & 1 deletion apiv2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@
"^@config/(.*)$": "<rootDir>/src/config/$1",
"^@infra/(.*)$": "<rootDir>/src/infra/$1",
"^@shared/(.*)$": "<rootDir>/src/shared/$1",
"^@notification/(.*)$": "<rootDir>/src/notification/$1"
"^@notification/(.*)$": "<rootDir>/src/notification/$1",
"^@task/(.*)$": "<rootDir>/src/task/$1"
},
"modulePathIgnorePatterns": [
"<rootDir>/packages"
Expand Down
9 changes: 5 additions & 4 deletions apiv2/src/App.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Global, Logger, MiddlewareConsumer, Module } from "@nestjs/common";
import configuration from "./config/configuration";
import { Logger, MiddlewareConsumer, Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { SentryModule } from "@sentry/nestjs/setup";
import configuration from "./config/configuration";

import { QueueModule } from "@infra/Queue.module";
import { AdminModule } from "./admin/Admin.module";
import { LoggerRequestMiddleware } from "./shared/infra/LoggerRequest.middleware";
import { CorrelationIdMiddleware } from "./shared/infra/CorrelationId.middleware.js";
import { SharedModule } from "./shared/Shared.module";
import { LoggerRequestMiddleware } from "./shared/infra/LoggerRequest.middleware";

@Module({
imports: [
Expand All @@ -15,6 +15,7 @@ import { SharedModule } from "./shared/Shared.module";
}),
SentryModule.forRoot(),
AdminModule,
QueueModule,
],
controllers: [],
providers: [Logger],
Expand Down
11 changes: 11 additions & 0 deletions apiv2/src/MainJob.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from "@nestjs/common";
import { NotificationJobModule } from "@notification/NotificationJob.module";
import { AdminJobModule } from "./admin/AdminJob.module";
import configuration from "@config/configuration";
import { ConfigModule } from "@nestjs/config";
import { QueueModule } from "@infra/Queue.module";

@Module({
imports: [QueueModule, NotificationJobModule, AdminJobModule],
})
export class MainJobModule {}
27 changes: 10 additions & 17 deletions apiv2/src/admin/Admin.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { DatabaseModule } from "@infra/Database.module"; // TO REMOVE ?
import { JwtAuthModule } from "@infra/JwtAuth.module";
import { ConfigModule } from "@nestjs/config";
// import { databaseProviders } from "@infra/Database.provider"; // TO REMOVE ?
import { BullModule } from "@nestjs/bullmq";
import { QueueModule } from "@infra/Queue.module";
import { Logger, MiddlewareConsumer, Module, RequestMethod } from "@nestjs/common";
import { NotificationGateway } from "@notification/core/Notification.gateway";
import { ContactProducer } from "@notification/infra/email/Contact.producer";
import { QueueType } from "@notification/infra/Notification";
import { NotificationProducer } from "@notification/infra/Notification.producer";
import { NotificationModule } from "@notification/Notification.module";
import { TaskGateway } from "@task/core/Task.gateway";
import { taskMongoProviders } from "@task/infra/TaskMongo.provider";
import { TaskModule } from "@task/Task.module";
import { ClsMiddleware, ClsModule } from "nestjs-cls";
import { SigninReferent } from "./core/iam/useCase/SigninReferent";
import { ClasseService } from "./core/sejours/cle/classe/Classe.service";
Expand All @@ -24,33 +25,25 @@ import { etablissementMongoProviders } from "./infra/sejours/cle/etablissement/p
import { gatewayProviders } from "./infra/sejours/cle/initProvider/gateway";
import { guardProviders } from "./infra/sejours/cle/initProvider/guard";
import { useCaseProvider as useCaseProviders } from "./infra/sejours/cle/initProvider/useCase";
import { AdminTaskRepository } from "./infra/task/AdminTaskMongo.repository";
import { AdminTaskController } from "./infra/task/api/AdminTask.controller";

@Module({
imports: [
ClsModule.forRoot({}),
ConfigModule,
DatabaseModule, //TO REMOVE ?
JwtAuthModule,
NotificationModule,
BullModule.registerQueue({
name: QueueType.EMAIL,
}),
BullModule.registerQueue({
name: QueueType.CONTACT,
}),
],
controllers: [ClasseController, AuthController],
imports: [ClsModule.forRoot({}), DatabaseModule, JwtAuthModule, NotificationModule, QueueModule, TaskModule],
controllers: [ClasseController, AuthController, AdminTaskController],
providers: [
ClasseService,
{ provide: AuthProvider, useClass: JwtTokenService },
...classeMongoProviders,
...referentMongoProviders,
...etablissementMongoProviders,
...guardProviders,
...taskMongoProviders,
Logger,
SigninReferent,
{ provide: NotificationGateway, useClass: NotificationProducer },
{ provide: ContactGateway, useClass: ContactProducer },
{ provide: TaskGateway, useClass: AdminTaskRepository },
...useCaseProviders,
...gatewayProviders,
],
Expand Down
25 changes: 25 additions & 0 deletions apiv2/src/admin/AdminJob.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import configuration from "@config/configuration";
import { DatabaseModule } from "@infra/Database.module";
import { QueueModule } from "@infra/Queue.module";
import { Logger, Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { TaskModule } from "@task/Task.module";
import { taskMongoProviders } from "@task/infra/TaskMongo.provider";
import { AdminTaskConsumer } from "./infra/task/AdminTask.consumer";
import { AdminTaskRepository } from "./infra/task/AdminTaskMongo.repository";

@Module({
imports: [TaskModule, DatabaseModule],
providers: [
Logger,
AdminTaskConsumer,
AdminTaskRepository,
...taskMongoProviders,
// add use case here
],
})
export class AdminJobModule {
constructor(private logger: Logger) {
this.logger.log("AdminJobModule has started", "AdminJobModule");
}
}
17 changes: 17 additions & 0 deletions apiv2/src/admin/infra/iam/guard/SuperAdmin.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { CanActivate, ExecutionContext, Injectable, Logger } from "@nestjs/common";
import { isSuperAdmin } from "snu-lib";

@Injectable()
export class SuperAdminGuard implements CanActivate {
constructor() {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();

// TODO : request.user mapping
if (isSuperAdmin({ role: request.user.role, subRole: request.user.sousRole })) {
return true;
}

return false;
}
}
9 changes: 8 additions & 1 deletion apiv2/src/admin/infra/sejours/cle/initProvider/guard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { SuperAdminGuard } from "src/admin/infra/iam/guard/SuperAdmin.guard";
import { ClasseAdminCleGuard } from "../classe/guard/ClasseAdminCle.guard";
import { ClasseDepartementGuard } from "../classe/guard/ClasseDepartement.guard";
import { ClasseGuardService } from "../classe/guard/ClasseGuard.service";
import { ClasseRegionGuard } from "../classe/guard/ClasseRegion.guard";

export const guardProviders = [ClasseGuardService, ClasseAdminCleGuard, ClasseRegionGuard, ClasseDepartementGuard];
export const guardProviders = [
ClasseGuardService,
ClasseAdminCleGuard,
ClasseRegionGuard,
ClasseDepartementGuard,
SuperAdminGuard,
];
39 changes: 39 additions & 0 deletions apiv2/src/admin/infra/task/AdminTask.consumer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Processor, WorkerHost } from "@nestjs/bullmq";
import { Logger } from "@nestjs/common";
import { ConsumerResponse } from "@shared/infra/ConsumerResponse";
import { QueueName, TaskQueue } from "@shared/infra/Queue";
import { Job } from "bullmq";
import { TaskName } from "snu-lib";
import { AdminTaskRepository } from "./AdminTaskMongo.repository";

@Processor(QueueName.ADMIN_TASK)
export class AdminTaskConsumer extends WorkerHost {
constructor(
private logger: Logger,
private adminTaskRepository: AdminTaskRepository,
) {
super();
}
// TODO call async usecase task
async process(job: Job<TaskQueue, any, TaskName>): Promise<ConsumerResponse> {
this.logger.log(`Processing task "${job.name}" with data ${JSON.stringify(job.data)}`, AdminTaskConsumer.name);
await this.adminTaskRepository.toSuccess(job.data.id);
return ConsumerResponse.SUCCESS;
// return this.taskProvider
// .execute(job.name, job.data)
// .then(() => {
// this.logger.log(
// `Task "${job.name}" processed successfully with data ${JSON.stringify(job.data)}`,
// AdminTaskConsumer.name,
// );
// return ConsumerResponse.SUCCESS;
// })
// .catch((error) => {
// this.logger.error(
// `Error processing task "${job.name}" - ${error.message} - ${error.stack}`,
// AdminTaskConsumer.name,
// );
// throw error;
// });
}
}
26 changes: 26 additions & 0 deletions apiv2/src/admin/infra/task/AdminTaskMongo.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { InjectQueue } from "@nestjs/bullmq";
import { Inject, Injectable } from "@nestjs/common";
import { QueueName } from "@shared/infra/Queue";
import { TaskGateway } from "@task/core/Task.gateway";
import { CreateTaskModel, TaskModel } from "@task/core/Task.model";
import { TaskMapper } from "@task/infra/Task.mapper";
import { Queue } from "bullmq";
import { Model } from "mongoose";
import { TASK_MONGOOSE_ENTITY, TaskDocument } from "src/task/infra/TaskMongo.provider";
import { TaskRepository } from "src/task/infra/TaskMongo.repository";

@Injectable()
export class AdminTaskRepository extends TaskRepository implements TaskGateway {
constructor(
@Inject(TASK_MONGOOSE_ENTITY) protected taskMongooseEntity: Model<TaskDocument>,
@InjectQueue(QueueName.ADMIN_TASK) private adminTaskQueue: Queue,
) {
super(taskMongooseEntity);
}

async create(task: CreateTaskModel): Promise<TaskModel> {
const createdTask = await super.create(task);
await this.adminTaskQueue.add(task.name, { ...TaskMapper.toQueue(createdTask) });
return createdTask;
}
}
28 changes: 28 additions & 0 deletions apiv2/src/admin/infra/task/api/AdminTask.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Body, Controller, Get, Inject, Param, Post, UseGuards } from "@nestjs/common";
import { TaskGateway } from "@task/core/Task.gateway";
import { CreateTaskModel, TaskModel } from "@task/core/Task.model";
import { SuperAdminGuard } from "../../iam/guard/SuperAdmin.guard";

@Controller("task")
export class AdminTaskController {
constructor(@Inject(TaskGateway) private taskGateway: TaskGateway) {}

@Get("/")
@UseGuards(SuperAdminGuard)
findAll(): Promise<TaskModel[]> {
return this.taskGateway.findAll();
}

@Get(":id")
@UseGuards(SuperAdminGuard)
verify(@Param("id") id: string): Promise<TaskModel> {
return this.taskGateway.findById(id);
}

// TODO for testing purposes only => REMOVE
@Post("/")
@UseGuards(SuperAdminGuard)
create(@Body() task: CreateTaskModel): Promise<TaskModel> {
return this.taskGateway.create(task);
}
}
12 changes: 8 additions & 4 deletions apiv2/src/infra/Queue.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ConfigModule, ConfigService } from "@nestjs/config";

import { BullModule } from "@nestjs/bullmq";
import { Module } from "@nestjs/common";
import { QueueType } from "@notification/infra/Notification";
import { Global, Module } from "@nestjs/common";
import { QueueName } from "@shared/infra/Queue";

@Global()
@Module({
imports: [
BullModule.forRootAsync({
Expand All @@ -16,10 +17,13 @@ import { QueueType } from "@notification/infra/Notification";
}),
}),
BullModule.registerQueue({
name: QueueType.EMAIL,
name: QueueName.EMAIL,
}),
BullModule.registerQueue({
name: QueueType.CONTACT,
name: QueueName.CONTACT,
}),
BullModule.registerQueue({
name: QueueName.ADMIN_TASK,
}),
],
exports: [BullModule],
Expand Down
1 change: 0 additions & 1 deletion apiv2/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ConfigService } from "@nestjs/config";
import { ValidationPipe } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./App.module";
import { NotificationJobModule } from "./notification/NotificationJob.module";

async function bootstrap() {
const app = await NestFactory.create(AppModule);
Expand Down
6 changes: 3 additions & 3 deletions apiv2/src/mainJob.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import "./instrument"; // first

import { NestFactory } from "@nestjs/core";
import { NotificationJobModule } from "./notification/NotificationJob.module";
import { Logger } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { MainJobModule } from "./MainJob.module";

async function bootstrap() {
await NestFactory.createApplicationContext(NotificationJobModule);
await NestFactory.createApplicationContext(MainJobModule);
Logger.log(`Job started`, "bootstrap mainJob");
// TODO: handle logs error here ?
// process.on("uncaughtException", (error) => {
Expand Down
2 changes: 1 addition & 1 deletion apiv2/src/notification/Notification.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ContactProducer } from "./infra/email/Contact.producer";
import { NotificationProducer } from "./infra/Notification.producer";

@Module({
imports: [QueueModule, SharedModule],
imports: [SharedModule],
providers: [
{
provide: NotificationGateway,
Expand Down
1 change: 0 additions & 1 deletion apiv2/src/notification/NotificationJob.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { contactFactory, emailFactory } from "./infra/email/brevo/EmailContact.f
ConfigModule.forRoot({
load: [configuration],
}),
QueueModule,
],
providers: [Logger, EmailConsumer, ContactConsumer, emailFactory, contactFactory],
})
Expand Down
4 changes: 2 additions & 2 deletions apiv2/src/notification/infra/Notification.producer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { Injectable } from "@nestjs/common";
import { NotificationGateway } from "../core/Notification.gateway";
import { EmailTemplate } from "../core/Notification";
import { InjectQueue } from "@nestjs/bullmq";
import { QueueType } from "./Notification";
import { QueueName } from "@shared/infra/Queue";
import { Queue } from "bullmq";

@Injectable()
export class NotificationProducer implements NotificationGateway {
constructor(@InjectQueue(QueueType.EMAIL) private emailQueue: Queue) {}
constructor(@InjectQueue(QueueName.EMAIL) private emailQueue: Queue) {}
async sendEmail<T>(params: T, template: EmailTemplate): Promise<void> {
await this.emailQueue.add(template, params);
}
Expand Down
5 changes: 0 additions & 5 deletions apiv2/src/notification/infra/Notification.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
export enum QueueType {
EMAIL = "email",
CONTACT = "contact",
}

export enum ContactType {
REFERENT = "referent",
JEUNE = "jeune",
Expand Down
5 changes: 3 additions & 2 deletions apiv2/src/notification/infra/email/Contact.consumer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { Processor, WorkerHost } from "@nestjs/bullmq";
import { Inject, Logger } from "@nestjs/common";
import { Job } from "bullmq";
import { ReferentModel } from "src/admin/core/iam/Referent.model";
import { ContactType, QueueType } from "../Notification";
import { ContactType } from "../Notification";
import { QueueName } from "@shared/infra/Queue";
import { ContactProvider } from "./Contact.provider";
import { ConsumerResponse } from "@shared/infra/ConsumerResponse";

@Processor(QueueType.CONTACT)
@Processor(QueueName.CONTACT)
export class ContactConsumer extends WorkerHost {
constructor(
private logger: Logger,
Expand Down
5 changes: 3 additions & 2 deletions apiv2/src/notification/infra/email/Contact.producer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { Inject } from "@nestjs/common";
import { Queue } from "bullmq";
import { ReferentModel } from "src/admin/core/iam/Referent.model";
import { ContactGateway } from "src/admin/infra/iam/Contact.gateway";
import { ContactType, QueueType } from "../Notification";
import { ContactType } from "../Notification";
import { QueueName } from "@shared/infra/Queue";

export class ContactProducer implements ContactGateway {
constructor(@InjectQueue(QueueType.CONTACT) private contactQueue: Queue) {}
constructor(@InjectQueue(QueueName.CONTACT) private contactQueue: Queue) {}
async syncReferent(referent: ReferentModel): Promise<void> {
await this.contactQueue.add(ContactType.REFERENT, [referent]);
}
Expand Down
Loading
Loading