Skip to content

Commit

Permalink
feat(api): add v2 workflow api crud (#6460)
Browse files Browse the repository at this point in the history
Co-authored-by: GalT <[email protected]>
  • Loading branch information
djabarovgeorge and tatarco authored Oct 1, 2024
1 parent 2ce8e0d commit 47b9d25
Show file tree
Hide file tree
Showing 127 changed files with 2,159 additions and 380 deletions.
2 changes: 1 addition & 1 deletion .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/nx-angular-config.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/prettier.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/runConfigurations/API.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion apps/api/src/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ API_RATE_LIMIT_MAXIMUM_FREE_GLOBAL=
API_RATE_LIMIT_MAXIMUM_UNLIMITED_TRIGGER=
API_RATE_LIMIT_MAXIMUM_UNLIMITED_CONFIGURATION=
API_RATE_LIMIT_MAXIMUM_UNLIMITED_GLOBAL=

IS_USE_MERGED_DIGEST_ID_ENABLED=true

HUBSPOT_INVITE_NUDGE_EMAIL_USER_LIST_ID=
Expand Down
8 changes: 5 additions & 3 deletions apps/api/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable global-require */
import { DynamicModule, HttpException, Module, Logger, Provider } from '@nestjs/common';
import { DynamicModule, HttpException, Logger, Module, Provider } from '@nestjs/common';
import { RavenInterceptor, RavenModule } from 'nest-raven';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { Type } from '@nestjs/common/interfaces/type.interface';
Expand All @@ -15,7 +15,6 @@ import { HealthModule } from './app/health/health.module';
import { OrganizationModule } from './app/organization/organization.module';
import { EnvironmentsModule } from './app/environments/environments.module';
import { ExecutionDetailsModule } from './app/execution-details/execution-details.module';
import { WorkflowModule } from './app/workflows/workflow.module';
import { EventsModule } from './app/events/events.module';
import { WidgetsModule } from './app/widgets/widgets.module';
import { NotificationModule } from './app/notifications/notification.module';
Expand Down Expand Up @@ -44,6 +43,8 @@ import { InboxModule } from './app/inbox/inbox.module';
import { BridgeModule } from './app/bridge/bridge.module';
import { PreferencesModule } from './app/preferences';
import { StepSchemasModule } from './app/step-schemas/step-schemas.module';
import { WorkflowModule } from './app/workflows-v2/workflow.module';
import { WorkflowModuleV1 } from './app/workflows-v1/workflow-v1.module';

const enterpriseImports = (): Array<Type | DynamicModule | Promise<DynamicModule> | ForwardReference> => {
const modules: Array<Type | DynamicModule | Promise<DynamicModule> | ForwardReference> = [];
Expand Down Expand Up @@ -78,7 +79,7 @@ const baseModules: Array<Type | DynamicModule | Promise<DynamicModule> | Forward
HealthModule,
EnvironmentsModule,
ExecutionDetailsModule,
WorkflowModule,
WorkflowModuleV1,
EventsModule,
WidgetsModule,
InboxModule,
Expand All @@ -105,6 +106,7 @@ const baseModules: Array<Type | DynamicModule | Promise<DynamicModule> | Forward
TracingModule.register(packageJson.name, packageJson.version),
BridgeModule,
PreferencesModule,
WorkflowModule,
];

const enterpriseModules = enterpriseImports();
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/app/blueprint/blueprint.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';

import { SharedModule } from '../shared/shared.module';
import { WorkflowModule } from '../workflows/workflow.module';
import { USE_CASES } from './usecases';
import { BlueprintController } from './blueprint.controller';
import { WorkflowModuleV1 } from '../workflows-v1/workflow-v1.module';

@Module({
imports: [SharedModule, WorkflowModule],
imports: [SharedModule, WorkflowModuleV1],
controllers: [BlueprintController],
providers: [...USE_CASES],
exports: [...USE_CASES],
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/app/blueprint/e2e/get-blueprints-by-id.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from '@novu/shared';

import { GroupedBlueprintResponse } from '../dto/grouped-blueprint.response.dto';
import { CreateWorkflowRequestDto } from '../../workflows/dto';
import { CreateWorkflowRequestDto } from '../../workflows-v1/dto';

describe('Get blueprints by id - /blueprints/:templateId (GET)', async () => {
let session: UserSession;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import {
} from '@novu/application-generic';

import { GroupedBlueprintResponse } from '../dto/grouped-blueprint.response.dto';
import { CreateWorkflowRequestDto } from '../../workflows/dto';
import { GetGroupedBlueprints, POPULAR_TEMPLATES_ID_LIST } from '../usecases/get-grouped-blueprints';
// eslint-disable-next-line import/no-namespace
import * as blueprintStaticModule from '../usecases/get-grouped-blueprints/consts';
import { CreateWorkflowRequestDto } from '../../workflows-v1/dto';

describe('Get grouped notification template blueprints - /blueprints/group-by-category (GET)', async () => {
let session: UserSession;
Expand Down
14 changes: 7 additions & 7 deletions apps/api/src/app/bridge/bridge.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import {
UseInterceptors,
} from '@nestjs/common';

import { UserSessionData, ControlVariablesLevelEnum, WorkflowTypeEnum } from '@novu/shared';
import { ControlVariablesLevelEnum, UserSessionData, WorkflowTypeEnum } from '@novu/shared';
import { AnalyticsService, ExternalApiAccessible, UserAuthGuard, UserSession } from '@novu/application-generic';
import { EnvironmentRepository, NotificationTemplateRepository, ControlVariablesRepository } from '@novu/dal';
import { ControlValuesRepository, EnvironmentRepository, NotificationTemplateRepository } from '@novu/dal';

import { ApiExcludeController } from '@nestjs/swagger';

import { StoreControlVariables, StoreControlVariablesCommand } from './usecases/store-control-variables';
import { StoreControlVariablesCommand, StoreControlVariablesUseCase } from './usecases/store-control-variables';
import { PreviewStep, PreviewStepCommand } from './usecases/preview-step';
import { SyncCommand } from './usecases/sync';
import { Sync } from './usecases/sync/sync.usecase';
Expand All @@ -40,8 +40,8 @@ export class BridgeController {
private validateBridgeUrlUsecase: GetBridgeStatus,
private environmentRepository: EnvironmentRepository,
private notificationTemplateRepository: NotificationTemplateRepository,
private controlVariablesRepository: ControlVariablesRepository,
private storeControlVariables: StoreControlVariables,
private controlValuesRepository: ControlValuesRepository,
private storeControlVariablesUseCase: StoreControlVariablesUseCase,
private previewStep: PreviewStep,
private analyticsService: AnalyticsService
) {}
Expand Down Expand Up @@ -175,7 +175,7 @@ export class BridgeController {
throw new NotFoundException('Workflow not found');
}

const result = await this.controlVariablesRepository.findOne({
const result = await this.controlValuesRepository.findOne({
_environmentId: user.environmentId,
_organizationId: user.organizationId,
_workflowId: workflowExist._id,
Expand All @@ -195,7 +195,7 @@ export class BridgeController {
@UserSession() user: UserSessionData,
@Body() body: any
) {
return this.storeControlVariables.execute(
return this.storeControlVariablesUseCase.execute(
StoreControlVariablesCommand.create({
stepId,
workflowId,
Expand Down
2 changes: 2 additions & 0 deletions apps/api/src/app/bridge/bridge.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
UpdateChange,
UpdateMessageTemplate,
UpdateWorkflow,
UpsertControlValuesUseCase,
UpsertPreferences,
} from '@novu/application-generic';
import { PreferencesRepository } from '@novu/dal';
Expand All @@ -24,6 +25,7 @@ const PROVIDERS = [
UpdateChange,
PreferencesRepository,
UpsertPreferences,
UpsertControlValuesUseCase,
];

@Module({
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/app/bridge/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface IWorkflowDefineStep {
type: StepType;

inputs: IStepControl;

controls: IStepControl;

outputs: IStepOutput;
Expand Down
12 changes: 10 additions & 2 deletions apps/api/src/app/bridge/usecases/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { UpsertControlValuesUseCase } from '@novu/application-generic';
import { DeleteWorkflow } from './delete-workflow';
import { GetBridgeStatus } from './get-bridge-status';
import { PreviewStep } from './preview-step';
import { StoreControlVariables } from './store-control-variables';
import { StoreControlVariablesUseCase } from './store-control-variables';
import { Sync } from './sync';

export const USECASES = [DeleteWorkflow, GetBridgeStatus, PreviewStep, StoreControlVariables, Sync];
export const USECASES = [
DeleteWorkflow,
GetBridgeStatus,
PreviewStep,
StoreControlVariablesUseCase,
Sync,
UpsertControlValuesUseCase,
];
Original file line number Diff line number Diff line change
@@ -1,33 +1,19 @@
import { Injectable } from '@nestjs/common';
import _ from 'lodash';
import defaults from 'json-schema-defaults';

import { NotificationTemplateRepository, ControlVariablesRepository } from '@novu/dal';
import { ApiException } from '@novu/application-generic';
import { ControlVariablesLevelEnum } from '@novu/shared';
import { NotificationTemplateRepository } from '@novu/dal';
import { JsonSchema } from '@novu/framework';

import { ApiException, UpsertControlValuesCommand, UpsertControlValuesUseCase } from '@novu/application-generic';
import { StoreControlVariablesCommand } from './store-control-variables.command';

@Injectable()
export class StoreControlVariables {
export class StoreControlVariablesUseCase {
constructor(
private controlVariablesRepository: ControlVariablesRepository,
private notificationTemplateRepository: NotificationTemplateRepository
private notificationTemplateRepository: NotificationTemplateRepository,
private upsertControlValuesUseCase: UpsertControlValuesUseCase
) {}

private difference(object, base) {
const changes = (objectControl, baseControl) => {
return _.transform(objectControl, function (result, value, key) {
if (!_.isEqual(value, base[key])) {
// eslint-disable-next-line no-param-reassign
result[key] = _.isObject(value) && _.isObject(baseControl[key]) ? changes(value, baseControl[key]) : value;
}
});
};

return changes(object, base);
}

async execute(command: StoreControlVariablesCommand) {
const workflowExist = await this.notificationTemplateRepository.findByTriggerIdentifier(
command.environmentId,
Expand All @@ -40,60 +26,24 @@ export class StoreControlVariables {

const step = workflowExist?.steps.find((item) => item.stepId === command.stepId);

if (!step) {
if (!step || !step._id) {
throw new ApiException('Step not found');
}

const stepDefault = defaults(
const stepDefaultControls = defaults(
(step.template as any)?.controls?.schema || (step.template as any)?.inputs?.schema,
{}
) as JsonSchema;

return await this.upsertControlValuesUseCase.execute(
UpsertControlValuesCommand.create({
organizationId: command.organizationId,
environmentId: command.environmentId,
notificationStepEntity: step,
workflowId: workflowExist._id,
newControlValues: command.variables,
controlSchemas: { schema: stepDefaultControls },
})
);

const variables = this.difference(command.variables, stepDefault);

const found = await this.controlVariablesRepository.findOne({
_environmentId: command.environmentId,
_workflowId: workflowExist._id,
level: ControlVariablesLevelEnum.STEP_CONTROLS,
priority: 0,
stepId: command.stepId,
});

if (found) {
await this.controlVariablesRepository.update(
{
_id: found._id,
_organizationId: command.organizationId,
_environmentId: command.environmentId,
},
{
level: ControlVariablesLevelEnum.STEP_CONTROLS,
priority: 0,
inputs: variables,
controls: variables,
workflowId: command.workflowId,
stepId: command.stepId,
}
);

return this.controlVariablesRepository.findOne({
_id: found._id,
_organizationId: command.organizationId,
_environmentId: command.environmentId,
});
}

return await this.controlVariablesRepository.create({
_organizationId: command.organizationId,
_environmentId: command.environmentId,
_workflowId: workflowExist._id,
_stepId: step._id,
level: ControlVariablesLevelEnum.STEP_CONTROLS,
priority: 0,
inputs: variables,
controls: variables,
workflowId: command.workflowId,
stepId: command.stepId,
});
}
}
12 changes: 10 additions & 2 deletions apps/api/src/app/bridge/usecases/sync/sync.command.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { IsString, ValidateNested } from 'class-validator';
import { IsDefined, IsOptional, IsString, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';

import { EnvironmentWithUserCommand, IStepControl } from '@novu/application-generic';
import { NotificationTemplateCustomData, IPreferenceChannels, StepType } from '@novu/shared';
import { IPreferenceChannels, NotificationTemplateCustomData, StepType } from '@novu/shared';

import { IStepOutput, IWorkflowDefineStep } from '../../shared';

Expand Down Expand Up @@ -103,8 +103,16 @@ export interface ICreateBridges {
}

export class SyncCommand extends EnvironmentWithUserCommand implements ICreateBridges {
@IsOptional()
@ValidateNested({ each: true })
@Type(() => WorkflowDefine)
workflows?: WorkflowDefine[];

@IsString()
@IsDefined()
bridgeUrl: string;

@IsOptional()
@IsString()
source?: string;
}
13 changes: 7 additions & 6 deletions apps/api/src/app/bridge/usecases/sync/sync.usecase.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { BadRequestException, Injectable } from '@nestjs/common';

import {
NotificationTemplateRepository,
EnvironmentRepository,
NotificationGroupRepository,
NotificationTemplateEntity,
NotificationTemplateRepository,
} from '@novu/dal';
import {
AnalyticsService,
CreateWorkflow,
CreateWorkflowCommand,
ExecuteBridgeRequest,
GetFeatureFlag,
GetFeatureFlagCommand,
NotificationStep,
UpdateWorkflow,
UpdateWorkflowCommand,
ExecuteBridgeRequest,
UpsertPreferences,
UpsertWorkflowPreferencesCommand,
GetFeatureFlag,
GetFeatureFlagCommand,
} from '@novu/application-generic';
import { FeatureFlagsKeysEnum, WorkflowTypeEnum } from '@novu/shared';
import { FeatureFlagsKeysEnum, WorkflowCreationSourceEnum, WorkflowOriginEnum, WorkflowTypeEnum } from '@novu/shared';
import { DiscoverOutput, DiscoverStepOutput, DiscoverWorkflowOutput, GetActionEnum } from '@novu/framework';

import { SyncCommand } from './sync.command';
Expand Down Expand Up @@ -175,13 +175,14 @@ export class Sync {

savedWorkflow = await this.createWorkflowUsecase.execute(
CreateWorkflowCommand.create({
origin: WorkflowOriginEnum.EXTERNAL,
notificationGroupId,
draft: !isWorkflowActive,
environmentId: command.environmentId,
organizationId: command.organizationId,
userId: command.userId,
name: workflow.workflowId,
__source: 'bridge',
__source: WorkflowCreationSourceEnum.BRIDGE,
type: WorkflowTypeEnum.BRIDGE,
steps: this.mapSteps(workflow.steps),
/** @deprecated */
Expand Down
Loading

0 comments on commit 47b9d25

Please sign in to comment.