-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1946 from bcgov/feature/ALCS-2345-tags-on-nois-ba…
…ckend ALCS-2345 Backend implementation
- Loading branch information
Showing
10 changed files
with
232 additions
and
20 deletions.
There are no files selected for viewing
31 changes: 31 additions & 0 deletions
31
...cs/src/alcs/notice-of-intent/notice-of-intent-tag/notice-of-intent-tag.controller.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { NoticeOfIntentTagController } from './notice-of-intent-tag.controller'; | ||
import { NoticeOfIntentTagService } from './notice-of-intent-tag.service'; | ||
import { DeepMocked } from '@golevelup/nestjs-testing'; | ||
import { ClsService } from 'nestjs-cls'; | ||
import { mockKeyCloakProviders } from '../../../../test/mocks/mockTypes'; | ||
|
||
describe('NoticeOfIntentTagController', () => { | ||
let controller: NoticeOfIntentTagController; | ||
let tagService: DeepMocked<NoticeOfIntentTagService>; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [NoticeOfIntentTagController], | ||
providers: [ | ||
{ provide: NoticeOfIntentTagService, useValue: tagService }, | ||
{ | ||
provide: ClsService, | ||
useValue: {}, | ||
}, | ||
...mockKeyCloakProviders, | ||
], | ||
}).compile(); | ||
|
||
controller = module.get<NoticeOfIntentTagController>(NoticeOfIntentTagController); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(controller).toBeDefined(); | ||
}); | ||
}); |
33 changes: 33 additions & 0 deletions
33
...ps/alcs/src/alcs/notice-of-intent/notice-of-intent-tag/notice-of-intent-tag.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Body, Controller, Delete, Get, Param, Post, UseGuards } from '@nestjs/common'; | ||
import { ApiOAuth2 } from '@nestjs/swagger'; | ||
import { RolesGuard } from '../../../common/authorization/roles-guard.service'; | ||
import * as config from 'config'; | ||
import { UserRoles } from '../../../common/authorization/roles.decorator'; | ||
import { ROLES_ALLOWED_APPLICATIONS } from '../../../common/authorization/roles'; | ||
import { NoticeOfIntentTagService } from './notice-of-intent-tag.service'; | ||
import { NoticeOfIntentTagDto } from './notice-of-intent-tag.dto'; | ||
|
||
@Controller('notice-of-intent/:fileNumber/tag') | ||
@ApiOAuth2(config.get<string[]>('KEYCLOAK.SCOPES')) | ||
@UseGuards(RolesGuard) | ||
export class NoticeOfIntentTagController { | ||
constructor(private service: NoticeOfIntentTagService) {} | ||
|
||
@Get('') | ||
@UserRoles(...ROLES_ALLOWED_APPLICATIONS) | ||
async getApplicationTags(@Param('fileNumber') fileNumber: string) { | ||
return await this.service.getNoticeOfIntentTags(fileNumber); | ||
} | ||
|
||
@Post('') | ||
@UserRoles(...ROLES_ALLOWED_APPLICATIONS) | ||
async addTagToApplication(@Param('fileNumber') fileNumber: string, @Body() dto: NoticeOfIntentTagDto) { | ||
return await this.service.addTagToNoticeOfIntent(fileNumber, dto.tagName); | ||
} | ||
|
||
@Delete('/:tagName') | ||
@UserRoles(...ROLES_ALLOWED_APPLICATIONS) | ||
async removeTagFromApplication(@Param('fileNumber') fileNumber: string, @Param('tagName') tagName: string) { | ||
return await this.service.removeTagFromNoticeOfIntent(fileNumber, tagName); | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
...ices/apps/alcs/src/alcs/notice-of-intent/notice-of-intent-tag/notice-of-intent-tag.dto.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { IsString } from 'class-validator'; | ||
|
||
export class NoticeOfIntentTagDto { | ||
@IsString() | ||
tagName: string; | ||
} |
36 changes: 36 additions & 0 deletions
36
.../alcs/src/alcs/notice-of-intent/notice-of-intent-tag/notice-of-intent-tag.service.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { NoticeOfIntentTagService } from './notice-of-intent-tag.service'; | ||
import { getRepositoryToken } from '@nestjs/typeorm'; | ||
import { NoticeOfIntent } from '../notice-of-intent.entity'; | ||
import { createMock, DeepMocked } from '@golevelup/nestjs-testing'; | ||
import { Repository } from 'typeorm'; | ||
import { Tag } from '../../tag/tag.entity'; | ||
|
||
describe('NoticeOfIntentTagService', () => { | ||
let service: NoticeOfIntentTagService; | ||
let noiRepository: DeepMocked<Repository<NoticeOfIntent>>; | ||
let tagRepository: DeepMocked<Repository<Tag>>; | ||
|
||
beforeEach(async () => { | ||
noiRepository = createMock(); | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
NoticeOfIntentTagService, | ||
{ | ||
provide: getRepositoryToken(NoticeOfIntent), | ||
useValue: noiRepository, | ||
}, | ||
{ | ||
provide: getRepositoryToken(Tag), | ||
useValue: tagRepository, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
service = module.get<NoticeOfIntentTagService>(NoticeOfIntentTagService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
}); |
81 changes: 81 additions & 0 deletions
81
.../apps/alcs/src/alcs/notice-of-intent/notice-of-intent-tag/notice-of-intent-tag.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
import { Tag } from '../../tag/tag.entity'; | ||
import { NoticeOfIntent } from '../notice-of-intent.entity'; | ||
import { Repository } from 'typeorm'; | ||
import { ServiceNotFoundException, ServiceValidationException } from '@app/common/exceptions/base.exception'; | ||
|
||
@Injectable() | ||
export class NoticeOfIntentTagService { | ||
constructor( | ||
@InjectRepository(Tag) private tagRepository: Repository<Tag>, | ||
@InjectRepository(NoticeOfIntent) private noiRepository: Repository<NoticeOfIntent>, | ||
) {} | ||
|
||
async addTagToNoticeOfIntent(fileNumber: string, tagName: string) { | ||
const noi = await this.noiRepository.findOne({ | ||
where: { fileNumber: fileNumber }, | ||
relations: ['tags'], | ||
}); | ||
if (!noi) { | ||
throw new ServiceNotFoundException(`Notice of Intent not found with number ${fileNumber}`); | ||
} | ||
|
||
const tag = await this.tagRepository.findOne({ where: { name: tagName } }); | ||
if (!tag) { | ||
throw new ServiceNotFoundException(`Tag not found with name ${tagName}`); | ||
} | ||
|
||
if (!noi.tags) { | ||
noi.tags = []; | ||
} | ||
|
||
const tagExists = noi.tags.some((t) => t.uuid === tag.uuid); | ||
console.log(tagExists); | ||
if (tagExists) { | ||
throw new ServiceValidationException(`Tag ${tagName} already exists`); | ||
} | ||
|
||
noi.tags.push(tag); | ||
return this.noiRepository.save(noi); | ||
} | ||
|
||
async removeTagFromNoticeOfIntent(fileNumber: string, tagName: string) { | ||
const noi = await this.noiRepository.findOne({ | ||
where: { fileNumber: fileNumber }, | ||
relations: ['tags'], | ||
}); | ||
if (!noi) { | ||
throw new ServiceNotFoundException(`Notice of Intent not found with number ${fileNumber}`); | ||
} | ||
|
||
const tag = await this.tagRepository.findOne({ where: { name: tagName } }); | ||
if (!tag) { | ||
throw new ServiceNotFoundException(`Tag not found with name ${tagName}`); | ||
} | ||
|
||
if (!noi.tags) { | ||
noi.tags = []; | ||
} | ||
|
||
const tagExists = noi.tags.some((t) => t.uuid === tag.uuid); | ||
if (!tagExists) { | ||
throw new ServiceValidationException(`Tag ${tagName} does not exist`); | ||
} | ||
|
||
noi.tags = noi.tags.filter((t) => t.uuid !== tag.uuid); | ||
return this.noiRepository.save(noi); | ||
} | ||
|
||
async getNoticeOfIntentTags(fileNumber: string) { | ||
const noi = await this.noiRepository.findOne({ | ||
where: { fileNumber: fileNumber }, | ||
relations: ['tags'], | ||
order: { auditCreatedAt: 'ASC' }, | ||
}); | ||
if (!noi) { | ||
throw new ServiceNotFoundException(`Notice of Intent not found with number ${fileNumber}`); | ||
} | ||
return noi.tags && noi.tags.length > 0 ? noi.tags : []; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
services/apps/alcs/src/providers/typeorm/migrations/1730326975258-add_tags_to_noi.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { MigrationInterface, QueryRunner } from "typeorm"; | ||
|
||
export class AddTagsToNoi1730326975258 implements MigrationInterface { | ||
name = 'AddTagsToNoi1730326975258' | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`CREATE TABLE "alcs"."notice_of_intent_tag" ("notice_of_intent_uuid" uuid NOT NULL, "tag_uuid" uuid NOT NULL, CONSTRAINT "PK_8ae82272ffcbd27427172fd5e11" PRIMARY KEY ("notice_of_intent_uuid", "tag_uuid"))`); | ||
await queryRunner.query(`CREATE INDEX "IDX_2baab887c8e66032ba78750b91" ON "alcs"."notice_of_intent_tag" ("notice_of_intent_uuid") `); | ||
await queryRunner.query(`CREATE INDEX "IDX_404540b8fc70a267572f0d506a" ON "alcs"."notice_of_intent_tag" ("tag_uuid") `); | ||
await queryRunner.query(`ALTER TABLE "alcs"."notice_of_intent_tag" ADD CONSTRAINT "FK_2baab887c8e66032ba78750b912" FOREIGN KEY ("notice_of_intent_uuid") REFERENCES "alcs"."notice_of_intent"("uuid") ON DELETE CASCADE ON UPDATE CASCADE`); | ||
await queryRunner.query(`ALTER TABLE "alcs"."notice_of_intent_tag" ADD CONSTRAINT "FK_404540b8fc70a267572f0d506aa" FOREIGN KEY ("tag_uuid") REFERENCES "alcs"."tag"("uuid") ON DELETE NO ACTION ON UPDATE NO ACTION`); | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`ALTER TABLE "alcs"."notice_of_intent_tag" DROP CONSTRAINT "FK_404540b8fc70a267572f0d506aa"`); | ||
await queryRunner.query(`ALTER TABLE "alcs"."notice_of_intent_tag" DROP CONSTRAINT "FK_2baab887c8e66032ba78750b912"`); | ||
await queryRunner.query(`DROP INDEX "alcs"."IDX_404540b8fc70a267572f0d506a"`); | ||
await queryRunner.query(`DROP INDEX "alcs"."IDX_2baab887c8e66032ba78750b91"`); | ||
await queryRunner.query(`DROP TABLE "alcs"."notice_of_intent_tag"`); | ||
} | ||
|
||
} |