Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const WorkflowRunActivityDrawer = forwardRef<HTMLDivElement, WorkflowRunA
return (
<Sheet open={isOpen} onOpenChange={onOpenChange}>
<SheetContent ref={forwardedRef} className="w-[490px]">
<SheetTitle className="text-label-sm text-text-strong border-b border-neutral-200 p-3">Event Logs</SheetTitle>
<SheetTitle className="text-label-sm text-text-strong border-b border-neutral-200 p-3">Workflow run</SheetTitle>

<div className="flex h-full max-h-full flex-1 flex-col overflow-auto">
{currentActivityId ? (
Expand Down
4 changes: 3 additions & 1 deletion apps/dashboard/src/components/workflow-editor/step-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ export const updateStepInWorkflow = (
steps: workflow.steps.map((step) => {
if (step.stepId === stepId) {
const existingControlValues = step.controls?.values || {};
const updatedControlValues = updateStep.controlValues || existingControlValues;
const updatedControlValues = updateStep.controlValues !== undefined
? updateStep.controlValues
: existingControlValues;

return {
...step,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { InlineToast } from '@/components/primitives/inline-toast';
import { Separator } from '@/components/primitives/separator';
import { Switch } from '@/components/primitives/switch';
import { SidebarContent } from '@/components/side-navigation/sidebar';
import { updateStepInWorkflow } from '@/components/workflow-editor/step-utils';
import { useSaveForm } from '@/components/workflow-editor/steps/save-form-context';
import { ResourceOriginEnum } from '@/utils/enums';
import { buildDefaultValuesOfDataSchema } from '@/utils/schema';
Expand All @@ -29,7 +30,7 @@ const CONTROLS_DOCS_LINK = 'https://docs.novu.co/framework/controls';
export const CustomStepControls = (props: CustomStepControlsProps) => {
const { className, dataSchema, origin } = props;
const [isRestoreDefaultModalOpen, setIsRestoreDefaultModalOpen] = useState(false);
const { step } = useWorkflow();
const { step, workflow, update } = useWorkflow();
const [isOverridden, setIsOverridden] = useState(() => Object.keys(step?.controls.values ?? {}).length > 0);
const { reset } = useFormContext();
const { saveForm } = useSaveForm();
Expand Down Expand Up @@ -98,9 +99,11 @@ export const CustomStepControls = (props: CustomStepControlsProps) => {
open={isRestoreDefaultModalOpen}
onOpenChange={setIsRestoreDefaultModalOpen}
onConfirm={async () => {
const defaultValues = buildDefaultValuesOfDataSchema(step?.controls.dataSchema ?? {});
if (!workflow || !step) return;

const defaultValues = buildDefaultValuesOfDataSchema(step.controls.dataSchema ?? {});
reset(defaultValues);
saveForm({ forceSubmit: true });
update(updateStepInWorkflow(workflow, step.stepId, { controlValues: null }));
setIsRestoreDefaultModalOpen(false);
setIsOverridden(false);
}}
Expand Down Expand Up @@ -149,7 +152,12 @@ export const CustomStepControls = (props: CustomStepControlsProps) => {
</AccordionTrigger>

<AccordionContent>
<div className="bg-background rounded-md border border-dashed p-3">
<div
className={cn(
'bg-background rounded-md border border-dashed p-3',
!isOverridden && 'opacity-60 pointer-events-none'
)}
>
<JsonForm schema={(dataSchema as RJSFSchema) || {}} disabled={!isOverridden} />
</div>
</AccordionContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const TestWorkflowActivityDrawer = forwardRef<HTMLDivElement, TestWorkflo
return (
<Sheet open={isOpen} onOpenChange={onOpenChange}>
<SheetContent ref={forwardedRef} className="w-[490px]">
<SheetTitle className="text-label-sm text-text-strong border-b border-neutral-200 p-3">Event Logs</SheetTitle>
<SheetTitle className="text-label-sm text-text-strong border-b border-neutral-200 p-3">Workflow run</SheetTitle>

<div className="flex h-full max-h-full flex-1 flex-col overflow-auto">
{localTransactionId ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export const TestWorkflowLogsSidebar = (props: TestWorkflowLogsSidebarProps) =>
</div>
<div className="flex flex-col gap-2">
<p className="text-foreground-400 max-w-[30ch] text-sm">
No logs to show, trigger test run to see event logs appear here
No logs to show, trigger test run to see workflow run appear here
</p>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EnvironmentEntity, NotificationTemplateEntity, OrganizationEntity } from '@novu/dal';
import type { EnvironmentEntity, NotificationTemplateEntity, OrganizationEntity, PreferencesEntity } from '@novu/dal';
import type { UserSessionData } from '@novu/shared';

export enum InMemoryLRUCacheStore {
Expand All @@ -8,6 +8,7 @@ export enum InMemoryLRUCacheStore {
API_KEY_USER = 'api-key-user',
VALIDATOR = 'validator',
ACTIVE_WORKFLOWS = 'active-workflows',
WORKFLOW_PREFERENCES = 'workflow-preferences',
}

export type WorkflowCacheData = NotificationTemplateEntity | null;
Expand All @@ -16,6 +17,7 @@ export type EnvironmentCacheData = Pick<EnvironmentEntity, '_id' | 'echo' | 'api
export type ApiKeyUserCacheData = UserSessionData | null;
export type ValidatorCacheData = unknown;
export type ActiveWorkflowsCacheData = NotificationTemplateEntity[];
export type WorkflowPreferencesCacheData = [PreferencesEntity | null, PreferencesEntity | null];

export type CacheStoreDataTypeMap = {
[InMemoryLRUCacheStore.WORKFLOW]: WorkflowCacheData;
Expand All @@ -24,6 +26,7 @@ export type CacheStoreDataTypeMap = {
[InMemoryLRUCacheStore.API_KEY_USER]: ApiKeyUserCacheData;
[InMemoryLRUCacheStore.VALIDATOR]: ValidatorCacheData;
[InMemoryLRUCacheStore.ACTIVE_WORKFLOWS]: ActiveWorkflowsCacheData;
[InMemoryLRUCacheStore.WORKFLOW_PREFERENCES]: WorkflowPreferencesCacheData;
};

export type StoreConfig = {
Expand Down Expand Up @@ -65,4 +68,9 @@ export const STORE_CONFIGS: Record<InMemoryLRUCacheStore, StoreConfig> = {
ttl: 1000 * 60,
featureFlagComponent: 'active-workflows',
},
[InMemoryLRUCacheStore.WORKFLOW_PREFERENCES]: {
max: 1000,
ttl: 1000 * 60,
featureFlagComponent: 'workflow-preferences',
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '@novu/shared';
import { Instrument, InstrumentUsecase } from '../../instrumentation';
import { FeatureFlagsService } from '../../services/feature-flags';
import { InMemoryLRUCacheService, InMemoryLRUCacheStore } from '../../services/in-memory-lru-cache';
import { MergePreferencesCommand } from '../merge-preferences/merge-preferences.command';
import { MergePreferences } from '../merge-preferences/merge-preferences.usecase';
import { GetPreferencesCommand } from './get-preferences.command';
Expand Down Expand Up @@ -41,7 +42,8 @@ class PreferencesNotFoundException extends BadRequestException {
export class GetPreferences {
constructor(
private preferencesRepository: PreferencesRepository,
private featureFlagsService: FeatureFlagsService
private featureFlagsService: FeatureFlagsService,
private inMemoryLRUCacheService: InMemoryLRUCacheService
) {}

@InstrumentUsecase()
Expand Down Expand Up @@ -249,12 +251,38 @@ export class GetPreferences {

const queryOptions = { readPreference: 'secondaryPreferred' as const };

const orConditions: Array<Record<string, unknown>> = [
{
_templateId: command.templateId,
type: { $in: [PreferencesTypeEnum.WORKFLOW_RESOURCE, PreferencesTypeEnum.USER_WORKFLOW] },
const cacheOptions = {
environmentId: command.environmentId,
organizationId: command.organizationId,
};

const workflowPreferences = await this.inMemoryLRUCacheService.get(
InMemoryLRUCacheStore.WORKFLOW_PREFERENCES,
`${command.environmentId}:${command.templateId}`,
async (): Promise<[PreferencesEntity | null, PreferencesEntity | null]> => {
const preferences = await this.preferencesRepository.find(
{
...baseQuery,
_templateId: command.templateId,
type: { $in: [PreferencesTypeEnum.WORKFLOW_RESOURCE, PreferencesTypeEnum.USER_WORKFLOW] },
},
undefined,
queryOptions
);

const workflowResourcePreference =
preferences.find((p) => p.type === PreferencesTypeEnum.WORKFLOW_RESOURCE) ?? null;
const workflowUserPreference = preferences.find((p) => p.type === PreferencesTypeEnum.USER_WORKFLOW) ?? null;

return [workflowResourcePreference, workflowUserPreference];
},
];
cacheOptions
);

const [workflowResourcePreference, workflowUserPreference] = workflowPreferences;

let subscriberWorkflowPreference: PreferencesEntity | null = null;
let subscriberGlobalPreference: PreferencesEntity | null = null;

if (command.subscriberId) {
const useContextFiltering = await this.featureFlagsService.getFlag({
Expand All @@ -267,47 +295,48 @@ export class GetPreferences {
enabled: useContextFiltering,
});

orConditions.push(
{
_subscriberId: command.subscriberId,
_templateId: command.templateId,
type: PreferencesTypeEnum.SUBSCRIBER_WORKFLOW,
...contextQuery,
},
{
_subscriberId: command.subscriberId,
type: PreferencesTypeEnum.SUBSCRIBER_GLOBAL,
...contextQuery,
}
);
[subscriberWorkflowPreference, subscriberGlobalPreference] = await Promise.all([
this.preferencesRepository.findOne(
{
...baseQuery,
_subscriberId: command.subscriberId,
_templateId: command.templateId,
type: PreferencesTypeEnum.SUBSCRIBER_WORKFLOW,
...contextQuery,
},
undefined,
queryOptions
),
this.preferencesRepository.findOne(
{
...baseQuery,
_subscriberId: command.subscriberId,
type: PreferencesTypeEnum.SUBSCRIBER_GLOBAL,
...contextQuery,
},
undefined,
queryOptions
),
]);
}

const allPreferences = await this.preferencesRepository.find(
{
...baseQuery,
$or: orConditions,
},
undefined,
queryOptions
);

const result: PreferenceSet = {};

for (const preference of allPreferences) {
switch (preference.type) {
case PreferencesTypeEnum.WORKFLOW_RESOURCE:
result.workflowResourcePreference = preference as PreferenceSet['workflowResourcePreference'];
break;
case PreferencesTypeEnum.USER_WORKFLOW:
result.workflowUserPreference = preference as PreferenceSet['workflowUserPreference'];
break;
case PreferencesTypeEnum.SUBSCRIBER_WORKFLOW:
result.subscriberWorkflowPreference = preference as PreferenceSet['subscriberWorkflowPreference'];
break;
case PreferencesTypeEnum.SUBSCRIBER_GLOBAL:
result.subscriberGlobalPreference = preference as PreferenceSet['subscriberGlobalPreference'];
break;
}
if (workflowResourcePreference) {
result.workflowResourcePreference = workflowResourcePreference as PreferenceSet['workflowResourcePreference'];
}

if (workflowUserPreference) {
result.workflowUserPreference = workflowUserPreference as PreferenceSet['workflowUserPreference'];
}

if (subscriberWorkflowPreference) {
result.subscriberWorkflowPreference =
subscriberWorkflowPreference as PreferenceSet['subscriberWorkflowPreference'];
}

if (subscriberGlobalPreference) {
result.subscriberGlobalPreference = subscriberGlobalPreference as PreferenceSet['subscriberGlobalPreference'];
}

return result;
Expand Down
Loading