Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secrets list as dropdown from existing secrets and frontend warnings for deleted secrets #37

Merged

Conversation

leon-schmid
Copy link
Member

@leon-schmid leon-schmid commented Oct 23, 2024

This PR introduces a new SaveWorkflowProvder which is used in the save-workflow-button for instacen.
Additionally, the user can now select a secret for an action node via a dropdown showing the users's saved secrets.
Should a previously selected secret be deleted, it will show as a warning for the particual node and in the respective edit-panel.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced ActionEditPanel with a dropdown for selecting secrets and an option to save and redirect.
    • Added visual indicators in NodeBase for nodes with missing secrets.
    • Introduced improved state management for deleted secrets in the workflow store.
    • Updated Secrets component for better secret management and cleanup.
  • Bug Fixes

    • Updated error handling in the workflow saving process to return explicit success or failure.
  • Documentation

    • Clarified method names for better understanding of functionality.
  • Chores

    • Improved cache control in the workflow API hook.

Copy link

vercel bot commented Oct 23, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
admyral-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 4, 2024 8:15pm

@leon-schmid leon-schmid force-pushed the ADM-369/Secrets-list-as-dropdown-from-existing-secrets branch from 933f15a to 39f66a1 Compare October 28, 2024 13:12
@leon-schmid leon-schmid marked this pull request as ready for review October 28, 2024 16:38
@leon-schmid leon-schmid changed the title WIP: Secrets list as dropdown from existing secrets Secrets list as dropdown from existing secrets and frontend warnings for deleted secrets Oct 28, 2024
return;
}
workflow.nodes.forEach((node, nodeIdx) => {
console.log("node", node);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

@@ -56,6 +56,7 @@ type WorkflowStoreState = TReactFlowGraph & {
webhookSecret: string | null;
lastDeletedEdges: TReactFlowEdge[];
payloadCache: string;
missingSecretForNodes: string[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think a mapping from index to missing secret is correct because the index of a node can change, e.g., if a node with a smaller index is deleted. You need to remember the nodes by their id and, therefore, I would suggest to use Set<string>.

General feedback on data structure here: If you want to use an array for flagging something, then don't use strings (waste of memory) instead use an array of boolean (still not optimal) or even better a bitset.

Comment on lines 69 to 85
{hasMissingSecret && (
<Flex
className="absolute top-2 right-2"
justify="end"
align="start"
>
<Tooltip content="Missing secret">
<Flex
className="bg-red-500 rounded-full w-6 h-6"
justify="center"
align="center"
>
<ExclamationTriangleIcon className="text-white w-4 h-4" />
</Flex>
</Tooltip>
</Flex>
)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Briefly tested it, this is also displayed for me if I click on "Create new workflow".

Comment on lines 120 to 123
const secretName = Object.values(node.secretsMapping)[0];
if (
secretName &&
!encryptedSecrets.find((s) => s.secretId === secretName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: imo it is a bit cleaner to build a set of the secret ids before iterating over the nodes because then you can perform a lookup in constant time instead of always iterating here over all the encrypted secrets.

@@ -87,6 +88,9 @@ type WorkflowStoreState = TReactFlowGraph & {
deleteNodeByIdx: (nodeIdx: number) => void;
deleteControlByIdx: (controlIdx: number) => void;
duplicateNodeByIdx: (nodeIdx: number) => void;
addMissingSecretByIdx: (secret: string, nodeIdx: number) => void;
deleteMissingSecretByIdx: (idx: number) => void;
getMissingSecretByIdx: (idx: number) => string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasMissingSecret: (nodeId: string) => bool; is a cleaner interface imo

addMissingSecretByIdx: (secret: string, nodeIdx: number) =>
set(
produce((draft) => {
draft.missingSecretForNodes[nodeIdx] = secret;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Just learned that for out-of-bounds access the array automatically grows and initializes the new fields with undefined. Since you are relying on this, adding new nodes will always display an error regarding the not available secrets (see my start node comment)

if (!acc.has(node.id)) {
acc.set(node.id, new Set());
}
acc.get(node.id)?.add(secretIdx);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed that the secrets mapping in TEditorWorkflowActionNode is of type z.record(z.string(), z.string()) and not an array. So, the data structure should then be Map<string, Set<string>> while we add the secretsMapping to the set. So, you now need to iterate over the keys of node.secretsMapping instead of the values.

@@ -29,5 +29,6 @@ export const useGetWorkflowApi = (workflowId: string, doApiCall: boolean) => {
queryKey: ["workflow", workflowId],
queryFn: () => getWorkflow({ workflowId }),
enabled: doApiCall,
gcTime: 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment here that this disables the cache?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or instead: let's introduce a constant const DISABLE_CACHE = 0; and replace that with the magic number here.

Comment on lines 386 to 395
if (missingSecrets) {
if (secretIdx !== undefined) {
return (
missingSecrets.has(nodeId) &&
missingSecrets.get(nodeId)!.has(secretIdx)
);
}
return missingSecrets.has(nodeId);
}
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification suggestion:

Suggested change
if (missingSecrets) {
if (secretIdx !== undefined) {
return (
missingSecrets.has(nodeId) &&
missingSecrets.get(nodeId)!.has(secretIdx)
);
}
return missingSecrets.has(nodeId);
}
return false;
return !!(secretIdx === undefined ? missingSecrets?.has(nodeId) : (missingSecrets?.get(nodeId)?.has(secretIdx));

Note: secretIdx should be replaced with secretMappingPlaceholder (see comment above)

Comment on lines 406 to 411
if (draft.missingSecretForNodes.has(nodeId)) {
draft.missingSecretForNodes.get(nodeId).delete(secretIdx);
if (draft.missingSecretForNodes.get(nodeId).size === 0) {
draft.missingSecretForNodes.delete(nodeId);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to perform lookup 3 times. I think you can just do:

Suggested change
if (draft.missingSecretForNodes.has(nodeId)) {
draft.missingSecretForNodes.get(nodeId).delete(secretIdx);
if (draft.missingSecretForNodes.get(nodeId).size === 0) {
draft.missingSecretForNodes.delete(nodeId);
}
}
const secretPlaceholdersWithDeletedSecret = draft.missingSecretForNodes.get(nodeId);
secretPlaceholdersWithDeletedSecret?.delete(secretIdx);
if (secretPlaceholdersWithDeletedSecret?.size === 0) {
draft.missingSecretForNodes.delete(nodeId);
}

@@ -106,6 +112,7 @@ export const useWorkflowStore = create<WorkflowStoreState>((set, get) => ({
isActive: false,
nodes: [],
edges: [],
missingSecretForNodes: undefined,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this name is confusing because we are tracking nodes with deleted secrets.

transform: "translate(10px, 24px)",
}}
>
<Flex className="absolute inset-0 bg-[var(--red-9)] rounded-full " />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove the whitespace at the end?

Suggested change
<Flex className="absolute inset-0 bg-[var(--red-9)] rounded-full " />
<Flex className="absolute inset-0 bg-[var(--red-9)] rounded-full" />

const onChangeSecretsMapping = (
secretPlaceholder: string,
event: ChangeEvent<HTMLInputElement>,
value: string,
secretIdx: number,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please switch to secretPlaceholder (see comment below)

@@ -87,6 +90,12 @@ type WorkflowStoreState = TReactFlowGraph & {
deleteNodeByIdx: (nodeIdx: number) => void;
deleteControlByIdx: (controlIdx: number) => void;
duplicateNodeByIdx: (nodeIdx: number) => void;
hasDeletedSecret: (nodeId: string, secretPlaceholer?: string) => boolean;
addDeletedSecrets: (missingSecrets: Map<string, Set<string>>) => void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This a setter, so the better naming would be setDeletedSecrets. Also, the parameter name mismatches the function name. deletedSecrets would probably be more appropriate.

.filter((node) => node.type === "action")
.reduce((acc, node) => {
const secretsMapping = node.secretsMapping;
const secretsForNode = Object.keys(secretsMapping);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

secretsForNode --> secretPlaceholders

useEffect(() => {
if (encryptedSecrets) {
setSecrets(encryptedSecrets);
return () => clear();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should rename clear from the secrets store to clearSecretsStore to be consistent with the workflow store naming. Also, makes it more clear here what's happening.

<Flex
direction="row"
align="center"
className="text-red-500 font-medium mt-2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the component has props for doing what you want (text color, font weight), then please use this instead of using tailwindcss. You can just do <Text color="red" weight="medium"> for example.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, <Flex> has props for margin top: <Flex mt="2">.

Copy link

coderabbitai bot commented Nov 4, 2024

Walkthrough

The changes primarily involve updates to several components and hooks within the codebase, focusing on the management of secrets and workflow processes. Key modifications include the renaming of a method in the secrets store, the introduction of new hooks and properties in the workflow editor, and enhancements to the workflow saving mechanism. Additionally, new logic has been added to handle deleted secrets more effectively, improving state management across the application.

Changes

File Path Change Summary
web/src/components/secrets/secrets.tsx Renamed clear method to clearSecretsStore in useSecretsStore and updated useEffect hook.
web/src/components/workflow-editor/edit-panel/action-edit-panel.tsx Added Select import, integrated useSecretsStore and useSaveWorkflow, updated onChangeSecretsMapping function to handle new cases, and modified rendering logic for secrets mapping.
web/src/components/workflow-editor/workflow-editor.tsx Added useListSecretsApi and useSecretsStore, introduced effects for managing secrets and deleted secrets.
web/src/components/workflow-graph/node-base.tsx Updated icon imports, added NodeToolbar for missing secrets indication using hasDeletedSecret.
web/src/hooks/use-get-workflow-api.ts Introduced DISABLE_CACHE constant and modified useGetWorkflowApi to include cache control.
web/src/providers/save-workflow.tsx Changed saveWorkflow return type from Promise<void> to Promise<boolean> and updated logic for success and error handling.
web/src/stores/secrets-store.ts Renamed clear method to clearSecretsStore in SecretsStore type.
web/src/stores/workflow-store.ts Added deletedSecretsForNodes property, new methods for managing deleted secrets, and updated existing methods to accommodate new state structure.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ActionEditPanel
    participant WorkflowEditor
    participant SecretsStore

    User->>ActionEditPanel: Select secret or save workflow
    ActionEditPanel->>SecretsStore: Call removeDeletedSecretByPlaceholder
    SecretsStore-->>ActionEditPanel: Update state
    ActionEditPanel->>User: Show updated selection or message
    User->>WorkflowEditor: View workflow
    WorkflowEditor->>SecretsStore: Retrieve secrets
    SecretsStore-->>WorkflowEditor: Provide secrets
    WorkflowEditor->>User: Display workflow with secrets
Loading

🐰 "In the meadow where secrets lay,
A new method hops in to play.
With dropdowns and tools, all in a row,
Saving workflows, watch them grow!
Clear the old, embrace the new,
In our code, the magic's true!" 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between df638cd and 4f62392.

📒 Files selected for processing (1)
  • web/src/components/workflow-editor/workflow-editor.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/src/components/workflow-editor/workflow-editor.tsx

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (12)
web/src/hooks/use-get-workflow-api.ts (1)

10-11: Add documentation for the DISABLE_CACHE constant.

While the constant effectively addresses the magic number concern, it would be helpful to add a comment explaining why cache disabling is necessary in this context.

+// Set to 0 to disable caching, ensuring we always fetch fresh workflow data
+// This is important for maintaining consistency with the workflow editor state
 const DISABLE_CACHE = 0;
web/src/components/secrets/secrets.tsx (2)

26-31: Consider unconditional cleanup.

The cleanup function is currently only registered when encryptedSecrets exists. Consider moving it outside the conditional to ensure cleanup always occurs:

 useEffect(() => {
   if (encryptedSecrets) {
     setSecrets(encryptedSecrets);
-    return () => clearSecretsStore();
   }
+  return () => clearSecretsStore();
 }, [encryptedSecrets, setSecrets, clearSecretsStore]);

Line range hint 33-35: Consider adding a loading indicator.

Currently, the component returns null while loading. Consider adding a loading spinner or skeleton to improve user experience.

 if (isListingSecretsLoading) {
-  return null;
+  return <LoadingSpinner />; // or <Skeleton />
 }
web/src/components/workflow-graph/node-base.tsx (1)

66-97: Consider improving accessibility and simplifying the warning indicator structure.

While the implementation works, there are a few areas that could be enhanced:

  1. Add ARIA attributes for better accessibility
  2. Consider using CSS variables for positioning instead of hardcoded pixel values
  3. Simplify the nested Flex structure

Here's a suggested improvement:

 <NodeToolbar
   isVisible={hasDeletedSecret(nodeId)}
   position={Position.Top}
   offset={8}
   align="end"
 >
-  <Tooltip content="Missing secret">
+  <Tooltip content="Missing secret" role="alert">
     <Flex
+      className="transform translate-x-2.5 translate-y-6"
       style={{
-        transform: "translate(10px, 24px)",
+        "--warning-size": "24px",
+        "--icon-size": "14px"
       }}
     >
-      <Flex
-        inset="0"
-        className="absolute bg-[var(--red-9)] rounded-full"
-      />
-      <Flex
-        className="relative rounded-full"
-        width="24px"
-        height="24px"
-        justify="center"
-        align="center"
-      >
+      <Flex
+        className="relative bg-[var(--red-9)] rounded-full flex items-center justify-center"
+        width="var(--warning-size)"
+        height="var(--warning-size)"
+        aria-label="Secret missing warning"
+      >
         <ExclamationTriangleIcon
           className="text-white"
-          width="14px"
-          height="14px"
+          width="var(--icon-size)"
+          height="var(--icon-size)"
         />
       </Flex>
     </Flex>
   </Tooltip>
 </NodeToolbar>

The changes:

  • Add ARIA attributes for better screen reader support
  • Use CSS variables for consistent sizing
  • Use Tailwind classes for transforms
  • Simplify the nested Flex structure
  • Add semantic role="alert" for accessibility
web/src/providers/save-workflow.tsx (1)

Line range hint 71-89: Consider extracting the argument filtering logic.

The argument cleanup logic is complex and could benefit from being extracted into a separate function for better maintainability.

Consider refactoring like this:

+const filterValidActionArgs = (
+  args: Record<string, string>,
+  validArgs: Set<string>
+): Record<string, string> => {
+  return Object.keys(args)
+    .filter((argName) => validArgs.has(argName))
+    .reduce(
+      (acc, argName) => {
+        acc[argName] = args[argName];
+        return acc;
+      },
+      {} as Record<string, string>
+    );
+};

 if (node.type === EditorWorkflowNodeType.ACTION) {
   const argsFilter = new Set(
     actionsIndex[node.actionType].arguments.map(
       (arg) => arg.argName,
     ),
   );
-  const args = Object.keys(node.args)
-    .filter((argName) => argsFilter.has(argName))
-    .reduce(
-      (acc, argName) => {
-        acc[argName] = node.args[argName];
-        return acc;
-      },
-      {} as Record<string, string>,
-    );
+  const args = filterValidActionArgs(node.args, argsFilter);
   return {
     ...node,
     args,
   };
 }
web/src/components/workflow-editor/workflow-editor.tsx (1)

114-142: Consider extracting the deleted secrets detection logic.

While the implementation is correct and efficiently uses Set/Map for lookups, the effect's complexity could benefit from extraction into a separate utility function for better readability and testability.

Consider refactoring like this:

const detectDeletedSecrets = (
  nodes: WorkflowNode[],
  secretIds: Set<string>
): Map<string, Set<string>> => {
  return nodes
    .filter((node) => node.type === "action")
    .reduce((acc, node) => {
      const secretsMapping = node.secretsMapping;
      const secretPlaceholders = Object.keys(secretsMapping);
      
      secretPlaceholders.forEach((placeholder) => {
        if (!secretIds.has(secretsMapping[placeholder])) {
          if (!acc.has(node.id)) {
            acc.set(node.id, new Set());
          }
          acc.get(node.id)?.add(placeholder);
        }
      });
      return acc;
    }, new Map<string, Set<string>>());
};

// In the effect:
useEffect(() => {
  if (!workflow?.nodes || !encryptedSecrets) {
    return;
  }
  
  const secretIds = new Set(encryptedSecrets.map((s) => s.secretId));
  const deletedSecrets = detectDeletedSecrets(workflow.nodes, secretIds);
  
  if (deletedSecrets.size > 0) {
    setDeletedSecrets(deletedSecrets);
    errorToast(
      "Some secrets are missing. Please add them before running the workflow."
    );
  }
}, [encryptedSecrets, workflow, setDeletedSecrets]);
web/src/components/workflow-editor/edit-panel/action-edit-panel.tsx (3)

70-75: Consider adding error feedback for failed save operations.

The save operation correctly handles success, but users might benefit from feedback when the save fails.

 const saveWorkflowAndRedirect = async () => {
   const saveSuccessful = await saveWorkflow();
   if (saveSuccessful) {
     router.push("/settings");
+  } else {
+    // Consider using your notification system here
+    console.error("Failed to save workflow");
   }
 };

81-84: Extract magic string to constant.

The special value "$$$save_and_redirect$$$" should be extracted to a named constant for better maintainability and reuse.

+const SAVE_AND_REDIRECT_ACTION = "$$$save_and_redirect$$$" as const;
+
 const onChangeSecretsMapping = (
   secretPlaceholder: string,
   value: string,
 ) => {
-  if (value === "$$$save_and_redirect$$$") {
+  if (value === SAVE_AND_REDIRECT_ACTION) {
     saveWorkflowAndRedirect();
     return;
   }

204-218: Enhance accessibility for the warning message.

Consider adding appropriate ARIA attributes to improve screen reader experience for the warning message.

 <Flex
   mt="2"
   direction="row"
   align="center"
+  role="alert"
+  aria-live="polite"
 >
   <Text color="red" weight="medium">
     The previously selected secret could not be found.
   </Text>
 </Flex>
web/src/stores/workflow-store.ts (3)

61-61: Fix typo in parameter name

There's a typo in the parameter name 'secretPlaceholer' (missing 'd'). This should be fixed for consistency.

Apply this fix:

-	hasDeletedSecret: (nodeId: string, secretPlaceholer?: string) => boolean;
+	hasDeletedSecret: (nodeId: string, secretPlaceholder?: string) => boolean;
-		secretPlaceholer: string,
+		secretPlaceholder: string,

Also applies to: 93-98


399-412: Improve variable naming consistency

The variable name secretPlaceholdersWithDeletedSecrets should match the method name's terminology for better consistency.

Apply this change:

-				const secretPlaceholdersWithDeletedSecrets =
+				const deletedSecrets =
 					draft.deletedSecretsForNodes?.get(nodeId);
-				secretPlaceholdersWithDeletedSecrets?.delete(secretPlaceholder);
-				if (secretPlaceholdersWithDeletedSecrets?.size === 0) {
+				deletedSecrets?.delete(secretPlaceholder);
+				if (deletedSecrets?.size === 0) {
 					draft.deletedSecretsForNodes?.delete(nodeId);
 				}

Line range hint 163-187: Handle deletedSecretsForNodes in setWorkflow

The setWorkflow method should handle the deletedSecretsForNodes state to maintain consistency with other workflow properties.

Apply this change:

 				draft.isActive = workflow.isActive;
 				draft.nodes = workflow.nodes;
 				draft.edges = workflow.edges;
+				draft.deletedSecretsForNodes = undefined;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ec8a0ec and 32e10d4.

📒 Files selected for processing (8)
  • web/src/components/secrets/secrets.tsx (1 hunks)
  • web/src/components/workflow-editor/edit-panel/action-edit-panel.tsx (4 hunks)
  • web/src/components/workflow-editor/workflow-editor.tsx (4 hunks)
  • web/src/components/workflow-graph/node-base.tsx (3 hunks)
  • web/src/hooks/use-get-workflow-api.ts (2 hunks)
  • web/src/providers/save-workflow.tsx (4 hunks)
  • web/src/stores/secrets-store.ts (2 hunks)
  • web/src/stores/workflow-store.ts (8 hunks)
🔇 Additional comments (14)
web/src/hooks/use-get-workflow-api.ts (1)

34-34: LGTM! Cache control implementation looks good.

The addition of gcTime: DISABLE_CACHE ensures that the workflow data is always fresh, which is crucial for maintaining consistency with the secret selection dropdown and deleted secret warnings introduced in this PR.

web/src/stores/secrets-store.ts (2)

19-22: Implementation correctly matches the type definition.

The implementation maintains the same functionality while using the new method name.


7-7: Method rename improves clarity but verify all usages.

The rename from clear to clearSecretsStore is more descriptive and better indicates the method's purpose.

Let's verify all usages have been updated:

✅ Verification successful

Method rename from clear to clearSecretsStore has been properly implemented

Based on the verification results:

  • The only usage of clearSecretsStore is in workflow-editor.tsx where it's correctly using the new name
  • The clear() method found in api-keys.tsx is from a different store (useApiKeysStore) and is unrelated
  • No other instances of the old method name were found in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any remaining usages of the old method name
# Expect: No occurrences of `.clear()` calls on the secrets store

# Test 1: Search for direct clear method calls
rg -A 2 "useSecretsStore.*clear\(" 

# Test 2: Search for destructured clear method usage
rg -A 2 "const.*\{.*clear.*\}.*=.*useSecretsStore"

Length of output: 337


Script:

#!/bin/bash
# Let's verify if there are any other usages of the old 'clear' method name
# by searching for it in combination with secrets-related terms

# Search for any remaining references to 'clear' method in combination with 'secrets'
rg -A 2 "clear.*secrets|secrets.*clear" --type ts --type tsx

# Search for any destructured clear method from useSecretsStore with different patterns
rg -A 2 "useSecretsStore.*\{[^\}]*clear[^\}]*\}" --type ts --type tsx

# Search for any direct references to clear in test files that might be related to secrets
rg -A 2 "clear.*\(" "**/*test*" "**/*spec*"

Length of output: 336


Script:

#!/bin/bash
# Let's try a different approach without file type specifications
# and use proper glob patterns for test files

# Search for any remaining references to 'clear' method in combination with 'secrets'
rg -A 2 "clear.*secrets|secrets.*clear"

# Search for any destructured clear method from useSecretsStore
rg -A 2 "useSecretsStore.*\{[^\}]*clear[^\}]*\}"

# Search for test files using fd and then search within them
fd -e test.ts -e test.tsx -e spec.ts -e spec.tsx | xargs rg -A 2 "clear.*\("

Length of output: 171


Script:

#!/bin/bash
# Let's do one final check using ast-grep to find any potential usages
# of the old 'clear' method name in the codebase

# Search for any method calls named 'clear' in TypeScript/JavaScript files
ast-grep --pattern 'clear($$$)'

# Search for any property access with 'clear'
ast-grep --pattern '$_.clear'

# Search for any destructuring patterns with 'clear'
ast-grep --pattern '{ $$$, clear, $$$ }'

Length of output: 177


Script:

#!/bin/bash
# Let's check the context of the clear() usage in api-keys.tsx
# to ensure it's not related to the secrets store

# Get the content of the api-keys file with context
rg -B 10 -A 5 "clear\(\)" web/src/components/api-keys/api-keys.tsx

# Also check for any imports or store usage in this file
rg "useSecretsStore|from.*stores/secrets-store" web/src/components/api-keys/api-keys.tsx

Length of output: 554

web/src/components/secrets/secrets.tsx (2)

18-24: LGTM! Clear and descriptive method naming.

The rename from clear to clearSecretsStore improves code clarity by making the method's purpose more explicit.


Line range hint 1-85: Verify integration with workflow warnings.

Since this component is part of the secrets management system that will show warnings for deleted secrets in workflows, we should verify the integration points.

✅ Verification successful

Integration with workflow warnings is properly implemented

The verification shows that the secrets component is well-integrated with the workflow system:

  • The workflow editor (workflow-editor.tsx) checks for missing secrets and validates them before running workflows
  • There's proper secret mapping and validation in the action edit panel
  • The system tracks deleted secrets and shows appropriate UI feedback when secrets are missing

The integration points include:

  • Secret validation before workflow execution
  • Secret mapping in action configurations
  • Proper usage of the useSecretsStore hook in workflow components
  • UI components to select and manage secrets within workflow actions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for integration between secrets and workflow components

# Look for workflow components that might use these secrets
echo "Checking for workflow components that consume secrets..."
rg -l "useSecretsStore|getSecrets" "web/src/components/workflow"

# Look for warning-related code in workflow components
echo "Checking for warning implementations..."
rg "warning|deleted.*secret" "web/src/components/workflow"

Length of output: 471


Script:

#!/bin/bash
# Let's first find the correct workflow-related directories
echo "Finding workflow-related directories..."
fd -t d "workflow"

# Then search for secret-related code in those directories
echo "Searching for secret-related code..."
rg -l "secret|credential" --type tsx --type ts

# Look for any warning or validation components
echo "Searching for warning/validation components..."
fd -e tsx -e ts "warning|validation|validator"

Length of output: 931


Script:

#!/bin/bash
# Let's check the workflow validation file for secret-related validations
echo "Checking workflow validation..."
cat web/src/lib/workflow-validation.ts

# Let's also check workflow editor components for secret usage
echo "Checking workflow editor components..."
rg -g "*.{ts,tsx}" "secret|credential" web/src/components/workflow-editor/

# Check for any secret-related warnings in the workflow components
echo "Checking for secret warnings..."
rg -g "*.{ts,tsx}" "warning.*secret|deleted.*secret|missing.*secret" web/src/components/workflow-editor/

Length of output: 4343

web/src/components/workflow-graph/node-base.tsx (1)

6-6: LGTM: Clean implementation of secret validation hook.

The addition of ExclamationTriangleIcon and hasDeletedSecret hook is well-structured and aligns with the PR's objective to show warnings for deleted secrets.

Also applies to: 23-24

web/src/providers/save-workflow.tsx (2)

Line range hint 105-118: LGTM! Error handling looks solid.

The error handling is comprehensive and provides good user feedback:

  • Handles API errors with specific messages
  • Handles validation errors
  • Provides fallback error message
  • Consistently returns false for all error cases

23-23: Verify the impact of the saveWorkflow return type change.

The change from Promise<void> to Promise<boolean> improves error handling but might affect existing consumers of this context.

Also applies to: 28-28

✅ Verification successful

Return type change is properly handled in all consumers

The search results show that all consumers of saveWorkflow are handling the boolean return value appropriately:

  • action-edit-panel.tsx explicitly checks the return value with if (saveSuccessful)
  • new-workflow-modal.tsx doesn't need the return value for its use case
  • use-save-workflow-api.ts is just forwarding the call

The type change is safe and improves error handling without breaking existing functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usages of saveWorkflow to ensure all consumers handle the new boolean return value
rg -A 3 "saveWorkflow\(" --type tsx

Length of output: 65


Script:

#!/bin/bash
# Search for usages of saveWorkflow with proper file type
rg -t ts -t tsx -A 3 "saveWorkflow\("

Length of output: 67


Script:

#!/bin/bash
# Search for usages of saveWorkflow using ripgrep without file type filters
rg -A 3 "saveWorkflow\(" web/src/

Length of output: 860

web/src/components/workflow-editor/workflow-editor.tsx (3)

4-4: LGTM: Import statements are well-organized.

The new imports for secrets management functionality are properly structured and follow the project's conventions.

Also applies to: 8-8


41-42: LGTM: Store hooks are properly destructured.

The implementation follows the suggested naming convention for clearSecretsStore, maintaining consistency with the workflow store naming pattern.

Also applies to: 45-45


63-70: LGTM: Secrets management effect is well-implemented.

The effect properly manages the lifecycle of secrets data:

  • Sets secrets when data is available
  • Includes proper cleanup on unmount
  • Has correct dependency array
web/src/stores/workflow-store.ts (3)

13-13: LGTM: Proper initialization of immer for Map/Set support

The addition of enableMapSet from immer and its initialization is correct and necessary for immutable updates to Map and Set data structures.

Also applies to: 51-51


387-392: LGTM: Clean implementation of hasDeletedSecret

The implementation correctly handles both node-level and placeholder-level secret checks with proper null safety.


393-398: LGTM: Proper implementation of setDeletedSecrets

The implementation follows the store's immutable update pattern using immer's produce.

@@ -42,7 +42,7 @@ export function SaveWorkflowProvider({

const handleSaveWorkflow = async () => {
if (isPending) {
return;
return false;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix workflow name validation control flow.

The workflow name validation check doesn't return early, allowing invalid workflows to proceed with the save operation. This could lead to inconsistent state.

Apply this fix:

 if (!isValidWorkflowName(workflow.workflowName)) {
   errorToast(WORKFLOW_NAME_VALIDATION_ERROR_MESSAGE);
+  return false;
 }

Also applies to: 59-59, 60-60, 61-61, 62-62

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
web/src/providers/save-workflow.tsx (2)

106-106: Consider enhancing error logging for unknown errors.

While the success/failure return values are good, consider adding error logging for unknown errors to help with debugging production issues.

 } else {
+  console.error('Unexpected error while saving workflow:', error);
   errorToast("Failed to save workflow. Please try again.");
 }

Also applies to: 120-120


Line range hint 34-124: Consider extracting workflow validation logic for better testability.

The validation logic could be extracted into a separate service/hook to make it easier to test and maintain. This would also allow for better mocking in tests.

Example structure:

// useWorkflowValidation.ts
export function useWorkflowValidation() {
  return {
    validateWorkflow: (workflow: Workflow, actionsIndex: ActionsIndex) => {
      if (Object.keys(actionsIndex).length === 0) {
        throw new WorkflowValidationError(
          "Editor actions must be loaded to save the workflow."
        );
      }
      // ... other validation rules
    }
  };
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 32e10d4 and 013e8ae.

📒 Files selected for processing (1)
  • web/src/providers/save-workflow.tsx (5 hunks)
🔇 Additional comments (2)
web/src/providers/save-workflow.tsx (2)

23-23: LGTM! Good improvement to the return type.

The change from void to boolean return type improves error handling by allowing consumers to react to save failures, which is particularly useful for the secrets dropdown feature.

Also applies to: 28-28


45-46: LGTM! Good prevention of concurrent saves.

The early return when a save is already pending prevents race conditions and potential data inconsistencies.

align="center"
>
<ExclamationTriangleIcon
className="text-white"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Radix UI icons have a color prop. Why not use this?

className="absolute bg-[var(--red-9)] rounded-full"
/>
<Flex
className="relative rounded-full"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

position exists as a prop in <Flex>, so please move absolute and relative out of className

@@ -42,7 +42,7 @@ export function SaveWorkflowProvider({

const handleSaveWorkflow = async () => {
if (isPending) {
return;
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is okay to remove the if-condition. It is probably more dangerous to return false in this case because if we would do error handling based on the result, it would trigger an error although there is none.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (3)
web/src/components/workflow-graph/node-base.tsx (1)

66-99: Consider simplifying the warning indicator structure

While the implementation works well, the nested Flex components could be simplified for better maintainability.

Consider this more streamlined structure:

 <NodeToolbar
   isVisible={hasDeletedSecret(nodeId)}
   position={Position.Top}
   offset={8}
   align="end"
 >
   <Tooltip content="Missing Secret">
-    <Flex
-      style={{
-        transform: "translate(10px, 24px)",
-      }}
-    >
-      <Flex
-        inset="0"
-        position="absolute"
-        className="bg-[var(--red-9)] rounded-full"
-      />
-      <Flex
-        position="relative"
-        className="rounded-full"
-        width="24px"
-        height="24px"
-        justify="center"
-        align="center"
-      >
-        <ExclamationTriangleIcon
-          color="white"
-          width="14px"
-          height="14px"
-        />
-      </Flex>
-    </Flex>
+    <Flex
+      className="rounded-full bg-[var(--red-9)]"
+      style={{
+        transform: "translate(10px, 24px)",
+      }}
+      width="24px"
+      height="24px"
+      justify="center"
+      align="center"
+    >
+      <ExclamationTriangleIcon
+        color="white"
+        width="14px"
+        height="14px"
+      />
+    </Flex>
   </Tooltip>
 </NodeToolbar>
web/src/providers/save-workflow.tsx (2)

103-103: Consider adding type safety for webhook response.

While the success path is clear, consider adding type safety for the webhook response to prevent potential runtime issues.

-const { webhookId, webhookSecret } = await saveWorkflow.mutateAsync(
+interface WebhookResponse {
+  webhookId?: string;
+  webhookSecret?: string;
+}
+const { webhookId, webhookSecret } = await saveWorkflow.mutateAsync<WebhookResponse>(

Line range hint 105-117: Improve type safety in error handling.

The error handling is comprehensive but relies on unsafe type assertions. Consider defining proper types for the API error response.

+interface WorkflowApiError {
+  detail: string;
+}
+
 if (
   error instanceof AxiosError &&
-  (error.response?.data as any).detail.startsWith(
+  (error.response?.data as WorkflowApiError).detail.startsWith(
     "A workflow with the name",
   )
 ) {
-  errorToast((error.response?.data as any).detail);
+  errorToast((error.response?.data as WorkflowApiError).detail);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 013e8ae and ad89e44.

📒 Files selected for processing (2)
  • web/src/components/workflow-graph/node-base.tsx (3 hunks)
  • web/src/providers/save-workflow.tsx (4 hunks)
🔇 Additional comments (4)
web/src/components/workflow-graph/node-base.tsx (2)

6-6: LGTM: Clean integration of new functionality

The addition of ExclamationTriangleIcon and hasDeletedSecret hook is well-integrated and aligns with the PR's objective of showing warnings for deleted secrets.

Also applies to: 23-24


74-76: Verify warning indicator positioning

The use of hardcoded transform values might cause positioning issues in different scenarios.

Let's verify the positioning behavior:

✅ Verification successful

Transform values are intentional for warning indicator positioning

The transform values are used specifically for positioning the warning indicator (ExclamationTriangleIcon) within a NodeToolbar component from reactflow. This is a deliberate positioning choice that:

  1. Only affects the warning indicator tooltip
  2. Is contained within a NodeToolbar component that already handles responsive positioning
  3. Works in conjunction with other layout properties (offset={8}, position={Position.Top}, align="end")

The implementation shows this is a specific UI enhancement rather than a potential positioning issue.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for responsive design considerations and potential positioning issues

# Test 1: Look for viewport-related styling
rg -l "viewport|media|screen" --type tsx

# Test 2: Check for other transform usages that might conflict
rg "transform:" --type tsx

# Test 3: Look for related test files
fd "node-base.test" --extension tsx --extension ts

Length of output: 176


Script:

#!/bin/bash
# Let's try without file type restrictions since tsx wasn't recognized
# Check for viewport-related styling and responsive design patterns
rg -l "viewport|media|screen" web/src/

# Look for transform usage patterns in the component and related files
rg "transform:" web/src/components/workflow-graph/

# Check the component's implementation for context
cat web/src/components/workflow-graph/node-base.tsx

# Look for test files without extension restrictions
find web/src -name "*node-base.test*"

Length of output: 3814

web/src/providers/save-workflow.tsx (2)

23-23: LGTM! Good improvement to the API contract.

The change from Promise<void> to Promise<boolean> provides better control flow by allowing callers to handle success/failure cases explicitly.

Also applies to: 28-28


59-61: LGTM! Improved error handling approach.

Using WorkflowValidationError instead of direct error toasts is a better practice as it:

  1. Separates validation logic from UI feedback
  2. Allows for consistent error handling in the catch block
  3. Makes the code more testable

Comment on lines 119 to 133
const deletedSecrets = workflow?.nodes
.filter((node) => node.type === "action")
.reduce((acc, node) => {
const secretsMapping = node.secretsMapping;
const secretPlaceholders = Object.keys(secretsMapping);
secretPlaceholders.forEach((secretPlaceholder) => {
if (!secretIds.has(secretsMapping[secretPlaceholder])) {
if (!acc.has(node.id)) {
acc.set(node.id, new Set());
}
acc.get(node.id)?.add(secretPlaceholder);
}
});
return acc;
}, new Map<string, Set<string>>());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can write this in a cleaner way:

Suggested change
const deletedSecrets = workflow?.nodes
.filter((node) => node.type === "action")
.reduce((acc, node) => {
const secretsMapping = node.secretsMapping;
const secretPlaceholders = Object.keys(secretsMapping);
secretPlaceholders.forEach((secretPlaceholder) => {
if (!secretIds.has(secretsMapping[secretPlaceholder])) {
if (!acc.has(node.id)) {
acc.set(node.id, new Set());
}
acc.get(node.id)?.add(secretPlaceholder);
}
});
return acc;
}, new Map<string, Set<string>>());
const deletedSecrets = new Map(workflow?.nodes
.filter((node) => node.type === "action")
.map((node) => {
const secretsMapping = node.secretsMapping;
const deletedSecrets = new Set(Object.keys(secretsMapping).filter((secretPlaceholder) => !secretIds.has(secretsMapping[secretPlaceholder])));
return [node.id, deletedSecrets];
})
.filter((nodeIdAndDeletedSecrets) => nodeIdAndDeletedSecrets[1].size > 0));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also check what happens if workflow is undefined, i.e., new Map(undefined).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ad89e44 and df638cd.

📒 Files selected for processing (1)
  • web/src/components/workflow-editor/workflow-editor.tsx (4 hunks)
🔇 Additional comments (2)
web/src/components/workflow-editor/workflow-editor.tsx (2)

4-4: LGTM: Clean integration of secrets management.

The new imports and hook usage are well-organized and follow React best practices.

Also applies to: 8-8, 41-42, 45-45


63-70: LGTM: Well-implemented secrets management effect.

The effect properly manages the lifecycle of secrets with appropriate cleanup.

Comment on lines 114 to 147
useEffect(() => {
if (!workflow || !encryptedSecrets) {
return;
}
const secretIds = new Set(encryptedSecrets.map((s) => s.secretId));
const deletedSecrets = new Map(
workflow?.nodes
.filter((node) => node.type === "action")
.map((node) => {
const secretsMapping = node.secretsMapping;
const deletedSecrets = new Set(
Object.keys(secretsMapping).filter(
(secretPlaceholder) =>
!secretIds.has(
secretsMapping[secretPlaceholder],
),
),
);
return [node.id, deletedSecrets] as [string, Set<string>];
})
.filter(
(nodeIdAndDeletedSecrets) =>
nodeIdAndDeletedSecrets[1].size > 0,
),
);
if (deletedSecrets) {
setDeletedSecrets(deletedSecrets);
}
if (deletedSecrets && deletedSecrets.size > 0) {
errorToast(
"Some secrets are missing. Please add them before running the workflow.",
);
}
}, [encryptedSecrets, workflow, setDeletedSecrets]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve deleted secrets detection implementation.

The logic for detecting deleted secrets can be simplified and improved:

  1. Remove the redundant deletedSecrets check at line 139.
  2. Combine the conditions for setting deleted secrets and showing the toast.
  3. Enhance the error message to be more specific.
  4. Use proper typing to avoid type assertion.

Here's the suggested implementation:

 useEffect(() => {
   if (!workflow || !encryptedSecrets) {
     return;
   }
   const secretIds = new Set(encryptedSecrets.map((s) => s.secretId));
   const deletedSecrets = new Map(
     workflow?.nodes
       .filter((node) => node.type === "action")
       .map((node) => {
         const secretsMapping = node.secretsMapping;
         const deletedSecrets = new Set(
           Object.keys(secretsMapping).filter(
             (secretPlaceholder) =>
               !secretIds.has(
                 secretsMapping[secretPlaceholder],
               ),
           ),
         );
-        return [node.id, deletedSecrets] as [string, Set<string>];
+        return [node.id, deletedSecrets];
       })
       .filter(
         (nodeIdAndDeletedSecrets) =>
           nodeIdAndDeletedSecrets[1].size > 0,
       ),
   );
-  if (deletedSecrets) {
-    setDeletedSecrets(deletedSecrets);
-  }
-  if (deletedSecrets && deletedSecrets.size > 0) {
+  setDeletedSecrets(deletedSecrets);
+  if (deletedSecrets.size > 0) {
+    const nodeCount = deletedSecrets.size;
     errorToast(
-      "Some secrets are missing. Please add them before running the workflow.",
+      `Missing secrets detected in ${nodeCount} ${
+        nodeCount === 1 ? 'node' : 'nodes'
+      }. Please add them before running the workflow.`,
     );
   }
 }, [encryptedSecrets, workflow, setDeletedSecrets]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (!workflow || !encryptedSecrets) {
return;
}
const secretIds = new Set(encryptedSecrets.map((s) => s.secretId));
const deletedSecrets = new Map(
workflow?.nodes
.filter((node) => node.type === "action")
.map((node) => {
const secretsMapping = node.secretsMapping;
const deletedSecrets = new Set(
Object.keys(secretsMapping).filter(
(secretPlaceholder) =>
!secretIds.has(
secretsMapping[secretPlaceholder],
),
),
);
return [node.id, deletedSecrets] as [string, Set<string>];
})
.filter(
(nodeIdAndDeletedSecrets) =>
nodeIdAndDeletedSecrets[1].size > 0,
),
);
if (deletedSecrets) {
setDeletedSecrets(deletedSecrets);
}
if (deletedSecrets && deletedSecrets.size > 0) {
errorToast(
"Some secrets are missing. Please add them before running the workflow.",
);
}
}, [encryptedSecrets, workflow, setDeletedSecrets]);
useEffect(() => {
if (!workflow || !encryptedSecrets) {
return;
}
const secretIds = new Set(encryptedSecrets.map((s) => s.secretId));
const deletedSecrets = new Map(
workflow?.nodes
.filter((node) => node.type === "action")
.map((node) => {
const secretsMapping = node.secretsMapping;
const deletedSecrets = new Set(
Object.keys(secretsMapping).filter(
(secretPlaceholder) =>
!secretIds.has(
secretsMapping[secretPlaceholder],
),
),
);
return [node.id, deletedSecrets];
})
.filter(
(nodeIdAndDeletedSecrets) =>
nodeIdAndDeletedSecrets[1].size > 0,
),
);
setDeletedSecrets(deletedSecrets);
if (deletedSecrets.size > 0) {
const nodeCount = deletedSecrets.size;
errorToast(
`Missing secrets detected in ${nodeCount} ${
nodeCount === 1 ? 'node' : 'nodes'
}. Please add them before running the workflow.`,
);
}
}, [encryptedSecrets, workflow, setDeletedSecrets]);

@danielgrittner danielgrittner merged commit eafca02 into main Nov 4, 2024
6 checks passed
@danielgrittner danielgrittner deleted the ADM-369/Secrets-list-as-dropdown-from-existing-secrets branch November 4, 2024 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants