From 06959065974241ccc4c02305dbfe77500f0de7ef Mon Sep 17 00:00:00 2001 From: masterchief-Dave Date: Sat, 10 Aug 2024 04:03:47 +0100 Subject: [PATCH] fix: newsletter pagination --- .../NewsLetterSubscriptionController.ts | 180 +++++++++++++++++- src/controllers/OrgController.ts | 10 +- src/middleware/error.ts | 2 + src/models/newsLetterSubscription.ts | 10 +- .../newsLetterSubscription.service.ts | 44 ++++- src/utils/mail.ts | 8 +- 6 files changed, 229 insertions(+), 25 deletions(-) diff --git a/src/controllers/NewsLetterSubscriptionController.ts b/src/controllers/NewsLetterSubscriptionController.ts index 37b94fd8..69c02157 100644 --- a/src/controllers/NewsLetterSubscriptionController.ts +++ b/src/controllers/NewsLetterSubscriptionController.ts @@ -80,9 +80,9 @@ const subscribeToNewsletter = async ( throw new BadRequest("Email is missing in request body."); } const subscriber = await newsLetterSubscriptionService.subscribeUser(email); - res.status(!subscriber.isSubscribe ? 201 : 200).json({ + res.status(subscriber.isNewlySubscribe ? 201 : 200).json({ status: "success", - message: !subscriber.isSubscribe + message: subscriber.isNewlySubscribe ? "Subscriber subscription successful" : "You are already subscribed to our newsletter", }); @@ -91,6 +91,176 @@ const subscribeToNewsletter = async ( } }; +/** + * @swagger + * /newsletter/unsubscribe: + * post: + * summary: Unsubscribe from newsletter + * description: Allows a logedegin user to unsubscribe from the newsletter using their email address. + * tags: + * - Newsletter + * security: + * - bearerAuth: [] # Assumes you're using bearer token authentication + * responses: + * 200: + * description: Successfully unsubscribed from the newsletter. + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: success + * message: + * type: string + * example: Successfully unsubscribed from newsletter + * 400: + * description: Bad request, missing or invalid email. + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: unsuccessful + * status_code: + * type: number + * example: 400 + * message: + * type: string + * example: You already unsubscribed to newsletter. + * 404: + * description: User not subscribed ti newsletter. + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: unsuccessful + * status_code: + * type: number + * example: 404 + * message: + * type: string + * example: You are not subscribed to newsletter. + */ +const unSubscribeToNewsletter = async ( + req: Request, + res: Response, + next: NextFunction, +) => { + try { + const { email } = req.user; + if (!email) { + throw new BadRequest("Email is missing in request body."); + } + const subscriber = + await newsLetterSubscriptionService.unSubcribeUser(email); + if (subscriber) { + res.status(200).json({ + status: "success", + message: "Successfully unsubscribed from newsletter", + }); + } + } catch (error) { + next(error); + } +}; + +/** + * @swagger + * /api/v1/newsletters: + * get: + * summary: Get all newsletters with pagination + * tags: [Newsletters] + * parameters: + * - in: query + * name: page + * schema: + * type: integer + * default: 1 + * description: The page number for pagination + * - in: query + * name: limit + * schema: + * type: integer + * default: 10 + * description: The number of items per page + * responses: + * 200: + * description: A list of newsletters with pagination metadata + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * example: "success" + * message: + * type: string + * example: "Newsletters retrieved successfully" + * data: + * type: array + * items: + * type: object + * properties: + * id: + * type: string + * example: "newsletterId123" + * title: + * type: string + * example: "Weekly Update" + * content: + * type: string + * example: "This is the content of the newsletter." + * meta: + * type: object + * properties: + * total: + * type: integer + * example: 100 + * page: + * type: integer + * example: 1 + * limit: + * type: integer + * example: 10 + * totalPages: + * type: integer + * example: 10 + * 400: + * description: Bad request, possibly due to invalid query parameters + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: "Invalid page or limit parameter" + * status_code: + * type: integer + * example: 400 + * 500: + * description: An error occurred while fetching the newsletters + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: "Internal server error" + * status_code: + * type: integer + * example: 500 + */ + const getAllNewsletter = async ( req: Request, res: Response, @@ -105,8 +275,8 @@ const getAllNewsletter = async ( }); return res.status(200).json({ - status: "", - message: "", + status: "ok", + message: "Successful", data: data, meta, }); @@ -115,4 +285,4 @@ const getAllNewsletter = async ( } }; -export { getAllNewsletter, subscribeToNewsletter }; +export { getAllNewsletter, subscribeToNewsletter, unSubscribeToNewsletter }; diff --git a/src/controllers/OrgController.ts b/src/controllers/OrgController.ts index 32a4e322..1191c291 100644 --- a/src/controllers/OrgController.ts +++ b/src/controllers/OrgController.ts @@ -1,9 +1,13 @@ import { NextFunction, Request, Response } from "express"; -import { ResourceNotFound, ServerError, HttpError } from "../middleware"; +import { PermissionCategory } from "../enums/permission-category.enum"; +import { + HttpError, + InvalidInput, + ResourceNotFound, + ServerError, +} from "../middleware"; import { OrgService } from "../services/org.services"; import log from "../utils/logger"; -import { InvalidInput } from "../middleware"; -import { PermissionCategory } from "../enums/permission-category.enum"; export class OrgController { private orgService: OrgService; diff --git a/src/middleware/error.ts b/src/middleware/error.ts index 32ce4a4f..2547ab99 100644 --- a/src/middleware/error.ts +++ b/src/middleware/error.ts @@ -1,4 +1,5 @@ import { NextFunction, Request, Response } from "express"; +import log from "../utils/logger"; class HttpError extends Error { status_code: number; @@ -65,6 +66,7 @@ const errorHandler = ( _next: NextFunction, ) => { const { success, status_code, message } = err; + log.error({ err }); const cleanedMessage = message.replace(/"/g, ""); res.status(status_code).json({ success: success || "unsuccessful", diff --git a/src/models/newsLetterSubscription.ts b/src/models/newsLetterSubscription.ts index 8f83c605..1c0e4d30 100644 --- a/src/models/newsLetterSubscription.ts +++ b/src/models/newsLetterSubscription.ts @@ -1,9 +1,4 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, -} from "typeorm"; +import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; @Entity() export class NewsLetterSubscriber { @@ -12,4 +7,7 @@ export class NewsLetterSubscriber { @Column() email: string; + + @Column() + isSubscribe: boolean; } diff --git a/src/services/newsLetterSubscription.service.ts b/src/services/newsLetterSubscription.service.ts index 9e8e2f9a..e042b6f1 100644 --- a/src/services/newsLetterSubscription.service.ts +++ b/src/services/newsLetterSubscription.service.ts @@ -1,6 +1,6 @@ import { Repository } from "typeorm"; import AppDataSource from "../data-source"; -import { HttpError } from "../middleware"; +import { BadRequest, HttpError, ResourceNotFound } from "../middleware"; import { NewsLetterSubscriber } from "../models/newsLetterSubscription"; import { INewsLetterSubscriptionService } from "../types"; @@ -15,27 +15,55 @@ export class NewsLetterSubscriptionService } public async subscribeUser(email: string): Promise<{ - isSubscribe: boolean; + isNewlySubscribe: boolean; subscriber: NewsLetterSubscriber; }> { - let isSubscribe = false; + let isNewlySubscribe = true; + const isExistingSubscriber = await this.newsLetterSubscriber.findOne({ where: { email }, }); - if (isExistingSubscriber) { - isSubscribe = true; - return { isSubscribe, subscriber: isExistingSubscriber }; + if (isExistingSubscriber && isExistingSubscriber.isSubscribe === true) { + isNewlySubscribe = false; + return { isNewlySubscribe, subscriber: isExistingSubscriber }; + } + + if (isExistingSubscriber && isExistingSubscriber.isSubscribe === false) { + throw new BadRequest( + "You are already subscribed, please enable newsletter subscription to receive newsletter again", + ); } + const newSubscriber = new NewsLetterSubscriber(); newSubscriber.email = email; + newSubscriber.isSubscribe = true; + const subscriber = await this.newsLetterSubscriber.save(newSubscriber); + if (!subscriber) { throw new HttpError( 500, "An error occurred while processing your request", ); } - return { isSubscribe, subscriber }; + return { isNewlySubscribe, subscriber }; + } + public async unSubcribeUser(email: string): Promise { + const isExistingSubscriber = await this.newsLetterSubscriber.findOne({ + where: { email }, + }); + + if (!isExistingSubscriber) { + throw new ResourceNotFound("You are not subscribed to newsletter"); + } + + if (isExistingSubscriber && isExistingSubscriber.isSubscribe === true) { + isExistingSubscriber.isSubscribe = false; + await this.newsLetterSubscriber.save(isExistingSubscriber); + return isExistingSubscriber; + } + + throw new BadRequest("You already unsubscribed to newsletter"); } public async fetchAllNewsletter({ @@ -46,6 +74,8 @@ export class NewsLetterSubscriptionService limit?: number; }) { try { + page = page ? page : 1; + limit = limit ? limit : 10; const [newsletters, total] = await this.newsLetterSubscriber.findAndCount( { skip: (page - 1) * limit, diff --git a/src/utils/mail.ts b/src/utils/mail.ts index c67bb0f2..20a8f9fd 100644 --- a/src/utils/mail.ts +++ b/src/utils/mail.ts @@ -5,10 +5,10 @@ import log from "./logger"; const Sendmail = async (emailcontent: any) => { const transporter = nodemailer.createTransport({ - service: config.SMTP_SERVICE, - host: "smtp.gmail.com", - port: 587, - secure: false, + // service: config.SMTP_SERVICE, + host: "sandbox.smtp.mailtrap.io", + port: 2525, + // secure: false, auth: { user: config.SMTP_USER, pass: config.SMTP_PASSWORD,