Skip to content

Commit 709e297

Browse files
committed
wip
1 parent 7f5fadd commit 709e297

8 files changed

Lines changed: 191 additions & 8 deletions

File tree

packages/api-workflows/src/domain/notification/abstractions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ export const NotificationAdapter = createAbstraction<INotificationAdapter>(
170170

171171
export namespace NotificationAdapter {
172172
export type Interface = INotificationAdapter;
173-
export type SendParams = INotificationAdapterSendParams;
173+
export type Params = INotificationAdapterSendParams;
174+
export type User = INotificationAdapterUser;
174175
}
175176

176177
export namespace Notification {

packages/api-workflows/src/features/notification/NotificationMailAdapter/NotificationMailAdapter.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { MailerService } from "@webiny/api-mailer";
2-
import {
3-
NotificationAdapter,
4-
NotificationMessageBodyConverter
5-
} from "~/domain/notification/abstractions.js";
2+
import type { NotificationMessageBodyConverter } from "~/domain/notification/abstractions.js";
3+
import { NotificationAdapter } from "~/domain/notification/abstractions.js";
64

75
class NotificationMailAdapterImpl implements NotificationAdapter.Interface {
86
public readonly id = "e-mail";
@@ -18,7 +16,7 @@ class NotificationMailAdapterImpl implements NotificationAdapter.Interface {
1816
return null;
1917
}
2018

21-
public async send(params: NotificationAdapter.SendParams): Promise<void> {
19+
public async send(params: NotificationAdapter.Params): Promise<void> {
2220
const { users, message } = params;
2321

2422
const bcc = users
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { IWorkflow } from "~/domain/workflow/abstractions.js";
2+
import { GetWorkflow as GetWorkflowAbstraction } from "./abstractions.js";
3+
import { GetWorkflowRepository } from "~/features/workflow/GetWorkflow/index.js";
4+
import { Logger } from "@webiny/api-core/features/logger/index.js";
5+
6+
class GetWorkflowImpl implements GetWorkflowAbstraction.Interface {
7+
public constructor(
8+
private logger: Logger.Interface,
9+
private getWorkflowRepository: GetWorkflowRepository.Interface
10+
) {}
11+
12+
public async execute(params: GetWorkflowAbstraction.Params): Promise<IWorkflow | null> {
13+
const result = await this.getWorkflowRepository.execute({
14+
id: params.id,
15+
app: params.app
16+
});
17+
if (result.isFail()) {
18+
this.logger.error(
19+
`Could not load workflow "${params.id}"/${params.app}. More in a log below this line.`
20+
);
21+
this.logger.log(result.error);
22+
return null;
23+
}
24+
25+
return result.value;
26+
}
27+
}
28+
29+
export const GetWorkflow = GetWorkflowAbstraction.createImplementation({
30+
implementation: GetWorkflowImpl,
31+
dependencies: [Logger, GetWorkflowRepository]
32+
});
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
import { WorkflowStateApproveStepHandler } from "~/features/workflowState/ApproveWorkflowStateStep/events.js";
2+
import { GetWorkflow } from "~/features/notification/NotifyUsers/abstractions.js";
23

34
class NotifyUsersOnStateApproveStepImpl implements WorkflowStateApproveStepHandler.Interface {
5+
public constructor(private getWorkflow: GetWorkflow.Interface) {}
6+
47
public async handle(event: WorkflowStateApproveStepHandler.Event): Promise<void> {
58
const { state } = event.payload;
9+
10+
const workflow = await this.getWorkflow.execute({
11+
id: state.workflowId,
12+
app: state.app
13+
});
14+
if (!workflow) {
15+
return;
16+
}
17+
18+
619
}
720
}
821

922
export const NotifyUsersOnStateApproveStep = WorkflowStateApproveStepHandler.createImplementation({
1023
implementation: NotifyUsersOnStateApproveStepImpl,
11-
dependencies: []
24+
dependencies: [GetWorkflow]
1225
});
Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,49 @@
11
import { WorkflowStateAfterCreateHandler } from "~/features/workflowState/CreateWorkflowState/index.js";
2+
import { GetWorkflow, TriggerAdapters } from "~/features/notification/NotifyUsers/abstractions.js";
3+
import { NotificationAdapter } from "~/domain/notification/abstractions.js";
4+
import type { NonEmptyArray } from "@webiny/api/types.js";
25

6+
/**
7+
* This handler is responsible for notifying users when a new workflow state is created - a user requests a review of the entry.
8+
*/
39
class NotifyUsersOnStateCreateImpl implements WorkflowStateAfterCreateHandler.Interface {
10+
public constructor(
11+
private getWorkflow: GetWorkflow.Interface,
12+
private triggerAdapters: TriggerAdapters.Interface
13+
) {}
14+
415
public async handle(event: WorkflowStateAfterCreateHandler.Event): Promise<void> {
16+
/**
17+
* No point triggering adapters if none are registered.
18+
*/
19+
if (this.triggerAdapters.hasAny() === false) {
20+
return;
21+
}
522
const { state } = event.payload;
23+
24+
const workflow = await this.getWorkflow.execute({
25+
id: state.workflowId,
26+
app: state.app
27+
});
28+
if (!workflow) {
29+
return;
30+
}
31+
// need to find all users that need to be notified.
32+
const users: NotificationAdapter.User[] = [];
33+
if (users.length === 0) {
34+
return;
35+
}
36+
// and then trigger all adapters
37+
await this.triggerAdapters.execute({
38+
state,
39+
workflow,
40+
users: users as NonEmptyArray<NotificationAdapter.User>,
41+
message
42+
});
643
}
744
}
845

946
export const NotifyUsersOnStateCreate = WorkflowStateAfterCreateHandler.createImplementation({
1047
implementation: NotifyUsersOnStateCreateImpl,
11-
dependencies: []
48+
dependencies: [GetWorkflow, TriggerAdapters]
1249
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Result } from "@webiny/feature/api";
2+
import { NotificationAdapter } from "~/domain/notification/abstractions.js";
3+
import {
4+
type ITriggerAdaptersParams,
5+
TriggerAdapters as TriggerAdaptersAbstraction,
6+
type TriggerAdaptersResult
7+
} from "./abstractions.js";
8+
9+
class TriggerAdaptersImpl implements TriggerAdaptersAbstraction.Interface {
10+
public constructor(private adapters: NotificationAdapter.Interface[]) {}
11+
12+
public hasAny(): boolean {
13+
return this.adapters.length > 0;
14+
}
15+
16+
public async execute(params: ITriggerAdaptersParams): Promise<TriggerAdaptersResult> {
17+
const { users, message } = params;
18+
// execute all adapters in parallel?
19+
const promises = await Promise.all(
20+
this.adapters.map(async adapter => {
21+
try {
22+
return await adapter.send({
23+
message,
24+
users
25+
});
26+
} catch (ex) {
27+
return;
28+
}
29+
})
30+
);
31+
32+
return Result.ok();
33+
}
34+
}
35+
36+
export const TriggerAdapters = TriggerAdaptersAbstraction.createImplementation({
37+
implementation: TriggerAdaptersImpl,
38+
dependencies: [[NotificationAdapter, { multiple: true }]]
39+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { createAbstraction } from "@webiny/feature/createAbstraction.js";
2+
import type { IWorkflow } from "~/domain/workflow/abstractions.js";
3+
import type { IWorkflowState } from "~/domain/workflowState/abstractions.js";
4+
import type { Result } from "@webiny/feature/api/index.js";
5+
import type { NonEmptyArray } from "@webiny/api/types.js";
6+
import {
7+
type INotificationAdapterMessage,
8+
NotificationAdapter
9+
} from "~/domain/notification/abstractions.js";
10+
11+
/**
12+
* Get Workflow
13+
*/
14+
export interface IGetWorkflowExecuteParams {
15+
id: string;
16+
app: string;
17+
}
18+
19+
export interface IGetWorkflow {
20+
/**
21+
* This method should log errors internally and return null if the workflow is not found.
22+
*/
23+
execute(params: IGetWorkflowExecuteParams): Promise<IWorkflow | null>;
24+
}
25+
26+
export const GetWorkflow = createAbstraction<IGetWorkflow>("NotifyUsersGetWorkflow");
27+
28+
export namespace GetWorkflow {
29+
export type Interface = IGetWorkflow;
30+
export type Params = IGetWorkflowExecuteParams;
31+
}
32+
33+
/**
34+
* Trigger Adapters
35+
*/
36+
37+
export interface ITriggerAdaptersParams {
38+
workflow: IWorkflow;
39+
state: IWorkflowState;
40+
users: NonEmptyArray<NotificationAdapter.User>;
41+
message: INotificationAdapterMessage;
42+
}
43+
44+
export type TriggerAdaptersResult = Promise<Result<void, unknown>>;
45+
46+
export interface ITriggerAdapters {
47+
hasAny(): boolean;
48+
execute(params: ITriggerAdaptersParams): Promise<TriggerAdaptersResult>;
49+
}
50+
51+
export const TriggerAdapters = createAbstraction<ITriggerAdapters>("NotifyUsersTriggerAdapters");
52+
53+
export namespace TriggerAdapters {
54+
export type Interface = ITriggerAdapters;
55+
export type Params = ITriggerAdaptersParams;
56+
export type Result = TriggerAdaptersResult;
57+
}

packages/api-workflows/src/features/notification/NotifyUsers/feature.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ import { NotifyUsersOnStateCreate } from "./NotifyUsersOnStateCreate.js";
66
import { NotifyUsersOnStateDelete } from "./NotifyUsersOnStateDelete.js";
77
import { NotifyUsersOnStateStartStep } from "./NotifyUsersOnStateStartStep.js";
88
import { NotifyUsersOnStateTakeOverStep } from "./NotifyUsersOnStateTakeOverStep.js";
9+
import { TriggerAdapters } from "./TriggerAdapters.js";
10+
import { GetWorkflow } from "./GetWorkflow.js";
911

1012
export const NotifyUsersFeature = createFeature({
1113
name: "WorkflowNotifications/NotifyUsers",
1214
register(container) {
15+
// helpers for event handlers
16+
container.register(TriggerAdapters);
17+
container.register(GetWorkflow);
18+
// event handlerrs
1319
container.register(NotifyUsersOnStateCreate);
1420
container.register(NotifyUsersOnStateDelete);
1521
container.register(NotifyUsersOnStateStartStep);

0 commit comments

Comments
 (0)