diff --git a/src/admin/admin.controller.spec.ts b/src/admin/admin.controller.spec.ts index a93e257c82092e840f22326d70dbb637a97470a6..84dbf424a36ea0a54a4e59cf28ff657f8d546082 100644 --- a/src/admin/admin.controller.spec.ts +++ b/src/admin/admin.controller.spec.ts @@ -3,8 +3,8 @@ import { getModelToken } from '@nestjs/mongoose'; import { Test, TestingModule } from '@nestjs/testing'; import { ConfigurationModule } from '../configuration/configuration.module'; import { MailerService } from '../mailer/mailer.service'; -import { CreateStructureDto } from '../structures/dto/create-structure.dto'; -import { structureDto } from '../structures/dto/structure.dto'; +import { NewsletterSubscription } from '../newsletter/newsletter-subscription.schema'; +import { NewsletterService } from '../newsletter/newsletter.service'; import { Structure } from '../structures/schemas/structure.schema'; import { StructuresService } from '../structures/services/structures.service'; import { User } from '../users/schemas/user.schema'; @@ -21,11 +21,16 @@ describe('AdminController', () => { providers: [ UsersService, StructuresService, + NewsletterService, MailerService, { provide: getModelToken('User'), useValue: User, }, + { + provide: getModelToken('NewsletterSubscription'), + useValue: NewsletterSubscription, + }, { provide: getModelToken('Structure'), useValue: Structure, diff --git a/src/admin/admin.controller.ts b/src/admin/admin.controller.ts index 3947dd029646e816d889b6445a0e142aa4b17619..03442f8e55e2210e3bf1fc52f96e0c4617ff3add 100644 --- a/src/admin/admin.controller.ts +++ b/src/admin/admin.controller.ts @@ -2,6 +2,7 @@ import { Body, Delete, Param } from '@nestjs/common'; import { Controller, Get, Post, UseGuards } from '@nestjs/common'; import { ApiOperation, ApiParam } from '@nestjs/swagger'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; +import { NewsletterService } from '../newsletter/newsletter.service'; import { StructuresService } from '../structures/services/structures.service'; import { Roles } from '../users/decorators/roles.decorator'; import { RolesGuard } from '../users/guards/roles.guard'; @@ -10,7 +11,11 @@ import { PendingStructureDto } from './dto/pending-structure.dto'; @Controller('admin') export class AdminController { - constructor(private usersService: UsersService, private structuresService: StructuresService) {} + constructor( + private usersService: UsersService, + private structuresService: StructuresService, + private newsletterService: NewsletterService + ) {} @UseGuards(JwtAuthGuard, RolesGuard) @Roles('admin') @@ -96,4 +101,21 @@ export class AdminController { return this.usersService.searchUsers(searchString.searchString); else return this.usersService.findAll(); } + + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles('admin') + @Post('searchNewsletterSubscriptions') + public async getNewsletterSubscriptions(@Body() searchString: { searchString: string }) { + if (searchString && searchString.searchString.length > 0) + return this.newsletterService.searchNewsletterSubscription(searchString.searchString); + else return this.newsletterService.findAll(); + } + + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles('admin') + @Delete('newsletterSubscription/:email') + @ApiParam({ name: 'email', type: String, required: true }) + public async unsubscribeUserFromNewsletter(@Param() params) { + return await this.newsletterService.deleteOneEmail(params.email); + } } diff --git a/src/admin/admin.module.ts b/src/admin/admin.module.ts index a13616a1802d24dc4f58d513714a9647193c591b..7854c19b255df99cc35837f56d9f9dfd637d7bf5 100644 --- a/src/admin/admin.module.ts +++ b/src/admin/admin.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; +import { NewsletterModule } from '../newsletter/newsletter.module'; import { StructuresModule } from '../structures/structures.module'; import { UsersModule } from '../users/users.module'; import { AdminController } from './admin.controller'; import { AdminService } from './admin.service'; @Module({ - imports: [UsersModule, StructuresModule], + imports: [UsersModule, StructuresModule, NewsletterModule], controllers: [AdminController], providers: [AdminService], }) diff --git a/src/app.module.ts b/src/app.module.ts index 467fe404c31f6fdeadc948aec96bdf136aa676f3..ed6a03004e4e90edc2decf97c41b7f3478ca4f69 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,6 +12,7 @@ import { TclModule } from './tcl/tcl.module'; import { AdminModule } from './admin/admin.module'; import { PostsModule } from './posts/posts.module'; import { TempUserModule } from './temp-user/temp-user.module'; +import { NewsletterModule } from './newsletter/newsletter.module'; @Module({ imports: [ ConfigurationModule, @@ -28,6 +29,7 @@ import { TempUserModule } from './temp-user/temp-user.module'; AdminModule, PostsModule, TempUserModule, + NewsletterModule ], controllers: [AppController], }) diff --git a/src/newsletter/interface/newsletter-subscription.interface.ts b/src/newsletter/interface/newsletter-subscription.interface.ts new file mode 100644 index 0000000000000000000000000000000000000000..382e0911c13768f56e091db0c420ba4df013b3fd --- /dev/null +++ b/src/newsletter/interface/newsletter-subscription.interface.ts @@ -0,0 +1,5 @@ +import { Document } from 'mongoose'; + +export interface INewsletterSubscription extends Document { + email: string; +} diff --git a/src/newsletter/newsletter-subscription.schema.ts b/src/newsletter/newsletter-subscription.schema.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ff18ab015a2e00b018ac1929bed0fb0a7a7ac48 --- /dev/null +++ b/src/newsletter/newsletter-subscription.schema.ts @@ -0,0 +1,12 @@ +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { Document } from 'mongoose'; + +export type NewsletterSubscriptionDocument = NewsletterSubscription & Document; + +@Schema({ timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } }) +export class NewsletterSubscription { + @Prop({ required: true }) + email: string; +} + +export const NewsletterSubscriptionSchema = SchemaFactory.createForClass(NewsletterSubscription); diff --git a/src/newsletter/newsletter.controller.spec.ts b/src/newsletter/newsletter.controller.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..2e5adbb61affbb5606e32e2768ee9c60e09f1173 --- /dev/null +++ b/src/newsletter/newsletter.controller.spec.ts @@ -0,0 +1,44 @@ +import { HttpModule } from '@nestjs/common'; +import { getModelToken } from '@nestjs/mongoose'; +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigurationModule } from '../configuration/configuration.module'; +import { NewsletterSubscription } from './newsletter-subscription.schema'; +import { NewsletterController } from './newsletter.controller'; +import { NewsletterService } from './newsletter.service'; +describe('NewsletterController', () => { + let controller: NewsletterController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ConfigurationModule, HttpModule], + providers: [ + NewsletterService, + { + provide: getModelToken('NewsletterSubscription'), + useValue: NewsletterSubscription, + }, + ], + controllers: [NewsletterController], + }).compile(); + + controller = module.get<NewsletterController>(NewsletterController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + it('should subscribe user', async () => { + const result = { email: 'email@test.com' }; + jest.spyOn(controller, 'newsletterSubscribe').mockImplementation(async (): Promise<{ email }> => result); + const email = { email: 'email@test.com' }; + expect(await controller.newsletterSubscribe(email)).toBe(result); + }); + + it('should unsubscribe user', async () => { + const result = { email: 'email@test.com' }; + jest.spyOn(controller, 'newsletterUnsubscribe').mockImplementation(async (): Promise<{ email }> => result); + const email = { email: 'email@test.com' }; + expect(await controller.newsletterUnsubscribe(email)).toBe(result); + }); +}); diff --git a/src/newsletter/newsletter.controller.ts b/src/newsletter/newsletter.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a7c77d35689d37df3e66c9c42d9405cc35c6655 --- /dev/null +++ b/src/newsletter/newsletter.controller.ts @@ -0,0 +1,17 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { NewsletterService } from './newsletter.service'; + +@Controller('newsletter') +export class NewsletterController { + constructor(private newsletterService: NewsletterService) {} + + @Post('subscribe') + public async newsletterSubscribe(@Body() email: { email: string }) { + return this.newsletterService.newsletterSubscribe(email.email); + } + + @Post('unsubscribe') + public async newsletterUnsubscribe(@Body() email: { email: string }) { + return this.newsletterService.newsletterUnsubscribe(email.email); + } +} diff --git a/src/newsletter/newsletter.module.ts b/src/newsletter/newsletter.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f949c385cffe4e07c54b645c30d0221f41b31f8 --- /dev/null +++ b/src/newsletter/newsletter.module.ts @@ -0,0 +1,15 @@ +import { HttpModule, Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { NewsletterService } from './newsletter.service'; +import { NewsletterController } from './newsletter.controller'; +import { NewsletterSubscription, NewsletterSubscriptionSchema } from './newsletter-subscription.schema'; +@Module({ + imports: [ + MongooseModule.forFeature([{ name: NewsletterSubscription.name, schema: NewsletterSubscriptionSchema }]), + HttpModule, + ], + providers: [NewsletterService], + exports: [NewsletterService], + controllers: [NewsletterController], +}) +export class NewsletterModule {} diff --git a/src/newsletter/newsletter.service.ts b/src/newsletter/newsletter.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..c1ca5efc0e07368586b7a3ac99c7248c43d8a85f --- /dev/null +++ b/src/newsletter/newsletter.service.ts @@ -0,0 +1,50 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model, Types } from 'mongoose'; +import { INewsletterSubscription } from './interface/newsletter-subscription.interface'; +import { NewsletterSubscription } from './newsletter-subscription.schema'; + +@Injectable() +export class NewsletterService { + constructor( + @InjectModel(NewsletterSubscription.name) private newsletterSubscriptionModel: Model<INewsletterSubscription> + ) {} + + public async newsletterSubscribe(email: string): Promise<NewsletterSubscription> { + const existingEmail = await this.findOne(email); + if (existingEmail) { + throw new HttpException('Email already exists', HttpStatus.BAD_REQUEST); + } + const createSubscription = new this.newsletterSubscriptionModel({ email: email }); + createSubscription.save(); + return await this.findOne(email); + } + + public async newsletterUnsubscribe(email: string): Promise<NewsletterSubscription> { + const subscription = await this.newsletterSubscriptionModel.findOne({ email: email }).exec(); + if (!subscription) { + throw new HttpException('Invalid email', HttpStatus.BAD_REQUEST); + } + return subscription.deleteOne(); + } + + public async findOne(mail: string): Promise<NewsletterSubscription | undefined> { + return this.newsletterSubscriptionModel.findOne({ email: mail }).exec(); + } + + public async searchNewsletterSubscription(searchString: string) { + return this.newsletterSubscriptionModel.find({ email: new RegExp(searchString, 'i') }).exec(); + } + + public async deleteOneEmail(mail: string): Promise<NewsletterSubscription | undefined> { + const subscription = await this.newsletterSubscriptionModel.findOne({ email: mail }).exec(); + if (!subscription) { + throw new HttpException('Invalid email', HttpStatus.BAD_REQUEST); + } + return subscription.deleteOne(); + } + + public async findAll(): Promise<NewsletterSubscription[]> { + return await this.newsletterSubscriptionModel.find().exec(); + } +}