diff --git a/src/app.module.ts b/src/app.module.ts index 6add0be..9a4ef30 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -14,6 +14,8 @@ import { FloorModule } from './floor/floor.module'; import { UnitModule } from './unit/unit.module'; import { RoleModule } from './role/role.module'; import { SeederModule } from '@app/common/seed/seeder.module'; +import { UserNotificationModule } from './user-notification/user-notification.module'; +import { DeviceMessagesSubscriptionModule } from './device-messages/device-messages.module'; @Module({ imports: [ ConfigModule.forRoot({ @@ -30,7 +32,9 @@ import { SeederModule } from '@app/common/seed/seeder.module'; RoomModule, GroupModule, DeviceModule, + DeviceMessagesSubscriptionModule, UserDevicePermissionModule, + UserNotificationModule, SeederModule, ], controllers: [AuthenticationController], diff --git a/src/device-messages/controllers/device-messages.controller.ts b/src/device-messages/controllers/device-messages.controller.ts new file mode 100644 index 0000000..c8f7834 --- /dev/null +++ b/src/device-messages/controllers/device-messages.controller.ts @@ -0,0 +1,98 @@ +import { + Body, + Controller, + Delete, + Get, + HttpException, + HttpStatus, + Param, + Post, + UseGuards, +} from '@nestjs/common'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { DeviceMessagesSubscriptionService } from '../services/device-messages.service'; +import { DeviceMessagesAddDto } from '../dtos/device-messages.dto'; + +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; + +@ApiTags('Device Messages Status Module') +@Controller({ + version: '1', + path: 'device-messages/subscription', +}) +export class DeviceMessagesSubscriptionController { + constructor( + private readonly deviceMessagesSubscriptionService: DeviceMessagesSubscriptionService, + ) {} + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post() + async addDeviceMessagesSubscription( + @Body() deviceMessagesAddDto: DeviceMessagesAddDto, + ) { + try { + const addDetails = + await this.deviceMessagesSubscriptionService.addDeviceMessagesSubscription( + deviceMessagesAddDto, + ); + return { + statusCode: HttpStatus.CREATED, + message: 'Device Messages Subscription Added Successfully', + data: addDetails, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get(':deviceUuid/user/:userUuid') + async getDeviceMessagesSubscription( + @Param('deviceUuid') deviceUuid: string, + @Param('userUuid') userUuid: string, + ) { + try { + const deviceDetails = + await this.deviceMessagesSubscriptionService.getDeviceMessagesSubscription( + userUuid, + deviceUuid, + ); + return { + statusCode: HttpStatus.OK, + message: 'User Device Subscription fetched Successfully', + data: deviceDetails, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Delete() + async deleteDeviceMessagesSubscription( + @Body() deviceMessagesAddDto: DeviceMessagesAddDto, + ) { + try { + await this.deviceMessagesSubscriptionService.deleteDeviceMessagesSubscription( + deviceMessagesAddDto, + ); + return { + statusCode: HttpStatus.OK, + message: 'User subscription deleted Successfully', + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/device-messages/controllers/index.ts b/src/device-messages/controllers/index.ts new file mode 100644 index 0000000..01e86df --- /dev/null +++ b/src/device-messages/controllers/index.ts @@ -0,0 +1 @@ +export * from './device-messages.controller'; diff --git a/src/device-messages/device-messages.module.ts b/src/device-messages/device-messages.module.ts new file mode 100644 index 0000000..451f013 --- /dev/null +++ b/src/device-messages/device-messages.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { DeviceMessagesSubscriptionController } from './controllers'; +import { DeviceMessagesSubscriptionService } from './services'; +import { DeviceNotificationRepositoryModule } from '@app/common/modules/device-notification/device.notification.module'; +import { DeviceNotificationRepository } from '@app/common/modules/device-notification/repositories'; + +@Module({ + imports: [ConfigModule, DeviceNotificationRepositoryModule], + controllers: [DeviceMessagesSubscriptionController], + providers: [DeviceNotificationRepository, DeviceMessagesSubscriptionService], + exports: [DeviceMessagesSubscriptionService], +}) +export class DeviceMessagesSubscriptionModule {} diff --git a/src/device-messages/dtos/device-messages.dto.ts b/src/device-messages/dtos/device-messages.dto.ts new file mode 100644 index 0000000..ae45970 --- /dev/null +++ b/src/device-messages/dtos/device-messages.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class DeviceMessagesAddDto { + @ApiProperty({ + description: 'user uuid', + required: true, + }) + @IsString() + @IsNotEmpty() + userUuid: string; + + @ApiProperty({ + description: 'device uuid', + required: true, + }) + @IsString() + @IsNotEmpty() + deviceUuid: string; +} diff --git a/src/device-messages/dtos/index.ts b/src/device-messages/dtos/index.ts new file mode 100644 index 0000000..ad01af8 --- /dev/null +++ b/src/device-messages/dtos/index.ts @@ -0,0 +1 @@ +export * from './device-messages.dto'; diff --git a/src/device-messages/services/device-messages.service.ts b/src/device-messages/services/device-messages.service.ts new file mode 100644 index 0000000..73c8182 --- /dev/null +++ b/src/device-messages/services/device-messages.service.ts @@ -0,0 +1,75 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { DeviceMessagesAddDto } from '../dtos/device-messages.dto'; +import { DeviceNotificationRepository } from '@app/common/modules/device-notification/repositories'; + +@Injectable() +export class DeviceMessagesSubscriptionService { + constructor( + private readonly deviceNotificationRepository: DeviceNotificationRepository, + ) {} + + async addDeviceMessagesSubscription( + deviceMessagesAddDto: DeviceMessagesAddDto, + ) { + try { + return await this.deviceNotificationRepository.save({ + user: { + uuid: deviceMessagesAddDto.userUuid, + }, + device: { + uuid: deviceMessagesAddDto.deviceUuid, + }, + }); + } catch (error) { + if (error.code === '23505') { + throw new HttpException( + 'This User already belongs to this device', + HttpStatus.BAD_REQUEST, + ); + } + throw new HttpException( + error.message || 'Internal Server Error', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getDeviceMessagesSubscription(userUuid: string, deviceUuid: string) { + try { + const deviceUserSubscription = + await this.deviceNotificationRepository.findOne({ + where: { + user: { uuid: userUuid }, + device: { uuid: deviceUuid }, + }, + }); + + return { + uuid: deviceUserSubscription.uuid, + deviceUuid: deviceUserSubscription.deviceUuid, + userUuid: deviceUserSubscription.userUuid, + }; + } catch (error) { + throw new HttpException( + 'User device subscription not found', + HttpStatus.NOT_FOUND, + ); + } + } + async deleteDeviceMessagesSubscription( + deviceMessagesAddDto: DeviceMessagesAddDto, + ) { + try { + const result = await this.deviceNotificationRepository.delete({ + user: { uuid: deviceMessagesAddDto.userUuid }, + device: { uuid: deviceMessagesAddDto.deviceUuid }, + }); + + return result; + } catch (error) { + throw new HttpException( + error.message || 'device not found', + HttpStatus.NOT_FOUND, + ); + } + } +} diff --git a/src/device-messages/services/index.ts b/src/device-messages/services/index.ts new file mode 100644 index 0000000..8420bcb --- /dev/null +++ b/src/device-messages/services/index.ts @@ -0,0 +1 @@ +export * from './device-messages.service'; diff --git a/src/user-notification/controllers/index.ts b/src/user-notification/controllers/index.ts new file mode 100644 index 0000000..dcc9096 --- /dev/null +++ b/src/user-notification/controllers/index.ts @@ -0,0 +1 @@ +export * from './user-notification.controller'; diff --git a/src/user-notification/controllers/user-notification.controller.ts b/src/user-notification/controllers/user-notification.controller.ts new file mode 100644 index 0000000..83fd215 --- /dev/null +++ b/src/user-notification/controllers/user-notification.controller.ts @@ -0,0 +1,94 @@ +import { + Body, + Controller, + Get, + HttpException, + HttpStatus, + Param, + Post, + Put, + UseGuards, +} from '@nestjs/common'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { UserNotificationService } from '../services/user-notification.service'; +import { + UserNotificationAddDto, + UserNotificationUpdateDto, +} from '../dtos/user-notification.dto'; + +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; + +@ApiTags('User Notification Module') +@Controller({ + version: '1', + path: 'user-notification/subscription', +}) +export class UserNotificationController { + constructor( + private readonly userNotificationService: UserNotificationService, + ) {} + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post() + async addUserSubscription( + @Body() userNotificationAddDto: UserNotificationAddDto, + ) { + try { + const addDetails = await this.userNotificationService.addUserSubscription( + userNotificationAddDto, + ); + return { + statusCode: HttpStatus.CREATED, + message: 'User Notification Added Successfully', + data: addDetails, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get(':userUuid') + async fetchUserSubscriptions(@Param('userUuid') userUuid: string) { + try { + const userDetails = + await this.userNotificationService.fetchUserSubscriptions(userUuid); + return { + statusCode: HttpStatus.OK, + message: 'User Notification fetched Successfully', + data: { ...userDetails }, + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Put() + async updateUserSubscription( + @Body() userNotificationUpdateDto: UserNotificationUpdateDto, + ) { + try { + await this.userNotificationService.updateUserSubscription( + userNotificationUpdateDto, + ); + return { + statusCode: HttpStatus.OK, + message: 'User subscription updated Successfully', + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/user-notification/dtos/index.ts b/src/user-notification/dtos/index.ts new file mode 100644 index 0000000..d87c96f --- /dev/null +++ b/src/user-notification/dtos/index.ts @@ -0,0 +1 @@ +export * from './user-notification.dto'; diff --git a/src/user-notification/dtos/user-notification.dto.ts b/src/user-notification/dtos/user-notification.dto.ts new file mode 100644 index 0000000..46b9d7f --- /dev/null +++ b/src/user-notification/dtos/user-notification.dto.ts @@ -0,0 +1,44 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, IsNotEmpty, IsString } from 'class-validator'; + +export class UserNotificationAddDto { + @ApiProperty({ + description: 'user uuid', + required: true, + }) + @IsString() + @IsNotEmpty() + userUuid: string; + + @ApiProperty({ + description: 'subscription uuid', + required: true, + }) + @IsString() + @IsNotEmpty() + subscriptionUuid: string; +} +export class UserNotificationUpdateDto { + @ApiProperty({ + description: 'user uuid', + required: true, + }) + @IsString() + @IsNotEmpty() + userUuid: string; + + @ApiProperty({ + description: 'subscription uuid', + required: true, + }) + @IsString() + @IsNotEmpty() + subscriptionUuid: string; + @ApiProperty({ + description: 'active', + required: true, + }) + @IsBoolean() + @IsNotEmpty() + active: boolean; +} diff --git a/src/user-notification/services/index.ts b/src/user-notification/services/index.ts new file mode 100644 index 0000000..73988b4 --- /dev/null +++ b/src/user-notification/services/index.ts @@ -0,0 +1 @@ +export * from './user-notification.service'; diff --git a/src/user-notification/services/user-notification.service.ts b/src/user-notification/services/user-notification.service.ts new file mode 100644 index 0000000..d8992c0 --- /dev/null +++ b/src/user-notification/services/user-notification.service.ts @@ -0,0 +1,83 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { + UserNotificationAddDto, + UserNotificationUpdateDto, +} from '../dtos/user-notification.dto'; +import { UserNotificationRepository } from '@app/common/modules/user-notification/repositories'; + +@Injectable() +export class UserNotificationService { + constructor( + private readonly userNotificationRepository: UserNotificationRepository, + ) {} + + async addUserSubscription(userNotificationAddDto: UserNotificationAddDto) { + try { + return await this.userNotificationRepository.save({ + user: { + uuid: userNotificationAddDto.userUuid, + }, + subscriptionUuid: userNotificationAddDto.subscriptionUuid, + }); + } catch (error) { + if (error.code === '23505') { + throw new HttpException( + 'This User already has this subscription uuid', + HttpStatus.BAD_REQUEST, + ); + } + throw new HttpException( + error.message || 'Internal Server Error', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async fetchUserSubscriptions(userUuid: string) { + try { + const userNotifications = await this.userNotificationRepository.find({ + where: { + user: { uuid: userUuid }, + active: true, + }, + }); + return { + userUuid, + subscriptionUuids: [ + ...userNotifications.map((sub) => sub.subscriptionUuid), + ], + }; + } catch (error) { + throw new HttpException( + 'User subscription not found', + HttpStatus.NOT_FOUND, + ); + } + } + async updateUserSubscription( + userNotificationUpdateDto: UserNotificationUpdateDto, + ) { + try { + const result = await this.userNotificationRepository.update( + { + user: { uuid: userNotificationUpdateDto.userUuid }, + subscriptionUuid: userNotificationUpdateDto.subscriptionUuid, + }, + { active: userNotificationUpdateDto.active }, + ); + + if (result.affected === 0) { + throw new HttpException( + 'Subscription uuid not found', + HttpStatus.NOT_FOUND, + ); + } + + return result; + } catch (error) { + throw new HttpException( + error.message || 'Internal Server Error', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/user-notification/user-notification.module.ts b/src/user-notification/user-notification.module.ts new file mode 100644 index 0000000..0b03df1 --- /dev/null +++ b/src/user-notification/user-notification.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { UserNotificationRepositoryModule } from '@app/common/modules/user-notification/user.notification.repository.module'; +import { UserNotificationRepository } from '@app/common/modules/user-notification/repositories'; +import { UserNotificationService } from 'src/user-notification/services'; +import { UserNotificationController } from 'src/user-notification/controllers'; + +@Module({ + imports: [ConfigModule, UserNotificationRepositoryModule], + controllers: [UserNotificationController], + providers: [UserNotificationRepository, UserNotificationService], + exports: [UserNotificationService], +}) +export class UserNotificationModule {}