[Feature] Adding Azure Blob Storage support to File Upload workflow block (#3130)

This commit is contained in:
Trevor Sullivan
2025-08-07 22:59:37 -06:00
committed by GitHub
parent 71f71b8e77
commit b3e17c12b3
17 changed files with 667 additions and 169 deletions

View File

@@ -93,6 +93,9 @@ export const helpTooltips = {
aws_secret_access_key:
"The AWS secret access key to use to upload the file to S3.",
region_name: "The AWS region",
azure_storage_account_name: "The Azure Storage Account Name.",
azure_storage_account_key: "The Azure Storage Account Key.",
azure_blob_container_name: "The Azure Blob Container Name.",
},
download: {
...baseHelpTooltipContent,

View File

@@ -10,6 +10,13 @@ import { useDebugStore } from "@/store/useDebugStore";
import { cn } from "@/util/utils";
import { NodeHeader } from "../components/NodeHeader";
import { useParams } from "react-router-dom";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
function FileUploadNode({ id, data }: NodeProps<FileUploadNode>) {
const { updateNodeData } = useReactFlow();
@@ -22,11 +29,14 @@ function FileUploadNode({ id, data }: NodeProps<FileUploadNode>) {
const [inputs, setInputs] = useState({
storageType: data.storageType,
awsAccessKeyId: data.awsAccessKeyId,
awsSecretAccessKey: data.awsSecretAccessKey,
s3Bucket: data.s3Bucket,
regionName: data.regionName,
path: data.path,
awsAccessKeyId: data.awsAccessKeyId ?? "",
awsSecretAccessKey: data.awsSecretAccessKey ?? "",
s3Bucket: data.s3Bucket ?? "",
regionName: data.regionName ?? "",
path: data.path ?? "",
azureStorageAccountName: data.azureStorageAccountName ?? "",
azureStorageAccountKey: data.azureStorageAccountKey ?? "",
azureBlobContainerName: data.azureBlobContainerName ?? "",
});
function handleChange(key: string, value: unknown) {
@@ -77,94 +87,176 @@ function FileUploadNode({ id, data }: NodeProps<FileUploadNode>) {
content={helpTooltips["fileUpload"]["storage_type"]}
/>
</div>
<Input
value={data.storageType}
className="nopan text-xs"
disabled
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">
AWS Access Key ID
</Label>
<HelpTooltip
content={helpTooltips["fileUpload"]["aws_access_key_id"]}
/>
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("awsAccessKeyId", value);
}}
value={inputs.awsAccessKeyId}
className="nopan text-xs"
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">
AWS Secret Access Key
</Label>
<HelpTooltip
content={helpTooltips["fileUpload"]["aws_secret_access_key"]}
/>
</div>
<Input
type="password"
value={inputs.awsSecretAccessKey}
className="nopan text-xs"
onChange={(event) => {
handleChange("awsSecretAccessKey", event.target.value);
}}
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">S3 Bucket</Label>
<HelpTooltip content={helpTooltips["fileUpload"]["s3_bucket"]} />
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("s3Bucket", value);
}}
value={inputs.s3Bucket}
className="nopan text-xs"
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">Region Name</Label>
<HelpTooltip
content={helpTooltips["fileUpload"]["region_name"]}
/>
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("regionName", value);
}}
value={inputs.regionName}
className="nopan text-xs"
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">
(Optional) Folder Path
</Label>
<HelpTooltip content={helpTooltips["fileUpload"]["path"]} />
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("path", value);
}}
value={inputs.path}
className="nopan text-xs"
/>
<Select
value={inputs.storageType}
onValueChange={(value) => handleChange("storageType", value)}
disabled={!editable}
>
<SelectTrigger className="nopan text-xs">
<SelectValue placeholder="Select storage type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="s3">Amazon S3</SelectItem>
<SelectItem value="azure">Azure Blob Storage</SelectItem>
</SelectContent>
</Select>
</div>
{inputs.storageType === "s3" && (
<>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">
AWS Access Key ID
</Label>
<HelpTooltip
content={helpTooltips["fileUpload"]["aws_access_key_id"]}
/>
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("awsAccessKeyId", value);
}}
value={inputs.awsAccessKeyId as string}
className="nopan text-xs"
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">
AWS Secret Access Key
</Label>
<HelpTooltip
content={
helpTooltips["fileUpload"]["aws_secret_access_key"]
}
/>
</div>
<Input
type="password"
value={inputs.awsSecretAccessKey as string}
className="nopan text-xs"
onChange={(event) => {
handleChange("awsSecretAccessKey", event.target.value);
}}
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">S3 Bucket</Label>
<HelpTooltip
content={helpTooltips["fileUpload"]["s3_bucket"]}
/>
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("s3Bucket", value);
}}
value={inputs.s3Bucket as string}
className="nopan text-xs"
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">Region Name</Label>
<HelpTooltip
content={helpTooltips["fileUpload"]["region_name"]}
/>
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("regionName", value);
}}
value={inputs.regionName as string}
className="nopan text-xs"
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">
(Optional) Folder Path
</Label>
<HelpTooltip content={helpTooltips["fileUpload"]["path"]} />
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("path", value);
}}
value={inputs.path as string}
className="nopan text-xs"
/>
</div>
</>
)}
{inputs.storageType === "azure" && (
<>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">
Storage Account Name
</Label>
<HelpTooltip
content={
helpTooltips["fileUpload"]["azure_storage_account_name"]
}
/>
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("azureStorageAccountName", value);
}}
value={inputs.azureStorageAccountName as string}
className="nopan text-xs"
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">
Storage Account Key
</Label>
<HelpTooltip
content={
helpTooltips["fileUpload"]["azure_storage_account_key"]
}
/>
</div>
<Input
type="password"
value={inputs.azureStorageAccountKey as string}
className="nopan text-xs"
onChange={(event) => {
handleChange("azureStorageAccountKey", event.target.value);
}}
/>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label className="text-sm text-slate-400">
Blob Container Name
</Label>
<HelpTooltip
content={
helpTooltips["fileUpload"]["azure_blob_container_name"]
}
/>
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
handleChange("azureBlobContainerName", value);
}}
value={inputs.azureBlobContainerName as string}
className="nopan text-xs"
/>
</div>
</>
)}
</div>
</div>
</div>

View File

@@ -5,11 +5,14 @@ import { debuggableWorkflowBlockTypes } from "@/routes/workflows/types/workflowT
export type FileUploadNodeData = NodeBaseData & {
path: string;
editable: boolean;
storageType: string;
s3Bucket: string;
awsAccessKeyId: string;
awsSecretAccessKey: string;
regionName: string;
storageType: "s3" | "azure";
s3Bucket: string | null;
awsAccessKeyId: string | null;
awsSecretAccessKey: string | null;
regionName: string | null;
azureStorageAccountName: string | null;
azureStorageAccountKey: string | null;
azureBlobContainerName: string | null;
};
export type FileUploadNode = Node<FileUploadNodeData, "fileUpload">;
@@ -20,10 +23,13 @@ export const fileUploadNodeDefaultData: FileUploadNodeData = {
storageType: "s3",
label: "",
path: "",
s3Bucket: "",
awsAccessKeyId: "",
awsSecretAccessKey: "",
regionName: "",
s3Bucket: null,
awsAccessKeyId: null,
awsSecretAccessKey: null,
regionName: null,
azureStorageAccountName: null,
azureStorageAccountKey: null,
azureBlobContainerName: null,
continueOnFailure: false,
model: null,
} as const;

View File

@@ -522,10 +522,13 @@ function convertToNode(
...commonData,
path: block.path,
storageType: block.storage_type,
s3Bucket: block.s3_bucket,
awsAccessKeyId: block.aws_access_key_id,
awsSecretAccessKey: block.aws_secret_access_key,
regionName: block.region_name,
s3Bucket: block.s3_bucket ?? "",
awsAccessKeyId: block.aws_access_key_id ?? "",
awsSecretAccessKey: block.aws_secret_access_key ?? "",
regionName: block.region_name ?? "",
azureStorageAccountName: block.azure_storage_account_name ?? "",
azureStorageAccountKey: block.azure_storage_account_key ?? "",
azureBlobContainerName: block.azure_blob_container_name ?? "",
},
};
}
@@ -1249,10 +1252,13 @@ function getWorkflowBlock(node: WorkflowBlockNode): BlockYAML {
block_type: "file_upload",
path: node.data.path,
storage_type: node.data.storageType,
s3_bucket: node.data.s3Bucket,
aws_access_key_id: node.data.awsAccessKeyId,
aws_secret_access_key: node.data.awsSecretAccessKey,
region_name: node.data.regionName,
s3_bucket: node.data.s3Bucket ?? "",
aws_access_key_id: node.data.awsAccessKeyId ?? "",
aws_secret_access_key: node.data.awsSecretAccessKey ?? "",
region_name: node.data.regionName ?? "",
azure_storage_account_name: node.data.azureStorageAccountName ?? "",
azure_storage_account_key: node.data.azureStorageAccountKey ?? "",
azure_blob_container_name: node.data.azureBlobContainerName ?? "",
};
}
case "fileParser": {
@@ -2013,10 +2019,13 @@ function convertBlocksToBlockYAML(
block_type: "file_upload",
path: block.path,
storage_type: block.storage_type,
s3_bucket: block.s3_bucket,
aws_access_key_id: block.aws_access_key_id,
aws_secret_access_key: block.aws_secret_access_key,
region_name: block.region_name,
s3_bucket: block.s3_bucket ?? "",
aws_access_key_id: block.aws_access_key_id ?? "",
aws_secret_access_key: block.aws_secret_access_key ?? "",
region_name: block.region_name ?? "",
azure_storage_account_name: block.azure_storage_account_name ?? "",
azure_storage_account_key: block.azure_storage_account_key ?? "",
azure_blob_container_name: block.azure_blob_container_name ?? "",
};
return blockYaml;
}

View File

@@ -331,11 +331,14 @@ export type UploadToS3Block = WorkflowBlockBase & {
export type FileUploadBlock = WorkflowBlockBase & {
block_type: "file_upload";
path: string;
storage_type: string;
s3_bucket: string;
region_name: string;
aws_access_key_id: string;
aws_secret_access_key: string;
storage_type: "s3" | "azure";
s3_bucket: string | null;
region_name: string | null;
aws_access_key_id: string | null;
aws_secret_access_key: string | null;
azure_storage_account_name: string | null;
azure_storage_account_key: string | null;
azure_blob_container_name: string | null;
};
export type SendEmailBlock = WorkflowBlockBase & {

View File

@@ -290,6 +290,9 @@ export type FileUploadBlockYAML = BlockYAMLBase & {
region_name: string;
aws_access_key_id: string;
aws_secret_access_key: string;
azure_storage_account_name?: string | null;
azure_storage_account_key?: string | null;
azure_blob_container_name?: string | null;
};
export type SendEmailBlockYAML = BlockYAMLBase & {