Skip to content

Commit c1bac6c

Browse files
authored
Merge pull request #627 from johanbook/delete-current-profile
feat(api): add endpoint to delete current profile
2 parents 52338eb + 9b8cdb9 commit c1bac6c

File tree

8 files changed

+118
-8
lines changed

8 files changed

+118
-8
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export class DeleteCurrentProfileCommand {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { EventBus } from "@nestjs/cqrs";
2+
import { Repository } from "typeorm";
3+
4+
import { UserIdService } from "src/core/authentication";
5+
import { createEventBusMock } from "src/test/mocks";
6+
import { createMockRepository } from "src/test/mocks/repository.mock";
7+
import { createUserIdServiceMock } from "src/test/mocks/user-id.service.mock";
8+
9+
import { CurrentProfileService } from "../../../domain/services/current-profile.service";
10+
import { ProfileService } from "../../../domain/services/profile.service";
11+
import { Profile } from "../../../infrastructure/entities/profile.entity";
12+
import { DeleteCurrentProfileHandler } from "./delete-current-profile.handler";
13+
14+
describe(DeleteCurrentProfileHandler.name, () => {
15+
let commandHandler: DeleteCurrentProfileHandler;
16+
let currentProfileService: CurrentProfileService;
17+
let eventBus: EventBus;
18+
let profiles: Repository<Profile>;
19+
let profileService: ProfileService;
20+
let userIdService: UserIdService;
21+
22+
beforeEach(() => {
23+
eventBus = createEventBusMock();
24+
profiles = createMockRepository<Profile>();
25+
userIdService = createUserIdServiceMock();
26+
27+
profileService = new ProfileService(eventBus, profiles);
28+
29+
currentProfileService = new CurrentProfileService(profiles, userIdService);
30+
31+
commandHandler = new DeleteCurrentProfileHandler(
32+
currentProfileService,
33+
profileService,
34+
);
35+
});
36+
37+
describe("execute", () => {
38+
it("should delete current profile", async () => {
39+
const profile = new Profile();
40+
profiles.save(profile);
41+
42+
expect(profiles.find()).not.toEqual([]);
43+
44+
await commandHandler.execute();
45+
46+
expect(profiles.find()).toEqual([]);
47+
expect(eventBus.publish).toHaveBeenCalledTimes(1);
48+
});
49+
});
50+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { CommandHandler, ICommandHandler } from "@nestjs/cqrs";
2+
3+
import { CurrentProfileService } from "../../../domain/services/current-profile.service";
4+
import { ProfileService } from "../../../domain/services/profile.service";
5+
import { DeleteCurrentProfileCommand } from "../../contracts/commands/delete-current-profile.command";
6+
7+
@CommandHandler(DeleteCurrentProfileCommand)
8+
export class DeleteCurrentProfileHandler
9+
implements ICommandHandler<DeleteCurrentProfileHandler, void>
10+
{
11+
constructor(
12+
private readonly currentProfileService: CurrentProfileService,
13+
private readonly profileService: ProfileService,
14+
) {}
15+
16+
async execute() {
17+
const currentProfile =
18+
await this.currentProfileService.fetchCurrentProfile();
19+
20+
await this.profileService.deleteProfile(currentProfile);
21+
}
22+
}

services/api/src/core/profiles/client/controllers/current-profile.controller.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
import { Body, Controller, Get, Patch, Post, Put, Query } from "@nestjs/common";
1+
import {
2+
Body,
3+
Controller,
4+
Delete,
5+
Get,
6+
Patch,
7+
Post,
8+
Put,
9+
Query,
10+
} from "@nestjs/common";
211
import { CommandBus, QueryBus } from "@nestjs/cqrs";
312
import { ApiTags } from "@nestjs/swagger";
413

514
import { Multipart } from "src/core/multipart";
615

716
import { CreateProfileCommand } from "../../application/contracts/commands/create-profile.command";
17+
import { DeleteCurrentProfileCommand } from "../../application/contracts/commands/delete-current-profile.command";
818
import { UpdateProfilePhotoCommand } from "../../application/contracts/commands/update-profile-photo.command";
919
import { UpdateProfileCommand } from "../../application/contracts/commands/update-profile.command";
1020
import { ProfileDetails } from "../../application/contracts/dtos/profile.dto";
@@ -23,6 +33,13 @@ export class CurrentProfileController {
2333
return await this.queryBus.execute(query);
2434
}
2535

36+
@Delete()
37+
async deleteCurrentProfile(
38+
@Query() query: DeleteCurrentProfileCommand,
39+
): Promise<null> {
40+
return await this.queryBus.execute(query);
41+
}
42+
2643
@Get("/exists")
2744
async checkIfProfileExists(
2845
@Query() query: CheckIfProfileExistsQuery,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export class ProfileDeletedEvent {
2+
public readonly id!: number;
3+
}

services/api/src/core/profiles/domain/services/profile.service.ts

+11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { map } from "src/core/mapper";
77

88
import { Profile } from "../../infrastructure/entities/profile.entity";
99
import { ProfileCreatedEvent } from "../events/profile-created.event";
10+
import { ProfileDeletedEvent } from "../events/profile-deleted.event";
1011
import { ProfileUpdatedEvent } from "../events/profile-updated.event";
1112

1213
@Injectable()
@@ -29,6 +30,16 @@ export class ProfileService {
2930
this.eventBus.publish(event);
3031
}
3132

33+
async deleteProfile(profile: { id: number }): Promise<void> {
34+
await this.profiles.delete(profile.id);
35+
36+
const event = map(ProfileDeletedEvent, {
37+
id: profile.id,
38+
});
39+
40+
this.eventBus.publish(event);
41+
}
42+
3243
async updateProfile(profile: Profile): Promise<void> {
3344
const updatedProfile = await this.profiles.save(profile);
3445

services/api/src/core/profiles/profile.module.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { OrganizationModule } from "src/core/organizations/organization.module";
88
import { PhotosModule } from "src/core/photos/photos.module";
99

1010
import { CreateProfileHandler } from "./application/handlers/command-handlers/create-profile.handler";
11+
import { DeleteCurrentProfileHandler } from "./application/handlers/command-handlers/delete-current-profile.handler";
1112
import { UpdateProfilePhotoHandler } from "./application/handlers/command-handlers/update-profile-photo.handler";
1213
import { UpdateProfileHandler } from "./application/handlers/command-handlers/update-profile.handler";
1314
import { CheckIfProfileExistsHandler } from "./application/handlers/query-handlers/check-if-profile-exists.handler";
@@ -35,6 +36,7 @@ import { Profile } from "./infrastructure/entities/profile.entity";
3536
CurrentProfileService,
3637
CheckIfProfileExistsHandler,
3738
CreateProfileHandler,
39+
DeleteCurrentProfileHandler,
3840
GetCurrentProfileHandler,
3941
GetProfileHandler,
4042
ProfileService,

services/api/src/test/mocks/repository.mock.ts

+11-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ class MockRepository<T extends ObjectLiteral> {
77

88
constructor(private data: T[] = []) {}
99

10+
delete = jest.fn((id: string | number) => {
11+
const index = this.data.findIndex((item) => item.id === id);
12+
13+
if (index < 0) {
14+
throw new Error("Item not found");
15+
}
16+
17+
this.data.splice(index, 1);
18+
});
19+
1020
exist = jest.fn((element: T) =>
1121
this.data.find((item) => item.id === element.where.id),
1222
);
@@ -18,13 +28,7 @@ class MockRepository<T extends ObjectLiteral> {
1828
findOne = jest.fn(() => this.data[0]);
1929

2030
remove = jest.fn((element: T) => {
21-
const index = this.data.findIndex((item) => item.id === element.id);
22-
23-
if (index < 0) {
24-
throw new Error("Item not found");
25-
}
26-
27-
this.data.splice(index, 1);
31+
this.delete(element.id);
2832
});
2933

3034
save = jest.fn((element: T) => {

0 commit comments

Comments
 (0)