Support downloading files via HTTP Calls (for Centria) (#4440)
This commit is contained in:
@@ -442,7 +442,10 @@ function WorkflowRun() {
|
||||
<ScrollArea>
|
||||
<ScrollAreaViewport className="max-h-[250px] space-y-2">
|
||||
{fileUrls.length > 0 ? (
|
||||
fileUrls.map((url, index) => {
|
||||
fileUrls.map((url) => {
|
||||
// Extract filename from URL path, stripping query params from signed URLs
|
||||
const urlPath = url.split("?")[0] ?? url;
|
||||
const filename = urlPath.split("/").pop() || "download";
|
||||
return (
|
||||
<div key={url} title={url} className="flex gap-2">
|
||||
<FileIcon className="size-6" />
|
||||
@@ -450,7 +453,7 @@ function WorkflowRun() {
|
||||
href={url}
|
||||
className="underline underline-offset-4"
|
||||
>
|
||||
<span>{`File ${index + 1}`}</span>
|
||||
<span>{filename}</span>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -152,12 +152,15 @@ function DebuggerRunOutput() {
|
||||
<h1 className="text-sm font-bold">Workflow Run Downloaded Files</h1>
|
||||
<div className="space-y-2">
|
||||
{fileUrls.length > 0 ? (
|
||||
fileUrls.map((url, index) => {
|
||||
fileUrls.map((url) => {
|
||||
// Extract filename from URL path, stripping query params from signed URLs
|
||||
const urlPath = url.split("?")[0] ?? url;
|
||||
const filename = urlPath.split("/").pop() || "download";
|
||||
return (
|
||||
<div key={url} title={url} className="flex gap-2">
|
||||
<FileIcon className="size-6" />
|
||||
<a href={url} className="underline underline-offset-4">
|
||||
<span>{`File ${index + 1}`}</span>
|
||||
<span>{filename}</span>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -67,6 +67,8 @@ const filesTooltip =
|
||||
const timeoutTooltip = "Request timeout in seconds.";
|
||||
const followRedirectsTooltip =
|
||||
"Whether to automatically follow HTTP redirects.";
|
||||
const downloadFilenameTooltip =
|
||||
"The complete filename (without extension) for downloaded files. Extension is automatically determined from the response Content-Type.";
|
||||
|
||||
function HttpRequestNode({ id, data, type }: NodeProps<HttpRequestNodeType>) {
|
||||
const { editable } = data;
|
||||
@@ -431,6 +433,43 @@ function HttpRequestNode({ id, data, type }: NodeProps<HttpRequestNodeType>) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex gap-2">
|
||||
<Label className="text-xs text-slate-300">
|
||||
Save Response as File
|
||||
</Label>
|
||||
<HelpTooltip content="When enabled, the response body will be saved as a file instead of being parsed as JSON/text." />
|
||||
</div>
|
||||
<Switch
|
||||
checked={data.saveResponseAsFile}
|
||||
onCheckedChange={(checked) => {
|
||||
update({ saveResponseAsFile: checked });
|
||||
}}
|
||||
disabled={!editable}
|
||||
/>
|
||||
</div>
|
||||
{data.saveResponseAsFile && (
|
||||
<div className="space-y-2 border-l-2 border-slate-600 pl-4">
|
||||
<div className="flex gap-2">
|
||||
<Label className="text-xs text-slate-300">
|
||||
Download Filename
|
||||
</Label>
|
||||
<HelpTooltip content={downloadFilenameTooltip} />
|
||||
</div>
|
||||
<Input
|
||||
type="text"
|
||||
value={data.downloadFilename}
|
||||
onChange={(e) => {
|
||||
update({ downloadFilename: e.target.value });
|
||||
}}
|
||||
placeholder="Auto-generated from URL"
|
||||
className="nopan text-xs"
|
||||
disabled={!editable}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
@@ -11,6 +11,8 @@ export type HttpRequestNodeData = NodeBaseData & {
|
||||
timeout: number;
|
||||
followRedirects: boolean;
|
||||
parameterKeys: Array<string>;
|
||||
downloadFilename: string;
|
||||
saveResponseAsFile: boolean;
|
||||
};
|
||||
|
||||
export type HttpRequestNode = Node<HttpRequestNodeData, "http_request">;
|
||||
@@ -29,6 +31,8 @@ export const httpRequestNodeDefaultData: HttpRequestNodeData = {
|
||||
parameterKeys: [],
|
||||
editable: true,
|
||||
model: null,
|
||||
downloadFilename: "",
|
||||
saveResponseAsFile: false,
|
||||
};
|
||||
|
||||
export function isHttpRequestNode(node: Node): node is HttpRequestNode {
|
||||
|
||||
@@ -834,6 +834,8 @@ function convertToNode(
|
||||
timeout: block.timeout,
|
||||
followRedirects: block.follow_redirects,
|
||||
parameterKeys: block.parameters.map((p) => p.key),
|
||||
downloadFilename: block.download_filename ?? "",
|
||||
saveResponseAsFile: block.save_response_as_file ?? false,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -2325,6 +2327,8 @@ function getWorkflowBlock(
|
||||
timeout: node.data.timeout,
|
||||
follow_redirects: node.data.followRedirects,
|
||||
parameter_keys: node.data.parameterKeys,
|
||||
download_filename: node.data.downloadFilename || null,
|
||||
save_response_as_file: node.data.saveResponseAsFile,
|
||||
};
|
||||
}
|
||||
case "conditional": {
|
||||
@@ -3327,6 +3331,7 @@ function convertBlocksToBlockYAML(
|
||||
timeout: block.timeout,
|
||||
follow_redirects: block.follow_redirects,
|
||||
parameter_keys: block.parameters.map((p) => p.key),
|
||||
download_filename: block.download_filename,
|
||||
};
|
||||
return blockYaml;
|
||||
}
|
||||
|
||||
@@ -550,6 +550,8 @@ export type HttpRequestBlock = WorkflowBlockBase & {
|
||||
timeout: number;
|
||||
follow_redirects: boolean;
|
||||
parameters: Array<WorkflowParameter>;
|
||||
download_filename: string | null;
|
||||
save_response_as_file: boolean;
|
||||
};
|
||||
|
||||
export type WorkflowDefinition = {
|
||||
|
||||
@@ -400,4 +400,6 @@ export type HttpRequestBlockYAML = BlockYAMLBase & {
|
||||
timeout: number;
|
||||
follow_redirects: boolean;
|
||||
parameter_keys?: Array<string> | null;
|
||||
download_filename?: string | null;
|
||||
save_response_as_file?: boolean;
|
||||
};
|
||||
|
||||
@@ -152,12 +152,15 @@ function WorkflowRunOutput() {
|
||||
<h1 className="text-lg font-bold">Workflow Run Downloaded Files</h1>
|
||||
<div className="space-y-2">
|
||||
{fileUrls.length > 0 ? (
|
||||
fileUrls.map((url, index) => {
|
||||
fileUrls.map((url) => {
|
||||
// Extract filename from URL path, stripping query params from signed URLs
|
||||
const urlPath = url.split("?")[0] ?? url;
|
||||
const filename = urlPath.split("/").pop() || "download";
|
||||
return (
|
||||
<div key={url} title={url} className="flex gap-2">
|
||||
<FileIcon className="size-6" />
|
||||
<a href={url} className="underline underline-offset-4">
|
||||
<span>{`File ${index + 1}`}</span>
|
||||
<span>{filename}</span>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user