Skip to content

Commit

Permalink
Merge pull request #24 from bcgov/ches-email
Browse files Browse the repository at this point in the history
EMSEDT-178 - Notifications
  • Loading branch information
mgtennant authored Aug 13, 2024
2 parents 60f7a77 + 5366003 commit b2cd646
Show file tree
Hide file tree
Showing 29 changed files with 1,172 additions and 239 deletions.
3 changes: 2 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"test:watch": "jest --watch",
"test:cov": "jest --coverage --detectOpenHandles --forceExit",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
"test:e2e": "jest --config ./test/jest-e2e.json",
"prisma-init": "npx prisma db pull && npx prisma generate"
},
"dependencies": {
"@nestjs/axios": "^3.0.2",
Expand Down
10 changes: 10 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ model file_submission {
update_utc_timestamp DateTime @db.Timestamp(6)
submission_status submission_status_code @relation(fields: [submission_status_code], references: [submission_status_code], onDelete: NoAction, onUpdate: NoAction, map: "submission_status_code_fk")
}

model notifications {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
email String @unique @db.VarChar(200)
enabled Boolean @default(true)
create_user_id String @db.VarChar(200)
create_utc_timestamp DateTime @db.Timestamp(6)
update_user_id String @db.VarChar(200)
update_utc_timestamp DateTime @db.Timestamp(6)
}
18 changes: 6 additions & 12 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import "dotenv/config";
import {
Logger,
MiddlewareConsumer,
Module,
RequestMethod,
} from "@nestjs/common";
import { Logger, MiddlewareConsumer, Module, RequestMethod } from "@nestjs/common";
import { HTTPLoggerMiddleware } from "./middleware/req.res.logger";
import { loggingMiddleware, PrismaModule } from "nestjs-prisma";
import { ConfigModule } from "@nestjs/config";
Expand All @@ -15,8 +10,9 @@ import { TerminusModule } from "@nestjs/terminus";
import { HealthController } from "./health.controller";
import { JWTAuthModule } from "./auth/jwtauth.module";
import { AdminModule } from "./admin/admin.module";
import { FileSubmissionsModule } from './file_submissions/file_submissions.module';
import { FileStatusCodesModule } from './file_status_codes/file_status_codes.module';
import { FileSubmissionsModule } from "./file_submissions/file_submissions.module";
import { FileStatusCodesModule } from "./file_status_codes/file_status_codes.module";
import { NotificationsModule } from "./notifications/notifications.module";

const DB_HOST = process.env.POSTGRES_HOST || "localhost";
const DB_USER = process.env.POSTGRES_USER || "postgres";
Expand Down Expand Up @@ -57,6 +53,7 @@ function getMiddlewares() {
JWTAuthModule,
AdminModule,
FileSubmissionsModule,
NotificationsModule,
FileStatusCodesModule,
],
controllers: [AppController, MetricsController, HealthController],
Expand All @@ -67,10 +64,7 @@ export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(HTTPLoggerMiddleware)
.exclude(
{ path: "metrics", method: RequestMethod.ALL },
{ path: "health", method: RequestMethod.ALL }
)
.exclude({ path: "metrics", method: RequestMethod.ALL }, { path: "health", method: RequestMethod.ALL })
.forRoutes("*");
}
}
11 changes: 11 additions & 0 deletions backend/src/notifications/dto/create-notification_entry.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { PickType } from "@nestjs/swagger";
import { NotificationDto } from "./notification.dto";

export class CreateNotificationEntryDto extends PickType(NotificationDto, [
"email",
"enabled",
"create_user_id",
"create_utc_timestamp",
"update_user_id",
"update_utc_timestamp",
] as const) {}
33 changes: 33 additions & 0 deletions backend/src/notifications/dto/notification.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ApiProperty } from "@nestjs/swagger";

export class NotificationDto {
@ApiProperty({
description: "Email address",
})
email: string;

@ApiProperty({
description: "Notifications enabled",
})
enabled: boolean;

@ApiProperty({
description: "The id of the user that created the record",
})
create_user_id: string;

@ApiProperty({
description: "When the user created the record",
})
create_utc_timestamp: Date;

@ApiProperty({
description: "The id of the user that last updated the record",
})
update_user_id: string;

@ApiProperty({
description: "When the user last updated the record",
})
update_utc_timestamp: Date;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PickType } from "@nestjs/swagger";
import { NotificationDto } from "./notification.dto";

export class UpdateNotificationEntryDto extends PickType(NotificationDto, [
"enabled",
"update_user_id",
"update_utc_timestamp",
] as const) {}
18 changes: 18 additions & 0 deletions backend/src/notifications/notifications.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from "@nestjs/testing";
import { NotificationsController } from "./notifications.controller";

describe("NotificationsController", () => {
let controller: NotificationsController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [NotificationsController],
}).compile();

controller = module.get<NotificationsController>(NotificationsController);
});

it("should be defined", () => {
expect(controller).toBeDefined();
});
});
73 changes: 73 additions & 0 deletions backend/src/notifications/notifications.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { validate as uuidValidate } from "uuid";
import {
BadRequestException,
Body,
Controller,
Get,
Param,
Post,
} from "@nestjs/common";
import { NotificationsService } from "./notifications.service";
import { Public } from "src/auth/decorators/public.decorator";

@Controller("notifications")
export class NotificationsController {
constructor(private readonly notificationsService: NotificationsService) {}

@Get()
getNotificationData() {
return this.notificationsService.getNotificationData();
}

// test route TODO: delete this
@Get("send-email/:email")
sendEmail(@Param("email") email: string) {
const re =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!re.test(String(email).toLowerCase())) {
email = "[email protected]";
}

const variables = {
file_name: "test_file.csv",
user_account_name: "MTENNANT",
file_status: "Failed",
errors: "Something went wrong.",
warnings: "",
};
return this.notificationsService.sendContactNotification(email, variables);
}

@Post("update-notification")
updateNotification(
@Body() userData: { email: string; username: string; enabled: boolean }
) {
return this.notificationsService.updateNotificationEntry(
userData.email,
userData.username,
userData.enabled
);
}

@Post("get-notification-status")
getNotificationStatus(@Body() userData: { email: string; username: string }) {
return this.notificationsService.getNotificationStatus(
userData.email,
userData.username
);
}

@Post("subscribe")
subscribe(@Body() data: { guid: string }) {
return this.notificationsService.subscribe(data.guid);
}

@Public()
@Post("unsubscribe")
unsubscribe(@Body() data: { guid: string }) {
if (!uuidValidate(data.guid)) {
throw new BadRequestException("Invalid UUID");
}
return this.notificationsService.unsubscribe(data.guid);
}
}
11 changes: 11 additions & 0 deletions backend/src/notifications/notifications.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from "@nestjs/common";
import { NotificationsController } from "./notifications.controller";
import { NotificationsService } from "./notifications.service";
import { HttpModule } from "@nestjs/axios";

@Module({
imports: [HttpModule],
controllers: [NotificationsController],
providers: [NotificationsService],
})
export class NotificationsModule {}
18 changes: 18 additions & 0 deletions backend/src/notifications/notifications.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from "@nestjs/testing";
import { NotificationsService } from "./notifications.service";

describe("NotificationsService", () => {
let service: NotificationsService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [NotificationsService],
}).compile();

service = module.get<NotificationsService>(NotificationsService);
});

it("should be defined", () => {
expect(service).toBeDefined();
});
});
Loading

0 comments on commit b2cd646

Please sign in to comment.