Skip to content

Commit

Permalink
chore(root): Release 2024-12-03 10:32 (#7198)
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored Dec 3, 2024
2 parents e30bc32 + 32b40a1 commit 0e2d170
Show file tree
Hide file tree
Showing 25 changed files with 463 additions and 282 deletions.
22 changes: 16 additions & 6 deletions apps/api/src/app/workflows-v2/e2e/generate-preview.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ describe('Workflow Step Preview - POST /:workflowId/step/:stepId/preview', () =>
});
});

it('should return 404 for non-existent workflow', async () => {
it('should return 201 for non-existent workflow', async () => {
const pay = {
type: 'object',
properties: {
Expand All @@ -450,11 +450,16 @@ describe('Workflow Step Preview - POST /:workflowId/step/:stepId/preview', () =>
controlValues: {},
});

expect(response.status).to.equal(404);
expect(response.body.message).to.contain('Workflow cannot be found');
expect(response.status).to.equal(201);
expect(response.body.data).to.deep.equal({
result: {
preview: {},
},
previewPayloadExample: {},
});
});

it('should return 400 for non-existent step', async () => {
it('should return 201 for non-existent step', async () => {
const pay = {
type: 'object',
properties: {
Expand All @@ -478,8 +483,13 @@ describe('Workflow Step Preview - POST /:workflowId/step/:stepId/preview', () =>
controlValues: {},
});

expect(response.status).to.equal(400);
expect(response.body.message).to.contain('No step found');
expect(response.status).to.equal(201);
expect(response.body.data).to.deep.equal({
result: {
preview: {},
},
previewPayloadExample: {},
});
});

async function createWorkflow(overrides: Partial<NotificationTemplateEntity> = {}): Promise<WorkflowResponseDto> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common';
import _ from 'lodash';
import {
ChannelTypeEnum,
Expand All @@ -14,7 +14,9 @@ import {
WorkflowInternalResponseDto,
Instrument,
InstrumentUsecase,
PinoLogger,
} from '@novu/application-generic';
import { captureException } from '@sentry/node';
import { PreviewStep, PreviewStepCommand } from '../../../bridge/usecases/preview-step';
import { FrameworkPreviousStepsOutputState } from '../../../bridge/usecases/preview-step/preview-step.command';
import { BuildStepDataUsecase } from '../build-step-data';
Expand All @@ -24,48 +26,75 @@ import { pathsToObject } from '../../util/path-to-object';
import { createMockPayloadFromSchema, flattenObjectValues } from '../../util/utils';
import { PrepareAndValidateContentUsecase } from '../validate-content/prepare-and-validate-content/prepare-and-validate-content.usecase';

const LOG_CONTEXT = 'GeneratePreviewUsecase';

@Injectable()
export class GeneratePreviewUsecase {
constructor(
private legacyPreviewStepUseCase: PreviewStep,
private buildStepDataUsecase: BuildStepDataUsecase,
private getWorkflowByIdsUseCase: GetWorkflowByIdsUseCase,
private readonly logger: PinoLogger,
private prepareAndValidateContentUsecase: PrepareAndValidateContentUsecase
) {}

@InstrumentUsecase()
async execute(command: GeneratePreviewCommand): Promise<GeneratePreviewResponseDto> {
const { previewPayload: commandVariablesExample, controlValues: commandControlValues } =
command.generatePreviewRequestDto;
const stepData = await this.getStepData(command);
const workflow = await this.findWorkflow(command);
const preparedAndValidatedContent = await this.prepareAndValidateContentUsecase.execute({
user: command.user,
previewPayloadFromDto: commandVariablesExample,
controlValues: commandControlValues || stepData.controls.values || {},
controlDataSchema: stepData.controls.dataSchema || {},
variableSchema: stepData.variables,
});
const variablesExample = this.buildVariablesExample(
workflow,
preparedAndValidatedContent.finalPayload,
commandVariablesExample
);
try {
const { previewPayload: commandVariablesExample, controlValues: commandControlValues } =
command.generatePreviewRequestDto;
const stepData = await this.getStepData(command);
const workflow = await this.findWorkflow(command);
const preparedAndValidatedContent = await this.prepareAndValidateContentUsecase.execute({
user: command.user,
previewPayloadFromDto: commandVariablesExample,
controlValues: commandControlValues || stepData.controls.values || {},
controlDataSchema: stepData.controls.dataSchema || {},
variableSchema: stepData.variables,
});
const variablesExample = this.buildVariablesExample(
workflow,
preparedAndValidatedContent.finalPayload,
commandVariablesExample
);

const executeOutput = await this.executePreviewUsecase(
command,
stepData,
variablesExample,
preparedAndValidatedContent.finalControlValues
);
const executeOutput = await this.executePreviewUsecase(
command,
stepData,
variablesExample,
preparedAndValidatedContent.finalControlValues
);

return {
result: {
preview: executeOutput.outputs as any,
type: stepData.type as unknown as ChannelTypeEnum,
},
previewPayloadExample: variablesExample,
};
return {
result: {
preview: executeOutput.outputs as any,
type: stepData.type as unknown as ChannelTypeEnum,
},
previewPayloadExample: variablesExample,
};
} catch (error) {
this.logger.error(
{
err: error,
workflowIdOrInternalId: command.identifierOrInternalId,
stepIdOrInternalId: command.stepDatabaseId,
},
`Unexpected error while generating preview`,
LOG_CONTEXT
);

if (process.env.SENTRY_DSN) {
captureException(error);
}

return {
result: {
preview: {},
type: undefined,
},
previewPayloadExample: {},
} as any;
}
}

@Instrument()
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"cmdk": "1.0.0",
"date-fns": "^4.1.0",
"flat": "^6.0.1",
"framer-motion": "^11.3.19",
"motion": "^11.12.0",
"js-cookie": "^3.0.5",
"lodash.debounce": "^4.0.8",
"lodash.merge": "^4.6.2",
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/animated-outlet.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useRef } from 'react';
import { useLocation, useOutlet } from 'react-router-dom';
import { AnimatePresence } from 'framer-motion';
import { AnimatePresence } from 'motion/react';

export const AnimatedOutlet = (): React.JSX.Element => {
const { pathname, state } = useLocation();
Expand Down
15 changes: 10 additions & 5 deletions apps/dashboard/src/components/primitives/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,40 @@ import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react';

const badgeVariants = cva(
'inline-flex items-center [&>svg]:shrink-0 gap-1 h-fit border text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
'inline-flex items-center [&>svg]:shrink-0 gap-1 h-fit border transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
{
variants: {
variant: {
neutral: 'border-neutral-500 bg-neutral-500',
neutral: 'border-neutral-100 bg-neutral-100 text-neutral-500',
destructive: 'border-transparent bg-destructive/10 text-destructive',
success: 'border-transparent bg-success/10 text-success',
warning: 'border-transparent bg-warning/10 text-warning',
soft: 'bg-neutral-alpha-200 text-foreground-500 border-transparent',
outline: 'border-neutral-alpha-200 bg-transparent font-normal text-foreground-600 shadow-sm',
},
size: {
kind: {
default: 'rounded-md px-2 py-1',
pill: 'rounded-full px-2',
'pill-stroke': 'rounded-full px-2',
tag: 'rounded-md py-0.5 px-2',
},
size: {
default: 'text-xs',
'2xs': 'text-[10px] leading-[14px] font-medium',
},
},
defaultVariants: {
variant: 'neutral',
kind: 'default',
size: 'default',
},
}
);

export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}

function Badge({ className, variant, size, ...props }: BadgeProps) {
return <div className={cn(badgeVariants({ variant, size }), className)} {...props} />;
function Badge({ className, variant, kind, size, ...props }: BadgeProps) {
return <div className={cn(badgeVariants({ variant, kind, size }), className)} {...props} />;
}

export { Badge };
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/primitives/tag-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const TagInput = forwardRef<HTMLInputElement, TagInputProps>((props, ref) => {
</PopoverAnchor>
<div className="flex flex-wrap gap-2">
{tags.map((tag, index) => (
<Badge key={index} variant="outline" size="tag" className="gap-1">
<Badge key={index} variant="outline" kind="tag" className="gap-1">
<span style={{ wordBreak: 'break-all' }}>{tag}</span>
<button type="button" onClick={() => removeTag(tag)}>
<RiCloseFill className="-mr-0.5 size-3" />
Expand Down
2 changes: 0 additions & 2 deletions apps/dashboard/src/components/user-profile.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { UserButton } from '@clerk/clerk-react';
import { useNewDashboardOptIn } from '@/hooks/use-new-dashboard-opt-in';
import { RiSignpostFill } from 'react-icons/ri';
import { LEGACY_DASHBOARD_URL } from '@/config';

export function UserProfile() {
const { optOut } = useNewDashboardOptIn();

return (
<UserButton
afterSignOutUrl={`${LEGACY_DASHBOARD_URL}/auth/login`}
appearance={{
elements: {
avatarBox: 'h-6 w-6',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const MenuItem = ({
/>
<span className="text-xs">{children}</span>
{disabled && (
<Badge size="pill" variant="soft" className="ml-auto opacity-40">
<Badge kind="pill" variant="soft" className="ml-auto opacity-40">
coming soon
</Badge>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { motion } from 'framer-motion';
import { motion } from 'motion/react';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { FaCode } from 'react-icons/fa6';
import { WorkflowOriginEnum } from '@novu/shared';

import { ArrowRight, RouteFill } from '@/components/icons';
import {
Breadcrumb,
Expand All @@ -14,6 +17,7 @@ import { useEnvironment } from '@/context/environment/hooks';
import { buildRoute, ROUTES } from '@/utils/routes';
import { useFetchWorkflow } from '@/hooks';
import TruncatedText from '@/components/truncated-text';
import { Badge } from '@/components/primitives/badge';

export const EditorBreadcrumbs = () => {
const { workflowSlug = '' } = useParams<{ workflowSlug: string }>();
Expand All @@ -29,6 +33,11 @@ export const EditorBreadcrumbs = () => {
{
label: 'Workflows',
href: workflowsRoute,
node: (
<Badge kind="pill" size="2xs" className="no-underline">
BETA
</Badge>
),
},
];

Expand All @@ -43,21 +52,30 @@ export const EditorBreadcrumbs = () => {
</Button>
<Breadcrumb>
<BreadcrumbList>
{breadcrumbs.map(({ label, href }) => (
{breadcrumbs.map(({ label, href, node }) => (
<React.Fragment key={`${href}_${label}`}>
<BreadcrumbItem>
<BreadcrumbItem className="flex items-center gap-1">
<BreadcrumbLink to={href}>{label}</BreadcrumbLink>
{node}
</BreadcrumbItem>
<BreadcrumbSeparator />
</React.Fragment>
))}
<BreadcrumbItem>
<BreadcrumbPage>
<RouteFill />
<div className="flex max-w-[32ch]">
<TruncatedText>{workflow?.name}</TruncatedText>
</div>
</BreadcrumbPage>
{workflow && (
<BreadcrumbPage className="flex items-center gap-1">
{workflow.origin === WorkflowOriginEnum.EXTERNAL ? (
<Badge variant="warning" kind="pill" size="2xs">
<FaCode className="size-3.5" />
</Badge>
) : (
<RouteFill className="size-4" />
)}
<div className="flex max-w-[32ch]">
<TruncatedText>{workflow?.name}</TruncatedText>
</div>
</BreadcrumbPage>
)}
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
WorkflowOriginEnum,
WorkflowResponseDto,
} from '@novu/shared';
import { motion } from 'framer-motion';
import { motion } from 'motion/react';
import { useMemo, useState } from 'react';
import { useForm } from 'react-hook-form';
import { RiArrowLeftSLine, RiArrowRightSLine, RiCloseFill, RiDeleteBin2Line, RiPencilRuler2Fill } from 'react-icons/ri';
Expand Down Expand Up @@ -57,10 +57,16 @@ export const ConfigureStepForm = (props: ConfigureStepFormProps) => {
navigate(buildRoute(ROUTES.EDIT_WORKFLOW, { environmentSlug: environment.slug!, workflowSlug: workflow.slug }));
};

const defaultValues = {
name: step.name,
stepId: step.stepId,
};

const form = useForm<z.infer<typeof stepSchema>>({
defaultValues: {
name: step.name,
stepId: step.stepId,
defaultValues,
values: {
...defaultValues,
...step.controls.values,
},
resolver: zodResolver(stepSchema),
shouldFocusError: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export const ConfigureStepTemplateForm = (props: ConfigureStepTemplateFormProps)
const form = useForm({
resolver: zodResolver(schema),
defaultValues,
values: {
...defaultValues,
...step.controls.values,
},
shouldFocusError: false,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { motion } from 'framer-motion';
import { motion } from 'motion/react';
import { useNavigate, useParams } from 'react-router-dom';

import {
Expand Down
Loading

0 comments on commit 0e2d170

Please sign in to comment.