Merge pull request #13710 from Budibase/fix/rest-download-images

Download images rather than returning binary responses from REST queries
This commit is contained in:
Michael Drury 2024-05-17 14:59:57 +01:00 committed by GitHub
commit 15b86021ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 43 additions and 12 deletions

View File

@ -86,8 +86,9 @@ export const createValidatedConfigStore = (integration, config) => {
([$configStore, $errorsStore, $selectedValidatorsStore]) => { ([$configStore, $errorsStore, $selectedValidatorsStore]) => {
const validatedConfig = [] const validatedConfig = []
const allowedRestKeys = ["rejectUnauthorized", "downloadImages"]
Object.entries(integration.datasource).forEach(([key, properties]) => { Object.entries(integration.datasource).forEach(([key, properties]) => {
if (integration.name === "REST" && key !== "rejectUnauthorized") { if (integration.name === "REST" && !allowedRestKeys.includes(key)) {
return return
} }

View File

@ -1,22 +1,22 @@
import { import {
Integration,
DatasourceFieldType, DatasourceFieldType,
QueryType, HttpMethod,
PaginationConfig, Integration,
IntegrationBase, IntegrationBase,
PaginationConfig,
PaginationValues, PaginationValues,
RestQueryFields as RestQuery, QueryType,
RestConfig,
RestAuthType, RestAuthType,
RestBasicAuthConfig, RestBasicAuthConfig,
RestBearerAuthConfig, RestBearerAuthConfig,
HttpMethod, RestConfig,
RestQueryFields as RestQuery,
} from "@budibase/types" } from "@budibase/types"
import get from "lodash/get" import get from "lodash/get"
import * as https from "https" import * as https from "https"
import qs from "querystring" import qs from "querystring"
import fetch from "node-fetch"
import type { Response } from "node-fetch" import type { Response } from "node-fetch"
import fetch from "node-fetch"
import { formatBytes } from "../utilities" import { formatBytes } from "../utilities"
import { performance } from "perf_hooks" import { performance } from "perf_hooks"
import FormData from "form-data" import FormData from "form-data"
@ -87,6 +87,12 @@ const SCHEMA: Integration = {
default: true, default: true,
required: false, required: false,
}, },
downloadImages: {
display: "Download images",
type: DatasourceFieldType.BOOLEAN,
default: true,
required: false,
},
}, },
query: { query: {
create: { create: {
@ -139,7 +145,8 @@ class RestIntegration implements IntegrationBase {
filename: string | undefined filename: string | undefined
const { contentType, contentDisposition } = getAttachmentHeaders( const { contentType, contentDisposition } = getAttachmentHeaders(
response.headers response.headers,
{ downloadImages: this.config.downloadImages }
) )
if ( if (
contentDisposition.includes("filename") || contentDisposition.includes("filename") ||

View File

@ -1,13 +1,13 @@
import { getAttachmentHeaders } from "../utils/restUtils" import { getAttachmentHeaders } from "../utils/restUtils"
import type { Headers } from "node-fetch" import type { Headers } from "node-fetch"
function headers(dispositionValue: string) { function headers(dispositionValue: string, contentType?: string) {
return { return {
get: (name: string) => { get: (name: string) => {
if (name.toLowerCase() === "content-disposition") { if (name.toLowerCase() === "content-disposition") {
return dispositionValue return dispositionValue
} else { } else {
return "application/pdf" return contentType || "application/pdf"
} }
}, },
set: () => {}, set: () => {},
@ -35,4 +35,14 @@ describe("getAttachmentHeaders", () => {
) )
expect(contentDisposition).toBe(`inline; filename="report.pdf"`) expect(contentDisposition).toBe(`inline; filename="report.pdf"`)
}) })
it("should handle an image", () => {
const { contentDisposition } = getAttachmentHeaders(
headers("", "image/png"),
{
downloadImages: true,
}
)
expect(contentDisposition).toBe(`attachment; filename="image.png"`)
})
}) })

View File

@ -1,6 +1,9 @@
import type { Headers } from "node-fetch" import type { Headers } from "node-fetch"
export function getAttachmentHeaders(headers: Headers) { export function getAttachmentHeaders(
headers: Headers,
opts?: { downloadImages?: boolean }
) {
const contentType = headers.get("content-type") || "" const contentType = headers.get("content-type") || ""
let contentDisposition = headers.get("content-disposition") || "" let contentDisposition = headers.get("content-disposition") || ""
@ -23,6 +26,15 @@ export function getAttachmentHeaders(headers: Headers) {
} }
} }
} }
// for images which don't supply a content disposition, make one up, as binary
// data for images in REST responses isn't really useful, we should always download them
else if (opts?.downloadImages && contentType.startsWith("image/")) {
const format = contentType.split("/")[1]
return {
contentDisposition: `attachment; filename="image.${format}"`,
contentType,
}
}
return { contentDisposition, contentType } return { contentDisposition, contentType }
} }

View File

@ -46,6 +46,7 @@ export interface DynamicVariable {
export interface RestConfig { export interface RestConfig {
url: string url: string
rejectUnauthorized: boolean rejectUnauthorized: boolean
downloadImages?: boolean
defaultHeaders: { defaultHeaders: {
[key: string]: any [key: string]: any
} }