From 2e739ace07e5360e01cdd6b5f9ebe1dc01ad62a9 Mon Sep 17 00:00:00 2001 From: Itamar Yuran Date: Sun, 8 Dec 2024 17:58:08 +0200 Subject: [PATCH] maybe this time --- webui/src/lib/api/index.js | 24 +++++++++-- .../pages/repositories/repository/objects.jsx | 42 +++++++------------ 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/webui/src/lib/api/index.js b/webui/src/lib/api/index.js index b77c61e7e30..f857e53843f 100644 --- a/webui/src/lib/api/index.js +++ b/webui/src/lib/api/index.js @@ -55,6 +55,22 @@ export const defaultAPIHeaders = { "X-Lakefs-Client": "lakefs-webui/__buildVersion", }; +export const parseRawHeaders = (rawHeaders) => { + const headersString = typeof rawHeaders === 'string' ? rawHeaders : rawHeaders.toString(); + const cleanedHeadersString = headersString.trim(); + const headerLines = cleanedHeadersString.split('\n'); + const parsedHeaders = headerLines.reduce((acc, line) => { + let [key, ...value] = line.split(':'); // split into key and the rest of the value + key = key.trim(); + value = value.join(':').trim(); + if (key && value) { + acc[key.toLowerCase()] = value; + } + return acc; + }, {}); + return parsedHeaders; +}; + const authenticationError = "error authenticating request" const apiRequest = async (uri, requestData = {}, additionalHeaders = {}) => { @@ -670,15 +686,14 @@ export const uploadWithProgress = (url, file, method = 'POST', onProgress = null resolve({ status: xhr.status, body: xhr.responseText, - contentType: xhr.getResponseHeader('Content-Type'), rawHeaders: xhr.getAllResponseHeaders(), // add raw headers }); }); - xhr.addEventListener('error', () => resolve({ + xhr.addEventListener('error', () => reject({ + message: 'Upload Failed', status: xhr.status, body: xhr.responseText, rawHeaders: xhr.getAllResponseHeaders(), - error: new Error('Upload Failed'), })); xhr.addEventListener('abort', () => reject(new Error('Upload Aborted'))); xhr.open(method, url, true); @@ -753,8 +768,9 @@ class Objects { async upload(repoId, branchId, path, fileObject, onProgressFn = null) { const query = qs({path}); const uploadUrl = `${API_ENDPOINT}/repositories/${encodeURIComponent(repoId)}/branches/${encodeURIComponent(branchId)}/objects?` + query; - const {status, body, contentType} = await uploadWithProgress(uploadUrl, fileObject, 'POST', onProgressFn) + const {status, body, rawHeaders} = await uploadWithProgress(uploadUrl, fileObject, 'POST', onProgressFn) if (status !== 201) { + const contentType = rawHeaders ? parseRawHeaders(rawHeaders)['content-type'] : undefined; if (contentType === "application/json" && body) { const responseData = JSON.parse(body) throw new Error(responseData.message) diff --git a/webui/src/pages/repositories/repository/objects.jsx b/webui/src/pages/repositories/repository/objects.jsx index 4aedce2deee..6e03a58ed8d 100644 --- a/webui/src/pages/repositories/repository/objects.jsx +++ b/webui/src/pages/repositories/repository/objects.jsx @@ -19,7 +19,7 @@ import Alert from "react-bootstrap/Alert"; import { BsCloudArrowUp } from "react-icons/bs"; import {humanSize, Tree} from "../../../lib/components/repository/tree"; -import {objects, staging, retention, repositories, imports, NotFoundError, uploadWithProgress} from "../../../lib/api"; +import {objects, staging, retention, repositories, imports, NotFoundError, uploadWithProgress, parseRawHeaders} from "../../../lib/api"; import {useAPI, useAPIWithPagination} from "../../../lib/hooks/api"; import {useRefs} from "../../../lib/hooks/repo"; import {useRouter} from "../../../lib/hooks/router"; @@ -226,21 +226,7 @@ const ImportModal = ({config, repoId, referenceId, referenceType, path = '', onD ); }; -function parseRawHeaders(rawHeaders){ - const headersString = typeof rawHeaders === 'string' ? rawHeaders : rawHeaders.toString(); - const cleanedHeadersString = headersString.trim(); - const headerLines = cleanedHeadersString.split('\n'); - const parsedHeaders = headerLines.reduce((acc, line) => { - let [key, ...value] = line.split(':'); // split into key and the rest of the value - key = key.trim(); - value = value.join(':').trim(); - if (key && value) { - acc[key.toLowerCase()] = value; - } - return acc; - }, {}); - return parsedHeaders -} + function extractChecksumFromResponse(parsedHeaders) { if (parsedHeaders['content-md5']) { @@ -263,16 +249,20 @@ const uploadFile = async (config, repo, reference, path, file, onProgress) => { additionalHeaders = { "x-ms-blob-type": "BlockBlob" } } const getResp = await staging.get(repo.id, reference.id, fpath, config.pre_sign_support_ui); - const uploadResponse = await uploadWithProgress(getResp.presigned_url, file, 'PUT', onProgress, additionalHeaders); - if (uploadResponse.status >= 400) { - throw new Error(`Error uploading file: HTTP ${uploadResponse.status}`); - } - const parsedHeaders = parseRawHeaders(uploadResponse.rawHeaders); - const checksum = extractChecksumFromResponse(parsedHeaders); - if (checksum === null) { - throw new Error(`CORS settings error. Check documentation for more info.`); - } - await staging.link(repo.id, reference.id, fpath, getResp, checksum, file.size, file.type); + try { + const uploadResponse = await uploadWithProgress(getResp.presigned_url, file, 'PUT', onProgress, additionalHeaders); + const parsedHeaders = parseRawHeaders(uploadResponse.rawHeaders); + const checksum = extractChecksumFromResponse(parsedHeaders); + await staging.link(repo.id, reference.id, fpath, getResp, checksum, file.size, file.type); + } catch(error) { + if (error.status >= 400) { + throw new Error(`Error uploading file: HTTP ${error.status}`); + } + if (error.status === 0) { + throw new Error(`CORS settings error. Check documentation for more info.`); + } + throw error; + } } else { await objects.upload(repo.id, reference.id, fpath, file, onProgress); }