diff --git a/create table dataservice microservice.sql b/create table dataservice microservice.sql index a66c666..e8d9018 100644 --- a/create table dataservice microservice.sql +++ b/create table dataservice microservice.sql @@ -183,7 +183,7 @@ CREATE TABLE "theatre_additional_details" ( CREATE TABLE "events" ( "id" BIGSERIAL PRIMARY KEY, - "org_email" TEXT NOT NULL, + "organizer_id" TEXT NOT NULL, "event_name" TEXT, "start_date" DATE, "end_date" DATE, @@ -280,7 +280,7 @@ CREATE TABLE "tickets" ( "price" NUMERIC, "seat_number" TEXT, "qr_code" TEXT, - "buyer_email" TEXT NOT NULL, + "buyer_id" NUMERIC NOT NULL, "booking_date" DATE, "payment_status" TEXT, "rescheduled_at" DATE, @@ -299,7 +299,7 @@ CREATE TABLE "tickets" ( CREATE TABLE "reviews" ( "id" BIGSERIAL PRIMARY KEY, "event_id" NUMERIC NOT NULL, - "buyer_email" TEXT NOT NULL, + "buyer_id" TEXT NOT NULL, "rating" NUMERIC, "comment" TEXT, "status" TEXT, @@ -332,11 +332,11 @@ CREATE TABLE "seats" ( CREATE TABLE "payouts" ( "id" BIGSERIAL PRIMARY KEY, - "payee_email" TEXT NOT NULL, + "payee_id" TEXT NOT NULL, "amount" NUMERIC, "payment_method" TEXT, "transaction_id" TEXT, - "paid_to_email" TEXT, + "paid_to_id" TEXT, "payout_date" DATE, "status" TEXT, "validFrom" DATE, @@ -369,6 +369,7 @@ CREATE TABLE "refunds" ( CREATE TABLE "push_notification" ( "id" BIGSERIAL PRIMARY KEY, + "user_id" NUMERIC NOT NULL, "endpoint" TEXT NOT NULL, "p256dh" TEXT, "auth" TEXT, @@ -382,3 +383,96 @@ CREATE TABLE "push_notification" ( "deletedAt" DATE, "version" NUMERIC ); + + +CREATE TABLE "promotions" ( + "id" BIGSERIAL PRIMARY KEY, + "event_id" NUMERIC NOT NULL, + "start_date" DATE, + "end_date" DATE, + "location" TEXT, + "type" TEXT, + "description" TEXT, + "status" TEXT, + "validFrom" DATE, + "validTill" DATE, + "createdAt" DATE, + "updatedAt" DATE, + "createdBy" TEXT, + "modifiedBy" TEXT, + "deletedAt" DATE, + "version" NUMERIC +); + +CREATE TABLE "event_episodes" ( + "id" BIGSERIAL PRIMARY KEY, + "event_id" NUMERIC NOT NULL, + "episode_name" TEXT, + "episode_description" TEXT, + "episode_start_date" DATE, + "episode_end_date" DATE, + "episode_location" TEXT, + "episode_type" TEXT, + "images" JSON, + "status" TEXT, + "validFrom" DATE, + "validTill" DATE, + "createdAt" DATE, + "updatedAt" DATE, + "createdBy" TEXT, + "modifiedBy" TEXT, + "deletedAt" DATE, + "version" NUMERIC +); + +CREATE TABLE "cast_members" ( + "id" BIGSERIAL PRIMARY KEY, + "event_id" NUMERIC NOT NULL, + "cast_name" TEXT, + "role" TEXT, + "images" JSON, + "status" TEXT, + "validFrom" DATE, + "validTill" DATE, + "createdAt" DATE, + "updatedAt" DATE, + "createdBy" TEXT, + "modifiedBy" TEXT, + "deletedAt" DATE, + "version" NUMERIC +); + +CREATE TABLE "user_watchlist" ( + "id" BIGSERIAL PRIMARY KEY, + "user_id" NUMERIC NOT NULL, + "event_id" NUMERIC NOT NULL, + "status" TEXT, + "validFrom" DATE, + "validTill" DATE, + "createdAt" DATE, + "updatedAt" DATE, + "createdBy" TEXT, + "modifiedBy" TEXT, + "deletedAt" DATE, + "version" NUMERIC +); + +CREATE TABLE "event_analytics" ( + "id" BIGSERIAL PRIMARY KEY, + "event_id" NUMERIC NOT NULL, + "views" NUMERIC, + "ticket_sales" NUMERIC, + "engagement_rate" NUMERIC, + "promotion_views" NUMERIC, + "promotion_sales" NUMERIC, + "status" TEXT, + "validFrom" DATE, + "validTill" DATE, + "createdAt" DATE, + "updatedAt" DATE, + "createdBy" TEXT, + "modifiedBy" TEXT, + "deletedAt" DATE, + "version" NUMERIC +); + diff --git a/src/cast/cast.controller.ts b/src/cast/cast.controller.ts new file mode 100644 index 0000000..2032ef8 --- /dev/null +++ b/src/cast/cast.controller.ts @@ -0,0 +1,310 @@ +import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common'; +import { CastService } from './cast.service'; +import { Response } from 'express'; +import { GenericResponse } from '../common/GenericResponse.model'; +import Cast from './cast.entity'; +import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger'; + +@ApiTags('cast') +@Controller('cast') +export class CastController { + constructor(private castService: CastService) {} + + @Get("/all") + @ApiOperation({ summary: 'Get all casts' }) + @ApiResponse({ + status: 200, + description: 'Successfully retrieved all casts', + }) + @ApiResponse({ + status: 404, + description: 'No casts found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "No casts found" + }, + "data": null + } + }) + async getAllCasts(@Res() res: Response) { + const response = await this.castService.findAll() || []; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No casts found' + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.send(httpResponse); + } + + @Get(':id') + @ApiOperation({ summary: 'Get cast by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'Cast ID' }) + @ApiResponse({ + status: 400, + description: 'ID is required', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NO_ID_REQ", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'Cast not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "Cast not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully retrieved cast by ID', + }) + async findById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.castService.findByPk(id) || {}; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Cast with ID ${id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Post('/filter') + @ApiOperation({ summary: 'Filter casts based on criteria' }) + @ApiBody({ type: Cast, description: 'Filter criteria for casts' }) + @ApiResponse({ + status: 400, + description: 'Invalid filter criteria', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_CRITERIA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'No casts found based on filter criteria', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "No casts found based on the filter criteria" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully filtered casts', + }) + async filter(@Body() cast: Cast, @Res() res: Response) { + if (!cast) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_CRITERIA', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.castService.filter(cast) || []; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No casts found based on the filter criteria' + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Post() + @ApiOperation({ summary: 'Insert a new cast' }) + @ApiBody({ type: Cast, description: 'Cast data to insert' }) + @ApiResponse({ + status: 400, + description: 'Invalid cast data', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_DATA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 201, + description: 'Successfully created a cast', + }) + async insert(@Body() cast: Cast, @Res() res: Response) { + if (!cast) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_DATA', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + delete cast.id; // Ensure no ID is passed in the creation + const response = await this.castService.upsert(cast, true); + const httpResponse = new GenericResponse(null, response); + res.status(201).send(httpResponse); + } + + @Put() + @ApiOperation({ summary: 'Update an existing cast' }) + @ApiBody({ type: Cast, description: 'Cast data to update' }) + @ApiResponse({ + status: 400, + description: 'Invalid cast data or ID missing', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_DATA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'Cast not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "Cast not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully updated cast', + }) + async update(@Body() cast: Cast, @Res() res: Response) { + if (!cast || !cast.id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.castService.upsert(cast, false); + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Cast with ID ${cast.id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Delete(':id') + @ApiOperation({ summary: 'Delete a cast by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'Cast ID to delete' }) + @ApiResponse({ + status: 400, + description: 'ID parameter is required', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NO_ID_REQ", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'Cast not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "Cast not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully deleted cast', + }) + async deleteById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.castService.remove(id) || {}; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Cast with ID ${id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } +} diff --git a/src/cast/cast.dto.ts b/src/cast/cast.dto.ts new file mode 100644 index 0000000..b5fe881 --- /dev/null +++ b/src/cast/cast.dto.ts @@ -0,0 +1,71 @@ +import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class CastMemberDTO { + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + eventId: number; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + castName: string; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + role: string; + + @ApiProperty({ type: Object }) + @IsOptional() + images: object; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + status: string; + + @ApiProperty({ type: Date }) + @IsDate() + @IsOptional() + @Transform(({ value }) => new Date(value)) + validFrom: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @IsOptional() + @Transform(({ value }) => new Date(value)) + validTill: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + createdAt: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + updatedAt: Date; + + @ApiProperty({ type: String }) + @IsString() + createdBy: string; + + @ApiProperty({ type: String }) + @IsString() + modifiedBy: string; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => value ? new Date(value) : null) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + version: number; +} diff --git a/src/cast/cast.entity.ts b/src/cast/cast.entity.ts new file mode 100644 index 0000000..5dd8954 --- /dev/null +++ b/src/cast/cast.entity.ts @@ -0,0 +1,58 @@ +import { Table, Column, Model, DataType } from 'sequelize-typescript'; +import { ApiProperty } from '@nestjs/swagger'; + +@Table({ tableName: 'cast', paranoid: true }) +export default class Cast extends Model { + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, allowNull: false, field: 'event_id' }) + eventId: number; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'cast_name' }) + castName: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'role' }) + role: string; + + @ApiProperty({ type: Object }) + @Column({ type: DataType.JSON, field: 'images' }) + images: object; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'status' }) + status: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validFrom' }) + validFrom: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validTill' }) + validTill: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'createdAt' }) + createdAt: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'updatedAt' }) + updatedAt: Date; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'createdBy' }) + createdBy: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'modifiedBy' }) + modifiedBy: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'deletedAt' }) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'version' }) + version: number; +} diff --git a/src/cast/cast.module.ts b/src/cast/cast.module.ts new file mode 100644 index 0000000..c7b716d --- /dev/null +++ b/src/cast/cast.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { CastController } from './cast.controller'; +import { CastService } from './cast.service'; +import { HttpModule } from '@nestjs/axios'; + +@Module({ + imports: [HttpModule], + providers: [CastService], + controllers: [CastController], +}) +export class CastModule { } diff --git a/src/cast/cast.service.ts b/src/cast/cast.service.ts new file mode 100644 index 0000000..8430bed --- /dev/null +++ b/src/cast/cast.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import Cast from './cast.entity'; +import { HttpService } from '@nestjs/axios'; +import { Utility } from 'src/common/Utility'; + +@Injectable() +export class CastService { + constructor(private readonly httpService: HttpService) { } + + async findAll(): Promise<{ rows: Cast[], count: number }> { + return Cast.findAndCountAll(); + } + + async findByPk(id: number): Promise { + return Cast.findByPk(id); + } + + findOne(cast: Cast): Promise { + return Cast.findOne({ where: cast as any }); + } + + filter(cast: Cast): Promise { + return Cast.findAll({ where: cast as any }); + } + + async remove(id: number): Promise { + return Cast.destroy({ where: { id: id } }); + } + + async upsert(cast: Cast, insertIfNotFound: boolean): Promise { + if (cast.id) { + const existingCast = await this.findByPk(cast.id); + if (existingCast) { + return Cast.update(cast, { where: { id: cast.id } }); + } + } + if (insertIfNotFound) { + return Cast.create(cast as any); + } + } +} diff --git a/src/event-analytics/event-analytics.controller.ts b/src/event-analytics/event-analytics.controller.ts new file mode 100644 index 0000000..a0e531d --- /dev/null +++ b/src/event-analytics/event-analytics.controller.ts @@ -0,0 +1,286 @@ +import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common'; +import { EventAnalyticsService } from './event-analytics.service'; +import { Response } from 'express'; +import { GenericResponse } from '../common/GenericResponse.model'; +import EventAnalytics from './event-analytics.entity'; +import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger'; + +@ApiTags('event-analytics') +@Controller('event-analytics') +export class EventAnalyticsController { + constructor(private eventAnalyticsService: EventAnalyticsService) {} + + @Get('/all') + @ApiOperation({ summary: 'Get all event analytics' }) + @ApiResponse({ status: 200, description: 'Successfully retrieved all event analytics' }) + @ApiResponse({ + status: 404, + description: 'No event analytics found', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event analytics found' + }, + data: null + } + }) + async getAll(@Res() res: Response) { + const response = await this.eventAnalyticsService.findAll(); + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event analytics found' + }, null); + return res.status(404).send(errorResponse); + } + res.send(new GenericResponse(null, response)); + } + + @Get(':id') + @ApiOperation({ summary: 'Get event analytics by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'Event analytics ID' }) + @ApiResponse({ + status: 400, + description: 'ID is required', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, + data: null + } + }) + @ApiResponse({ + status: 404, + description: 'Event analytics not found', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'Event analytics not found' + }, + data: null + } + }) + @ApiResponse({ status: 200, description: 'Successfully retrieved event analytics by ID' }) + async findById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null)); + } + + const response = await this.eventAnalyticsService.findByPk(id); + if (!response) { + return res.status(404).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Event analytics with ID ${id} not found` + }, null)); + } + + res.status(200).send(new GenericResponse(null, response)); + } + + @Post('/filter') + @ApiOperation({ summary: 'Filter event analytics based on criteria' }) + @ApiBody({ type: EventAnalytics, description: 'Filter criteria for event analytics' }) + @ApiResponse({ + status: 400, + description: 'Invalid filter criteria', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_CRITERIA', + stackTrace: 'Request' + }, + data: null + } + }) + @ApiResponse({ + status: 404, + description: 'No event analytics found based on filter criteria', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event analytics found based on the filter criteria' + }, + data: null + } + }) + @ApiResponse({ status: 200, description: 'Successfully filtered event analytics' }) + async filter(@Body() analytics: EventAnalytics, @Res() res: Response) { + if (!analytics) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_CRITERIA', + stackTrace: 'Request' + }, null)); + } + + const response = await this.eventAnalyticsService.filter(analytics) || []; + if (!response.length) { + return res.status(404).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event analytics found based on the filter criteria' + }, null)); + } + + res.status(200).send(new GenericResponse(null, response)); + } + + @Post() + @ApiOperation({ summary: 'Insert a new event analytics record' }) + @ApiBody({ type: EventAnalytics, description: 'Event analytics data to insert' }) + @ApiResponse({ + status: 400, + description: 'Invalid event analytics data', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_DATA', + stackTrace: 'Request' + }, + data: null + } + }) + @ApiResponse({ status: 201, description: 'Successfully created event analytics record' }) + async insert(@Body() analytics: EventAnalytics, @Res() res: Response) { + if (!analytics) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_DATA', + stackTrace: 'Request' + }, null)); + } + + delete analytics.id; + const response = await this.eventAnalyticsService.upsert(analytics, true); + res.status(201).send(new GenericResponse(null, response)); + } + + @Put() + @ApiOperation({ summary: 'Update an existing event analytics record' }) + @ApiBody({ type: EventAnalytics, description: 'Event analytics data to update' }) + @ApiResponse({ + status: 400, + description: 'Invalid event analytics data or ID missing', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, + data: null + } + }) + @ApiResponse({ + status: 404, + description: 'Event analytics record not found', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'Event analytics not found' + }, + data: null + } + }) + @ApiResponse({ status: 200, description: 'Successfully updated event analytics record' }) + async update(@Body() analytics: EventAnalytics, @Res() res: Response) { + if (!analytics || !analytics.id) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null)); + } + + const response = await this.eventAnalyticsService.upsert(analytics, false); + if (!response) { + return res.status(404).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Event analytics with ID ${analytics.id} not found` + }, null)); + } + + res.status(200).send(new GenericResponse(null, response)); + } + + @Delete(':id') + @ApiOperation({ summary: 'Delete event analytics by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'Event analytics ID to delete' }) + @ApiResponse({ + status: 400, + description: 'ID parameter is required', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, + data: null + } + }) + @ApiResponse({ + status: 404, + description: 'Event analytics record not found', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'Event analytics not found' + }, + data: null + } + }) + @ApiResponse({ status: 200, description: 'Successfully deleted event analytics record' }) + async deleteById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null)); + } + + const response = await this.eventAnalyticsService.remove(id); + if (!response) { + return res.status(404).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Event analytics with ID ${id} not found` + }, null)); + } + + res.status(200).send(new GenericResponse(null, response)); + } +} diff --git a/src/event-analytics/event-analytics.dto.ts b/src/event-analytics/event-analytics.dto.ts new file mode 100644 index 0000000..1b265d8 --- /dev/null +++ b/src/event-analytics/event-analytics.dto.ts @@ -0,0 +1,92 @@ +import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class EventAnalyticsDTO { + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + eventId: number; + + @ApiProperty({ type: Number }) + @IsOptional() + @IsNumber() + views: number; + + @ApiProperty({ type: Number }) + @IsOptional() + @IsNumber() + ticketSales: number; + + @ApiProperty({ type: Number }) + @IsOptional() + @IsNumber() + engagementRate: number; + + @ApiProperty({ type: Number }) + @IsOptional() + @IsNumber() + promotionViews: number; + + @ApiProperty({ type: Number }) + @IsOptional() + @IsNumber() + promotionSales: number; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + status: string; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => new Date(value)) + validFrom: Date; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => new Date(value)) + validTill: Date; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => new Date(value)) + createdAt: Date; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => new Date(value)) + updatedAt: Date; + + @ApiProperty({ type: String }) + @IsOptional() + @IsString() + createdBy: string; + + @ApiProperty({ type: String }) + @IsOptional() + @IsString() + modifiedBy: string; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => value ? new Date(value) : null) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + version: number; +} +export class EventAnalyticsUpdateDTO extends EventAnalyticsDTO { + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + id: number; +} \ No newline at end of file diff --git a/src/event-analytics/event-analytics.entity.ts b/src/event-analytics/event-analytics.entity.ts new file mode 100644 index 0000000..c75b4a4 --- /dev/null +++ b/src/event-analytics/event-analytics.entity.ts @@ -0,0 +1,66 @@ +import { Table, Column, Model, DataType } from 'sequelize-typescript'; +import { ApiProperty } from '@nestjs/swagger'; + +@Table({ tableName: 'event_analytics', paranoid: true }) +export default class EventAnalytics extends Model { + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, allowNull: false, field: 'event_id' }) + eventId: number; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'views' }) + views: number; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'ticket_sales' }) + ticketSales: number; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'engagement_rate' }) + engagementRate: number; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'promotion_views' }) + promotionViews: number; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'promotion_sales' }) + promotionSales: number; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'status' }) + status: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validFrom' }) + validFrom: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validTill' }) + validTill: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'createdAt' }) + createdAt: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'updatedAt' }) + updatedAt: Date; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'createdBy' }) + createdBy: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'modifiedBy' }) + modifiedBy: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'deletedAt' }) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'version' }) + version: number; +} diff --git a/src/event-analytics/event-analytics.module.ts b/src/event-analytics/event-analytics.module.ts new file mode 100644 index 0000000..d7ac099 --- /dev/null +++ b/src/event-analytics/event-analytics.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { EventAnalyticsController } from './event-analytics.controller'; +import { EventAnalyticsService } from './event-analytics.service'; +import { HttpModule } from '@nestjs/axios'; + +@Module({ + imports: [HttpModule], + providers: [EventAnalyticsService], + controllers: [EventAnalyticsController], +}) +export class EventAnalyticsModule { } diff --git a/src/event-analytics/event-analytics.service.ts b/src/event-analytics/event-analytics.service.ts new file mode 100644 index 0000000..02e5413 --- /dev/null +++ b/src/event-analytics/event-analytics.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import EventAnalytics from './event-analytics.entity'; +import { HttpService } from '@nestjs/axios'; +import { Utility } from 'src/common/Utility'; + +@Injectable() +export class EventAnalyticsService { + constructor(private readonly httpService: HttpService) {} + + async findAll(): Promise<{ rows: EventAnalytics[], count: number }> { + return EventAnalytics.findAndCountAll(); + } + + async findByPk(id: number): Promise { + return EventAnalytics.findByPk(id); + } + + findOne(eventAnalytics: EventAnalytics): Promise { + return EventAnalytics.findOne({ where: eventAnalytics as any }); + } + + filter(eventAnalytics: EventAnalytics): Promise { + return EventAnalytics.findAll({ where: eventAnalytics as any }); + } + + async remove(id: number): Promise { + return EventAnalytics.destroy({ where: { id: id } }); + } + + async upsert(eventAnalytics: EventAnalytics, insertIfNotFound: boolean): Promise { + if (eventAnalytics.id) { + const existing = await this.findByPk(eventAnalytics.id); + if (existing) { + return EventAnalytics.update(eventAnalytics, { where: { id: eventAnalytics.id } }); + } + } + if (insertIfNotFound) { + return EventAnalytics.create(eventAnalytics as any); + } + } +} diff --git a/src/event-category/event-category.controller.ts b/src/event-category/event-category.controller.ts new file mode 100644 index 0000000..c438898 --- /dev/null +++ b/src/event-category/event-category.controller.ts @@ -0,0 +1,289 @@ +import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common'; +import { EventCategoryService } from './event-category.service'; +import { Response } from 'express'; +import { GenericResponse } from '../common/GenericResponse.model'; +import EventCategory from './event-category.entity'; +import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger'; + +@ApiTags('eventCategory') +@Controller('eventCategory') +export class EventCategoryController { + constructor(private eventCategoryService: EventCategoryService) {} + + @Get('/all') + @ApiOperation({ summary: 'Get all event categories' }) + @ApiResponse({ status: 200, description: 'Successfully retrieved all event categories' }) + @ApiResponse({ + status: 404, + description: 'No event categories found', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event categories found' + }, + data: null + } + }) + async getAll(@Res() res: Response) { + const response = await this.eventCategoryService.findAll(); + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event categories found' + }, null); + return res.status(404).send(errorResponse); + } + res.send(new GenericResponse(null, response)); + } + + @Get(':id') + @ApiOperation({ summary: 'Get event category by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'EventCategory ID' }) + @ApiResponse({ + status: 200, + description: 'Successfully retrieved event category by ID', + }) + @ApiResponse({ + status: 400, + description: 'ID is required', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, + data: null + } + }) + @ApiResponse({ + status: 404, + description: 'Event category not found', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'Event category not found' + }, + data: null + } + }) + async findById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null)); + } + + const response = await this.eventCategoryService.findByPk(id); + if (!response) { + return res.status(404).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Event category with ID ${id} not found` + }, null)); + } + + res.status(200).send(new GenericResponse(null, response)); + } + + @Post('/filter') + @ApiOperation({ summary: 'Filter event categories based on criteria' }) + @ApiBody({ type: EventCategory, description: 'Filter criteria for event categories' }) + @ApiResponse({ status: 200, description: 'Successfully filtered event categories' }) + @ApiResponse({ + status: 400, + description: 'Invalid filter criteria', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_CRITERIA', + stackTrace: 'Request' + }, + data: null + } + }) + @ApiResponse({ + status: 404, + description: 'No event categories found based on filter criteria', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event categories found based on the filter criteria' + }, + data: null + } + }) + async filter(@Body() category: EventCategory, @Res() res: Response) { + if (!category) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_CRITERIA', + stackTrace: 'Request' + }, null)); + } + + const response = await this.eventCategoryService.filter(category); + if (!response) { + return res.status(404).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event categories found based on the filter criteria' + }, null)); + } + + res.status(200).send(new GenericResponse(null, response)); + } + + @Post() + @ApiOperation({ summary: 'Insert a new event category' }) + @ApiBody({ type: EventCategory, description: 'Event category data to insert' }) + @ApiResponse({ status: 201, description: 'Successfully created an event category' }) + @ApiResponse({ + status: 400, + description: 'Invalid event category data', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_DATA', + stackTrace: 'Request' + }, + data: null + } + }) + async insert(@Body() category: EventCategory, @Res() res: Response) { + if (!category) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_DATA', + stackTrace: 'Request' + }, null)); + } + + delete category.id; + const response = await this.eventCategoryService.upsert(category, true); + res.status(201).send(new GenericResponse(null, response)); + } + + @Put() + @ApiOperation({ summary: 'Update an existing event category' }) + @ApiBody({ type: EventCategory, description: 'Event category data to update' }) + @ApiResponse({ status: 200, description: 'Successfully updated event category' }) + @ApiResponse({ + status: 400, + description: 'Invalid data or ID missing', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, + data: null + } + }) + @ApiResponse({ + status: 404, + description: 'Event category not found', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'Event category not found' + }, + data: null + } + }) + async update(@Body() category: EventCategory, @Res() res: Response) { + if (!category || !category.id) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null)); + } + + const response = await this.eventCategoryService.upsert(category, false); + if (!response) { + return res.status(404).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Event category with ID ${category.id} not found` + }, null)); + } + + res.status(200).send(new GenericResponse(null, response)); + } + + @Delete(':id') + @ApiOperation({ summary: 'Delete an event category by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'Event category ID to delete' }) + @ApiResponse({ status: 200, description: 'Successfully deleted event category' }) + @ApiResponse({ + status: 400, + description: 'ID parameter is required', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, + data: null + } + }) + @ApiResponse({ + status: 404, + description: 'Event category not found', + example: { + notification: { + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'Event category not found' + }, + data: null + } + }) + async deleteById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + return res.status(400).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null)); + } + + const response = await this.eventCategoryService.remove(id); + if (!response) { + return res.status(404).send(new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Event category with ID ${id} not found` + }, null)); + } + + res.status(200).send(new GenericResponse(null, response)); + } +} diff --git a/src/event-category/event-category.dto.ts b/src/event-category/event-category.dto.ts new file mode 100644 index 0000000..303042e --- /dev/null +++ b/src/event-category/event-category.dto.ts @@ -0,0 +1,64 @@ +import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class EventCategoryDTO { + + @ApiProperty({ type: String }) + @IsString() + @IsNotEmpty() + categoryName: string; + + @ApiProperty({ type: String }) + @IsString() + @IsNotEmpty() + status: string; + + @ApiProperty({ type: Date }) + @IsDate() + @IsNotEmpty() + @Transform(({ value }) => new Date(value)) + validFrom: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @IsNotEmpty() + @Transform(({ value }) => new Date(value)) + validTill: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + createdAt: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + updatedAt: Date; + + @ApiProperty({ type: String }) + @IsString() + createdBy: string; + + @ApiProperty({ type: String }) + @IsString() + modifiedBy: string; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => value ? new Date(value) : null) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + version: number; +} + +export class EventCategoryUpdateDTO extends EventCategoryDTO { + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + id: number; +} diff --git a/src/event-category/event-category.entity.ts b/src/event-category/event-category.entity.ts new file mode 100644 index 0000000..7410842 --- /dev/null +++ b/src/event-category/event-category.entity.ts @@ -0,0 +1,46 @@ +import { Table, Column, Model, DataType } from 'sequelize-typescript'; +import { ApiProperty } from '@nestjs/swagger'; + +@Table({ tableName: 'event_categories', paranoid: true }) +export default class EventCategory extends Model { + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'category_name' }) + categoryName: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'status' }) + status: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validFrom' }) + validFrom: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validTill' }) + validTill: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'createdAt' }) + createdAt: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'updatedAt' }) + updatedAt: Date; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'createdBy' }) + createdBy: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'modifiedBy' }) + modifiedBy: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'deletedAt' }) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'version' }) + version: number; +} diff --git a/src/event-category/event-category.module.ts b/src/event-category/event-category.module.ts new file mode 100644 index 0000000..220a2dd --- /dev/null +++ b/src/event-category/event-category.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { EventCategoryController } from './event-category.controller'; +import { EventCategoryService } from './event-category.service'; +import { HttpModule } from '@nestjs/axios'; + +@Module({ + imports: [HttpModule], + providers: [EventCategoryService], + controllers: [EventCategoryController], +}) +export class EventCategoryModule {} diff --git a/src/event-category/event-category.service.ts b/src/event-category/event-category.service.ts new file mode 100644 index 0000000..5944be3 --- /dev/null +++ b/src/event-category/event-category.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@nestjs/common'; +import EventCategory from './event-category.entity'; +import { HttpService } from '@nestjs/axios'; + +@Injectable() +export class EventCategoryService { + constructor(private readonly httpService: HttpService) {} + + async findAll(): Promise<{ rows: EventCategory[]; count: number }> { + return EventCategory.findAndCountAll(); + } + + async findByPk(id: number): Promise { + return EventCategory.findByPk(id); + } + + findOne(category: EventCategory): Promise { + return EventCategory.findOne({ where: category as any }); + } + + filter(category: EventCategory): Promise { + return EventCategory.findAll({ where: category as any }); + } + + async remove(id: number): Promise { + return EventCategory.destroy({ where: { id } }); + } + + async upsert(category: EventCategory, insertIfNotFound: boolean): Promise { + if (category.id) { + const existingCategory = await this.findByPk(category.id); + if (existingCategory) { + return EventCategory.update(category, { where: { id: category.id } }); + } + } + if (insertIfNotFound) { + return EventCategory.create(category as any); + } + } +} diff --git a/src/event-episodes/event-episodes.controller.ts b/src/event-episodes/event-episodes.controller.ts new file mode 100644 index 0000000..1269d33 --- /dev/null +++ b/src/event-episodes/event-episodes.controller.ts @@ -0,0 +1,310 @@ +import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common'; +import { EventEpisodeService } from './event-episodes.service'; +import { Response } from 'express'; +import { GenericResponse } from '../common/GenericResponse.model'; +import EventEpisode from './event-episodes.entity'; +import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger'; + +@ApiTags('event-episode') +@Controller('event-episode') +export class EventEpisodeController { + constructor(private eventEpisodeService: EventEpisodeService) {} + + @Get("/all") + @ApiOperation({ summary: 'Get all event episodes' }) + @ApiResponse({ + status: 200, + description: 'Successfully retrieved all event episodes', + }) + @ApiResponse({ + status: 404, + description: 'No event episodes found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "No event episodes found" + }, + "data": null + } + }) + async getAllEventEpisodes(@Res() res: Response) { + const response = await this.eventEpisodeService.findAll() || []; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event episodes found' + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.send(httpResponse); + } + + @Get(':id') + @ApiOperation({ summary: 'Get event episode by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'Event Episode ID' }) + @ApiResponse({ + status: 400, + description: 'ID is required', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NO_ID_REQ", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'Event episode not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "Event episode not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully retrieved event episode by ID', + }) + async findById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.eventEpisodeService.findByPk(id) || {}; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Event episode with ID ${id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Post('/filter') + @ApiOperation({ summary: 'Filter event episodes based on criteria' }) + @ApiBody({ type: EventEpisode, description: 'Filter criteria for event episodes' }) + @ApiResponse({ + status: 400, + description: 'Invalid filter criteria', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_CRITERIA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'No event episodes found based on filter criteria', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "No event episodes found based on the filter criteria" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully filtered event episodes', + }) + async filter(@Body() eventEpisode: EventEpisode, @Res() res: Response) { + if (!eventEpisode) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_CRITERIA', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.eventEpisodeService.filter(eventEpisode) || []; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No event episodes found based on the filter criteria' + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Post() + @ApiOperation({ summary: 'Insert a new event episode' }) + @ApiBody({ type: EventEpisode, description: 'Event episode data to insert' }) + @ApiResponse({ + status: 400, + description: 'Invalid event episode data', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_DATA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 201, + description: 'Successfully created an event episode', + }) + async insert(@Body() eventEpisode: EventEpisode, @Res() res: Response) { + if (!eventEpisode) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_DATA', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + delete eventEpisode.id; // Ensure no ID is passed in the creation + const response = await this.eventEpisodeService.upsert(eventEpisode, true); + const httpResponse = new GenericResponse(null, response); + res.status(201).send(httpResponse); + } + + @Put() + @ApiOperation({ summary: 'Update an existing event episode' }) + @ApiBody({ type: EventEpisode, description: 'Event episode data to update' }) + @ApiResponse({ + status: 400, + description: 'Invalid event episode data or ID missing', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_DATA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'Event episode not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "Event episode not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully updated event episode', + }) + async update(@Body() eventEpisode: EventEpisode, @Res() res: Response) { + if (!eventEpisode || !eventEpisode.id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.eventEpisodeService.upsert(eventEpisode, false); + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Event episode with ID ${eventEpisode.id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Delete(':id') + @ApiOperation({ summary: 'Delete an event episode by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'Event episode ID to delete' }) + @ApiResponse({ + status: 400, + description: 'ID parameter is required', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NO_ID_REQ", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'Event episode not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "Event episode not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully deleted event episode', + }) + async deleteById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.eventEpisodeService.remove(id) || {}; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Event episode with ID ${id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } +} diff --git a/src/event-episodes/event-episodes.dto.ts b/src/event-episodes/event-episodes.dto.ts new file mode 100644 index 0000000..1622c57 --- /dev/null +++ b/src/event-episodes/event-episodes.dto.ts @@ -0,0 +1,93 @@ +import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class EventEpisodeDTO { + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + eventId: number; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + episodeName: string; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + episodeDescription: string; + + @ApiProperty({ type: Date }) + @IsDate() + @IsOptional() + @Transform(({ value }) => new Date(value)) + episodeStartDate: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @IsOptional() + @Transform(({ value }) => new Date(value)) + episodeEndDate: Date; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + episodeLocation: string; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + episodeType: string; + + @ApiProperty({ type: Object }) + @IsOptional() + images: object; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + status: string; + + @ApiProperty({ type: Date }) + @IsDate() + @IsOptional() + @Transform(({ value }) => new Date(value)) + validFrom: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @IsOptional() + @Transform(({ value }) => new Date(value)) + validTill: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + createdAt: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + updatedAt: Date; + + @ApiProperty({ type: String }) + @IsString() + createdBy: string; + + @ApiProperty({ type: String }) + @IsString() + modifiedBy: string; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => value ? new Date(value) : null) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + version: number; +} diff --git a/src/event-episodes/event-episodes.entity.ts b/src/event-episodes/event-episodes.entity.ts new file mode 100644 index 0000000..bbb438b --- /dev/null +++ b/src/event-episodes/event-episodes.entity.ts @@ -0,0 +1,74 @@ +import { Table, Column, Model, DataType } from 'sequelize-typescript'; +import { ApiProperty } from '@nestjs/swagger'; + +@Table({ tableName: 'event_episodes', paranoid: true }) +export default class EventEpisode extends Model { + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, allowNull: false, field: 'event_id' }) + eventId: number; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'episode_name' }) + episodeName: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'episode_description' }) + episodeDescription: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'episode_start_date' }) + episodeStartDate: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'episode_end_date' }) + episodeEndDate: Date; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'episode_location' }) + episodeLocation: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'episode_type' }) + episodeType: string; + + @ApiProperty({ type: Object }) + @Column({ type: DataType.JSON, field: 'images' }) + images: object; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'status' }) + status: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validFrom' }) + validFrom: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validTill' }) + validTill: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'createdAt' }) + createdAt: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'updatedAt' }) + updatedAt: Date; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'createdBy' }) + createdBy: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'modifiedBy' }) + modifiedBy: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'deletedAt' }) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'version' }) + version: number; +} diff --git a/src/event-episodes/event-episodes.module.ts b/src/event-episodes/event-episodes.module.ts new file mode 100644 index 0000000..380a3b6 --- /dev/null +++ b/src/event-episodes/event-episodes.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { EventEpisodeController } from './event-episodes.controller'; +import { EventEpisodeService } from './event-episodes.service'; +import { HttpModule } from '@nestjs/axios'; + +@Module({ + imports: [HttpModule], + providers: [EventEpisodeService], + controllers: [EventEpisodeController], +}) +export class EventEpisodeModule {} diff --git a/src/event-episodes/event-episodes.service.ts b/src/event-episodes/event-episodes.service.ts new file mode 100644 index 0000000..e6e8079 --- /dev/null +++ b/src/event-episodes/event-episodes.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import EventEpisode from './event-episodes.entity'; +import { HttpService } from '@nestjs/axios'; +import { Utility } from 'src/common/Utility'; + +@Injectable() +export class EventEpisodeService { + constructor(private readonly httpService: HttpService) { } + + async findAll(): Promise<{ rows: EventEpisode[], count: number }> { + return EventEpisode.findAndCountAll(); + } + + async findByPk(id: number): Promise { + return EventEpisode.findByPk(id); + } + + findOne(eventEpisode: EventEpisode): Promise { + return EventEpisode.findOne({ where: eventEpisode as any }); + } + + filter(eventEpisode: EventEpisode): Promise { + return EventEpisode.findAll({ where: eventEpisode as any }); + } + + async remove(id: number): Promise { + return EventEpisode.destroy({ where: { id: id } }); + } + + async upsert(eventEpisode: EventEpisode, insertIfNotFound: boolean): Promise { + if (eventEpisode.id) { + const existingEventEpisode = await this.findByPk(eventEpisode.id); + if (existingEventEpisode) { + return EventEpisode.update(eventEpisode, { where: { id: eventEpisode.id } }); + } + } + if (insertIfNotFound) { + return EventEpisode.create(eventEpisode as any); + } + } +} diff --git a/src/event/event.dto.ts b/src/event/event.dto.ts index 7448547..222f962 100644 --- a/src/event/event.dto.ts +++ b/src/event/event.dto.ts @@ -4,9 +4,9 @@ import { Transform } from 'class-transformer'; export class EventDTO { - @ApiProperty({ type: String }) - @IsEmail() - orgEmail: string; + @ApiProperty({ type: Number }) + @IsNumber() + organizer_id: number; @ApiProperty({ type: String }) @IsString() diff --git a/src/event/event.entity.ts b/src/event/event.entity.ts index 8194aa7..5732320 100644 --- a/src/event/event.entity.ts +++ b/src/event/event.entity.ts @@ -4,9 +4,9 @@ import { ApiProperty } from '@nestjs/swagger'; @Table({ tableName: 'events', paranoid: true }) export default class Event extends Model { - @ApiProperty({ type: String }) - @Column({ type: DataType.TEXT, field: 'org_email' }) - orgEmail: string; + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'organizer_id' }) + organizerId: number; @ApiProperty({ type: String }) @Column({ type: DataType.TEXT, field: 'event_name' }) diff --git a/src/promotions/promotions.controller.ts b/src/promotions/promotions.controller.ts new file mode 100644 index 0000000..a93668e --- /dev/null +++ b/src/promotions/promotions.controller.ts @@ -0,0 +1,310 @@ +import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common'; +import { PromotionService } from './promotions.service'; // Updated service +import { Response } from 'express'; +import { GenericResponse } from '../common/GenericResponse.model'; +import Promotion from './promotions.entity'; // Updated entity +import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger'; + +@ApiTags('promotion') +@Controller('promotion') +export class PromotionController { + constructor(private promotionService: PromotionService) {} + + @Get("/all") + @ApiOperation({ summary: 'Get all promotions' }) + @ApiResponse({ + status: 200, + description: 'Successfully retrieved all promotions', + }) + @ApiResponse({ + status: 404, + description: 'No promotions found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "No promotions found" + }, + "data": null + } + }) + async getAllPromotions(@Res() res: Response) { + const response = await this.promotionService.findAll() || []; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No promotions found' + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.send(httpResponse); + } + + @Get(':id') + @ApiOperation({ summary: 'Get promotion by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'Promotion ID' }) + @ApiResponse({ + status: 400, + description: 'ID is required', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NO_ID_REQ", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'Promotion not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "Promotion not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully retrieved promotion by ID', + }) + async findById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.promotionService.findByPk(id) || {}; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Promotion with ID ${id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Post('/filter') + @ApiOperation({ summary: 'Filter promotions based on criteria' }) + @ApiBody({ type: Promotion, description: 'Filter criteria for promotions' }) + @ApiResponse({ + status: 400, + description: 'Invalid filter criteria', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_CRITERIA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'No promotions found based on filter criteria', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "No promotions found based on the filter criteria" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully filtered promotions', + }) + async filter(@Body() promotion: Promotion, @Res() res: Response) { + if (!promotion) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_CRITERIA', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.promotionService.filter(promotion) || []; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No promotions found based on the filter criteria' + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Post() + @ApiOperation({ summary: 'Insert a new promotion' }) + @ApiBody({ type: Promotion, description: 'Promotion data to insert' }) + @ApiResponse({ + status: 400, + description: 'Invalid promotion data', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_DATA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 201, + description: 'Successfully created a promotion', + }) + async insert(@Body() promotion: Promotion, @Res() res: Response) { + if (!promotion) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_DATA', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + delete promotion.id; // Ensure no ID is passed in the creation + const response = await this.promotionService.upsert(promotion, true); + const httpResponse = new GenericResponse(null, response); + res.status(201).send(httpResponse); + } + + @Put() + @ApiOperation({ summary: 'Update an existing promotion' }) + @ApiBody({ type: Promotion, description: 'Promotion data to update' }) + @ApiResponse({ + status: 400, + description: 'Invalid promotion data or ID missing', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_DATA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'Promotion not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "Promotion not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully updated promotion', + }) + async update(@Body() promotion: Promotion, @Res() res: Response) { + if (!promotion || !promotion.id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.promotionService.upsert(promotion, false); + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Promotion with ID ${promotion.id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Delete(':id') + @ApiOperation({ summary: 'Delete a promotion by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'Promotion ID to delete' }) + @ApiResponse({ + status: 400, + description: 'ID parameter is required', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NO_ID_REQ", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'Promotion not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "Promotion not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully deleted promotion', + }) + async deleteById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.promotionService.remove(id) || {}; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `Promotion with ID ${id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } +} diff --git a/src/promotions/promotions.dto.ts b/src/promotions/promotions.dto.ts new file mode 100644 index 0000000..7021ad8 --- /dev/null +++ b/src/promotions/promotions.dto.ts @@ -0,0 +1,91 @@ +import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class PromotionDTO { + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + eventId: number; + + @ApiProperty({ type: Date }) + @IsDate() + @IsNotEmpty() + @Transform(({ value }) => new Date(value)) + startDate: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @IsNotEmpty() + @Transform(({ value }) => new Date(value)) + endDate: Date; + + @ApiProperty({ type: String }) + @IsString() + @IsNotEmpty() + location: string; + + @ApiProperty({ type: String }) + @IsString() + @IsNotEmpty() + type: string; + + @ApiProperty({ type: String }) + @IsString() + @IsNotEmpty() + description: string; + + @ApiProperty({ type: String }) + @IsString() + @IsNotEmpty() + status: string; + + @ApiProperty({ type: Date }) + @IsDate() + @IsNotEmpty() + @Transform(({ value }) => new Date(value)) + validFrom: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @IsNotEmpty() + @Transform(({ value }) => new Date(value)) + validTill: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + createdAt: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + updatedAt: Date; + + @ApiProperty({ type: String }) + @IsString() + createdBy: string; + + @ApiProperty({ type: String }) + @IsString() + modifiedBy: string; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => value ? new Date(value) : null) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + version: number; +} + +export class PromotionUpdateDTO extends PromotionDTO { + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + id: number +} diff --git a/src/promotions/promotions.entity.ts b/src/promotions/promotions.entity.ts new file mode 100644 index 0000000..149dfd5 --- /dev/null +++ b/src/promotions/promotions.entity.ts @@ -0,0 +1,66 @@ +import { Table, Column, Model, DataType } from 'sequelize-typescript'; +import { ApiProperty } from '@nestjs/swagger'; + +@Table({ tableName: 'promotions', paranoid: true }) +export default class Promotion extends Model { + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, allowNull: false, field: 'event_id' }) + eventId: number; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'start_date' }) + startDate: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'end_date' }) + endDate: Date; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'location' }) + location: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'type' }) + type: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'description' }) + description: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'status' }) + status: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validFrom' }) + validFrom: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validTill' }) + validTill: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'createdAt' }) + createdAt: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'updatedAt' }) + updatedAt: Date; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'createdBy' }) + createdBy: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'modifiedBy' }) + modifiedBy: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'deletedAt' }) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'version' }) + version: number; +} diff --git a/src/promotions/promotions.module.ts b/src/promotions/promotions.module.ts new file mode 100644 index 0000000..5108d25 --- /dev/null +++ b/src/promotions/promotions.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PromotionController } from './promotions.controller'; +import { PromotionService } from './promotions.service'; +import { HttpModule } from '@nestjs/axios'; + +@Module({ + imports: [HttpModule], + providers: [PromotionService], + controllers: [PromotionController], +}) +export class PromotionModule { } diff --git a/src/promotions/promotions.service.ts b/src/promotions/promotions.service.ts new file mode 100644 index 0000000..19023f5 --- /dev/null +++ b/src/promotions/promotions.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import Promotion from './promotions.entity'; +import { HttpService } from '@nestjs/axios'; +import { Utility } from 'src/common/Utility'; + +@Injectable() +export class PromotionService { + constructor(private readonly httpService: HttpService) { } + + async findAll(): Promise<{ rows: Promotion[], count: number }> { + return Promotion.findAndCountAll(); + } + + async findByPk(id: number): Promise { + return Promotion.findByPk(id); + } + + findOne(promotion: Promotion): Promise { + return Promotion.findOne({ where: promotion as any }); + } + + filter(promotion: Promotion): Promise { + return Promotion.findAll({ where: promotion as any }); + } + + async remove(id: number): Promise { + return Promotion.destroy({ where: { id: id } }); + } + + async upsert(promotion: Promotion, insertIfNotFound: boolean): Promise { + if (promotion.id) { + const existingPromotion = await this.findByPk(promotion.id); + if (existingPromotion) { + return Promotion.update(promotion, { where: { id: promotion.id } }); + } + } + if (insertIfNotFound) { + return Promotion.create(promotion as any); + } + } +} diff --git a/src/user-watchlist/user-watchlist.controller.ts b/src/user-watchlist/user-watchlist.controller.ts new file mode 100644 index 0000000..b9fe105 --- /dev/null +++ b/src/user-watchlist/user-watchlist.controller.ts @@ -0,0 +1,310 @@ +import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common'; +import { UserWatchlistService } from './user-watchlist.service'; +import { Response } from 'express'; +import { GenericResponse } from '../common/GenericResponse.model'; +import UserWatchlist from './user-watchlist.entity'; +import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger'; + +@ApiTags('user-watchlist') +@Controller('user-watchlist') +export class UserWatchlistController { + constructor(private userWatchlistService: UserWatchlistService) {} + + @Get("/all") + @ApiOperation({ summary: 'Get all user watchlists' }) + @ApiResponse({ + status: 200, + description: 'Successfully retrieved all user watchlists', + }) + @ApiResponse({ + status: 404, + description: 'No user watchlists found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "No user watchlists found" + }, + "data": null + } + }) + async getAllUserWatchlists(@Res() res: Response) { + const response = await this.userWatchlistService.findAll() || []; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No user watchlists found' + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.send(httpResponse); + } + + @Get(':id') + @ApiOperation({ summary: 'Get user watchlist by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'User Watchlist ID' }) + @ApiResponse({ + status: 400, + description: 'ID is required', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NO_ID_REQ", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'User watchlist not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "User watchlist not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully retrieved user watchlist by ID', + }) + async findById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.userWatchlistService.findByPk(id) || {}; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `User watchlist with ID ${id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Post('/filter') + @ApiOperation({ summary: 'Filter user watchlists based on criteria' }) + @ApiBody({ type: UserWatchlist, description: 'Filter criteria for user watchlists' }) + @ApiResponse({ + status: 400, + description: 'Invalid filter criteria', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_CRITERIA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'No user watchlists found based on filter criteria', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "No user watchlists found based on the filter criteria" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully filtered user watchlists', + }) + async filter(@Body() userWatchlist: UserWatchlist, @Res() res: Response) { + if (!userWatchlist) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_CRITERIA', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.userWatchlistService.filter(userWatchlist) || []; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: 'No user watchlists found based on the filter criteria' + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Post() + @ApiOperation({ summary: 'Insert a new user watchlist' }) + @ApiBody({ type: UserWatchlist, description: 'User watchlist data to insert' }) + @ApiResponse({ + status: 400, + description: 'Invalid user watchlist data', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_DATA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 201, + description: 'Successfully created a user watchlist', + }) + async insert(@Body() userWatchlist: UserWatchlist, @Res() res: Response) { + if (!userWatchlist) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.INVALID_DATA', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + delete userWatchlist.id; // Ensure no ID is passed in the creation + const response = await this.userWatchlistService.upsert(userWatchlist, true); + const httpResponse = new GenericResponse(null, response); + res.status(201).send(httpResponse); + } + + @Put() + @ApiOperation({ summary: 'Update an existing user watchlist' }) + @ApiBody({ type: UserWatchlist, description: 'User watchlist data to update' }) + @ApiResponse({ + status: 400, + description: 'Invalid user watchlist data or ID missing', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.INVALID_DATA", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'User watchlist not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "User watchlist not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully updated user watchlist', + }) + async update(@Body() userWatchlist: UserWatchlist, @Res() res: Response) { + if (!userWatchlist || !userWatchlist.id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.userWatchlistService.upsert(userWatchlist, false); + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `User watchlist with ID ${userWatchlist.id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } + + @Delete(':id') + @ApiOperation({ summary: 'Delete a user watchlist by ID' }) + @ApiParam({ name: 'id', type: Number, description: 'User watchlist ID to delete' }) + @ApiResponse({ + status: 400, + description: 'ID parameter is required', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NO_ID_REQ", + "stackTrace": "Request" + }, + "data": null + } + }) + @ApiResponse({ + status: 404, + description: 'User watchlist not found', + example: { + "notification": { + "exception": true, + "exceptionSeverity": "HIGH", + "exceptionMessage": "ERR.NOT_FOUND", + "stackTrace": "User watchlist not found" + }, + "data": null + } + }) + @ApiResponse({ + status: 200, + description: 'Successfully deleted user watchlist', + }) + async deleteById(@Param('id') id: number, @Res() res: Response) { + if (!id) { + const response = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NO_ID_REQ', + stackTrace: 'Request' + }, null); + return res.status(400).send(response); + } + const response = await this.userWatchlistService.remove(id) || {}; + if (!response) { + const errorResponse = new GenericResponse({ + exception: true, + exceptionSeverity: 'HIGH', + exceptionMessage: 'ERR.NOT_FOUND', + stackTrace: `User watchlist with ID ${id} not found` + }, null); + return res.status(404).send(errorResponse); + } + const httpResponse = new GenericResponse(null, response); + res.status(200).send(httpResponse); + } +} diff --git a/src/user-watchlist/user-watchlist.dto.ts b/src/user-watchlist/user-watchlist.dto.ts new file mode 100644 index 0000000..bb7fa29 --- /dev/null +++ b/src/user-watchlist/user-watchlist.dto.ts @@ -0,0 +1,69 @@ +import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; + +export class UserWatchlistDTO { + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + userId: number; + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + eventId: number; + + @ApiProperty({ type: String }) + @IsString() + @IsOptional() + status: string; + + @ApiProperty({ type: Date }) + @IsDate() + @IsOptional() + @Transform(({ value }) => new Date(value)) + validFrom: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @IsOptional() + @Transform(({ value }) => new Date(value)) + validTill: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + createdAt: Date; + + @ApiProperty({ type: Date }) + @IsDate() + @Transform(({ value }) => new Date(value)) + updatedAt: Date; + + @ApiProperty({ type: String }) + @IsString() + createdBy: string; + + @ApiProperty({ type: String }) + @IsString() + modifiedBy: string; + + @ApiProperty({ type: Date }) + @IsOptional() + @IsDate() + @Transform(({ value }) => value ? new Date(value) : null) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + version: number; +} + +export class UserWatchlistUpdateDTO extends UserWatchlistDTO { + @ApiProperty({ type: Number }) + @IsNumber() + @IsNotEmpty() + id: number; +} diff --git a/src/user-watchlist/user-watchlist.entity.ts b/src/user-watchlist/user-watchlist.entity.ts new file mode 100644 index 0000000..2d68bf4 --- /dev/null +++ b/src/user-watchlist/user-watchlist.entity.ts @@ -0,0 +1,50 @@ +import { Table, Column, Model, DataType } from 'sequelize-typescript'; +import { ApiProperty } from '@nestjs/swagger'; + +@Table({ tableName: 'user_watchlist', paranoid: true }) +export default class UserWatchlist extends Model { + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, allowNull: false, field: 'user_id' }) + userId: number; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, allowNull: false, field: 'event_id' }) + eventId: number; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'status' }) + status: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validFrom' }) + validFrom: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATEONLY, field: 'validTill' }) + validTill: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'createdAt' }) + createdAt: Date; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'updatedAt' }) + updatedAt: Date; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'createdBy' }) + createdBy: string; + + @ApiProperty({ type: String }) + @Column({ type: DataType.TEXT, field: 'modifiedBy' }) + modifiedBy: string; + + @ApiProperty({ type: Date }) + @Column({ type: DataType.DATE, field: 'deletedAt' }) + deletedAt: Date; + + @ApiProperty({ type: Number }) + @Column({ type: DataType.NUMBER, field: 'version' }) + version: number; +} diff --git a/src/user-watchlist/user-watchlist.module.ts b/src/user-watchlist/user-watchlist.module.ts new file mode 100644 index 0000000..de0467a --- /dev/null +++ b/src/user-watchlist/user-watchlist.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { UserWatchlistController } from './user-watchlist.controller'; +import { UserWatchlistService } from './user-watchlist.service'; +import { HttpModule } from '@nestjs/axios'; + +@Module({ + imports: [HttpModule], + providers: [UserWatchlistService], + controllers: [UserWatchlistController], +}) +export class UserWatchlistModule { } diff --git a/src/user-watchlist/user-watchlist.service.ts b/src/user-watchlist/user-watchlist.service.ts new file mode 100644 index 0000000..da09c99 --- /dev/null +++ b/src/user-watchlist/user-watchlist.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import UserWatchlist from './user-watchlist.entity'; +import { HttpService } from '@nestjs/axios'; +import { Utility } from 'src/common/Utility'; + +@Injectable() +export class UserWatchlistService { + constructor(private readonly httpService: HttpService) { } + + async findAll(): Promise<{ rows: UserWatchlist[], count: number }> { + return UserWatchlist.findAndCountAll(); + } + + async findByPk(id: number): Promise { + return UserWatchlist.findByPk(id); + } + + findOne(userWatchlist: UserWatchlist): Promise { + return UserWatchlist.findOne({ where: userWatchlist as any }); + } + + filter(userWatchlist: UserWatchlist): Promise { + return UserWatchlist.findAll({ where: userWatchlist as any }); + } + + async remove(id: number): Promise { + return UserWatchlist.destroy({ where: { id: id } }); + } + + async upsert(userWatchlist: UserWatchlist, insertIfNotFound: boolean): Promise { + if (userWatchlist.id) { + const existingUserWatchlist = await this.findByPk(userWatchlist.id); + if (existingUserWatchlist) { + return UserWatchlist.update(userWatchlist, { where: { id: userWatchlist.id } }); + } + } + if (insertIfNotFound) { + return UserWatchlist.create(userWatchlist as any); + } + } +}