Skip to content

Commit 98db1be

Browse files
committed
List processes.
Renames notification methods to services. Support for `if` of `notify`.
1 parent e8385e7 commit 98db1be

18 files changed

+313
-64
lines changed

nest-cli.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
"collection": "@nestjs/schematics",
44
"sourceRoot": "src",
55
"compilerOptions": {
6-
"deleteOutDir": true
6+
"deleteOutDir": true,
7+
"assets": [
8+
{
9+
"include": "config/*.json",
10+
"outDir": "dist"
11+
}
12+
]
713
}
814
}

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"@types/negotiator": "^0.6.3",
4949
"@types/node": "^20.14.8",
5050
"@types/supertest": "^6.0.2",
51+
"@types/uuid": "^10.0.0",
5152
"@typescript-eslint/eslint-plugin": "^7.14.1",
5253
"@typescript-eslint/parser": "^7.14.1",
5354
"eslint": "^9.5.0",

src/common/config/config.service.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ export class ConfigService implements OnModuleInit, OnModuleDestroy {
4444
.filter(fs.existsSync);
4545

4646
config.loadFile(configFiles);
47-
config.set('notificationMethods', this.loadNotificationMethodsFromEnv(config.get('notificationMethods')));
47+
config.set('services', this.loadServicesFromEnv(config.get('services')));
4848

4949
config.validate({ allowed: 'warn' });
5050

5151
this.config = config;
5252
}
5353

54-
private loadNotificationMethodsFromEnv(methods: Record<string, Record<string, any>>) {
54+
private loadServicesFromEnv(methods: Record<string, Record<string, any>>) {
5555
Object.keys(process.env).forEach((key) => {
56-
if (key.startsWith('NOTIFICATION_')) {
56+
if (key.startsWith('SERVICE_')) {
5757
const [_, id, envProp] = key.split('_', 3);
5858
const prop = envProp.toLowerCase().replace(/_([a-z])/g, (_, group1) => group1.toUpperCase());
5959

src/config/schema.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import convict from 'convict';
2+
3+
// Define custom format to handle comma-separated lists
4+
convict.addFormat({
5+
name: 'comma-separated-list',
6+
validate: () => undefined,
7+
coerce: (val) => val.split(',').map((item: string) => item.trim()),
8+
});
9+
110
export default {
211
env: {
312
format: ['production', 'staging', 'development', 'test'],
@@ -18,6 +27,20 @@ export default {
1827
default: 'mongodb://localhost:27017/letsflow',
1928
env: 'DB',
2029
},
30+
summeryFields: {
31+
scenario: {
32+
doc: 'Custom fields of a scenario that should be returned when listing',
33+
default: [] as string[],
34+
format: 'comma-separated-list',
35+
env: 'ADDITIONAL_SCENARIO_SUMMERY_FIELDS',
36+
},
37+
process: {
38+
doc: 'Custom fields of a process that should be returned when listing',
39+
default: [] as string[],
40+
format: 'comma-separated-list',
41+
env: 'ADDITIONAL_PROCESS_SUMMERY_FIELDS',
42+
},
43+
},
2144
jwt: {
2245
issuer: {
2346
default: '',
@@ -38,8 +61,8 @@ export default {
3861
env: 'DEFAULT_ACCOUNT',
3962
},
4063
},
41-
notificationMethods: {
42-
doc: 'Dictionary of notification methods',
64+
services: {
65+
doc: 'Dictionary of services and workers',
4366
format: Object,
4467
default: {},
4568
},

src/config/test.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
{
2-
"db": "mongodb://localhost:27017/letsflow-test"
2+
"db": "mongodb://localhost:27017/letsflow-test",
3+
"summeryFields": {
4+
"scenario": ["extraScenarioField"],
5+
"process": ["extraProcessField"]
6+
}
37
}

src/notify/interfaces.ts

-5
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,3 @@ import { Process } from '@letsflow/core/process';
33
export interface NotifyProvider {
44
notify: (process: Process, args: Record<string, any>) => Promise<void>;
55
}
6-
7-
export interface NotifyArgs {
8-
method: string;
9-
[_: string]: any;
10-
}

src/notify/notify.service.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Injectable, Logger } from '@nestjs/common';
22
import { ZeromqService } from './zeromq/zeromq.service';
3-
import { Process } from '@letsflow/core/process';
3+
import { Process, Notify } from '@letsflow/core/process';
44
import { ConfigService } from '../common/config/config.service';
5-
import { NotifyArgs, NotifyProvider } from './interfaces';
5+
import { NotifyProvider } from './interfaces';
66

77
@Injectable()
88
export class NotifyService implements NotifyProvider {
@@ -14,29 +14,30 @@ export class NotifyService implements NotifyProvider {
1414

1515
async invoke(process: Process): Promise<void> {
1616
for (const args of process.current.notify) {
17+
if ('if' in args && !args.if) continue;
1718
await this.notify(process, args);
1819
}
1920
}
2021

21-
async notify(process: Process, args: NotifyArgs): Promise<void> {
22+
async notify(process: Process, args: Notify): Promise<void> {
2223
try {
2324
await this.getProvider(args.method).notify(process, args);
2425
} catch (err) {
2526
this.logger.error(err.message);
2627
}
2728
}
2829

29-
private getProvider(method: string): NotifyProvider {
30-
const settings = this.config.get('notificationMethods')[method];
30+
private getProvider(service: string): NotifyProvider {
31+
const settings = this.config.get('services')[service];
3132

32-
if (!settings) throw new Error(`Notification method '${method}' not configured`);
33-
if (!settings.provider) throw new Error(`Provider not specified for notification method '${method}'`);
33+
if (!settings) throw new Error(`Service '${service}' not configured`);
34+
if (!settings.provider) throw new Error(`Provider not specified for service '${service}'`);
3435

3536
switch (settings.provider) {
3637
case 'zeromq':
3738
return this.zeromq;
3839
default:
39-
throw new Error(`Unsupported provider ${settings.provider} for notification method '${method}'`);
40+
throw new Error(`Unsupported provider ${settings.provider} for service '${service}'`);
4041
}
4142
}
4243
}

src/notify/zeromq/zeromq.service.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Logger, Injectable, OnModuleDestroy } from '@nestjs/common';
2-
import { NotifyArgs, NotifyProvider } from '../interfaces';
3-
import { Process } from '@letsflow/core/process';
1+
import { Injectable, OnModuleDestroy } from '@nestjs/common';
2+
import { NotifyProvider } from '../interfaces';
3+
import { Process, Notify } from '@letsflow/core/process';
44
import { Push } from 'zeromq';
55
import { ConfigService } from '../../common/config/config.service';
66
import { NotifyMessageService } from '../notify-message/notify-message.service';
@@ -32,13 +32,13 @@ export class ZeromqService implements NotifyProvider, OnModuleDestroy {
3232
return socket;
3333
}
3434

35-
async notify(process: Process, args: NotifyArgs): Promise<void> {
36-
if (!args.action) {
35+
async notify(process: Process, args: Notify): Promise<void> {
36+
if ('action' in args && !args.action) {
3737
return; // No action to notify
3838
}
3939

40-
const settings = this.config.get('notificationMethods')[args.method];
41-
if (!settings.address) throw new Error(`ZeroMQ address not configured notification method ${args.method}`);
40+
const settings = this.config.get('services')[args.service];
41+
if (!settings.address) throw new Error(`ZeroMQ address not configured for service '${args.method}'`);
4242

4343
const message = this.message.create(process, args.action);
4444
const socket = this.getSocket(settings.address);

src/process/process.controller.ts

+9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ export class ProcessController {
2626
private validation: ValidationService,
2727
) {}
2828

29+
@ApiOperation({ summary: 'List all processes' })
30+
@ApiResponse({ status: 200, description: 'Success' })
31+
@ApiProduces('application/json')
32+
@Get()
33+
public async list(@Res() res: Response): Promise<void> {
34+
const processes = await this.service.list();
35+
res.status(200).json(processes);
36+
}
37+
2938
@ApiOperation({ summary: 'Start a process' })
3039
@ApiConsumes('application/json')
3140
@ApiBody({ required: true, type: StartInstructions })

src/process/process.dto.ts

+21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,25 @@
11
import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger';
2+
import { ScenarioSummary } from '../scenario/scenario.dto';
3+
4+
export class ProcessSummary {
5+
@ApiProperty()
6+
id: string;
7+
8+
@ApiProperty()
9+
title: string;
10+
11+
@ApiProperty()
12+
scenario: ScenarioSummary;
13+
14+
@ApiProperty()
15+
actors: Array<ProcessActor>;
16+
17+
@ApiProperty()
18+
created: Date;
19+
20+
@ApiProperty()
21+
lastUpdated: Date;
22+
}
223

324
export class ProcessActor {
425
@ApiProperty()

src/process/process.module.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import { ValidationService } from './validation/validation.service';
66
import { MongoModule } from '../common/mongo/mongo.module';
77
import { AuthModule } from '../common/auth/auth.module';
88
import { AjvModule } from '../common/ajv/ajv.module';
9+
import { NotifyModule } from '../notify/notify.module';
10+
import { ConfigModule } from '../common/config/config.module';
911

1012
@Module({
11-
imports: [ScenarioModule, MongoModule, AuthModule, AjvModule],
13+
imports: [ScenarioModule, MongoModule, AuthModule, AjvModule, NotifyModule, ConfigModule],
1214
providers: [ProcessService, ValidationService],
1315
controllers: [ProcessController],
1416
})

0 commit comments

Comments
 (0)