Skip to content

Commit

Permalink
Merge branch 'next' into patch-2
Browse files Browse the repository at this point in the history
  • Loading branch information
danikp authored Dec 11, 2024
2 parents 645309d + 7c0f452 commit 2afc80c
Show file tree
Hide file tree
Showing 102 changed files with 2,028 additions and 1,279 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ services:
# (Adding the "ports" property to this file will not forward from a Codespace.)

db:
image: mongo:latest
image: mongo:8.0.3
restart: unless-stopped
volumes:
- mongodb-content:/data/db
Expand Down
4 changes: 2 additions & 2 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"@sentry/node": "^8.33.1",
"@sentry/profiling-node": "^8.33.1",
"@sentry/tracing": "^7.40.0",
"@types/newrelic": "^9.14.0",
"@types/newrelic": "^9.14.6",
"@upstash/ratelimit": "^0.4.4",
"@novu/api": "^0.0.1-alpha.56",
"axios": "^1.6.8",
Expand All @@ -81,7 +81,7 @@
"lodash": "^4.17.15",
"nanoid": "^3.1.20",
"nest-raven": "10.1.0",
"newrelic": "^9.15.0",
"newrelic": "^12.8.1",
"nimma": "^0.6.0",
"passport": "0.7.0",
"passport-github2": "^0.1.12",
Expand Down
80 changes: 79 additions & 1 deletion apps/api/src/app/bridge/e2e/sync.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
MessageTemplateRepository,
ControlValuesRepository,
} from '@novu/dal';
import { WorkflowTypeEnum } from '@novu/shared';
import { WorkflowOriginEnum, WorkflowTypeEnum } from '@novu/shared';
import { workflow } from '@novu/framework';
import { BridgeServer } from '../../../../e2e/bridge.server';

Expand Down Expand Up @@ -650,4 +650,82 @@ describe('Bridge Sync - /bridge/sync (POST)', async () => {
const secondStepResponse = await session.testAgent.get(`/v1/bridge/controls/${workflowId}/send-email`);
expect(secondStepResponse.body.data.controls.subject).to.equal('Hello World again');
});

it('should throw an error when trying to sync a workflow with an ID that exists in dashboard', async () => {
const workflowId = 'dashboard-created-workflow';

// First create a workflow directly (simulating dashboard creation)
const dashboardWorkflow = await workflowsRepository.create({
_environmentId: session.environment._id,
name: workflowId,
triggers: [{ identifier: workflowId, type: 'event', variables: [] }],
steps: [],
active: true,
draft: false,
workflowId,
origin: WorkflowOriginEnum.NOVU_CLOUD,
});

// Now try to sync a workflow with the same ID through bridge
const newWorkflow = workflow(workflowId, async ({ step }) => {
await step.email('send-email', () => ({
subject: 'Welcome!',
body: 'Hello there',
}));
});
await bridgeServer.start({ workflows: [newWorkflow] });

const result = await session.testAgent.post(`/v1/bridge/sync`).send({
bridgeUrl: bridgeServer.serverPath,
});

expect(result.status).to.equal(400);
expect(result.body.message).to.contain(`was already created in Dashboard. Please use another workflowId.`);

// Verify the original workflow wasn't modified
const workflows = await workflowsRepository.findOne({
_environmentId: session.environment._id,
_id: dashboardWorkflow._id,
});
expect(workflows).to.deep.equal(dashboardWorkflow);
});

it('should allow syncing a workflow with same ID if original was created externally', async () => {
const workflowId = 'external-created-workflow';

// First create a workflow as external
const externalWorkflow = await workflowsRepository.create({
_environmentId: session.environment._id,
name: workflowId,
triggers: [{ identifier: workflowId, type: 'event', variables: [] }],
steps: [],
active: true,
draft: false,
workflowId,
origin: WorkflowOriginEnum.EXTERNAL,
});

// Now try to sync a workflow with the same ID through bridge
const newWorkflow = workflow(workflowId, async ({ step }) => {
await step.email('send-email', () => ({
subject: 'Updated Welcome!',
body: 'Updated Hello there',
}));
});
await bridgeServer.start({ workflows: [newWorkflow] });

const result = await session.testAgent.post(`/v1/bridge/sync`).send({
bridgeUrl: bridgeServer.serverPath,
});

expect(result.status).to.equal(201);

// Verify the workflow was updated
const workflows = await workflowsRepository.findOne({
_environmentId: session.environment._id,
_id: externalWorkflow._id,
});
expect(workflows?.origin).to.equal(WorkflowOriginEnum.EXTERNAL);
expect(workflows?.steps[0]?.stepId).to.equal('send-email');
});
});
42 changes: 26 additions & 16 deletions apps/api/src/app/bridge/usecases/sync/sync.usecase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,25 @@ export class Sync {
command: SyncCommand,
workflowsFromBridge: DiscoverWorkflowOutput[]
): Promise<NotificationTemplateEntity[]> {
const existingFrameworkWorkflows = await Promise.all(
workflowsFromBridge.map((workflow) =>
this.notificationTemplateRepository.findByTriggerIdentifier(command.environmentId, workflow.workflowId)
)
);

existingFrameworkWorkflows.forEach((workflow, index) => {
if (workflow?.origin && workflow.origin !== WorkflowOriginEnum.EXTERNAL) {
const { workflowId } = workflowsFromBridge[index];
throw new BadRequestException(
`Workflow ${workflowId} was already created in Dashboard. Please use another workflowId.`
);
}
});

return Promise.all(
workflowsFromBridge.map(async (workflow) => {
let savedWorkflow = await this.upsertWorkflow(command, workflow);
workflowsFromBridge.map(async (workflow, index) => {
const existingFrameworkWorkflow = existingFrameworkWorkflows[index];
let savedWorkflow = await this.upsertWorkflow(command, workflow, existingFrameworkWorkflow);

const validatedWorkflowWithIssues = await this.workflowUpdatePostProcess.execute({
user: {
Expand Down Expand Up @@ -197,24 +213,18 @@ export class Sync {

private async upsertWorkflow(
command: SyncCommand,
workflow: DiscoverWorkflowOutput
workflow: DiscoverWorkflowOutput,
existingFrameworkWorkflow: NotificationTemplateEntity | null
): Promise<NotificationTemplateEntity> {
const workflowExist = await this.notificationTemplateRepository.findByTriggerIdentifier(
command.environmentId,
workflow.workflowId
);

let savedWorkflow: NotificationTemplateEntity | undefined;

if (workflowExist) {
savedWorkflow = await this.updateWorkflowUsecase.execute(
UpdateWorkflowCommand.create(this.mapDiscoverWorkflowToUpdateWorkflowCommand(workflowExist, command, workflow))
if (existingFrameworkWorkflow) {
return await this.updateWorkflowUsecase.execute(
UpdateWorkflowCommand.create(
this.mapDiscoverWorkflowToUpdateWorkflowCommand(existingFrameworkWorkflow, command, workflow)
)
);
} else {
savedWorkflow = await this.createWorkflow(command, workflow);
}

return savedWorkflow;
return await this.createWorkflow(command, workflow);
}

private async createWorkflow(
Expand Down
Loading

0 comments on commit 2afc80c

Please sign in to comment.