Skip to content

Commit e270efb

Browse files
authored
Migrate handlers ProjectUpdatedEvent -> ProjectTransitionedEvent (#3254)
1 parent 1781654 commit e270efb

File tree

10 files changed

+113
-119
lines changed

10 files changed

+113
-119
lines changed
Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,51 @@
11
import { EventsHandler, IEventHandler } from '~/core';
22
import { ProjectStatus, stepToStatus } from '../../project/dto';
3-
import { ProjectUpdatedEvent } from '../../project/events';
3+
import { ProjectTransitionedEvent } from '../../project/workflow/events/project-transitioned.event';
44
import { BudgetService } from '../budget.service';
55
import { BudgetStatus } from '../dto';
66

7-
@EventsHandler(ProjectUpdatedEvent)
7+
@EventsHandler(ProjectTransitionedEvent)
88
export class UpdateProjectBudgetStatusHandler
9-
implements IEventHandler<ProjectUpdatedEvent>
9+
implements IEventHandler<ProjectTransitionedEvent>
1010
{
1111
constructor(private readonly budgets: BudgetService) {}
1212

13-
async handle({ previous, updates, session }: ProjectUpdatedEvent) {
14-
// Continue if project just became active
15-
if (!updates.step) {
16-
return;
17-
}
18-
let budgetStatus: BudgetStatus = BudgetStatus.Current;
13+
async handle(event: ProjectTransitionedEvent) {
14+
const { project, session } = event;
15+
16+
const prevStatus = stepToStatus(event.previousStep);
17+
const nextStatus = stepToStatus(event.workflowEvent.to);
18+
19+
let change: [from: BudgetStatus, to: BudgetStatus] | undefined;
1920
if (
20-
stepToStatus(updates.step) !== ProjectStatus.Active ||
21-
previous.status === ProjectStatus.Active
21+
prevStatus === ProjectStatus.InDevelopment &&
22+
nextStatus === ProjectStatus.Active
2223
) {
23-
// If Project status became In Dev from Active
24-
if (
25-
previous.status === ProjectStatus.Active &&
26-
stepToStatus(updates.step) === ProjectStatus.InDevelopment
27-
) {
28-
budgetStatus = BudgetStatus.Pending;
29-
} else {
30-
return;
31-
}
24+
change = [BudgetStatus.Pending, BudgetStatus.Current];
25+
} else if (
26+
prevStatus === ProjectStatus.Active &&
27+
nextStatus === ProjectStatus.InDevelopment
28+
) {
29+
change = [BudgetStatus.Current, BudgetStatus.Pending];
30+
}
31+
if (!change) {
32+
return;
3233
}
3334

3435
const budgets = await this.budgets.list(
3536
{
3637
filter: {
37-
projectId: previous.id,
38+
projectId: project.id,
3839
},
3940
},
4041
session,
4142
);
4243

43-
const budget = budgets.items.find(
44-
(b) =>
45-
b.status ===
46-
(budgetStatus === BudgetStatus.Current
47-
? BudgetStatus.Pending
48-
: BudgetStatus.Current),
49-
);
44+
const budget = budgets.items.find((b) => b.status === change![0]);
5045
if (!budget) {
51-
// no pending or current budget, nothing to do
5246
return;
5347
}
5448

55-
// Set pending budget to current, now that the project is active
56-
await this.budgets.update({ id: budget.id, status: budgetStatus }, session);
49+
await this.budgets.update({ id: budget.id, status: change[1] }, session);
5750
}
5851
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export * from './set-initial-end-date.handler';
2-
export * from './update-project-status.handler';
2+
export * from './update-engagement-status.handler';
33
export * from './set-last-status-date.handler';
44
export * from './apply-finalized-changeset-to-engagement.handler';

src/components/engagement/handlers/update-project-status.handler.ts renamed to src/components/engagement/handlers/update-engagement-status.handler.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import {
66
ProjectStatus,
77
ProjectStep,
88
ProjectType,
9+
stepToStatus,
910
} from '../../project/dto';
10-
import { ProjectUpdatedEvent } from '../../project/events';
11+
import { ProjectTransitionedEvent } from '../../project/workflow/events/project-transitioned.event';
1112
import { EngagementStatus } from '../dto';
1213
import { EngagementRepository } from '../engagement.repository';
1314
import { EngagementService } from '../engagement.service';
@@ -72,39 +73,53 @@ type Condition = MergeExclusive<
7273
{ step: ProjectStep }
7374
>;
7475

75-
const changeMatcher =
76-
(previous: UnsecuredDto<Project>, updated: UnsecuredDto<Project>) =>
77-
({ from, to }: Change) => {
76+
type ProjectState = Pick<UnsecuredDto<Project>, 'status' | 'step'>;
77+
const changeMatcher = (previousStep: ProjectStep, updatedStep: ProjectStep) => {
78+
const previous: ProjectState = {
79+
step: previousStep,
80+
status: stepToStatus(previousStep),
81+
};
82+
const updated: ProjectState = {
83+
step: updatedStep,
84+
status: stepToStatus(updatedStep),
85+
};
86+
return ({ from, to }: Change) => {
7887
const toMatches = to ? matches(to, updated) : !matches(from!, updated);
7988
const fromMatches = from
8089
? matches(from, previous)
8190
: !matches(to!, previous);
8291
return toMatches && fromMatches;
8392
};
84-
const matches = (cond: Condition, p: UnsecuredDto<Project>) =>
93+
};
94+
const matches = (cond: Condition, p: ProjectState) =>
8595
cond.step ? cond.step === p.step : cond.status === p.status;
8696

87-
@EventsHandler(ProjectUpdatedEvent)
88-
export class UpdateProjectStatusHandler
89-
implements IEventHandler<ProjectUpdatedEvent>
97+
@EventsHandler(ProjectTransitionedEvent)
98+
export class UpdateEngagementStatusHandler
99+
implements IEventHandler<ProjectTransitionedEvent>
90100
{
91101
constructor(
92102
private readonly repo: EngagementRepository,
93103
private readonly engagementService: EngagementService,
94104
) {}
95105

96-
async handle({ previous, updated, session }: ProjectUpdatedEvent) {
106+
async handle({
107+
project,
108+
previousStep,
109+
workflowEvent,
110+
session,
111+
}: ProjectTransitionedEvent) {
97112
const engagementStatus = changes.find(
98-
changeMatcher(previous, updated),
113+
changeMatcher(previousStep, workflowEvent.to),
99114
)?.newStatus;
100115
if (!engagementStatus) return;
101116

102-
const engagementIds = await this.repo.getOngoingEngagementIds(updated.id);
117+
const engagementIds = await this.repo.getOngoingEngagementIds(project.id);
103118

104119
await this.updateEngagements(
105120
engagementStatus,
106121
engagementIds,
107-
updated.type,
122+
project.type,
108123
session,
109124
);
110125
}
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
export * from './set-department-id.handler';
22
export * from './set-initial-mou-end.handler';
3-
export * from './set-step-changed-at.handler';
43
export * from './apply-finalized-changeset-to-project.handler';

src/components/project/handlers/set-department-id.handler.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import { ClientException, ID, ServerException, UnsecuredDto } from '~/common';
22
import { ConfigService, EventsHandler, IEventHandler } from '~/core';
33
import { DatabaseService } from '~/core/database';
4-
import { Project, ProjectStep, ProjectType } from '../dto';
5-
import { ProjectUpdatedEvent } from '../events';
4+
import {
5+
Project,
6+
ProjectStatus,
7+
ProjectStep,
8+
ProjectType,
9+
stepToStatus,
10+
} from '../dto';
11+
import { ProjectTransitionedEvent } from '../workflow/events/project-transitioned.event';
612

7-
type SubscribedEvent = ProjectUpdatedEvent;
13+
type SubscribedEvent = ProjectTransitionedEvent;
814

9-
@EventsHandler(ProjectUpdatedEvent)
15+
@EventsHandler(ProjectTransitionedEvent)
1016
export class SetDepartmentId implements IEventHandler<SubscribedEvent> {
1117
constructor(
1218
private readonly db: DatabaseService,
@@ -18,23 +24,33 @@ export class SetDepartmentId implements IEventHandler<SubscribedEvent> {
1824
return;
1925
}
2026

27+
const step = event.workflowEvent.to;
28+
const status = stepToStatus(step);
29+
2130
const shouldSetDepartmentId =
22-
event.updates.step === ProjectStep.PendingFinanceConfirmation &&
23-
!event.updated.departmentId;
31+
!event.project.departmentId &&
32+
ProjectStatus.entries.findIndex((s) => s.value === status) <=
33+
ProjectStatus.entries.findIndex(
34+
(s) => s.value === ProjectStatus.Active,
35+
) &&
36+
ProjectStep.entries.findIndex((s) => s.value === step) >=
37+
ProjectStep.entries.findIndex(
38+
(s) => s.value === ProjectStep.PendingFinanceConfirmation,
39+
);
2440
if (!shouldSetDepartmentId) {
2541
return;
2642
}
2743

28-
if (!event.updated.primaryLocation) {
44+
if (!event.project.primaryLocation) {
2945
throw new ClientException('Primary Location on project must be set');
3046
}
3147

3248
try {
3349
const departmentId = await this.assignDepartmentIdForProject(
34-
event.updated,
50+
event.project,
3551
);
36-
event.updated = {
37-
...event.updated,
52+
event.project = {
53+
...event.project,
3854
departmentId,
3955
};
4056
} catch (exception) {

src/components/project/handlers/set-initial-mou-end.handler.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import { ServerException } from '~/common';
22
import { EventsHandler, IEventHandler, ILogger, Logger } from '~/core';
33
import { DatabaseService } from '~/core/database';
44
import { IProject, ProjectStatus } from '../dto';
5-
import { ProjectCreatedEvent, ProjectUpdatedEvent } from '../events';
5+
import { ProjectCreatedEvent } from '../events';
6+
import { ProjectTransitionedEvent } from '../workflow/events/project-transitioned.event';
67

7-
type SubscribedEvent = ProjectCreatedEvent | ProjectUpdatedEvent;
8+
type SubscribedEvent = ProjectCreatedEvent | ProjectTransitionedEvent;
89

9-
@EventsHandler(ProjectCreatedEvent, ProjectUpdatedEvent)
10+
@EventsHandler(ProjectCreatedEvent, ProjectTransitionedEvent)
1011
export class SetInitialMouEnd implements IEventHandler<SubscribedEvent> {
1112
constructor(
1213
private readonly db: DatabaseService,
@@ -19,10 +20,10 @@ export class SetInitialMouEnd implements IEventHandler<SubscribedEvent> {
1920
event: event.constructor.name,
2021
});
2122

22-
const project = 'project' in event ? event.project : event.updated;
23+
const { project } = event;
2324

2425
if (
25-
event instanceof ProjectUpdatedEvent && // allow setting initial if creating with non-in-dev status
26+
event instanceof ProjectTransitionedEvent && // allow setting initial if creating with non-in-dev status
2627
project.status !== ProjectStatus.InDevelopment
2728
) {
2829
return;
@@ -40,8 +41,8 @@ export class SetInitialMouEnd implements IEventHandler<SubscribedEvent> {
4041
},
4142
});
4243

43-
if (event instanceof ProjectUpdatedEvent) {
44-
event.updated = updatedProject;
44+
if (event instanceof ProjectTransitionedEvent) {
45+
event.project = updatedProject;
4546
} else {
4647
event.project = updatedProject;
4748
}

src/components/project/handlers/set-step-changed-at.handler.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import type { UnsecuredDto } from '~/common';
1+
import type { Session, UnsecuredDto } from '~/common';
22
import type { Project, ProjectStep } from '../../dto';
33
import type { ProjectWorkflowEvent as WorkflowEvent } from '../dto';
44
import type { ProjectWorkflow } from '../project-workflow';
55

66
export class ProjectTransitionedEvent {
77
constructor(
8-
readonly project: Project,
8+
public project: UnsecuredDto<Project>,
99
readonly previousStep: ProjectStep,
1010
readonly next: (typeof ProjectWorkflow)['resolvedTransition'] | ProjectStep,
1111
readonly workflowEvent: UnsecuredDto<WorkflowEvent>,
12+
readonly session: Session,
1213
) {}
1314
}

0 commit comments

Comments
 (0)