Skip to content

Commit 95250bb

Browse files
committed
Workflow DTO Issues
1 parent 2588c89 commit 95250bb

File tree

263 files changed

+12306
-3893
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

263 files changed

+12306
-3893
lines changed

apps/api/src/app/blueprint/usecases/get-grouped-blueprints/get-grouped-blueprints.usecase.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ export class GetGroupedBlueprints {
2323

2424
const popularGroup = { name: POPULAR_GROUPED_NAME, blueprints: updatePopularBlueprints };
2525

26-
return { general: generalGroups as IGroupedBlueprint[], popular: popularGroup as IGroupedBlueprint };
26+
return {
27+
general: generalGroups as unknown as IGroupedBlueprint[],
28+
popular: popularGroup as unknown as IGroupedBlueprint,
29+
};
2730
}
2831

2932
private async fetchGroupedBlueprints() {

apps/api/src/app/bridge/usecases/preview-step/preview-step.usecase.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { Event, ExecuteOutput, HttpQueryKeysEnum, PostActionEnum } from '@novu/f
33
import { ExecuteBridgeRequest, ExecuteBridgeRequestCommand, InstrumentUsecase } from '@novu/application-generic';
44

55
import { PreviewStepCommand } from './preview-step.command';
6+
import { Subscriber } from '../../../inbox/utils/types';
7+
import { SubscriberResponseDto } from '../../../subscribers/dtos';
68

79
@Injectable()
810
export class PreviewStep {
@@ -37,10 +39,29 @@ export class PreviewStep {
3739
controls: command.controls || {},
3840
payload: command.payload || {},
3941
state: command.state || [],
40-
subscriber: command.subscriber || {},
42+
subscriber: this.mapSubscriberResponseToSubscriber(command.subscriber),
4143
stepId: command.stepId,
4244
workflowId: command.workflowId,
4345
action: PostActionEnum.PREVIEW,
4446
};
4547
}
48+
49+
private mapSubscriberResponseToSubscriber(
50+
subscriberResponse?: Partial<SubscriberResponseDto> | undefined
51+
): Subscriber {
52+
if (!subscriberResponse) {
53+
return {
54+
id: '',
55+
subscriberId: '',
56+
};
57+
}
58+
59+
return {
60+
id: subscriberResponse._id || subscriberResponse.subscriberId || '',
61+
firstName: subscriberResponse.firstName,
62+
lastName: subscriberResponse.lastName,
63+
avatar: subscriberResponse.avatar,
64+
subscriberId: subscriberResponse.subscriberId || '',
65+
};
66+
}
4667
}

apps/api/src/app/bridge/usecases/sync/sync.command.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { IsDefined, IsOptional, IsString, ValidateNested } from 'class-validator
22
import { Type } from 'class-transformer';
33

44
import { EnvironmentWithUserCommand, IStepControl } from '@novu/application-generic';
5-
import type { IPreferenceChannels, CustomDataType, StepType, JSONSchemaDto } from '@novu/shared';
5+
import type { CustomDataType, IPreferenceChannels, JSONSchemaDto, StepType } from '@novu/shared';
66

77
interface IStepOutput {
88
schema: JSONSchemaDto;

apps/api/src/app/bridge/usecases/sync/sync.usecase.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ import {
1414
DeleteWorkflowCommand,
1515
DeleteWorkflowUseCase,
1616
ExecuteBridgeRequest,
17+
JSONSchema,
1718
NotificationStep,
1819
UpdateWorkflow,
1920
UpdateWorkflowCommand,
2021
} from '@novu/application-generic';
2122
import {
2223
buildWorkflowPreferences,
23-
JSONSchemaDto,
24-
StepIssuesDto,
2524
StepTypeEnum,
2625
UserSessionData,
2726
WorkflowCreationSourceEnum,
@@ -35,6 +34,7 @@ import { SyncCommand } from './sync.command';
3534
import { CreateBridgeResponseDto } from '../../dtos/create-bridge-response.dto';
3635
import { BuildStepIssuesUsecase } from '../../../workflows-v2/usecases/build-step-issues/build-step-issues.usecase';
3736
import { computeWorkflowStatus } from '../../../workflows-v2/shared/compute-workflow-status';
37+
import { JSONSchemaDto, StepIssuesDto } from '../../../workflows-v2/dtos';
3838

3939
@Injectable()
4040
export class Sync {
@@ -183,9 +183,8 @@ export class Sync {
183183
return Promise.all(
184184
workflowsFromBridge.map(async (workflow, index) => {
185185
const existingFrameworkWorkflow = existingFrameworkWorkflows[index];
186-
const savedWorkflow = await this.upsertWorkflow(command, workflow, existingFrameworkWorkflow);
187186

188-
return savedWorkflow;
187+
return await this.upsertWorkflow(command, workflow, existingFrameworkWorkflow);
189188
})
190189
);
191190
}
@@ -235,10 +234,10 @@ export class Sync {
235234
__source: WorkflowCreationSourceEnum.BRIDGE,
236235
steps,
237236
controls: {
238-
schema: workflow.controls?.schema as JSONSchemaDto,
237+
schema: workflow.controls?.schema as unknown as JSONSchema,
239238
},
240239
rawData: workflow as unknown as Record<string, unknown>,
241-
payloadSchema: workflow.payload?.schema as JSONSchemaDto,
240+
payloadSchema: workflow.payload?.schema as unknown as JSONSchema,
242241
active: workflowActive,
243242
status: computeWorkflowStatus(workflowActive, steps),
244243
description: this.getWorkflowDescription(workflow),
@@ -266,7 +265,7 @@ export class Sync {
266265
workflowId: workflow.workflowId,
267266
steps,
268267
controls: {
269-
schema: workflow.controls?.schema as JSONSchemaDto,
268+
schema: workflow.controls?.schema as unknown as JSONSchemaDto,
270269
},
271270
rawData: workflow,
272271
payloadSchema: workflow.payload?.schema as unknown as JSONSchemaDto,
@@ -299,7 +298,7 @@ export class Sync {
299298
stepInternalId: foundStep?._id,
300299
workflow,
301300
stepType: step.type as StepTypeEnum,
302-
controlSchema: step.controls?.schema as JSONSchemaDto,
301+
controlSchema: step.controls?.schema as unknown as JSONSchemaDto,
303302
});
304303

305304
const template = {

apps/api/src/app/environments-v1/usecases/construct-framework-workflow/construct-framework-workflow.usecase.ts

+20-24
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import { Injectable, InternalServerErrorException } from '@nestjs/common';
22
import { workflow } from '@novu/framework/express';
3-
import { ActionStep, ChannelStep, JsonSchema, Step, StepOptions, StepOutput, Workflow } from '@novu/framework/internal';
3+
import { ActionStep, ChannelStep, Schema, Step, StepOutput, Workflow } from '@novu/framework/internal';
44
import { NotificationStepEntity, NotificationTemplateEntity, NotificationTemplateRepository } from '@novu/dal';
5-
import { JSONSchemaDefinition, StepTypeEnum, WorkflowOriginEnum } from '@novu/shared';
5+
import { StepTypeEnum } from '@novu/shared';
66
import { Instrument, InstrumentUsecase, PinoLogger } from '@novu/application-generic';
77
import { AdditionalOperation, RulesLogic } from 'json-logic-js';
88
import _ from 'lodash';
99
import { ConstructFrameworkWorkflowCommand } from './construct-framework-workflow.command';
1010
import {
1111
ChatOutputRendererUsecase,
12+
EmailOutputRendererUsecase,
1213
FullPayloadForRender,
1314
InAppOutputRendererUsecase,
1415
PushOutputRendererUsecase,
15-
EmailOutputRendererUsecase,
1616
SmsOutputRendererUsecase,
1717
} from '../output-renderers';
1818
import { DelayOutputRendererUsecase } from '../output-renderers/delay-output-renderer.usecase';
@@ -54,7 +54,7 @@ export class ConstructFrameworkWorkflow {
5454
dbWorkflow.triggers[0].identifier,
5555
async ({ step, payload, subscriber }) => {
5656
const fullPayloadForRender: FullPayloadForRender = { payload, subscriber, steps: {} };
57-
for await (const staticStep of dbWorkflow.steps) {
57+
for (const staticStep of dbWorkflow.steps) {
5858
fullPayloadForRender.steps[staticStep.stepId || staticStep._templateId] = await this.constructStep(
5959
step,
6060
staticStep,
@@ -170,8 +170,12 @@ export class ConstructFrameworkWorkflow {
170170
staticStep: NotificationStepEntity,
171171
fullPayloadForRender: FullPayloadForRender
172172
): Required<Parameters<ChannelStep>[2]> {
173+
const skipFunction = (controlValues: Record<string, unknown>) =>
174+
this.processSkipOption(controlValues, fullPayloadForRender);
175+
173176
return {
174-
...this.constructCommonStepOptions(staticStep, fullPayloadForRender),
177+
skip: skipFunction,
178+
controlSchema: staticStep.template!.controls!.schema as unknown as Schema,
175179
disableOutputSanitization: true,
176180
providers: {},
177181
};
@@ -182,10 +186,17 @@ export class ConstructFrameworkWorkflow {
182186
staticStep: NotificationStepEntity,
183187
fullPayloadForRender: FullPayloadForRender
184188
): Required<Parameters<ActionStep>[2]> {
185-
const stepOptions = this.constructCommonStepOptions(staticStep, fullPayloadForRender);
186-
187-
let controlSchema = stepOptions.controlSchema as JSONSchemaDefinition;
188189
const stepType = staticStep.template!.type;
190+
const controlSchema = this.optionalAugmentControlSchemaDueToAjvBug(staticStep, stepType);
191+
192+
return {
193+
controlSchema: controlSchema as unknown as Schema,
194+
skip: (controlValues: Record<string, unknown>) => this.processSkipOption(controlValues, fullPayloadForRender),
195+
};
196+
}
197+
198+
private optionalAugmentControlSchemaDueToAjvBug(staticStep: NotificationStepEntity, stepType: StepTypeEnum) {
199+
let controlSchema = staticStep.template!.controls!.schema;
189200

190201
/*
191202
* because of the known AJV issue with anyOf, we need to find the first schema that matches the control values
@@ -198,22 +209,7 @@ export class ConstructFrameworkWorkflow {
198209
controlSchema = fistSchemaMatch ?? controlSchema.anyOf[0];
199210
}
200211

201-
return {
202-
...stepOptions,
203-
controlSchema: controlSchema as JsonSchema,
204-
};
205-
}
206-
207-
@Instrument()
208-
private constructCommonStepOptions(
209-
staticStep: NotificationStepEntity,
210-
fullPayloadForRender: FullPayloadForRender
211-
): Required<StepOptions> {
212-
return {
213-
// TODO: fix the `JSONSchemaDto` type to enforce a non-primitive schema type.
214-
controlSchema: staticStep.template!.controls!.schema as JsonSchema,
215-
skip: (controlValues: Record<string, unknown>) => this.processSkipOption(controlValues, fullPayloadForRender),
216-
};
212+
return controlSchema;
217213
}
218214

219215
@Instrument()

apps/api/src/app/notifications/dtos/activities-response.dto.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,15 @@ export class DigestTimedConfigDto {
2424
@IsString()
2525
atTime?: string;
2626

27-
@ApiPropertyOptional({ description: 'Days of the week for the digest', type: [String], enum: DaysEnum })
27+
@ApiPropertyOptional({
28+
description: 'Days of the week for the digest',
29+
type: 'array',
30+
items: {
31+
type: 'string',
32+
enum: Object.values(DaysEnum),
33+
},
34+
enumName: 'DaysEnum',
35+
})
2836
@IsOptional()
2937
@IsArray()
3038
@IsEnum(DaysEnum, { each: true })

apps/api/src/app/preferences/dtos/preferences.dto.ts

+54
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,101 @@ import { ChannelTypeEnum } from '@novu/shared';
22
import { Type } from 'class-transformer';
33
import { IsBoolean, ValidateNested } from 'class-validator';
44

5+
/**
6+
* @deprecated Use an updated preference structure.
7+
* This class will be removed in future versions.
8+
*/
59
export class WorkflowPreference {
10+
/**
11+
* @deprecated Use alternative enablement mechanism.
12+
*/
613
@IsBoolean()
714
enabled: boolean;
815

16+
/**
17+
* @deprecated Read-only flag is no longer supported.
18+
*/
919
@IsBoolean()
1020
readOnly: boolean;
1121
}
1222

23+
/**
24+
* @deprecated Use an updated channel preference structure.
25+
* Will be removed in future versions.
26+
*/
1327
export class ChannelPreference {
28+
/**
29+
* @deprecated Use alternative channel enablement method.
30+
*/
1431
@IsBoolean()
1532
enabled: boolean;
1633
}
1734

35+
/**
36+
* @deprecated Channels configuration is being restructured.
37+
* Use the new channel management approach.
38+
*/
1839
export class Channels {
40+
/**
41+
* @deprecated In-app channel preference is deprecated.
42+
*/
1943
@ValidateNested({ each: true })
2044
@Type(() => ChannelPreference)
2145
[ChannelTypeEnum.IN_APP]: ChannelPreference;
2246

47+
/**
48+
* @deprecated Email channel preference is deprecated.
49+
*/
2350
@ValidateNested({ each: true })
2451
@Type(() => ChannelPreference)
2552
[ChannelTypeEnum.EMAIL]: ChannelPreference;
2653

54+
/**
55+
* @deprecated SMS channel preference is deprecated.
56+
*/
2757
@ValidateNested({ each: true })
2858
@Type(() => ChannelPreference)
2959
[ChannelTypeEnum.SMS]: ChannelPreference;
3060

61+
/**
62+
* @deprecated Chat channel preference is deprecated.
63+
*/
3164
@ValidateNested({ each: true })
3265
@Type(() => ChannelPreference)
3366
[ChannelTypeEnum.CHAT]: ChannelPreference;
3467

68+
/**
69+
* @deprecated Push channel preference is deprecated.
70+
*/
3571
@ValidateNested({ each: true })
3672
@Type(() => ChannelPreference)
3773
[ChannelTypeEnum.PUSH]: ChannelPreference;
3874
}
3975

76+
/**
77+
* @deprecated Preferences DTO is being replaced.
78+
* Use the new preferences management approach.
79+
*/
4080
export class PreferencesDto {
81+
/**
82+
* @deprecated Global workflow preference is no longer used.
83+
*/
4184
@ValidateNested({ each: true })
4285
@Type(() => WorkflowPreference)
4386
all: WorkflowPreference;
4487

88+
/**
89+
* @deprecated Channels configuration is deprecated.
90+
*/
4591
@ValidateNested({ each: true })
4692
@Type(() => Channels)
4793
channels: Channels;
4894
}
95+
96+
// Optional: Runtime deprecation warning
97+
if (process.env.NODE_ENV !== 'production') {
98+
console.warn(
99+
'DEPRECATION WARNING: PreferencesDto and related classes are deprecated ' +
100+
'and will be removed in future versions. Please migrate to the new preferences structure.'
101+
);
102+
}

apps/api/src/app/preferences/dtos/upsert-preferences.dto.ts

+10
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,20 @@ import { Type } from 'class-transformer';
22
import { IsString, ValidateNested } from 'class-validator';
33
import { PreferencesDto } from './preferences.dto';
44

5+
/**
6+
* @deprecated This DTO is no longer recommended for use.
7+
* Consider using an alternative implementation or updated data transfer object.
8+
*/
59
export class UpsertPreferencesDto {
10+
/**
11+
* @deprecated Use an alternative workflow identification method.
12+
*/
613
@IsString()
714
workflowId: string;
815

16+
/**
17+
* @deprecated Preferences structure is outdated.
18+
*/
919
@ValidateNested({ each: true })
1020
@Type(() => PreferencesDto)
1121
preferences: PreferencesDto;

apps/api/src/app/preferences/preferences.controller.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
Get,
77
Post,
88
Query,
9-
UseGuards,
109
UseInterceptors,
1110
} from '@nestjs/common';
1211
import {
@@ -20,8 +19,8 @@ import {
2019
} from '@novu/application-generic';
2120
import { PreferencesTypeEnum, UserSessionData } from '@novu/shared';
2221
import { ApiExcludeController } from '@nestjs/swagger';
23-
import { UpsertPreferencesDto } from './dtos/upsert-preferences.dto';
2422
import { UserAuthentication } from '../shared/framework/swagger/api.key.security';
23+
import { UpsertPreferencesDto } from './dtos/upsert-preferences.dto';
2524

2625
/**
2726
* @deprecated - set workflow preferences using the `/workflows` endpoint instead

0 commit comments

Comments
 (0)