Skip to content

Commit

Permalink
Merge pull request #1942 from bcgov/feature/ALCS-644-tags-adjustments
Browse files Browse the repository at this point in the history
ALCS-644 Tags/Categories seed and nullable categories
  • Loading branch information
fbarreta authored Oct 30, 2024
2 parents 1464808 + 6710852 commit 0f11b51
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ <h4>{{ isEdit ? 'Edit' : 'Create New' }} Tag</h4>
<div>
<ng-select
appearance="outline"
required
name="categoryId"
[items]="categories"
appendTo="body"
placeholder="Select Category"
bindLabel="name"
bindValue="uuid"
[clearable]="false"
[clearable]="true"
[(ngModel)]="categoryId"
>
<ng-template ng-option-tmp let-item="item" let-search="searchTerm">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class TagDialogComponent implements OnInit {
destroy = new Subject<void>();
uuid = '';
name = '';
category: TagCategoryDto = {
category: TagCategoryDto | undefined = {
uuid: '',
name: '',
};
Expand All @@ -37,8 +37,8 @@ export class TagDialogComponent implements OnInit {
if (data) {
this.uuid = data.uuid;
this.name = data.name;
this.category = data.category;
this.categoryId = data.category.uuid;
this.category = data.category ? data.category : undefined;
this.categoryId = data.category ? data.category.uuid : '';
this.isActive = data.isActive.toString();
}
this.isEdit = !!data;
Expand All @@ -60,14 +60,13 @@ export class TagDialogComponent implements OnInit {

async onSubmit() {
this.isLoading = true;

const dto: TagDto = {
uuid: this.uuid,
name: this.name,
category: {
category: this.categoryId && this.categoryId !== '' ? {
uuid: this.categoryId,
name: '',
},
} : undefined,
isActive: this.isActive === 'true',
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

<ng-container matColumnDef="category">
<th mat-header-cell *matHeaderCellDef>Category</th>
<td mat-cell *matCellDef="let row">{{ row.category.name }}</td>
<td mat-cell *matCellDef="let row">{{ row.category ? row.category.name : '' }}</td>
</ng-container>

<ng-container matColumnDef="isActive">
Expand Down
2 changes: 1 addition & 1 deletion alcs-frontend/src/app/services/tag/tag.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import { TagCategoryDto } from "./tag-category/tag-category.dto";
export interface TagDto {
uuid: string;
name: string;
category: TagCategoryDto;
category?: TagCategoryDto;
isActive: boolean;
}
17 changes: 14 additions & 3 deletions services/apps/alcs/src/alcs/tag/tag.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { initTagMockEntity } from '../../../test/mocks/mockEntities';
import { mockKeyCloakProviders } from '../../../test/mocks/mockTypes';
import { Tag } from './tag.entity';
import { TagDto } from './tag.dto';
import { UpdateResult } from 'typeorm';

describe('TagController', () => {
let controller: TagController;
Expand Down Expand Up @@ -46,9 +47,19 @@ describe('TagController', () => {
});

it('should create a tag', async () => {
const dto: TagDto = {
name: mockTag.name,
category: mockTag.category
? {
uuid: mockTag.category?.uuid,
name: mockTag.name,
}
: null,
isActive: mockTag.isActive,
};
tagService.create.mockResolvedValue(mockTag);

const result = await controller.create(mockTag);
const result = await controller.create(dto);
expect(tagService.create).toHaveBeenCalledTimes(1);
expect(result).toEqual(mockTag);
});
Expand All @@ -71,10 +82,10 @@ describe('TagController', () => {
});

it('should delete a tag', async () => {
tagService.delete.mockResolvedValue(mockTag);
tagService.delete.mockResolvedValue({} as UpdateResult);

const result = await controller.delete(mockTag.uuid);
expect(tagService.delete).toHaveBeenCalledTimes(1);
expect(result).toEqual(mockTag);
expect(result).toBeDefined();
});
});
5 changes: 3 additions & 2 deletions services/apps/alcs/src/alcs/tag/tag.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IsBoolean, IsObject, IsString } from 'class-validator';
import { IsBoolean, IsObject, IsOptional, IsString } from 'class-validator';
import { TagCategoryDto } from './tag-category/tag-category.dto';

export class TagDto {
Expand All @@ -9,5 +9,6 @@ export class TagDto {
isActive: boolean;

@IsObject()
category: TagCategoryDto;
@IsOptional()
category?: TagCategoryDto | null;
}
6 changes: 4 additions & 2 deletions services/apps/alcs/src/alcs/tag/tag.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export class Tag extends Base {
@Column({ default: true })
isActive: boolean;

@ManyToOne(() => TagCategory)
category: TagCategory;
@ManyToOne(() => TagCategory, {
nullable: true,
})
category?: TagCategory | null;
}
48 changes: 1 addition & 47 deletions services/apps/alcs/src/alcs/tag/tag.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@ import { Repository } from 'typeorm';
import { Tag } from './tag.entity';
import { createMock, DeepMocked } from '@golevelup/nestjs-testing';
import { TagCategory } from '../tag/tag-category/tag-category.entity';
import {
initTagCategoryMockEntity,
initTagMockEntity,
} from '../../../test/mocks/mockEntities';
import { initTagCategoryMockEntity, initTagMockEntity } from '../../../test/mocks/mockEntities';
import { AutomapperModule } from 'automapper-nestjs';
import { classes } from 'automapper-classes';
import { getRepositoryToken } from '@nestjs/typeorm';
import * as config from 'config';
import { TagDto } from './tag.dto';
import { ServiceValidationException } from '@app/common/exceptions/base.exception';

describe('TagCategoryService', () => {
let service: TagService;
Expand Down Expand Up @@ -82,29 +78,6 @@ describe('TagCategoryService', () => {
expect(tagRepositoryMock.save).toHaveBeenCalledWith(mockTagEntity);
});

it('should fail update if tag does not exist', async () => {
const payload: TagDto = {
name: mockTagEntity.name,
isActive: mockTagEntity.isActive,
category: mockTagCategoryEntity,
};

tagRepositoryMock.findOneOrFail.mockRejectedValue(
new ServiceValidationException(
`Tag for with ${mockTagEntity.uuid} not found`,
),
);

await expect(
service.update(mockTagEntity.uuid, payload),
).rejects.toMatchObject(
new ServiceValidationException(
`Tag for with ${mockTagEntity.uuid} not found`,
),
);
expect(tagRepositoryMock.save).toBeCalledTimes(0);
});

it('should call save when tag successfully create', async () => {
const payload: TagDto = {
name: mockTagEntity.name,
Expand All @@ -116,23 +89,4 @@ describe('TagCategoryService', () => {

expect(tagRepositoryMock.save).toBeCalledTimes(1);
});

it('should fail on create if Tag Category does not exist', async () => {
const payload: TagDto = {
name: mockTagEntity.name,
isActive: mockTagEntity.isActive,
category: {
uuid: 'fakeuuid',
name: '',
},
};

tagCategoryRepositoryMock.findOne.mockResolvedValue(null);

await expect(service.create(payload)).rejects.toMatchObject(
new ServiceValidationException('Provided category does not exist'),
);

expect(tagRepositoryMock.save).toBeCalledTimes(0);
});
});
41 changes: 17 additions & 24 deletions services/apps/alcs/src/alcs/tag/tag.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Tag } from './tag.entity';
import { TagDto } from './tag.dto';
import { TagCategory } from './tag-category/tag-category.entity';
import { ServiceValidationException } from '@app/common/exceptions/base.exception';

@Injectable()
export class TagService {
Expand Down Expand Up @@ -38,20 +37,18 @@ export class TagService {
}

async create(dto: TagDto) {
const category = await this.categoryRepository.findOne({
where: {
uuid: dto.category.uuid,
},
});

if (!category) {
throw new ServiceValidationException('Provided category does not exist');
}
const category = dto.category
? await this.categoryRepository.findOne({
where: {
uuid: dto.category.uuid,
},
})
: null;

const newTag = new Tag();
newTag.name = dto.name;
newTag.isActive = dto.isActive;
newTag.category = category;
newTag.category = category ? category : null;
return this.repository.save(newTag);
}

Expand All @@ -62,25 +59,21 @@ export class TagService {
}

async update(uuid: string, updateDto: TagDto) {
const category = await this.categoryRepository.findOne({
where: {
uuid: updateDto.category.uuid,
},
});

if (!category) {
throw new ServiceValidationException('Provided category does not exist');
}
const category = updateDto.category
? await this.categoryRepository.findOne({
where: {
uuid: updateDto.category.uuid,
},
})
: null;
const tag = await this.getOneOrFail(uuid);
tag.name = updateDto.name;
tag.isActive = updateDto.isActive;
tag.category = category;
tag.category = category ? category : null;
return await this.repository.save(tag);
}

async delete(uuid: string) {
const tag = await this.getOneOrFail(uuid);

return await this.repository.remove(tag);
return await this.repository.softDelete(uuid);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class NullableTagCategory1730239196619 implements MigrationInterface {

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "alcs"."tag" ALTER COLUMN "category_uuid" DROP NOT NULL`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
// N/A
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class SeedTagCategories1730244813395 implements MigrationInterface {

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
INSERT INTO "alcs"."tag_category" ("audit_deleted_date_at", "audit_created_at", "audit_updated_at", "audit_created_by", "audit_updated_by", "name") VALUES
(NULL, NOW(), NULL, 'migration_seed', NULL, 'Residential'),
(NULL, NOW(), NULL, 'migration_seed', NULL, 'Transportation'),
(NULL, NOW(), NULL, 'migration_seed', NULL, 'Meat'),
(NULL, NOW(), NULL, 'migration_seed', NULL, 'Energy Production'),
(NULL, NOW(), NULL, 'migration_seed', NULL, 'Berries'),
(NULL, NOW(), NULL, 'migration_seed', NULL, 'Alcohol'),
(NULL, NOW(), NULL, 'migration_seed', NULL, 'Utilities');
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
// N/A
}

}
Loading

0 comments on commit 0f11b51

Please sign in to comment.