From bb30032f2e455252d5d7eba19bfc8e15ca9401aa Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Tue, 13 Aug 2024 10:25:42 +0200 Subject: [PATCH] feat: revive project (#7847) --- .../project/project-service.e2e.test.ts | 24 +++++++++++++++++++ src/lib/features/project/project-service.ts | 12 ++++++++++ .../features/project/project-store-type.ts | 1 + src/lib/features/project/project-store.ts | 4 ++++ src/lib/types/events.ts | 14 +++++++++++ src/test/fixtures/fake-project-store.ts | 6 +++++ 6 files changed, 61 insertions(+) diff --git a/src/lib/features/project/project-service.e2e.test.ts b/src/lib/features/project/project-service.e2e.test.ts index 1388d26e27..723f4dd83a 100644 --- a/src/lib/features/project/project-service.e2e.test.ts +++ b/src/lib/features/project/project-service.e2e.test.ts @@ -308,6 +308,30 @@ test('should archive project', async () => { expect(projects.length).not.toBe(0); }); +test('should revive project', async () => { + const project = { + id: 'test-revive', + name: 'New project', + description: 'Blah', + mode: 'open' as const, + defaultStickiness: 'default', + }; + + await projectService.createProject(project, user, TEST_AUDIT_USER); + await projectService.archiveProject(project.id, TEST_AUDIT_USER); + await projectService.reviveProject(project.id, TEST_AUDIT_USER); + + const events = await stores.eventStore.getEvents(); + + expect(events[0]).toMatchObject({ + type: 'project-revived', + createdBy: TEST_AUDIT_USER.username, + }); + + const projects = await projectService.getProjects(); + expect(projects.find((p) => p.id === project.id)).toMatchObject(project); +}); + test('should not be able to archive project with flags', async () => { const project = { id: 'test-archive-with-flags', diff --git a/src/lib/features/project/project-service.ts b/src/lib/features/project/project-service.ts index ec58fd372e..da898daed4 100644 --- a/src/lib/features/project/project-service.ts +++ b/src/lib/features/project/project-service.ts @@ -47,6 +47,7 @@ import { ProjectGroupAddedEvent, ProjectGroupRemovedEvent, ProjectGroupUpdateRoleEvent, + ProjectRevivedEvent, ProjectUpdatedEvent, ProjectUserAddedEvent, ProjectUserRemovedEvent, @@ -625,6 +626,17 @@ export default class ProjectService { ); } + async reviveProject(id: string, auditUser: IAuditUser): Promise { + await this.projectStore.revive(id); + + await this.eventService.storeEvent( + new ProjectRevivedEvent({ + project: id, + auditUser, + }), + ); + } + async validateId(id: string): Promise { await nameType.validateAsync(id); await this.validateUniqueId(id); diff --git a/src/lib/features/project/project-store-type.ts b/src/lib/features/project/project-store-type.ts index e89257da6d..f58c51ff1d 100644 --- a/src/lib/features/project/project-store-type.ts +++ b/src/lib/features/project/project-store-type.ts @@ -138,4 +138,5 @@ export interface IProjectStore extends Store { ): Promise; archive(projectId: string): Promise; + revive(projectId: string): Promise; } diff --git a/src/lib/features/project/project-store.ts b/src/lib/features/project/project-store.ts index 45d8e56741..3f33e55410 100644 --- a/src/lib/features/project/project-store.ts +++ b/src/lib/features/project/project-store.ts @@ -420,6 +420,10 @@ class ProjectStore implements IProjectStore { await this.db(TABLE).where({ id }).update({ archived_at: now }); } + async revive(id: string): Promise { + await this.db(TABLE).where({ id }).update({ archived_at: null }); + } + async getProjectLinksForEnvironments( environments: string[], ): Promise { diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts index bfbc7ac532..bdd74a72b3 100644 --- a/src/lib/types/events.ts +++ b/src/lib/types/events.ts @@ -82,6 +82,7 @@ export const PROJECT_CREATED = 'project-created' as const; export const PROJECT_UPDATED = 'project-updated' as const; export const PROJECT_DELETED = 'project-deleted' as const; export const PROJECT_ARCHIVED = 'project-archived' as const; +export const PROJECT_REVIVED = 'project-revived' as const; export const PROJECT_IMPORT = 'project-import' as const; export const PROJECT_USER_ADDED = 'project-user-added' as const; export const PROJECT_USER_REMOVED = 'project-user-removed' as const; @@ -251,6 +252,7 @@ export const IEventTypes = [ PROJECT_UPDATED, PROJECT_DELETED, PROJECT_ARCHIVED, + PROJECT_REVIVED, PROJECT_IMPORT, PROJECT_USER_ADDED, PROJECT_USER_REMOVED, @@ -593,6 +595,18 @@ export class ProjectArchivedEvent extends BaseEvent { } } +export class ProjectRevivedEvent extends BaseEvent { + readonly project: string; + + constructor(eventData: { + project: string; + auditUser: IAuditUser; + }) { + super(PROJECT_REVIVED, eventData.auditUser); + this.project = eventData.project; + } +} + export class RoleUpdatedEvent extends BaseEvent { readonly data: any; readonly preData: any; diff --git a/src/test/fixtures/fake-project-store.ts b/src/test/fixtures/fake-project-store.ts index b0da4b6f07..160a8a69bf 100644 --- a/src/test/fixtures/fake-project-store.ts +++ b/src/test/fixtures/fake-project-store.ts @@ -234,4 +234,10 @@ export default class FakeProjectStore implements IProjectStore { : project, ); } + + async revive(id: string): Promise { + this.projects = this.projects.map((project) => + project.id === id ? { ...project, archivedAt: null } : project, + ); + } }