import type { key_val } from '$lib/ae_stores'; export let temp_get_blob_percent_completed = 0; export let get_blob_percent_completed = temp_get_blob_percent_completed; export let temp_get_object_percent_completed = 0; export let get_object_percent_completed = temp_get_object_percent_completed; export let get_object = async function get_object( { api_cfg = null, endpoint = '', headers = {}, params = {}, data = {}, timeout = 60000, // return_meta = false, return_blob = false, filename = '', auto_download = false, as_list = false, // Is this still really needed? // The task_id value should be a random string that is unique to the task. This is used to identify the task in the message event. task_id = crypto.randomUUID(), log_lvl = 0, retry_count = 5 // Number of retry attempts }: { api_cfg: any, endpoint: string, headers?: any, params?: any, data?: any, timeout?: number, // return_meta?: boolean, return_blob?: boolean, filename?: null | string, auto_download?: boolean, as_list?: boolean, task_id?: string, log_lvl?: number, retry_count?: number } ) { if (log_lvl) { console.log(`*** get_object() *** Endpoint: ${endpoint} AE Task ID: ${task_id}`); console.log('Params:', params); if (log_lvl > 1) { console.log('Data:', data); } } if (!api_cfg) { console.log('No API Config was provided. Returning false.'); return false; } const url = new URL(endpoint, api_cfg['base_url']); Object.keys(params).forEach(key => url.searchParams.append(key, params[key])); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); // Remove a header parameter if it is set to null if (api_cfg['headers'].hasOwnProperty('x-no-account-id') && api_cfg['headers']['x-no-account-id'] === null) { delete api_cfg['headers']['x-no-account-id']; } // Clean the headers let headers_cleaned: key_val = {}; for (const prop in headers) { let prop_cleaned = prop.replaceAll('_', '-'); if (typeof headers[prop] != 'string') { headers[prop] = JSON.stringify(headers[prop]); } headers_cleaned[prop_cleaned] = headers[prop]; if (log_lvl > 1) { console.log(`${prop_cleaned}: ${headers_cleaned[prop_cleaned]}`); } } headers = headers_cleaned; if (log_lvl > 1) { console.log('All headers cleaned:', headers); } const fetchOptions: RequestInit = { method: 'GET', headers: { ...api_cfg['headers'], ...headers }, signal: controller.signal }; if (log_lvl > 1) { console.log('Fetch options:', fetchOptions); } for (let attempt = 1; attempt <= retry_count; attempt++) { try { const response = await fetch(url.toString(), fetchOptions) .catch(function (error) { console.log('API GET Object *fetch* request was aborted or failed in an unexpected way.', error); }); clearTimeout(timeoutId); if (!response) { if (log_lvl > 1) { console.log('API GET Object: Something went wrong with *fetch* request. Returning false? Throwing an error!'); } throw new Error(`HTTP fetch request was aborted or failed in an unexpected way! URL = ${url.toString()}`); // This will allow it to retry // return false; // This will stop the retries } if (log_lvl) { console.log(`Response: status=${response.status} statusText=${response.statusText} url=${response.url} attempt=${attempt}`); } if (log_lvl > 1) { console.log('Response:', response); } if (!response.ok) { if (response.status === 404) { if (log_lvl) { console.log('The response was a 404 not found "error". Returning null.'); } return null; } console.log('The response was not ok. Throwing an error!'); throw new Error(`HTTP error! status: ${response.status}`); } if (!return_blob) { const json = await response.json(); if (log_lvl > 1) { console.log('Response JSON:', json); } if (!Array.isArray(json.data) && as_list) { return [json.data]; } return json.data || json; } else { const reader = response.body?.getReader(); const contentLength = +response.headers.get('Content-Length')!; let receivedLength = 0; const chunks = []; while (true) { const { done, value } = await reader!.read(); if (done) break; chunks.push(value); receivedLength += value.length; const percent_completed = Math.round((receivedLength * 100) / contentLength); if (log_lvl > 1) { console.log('GET Blob Progress:', percent_completed, 'Total:', contentLength, 'Loaded:', receivedLength, 'Percent Completed', percent_completed); } temp_get_blob_percent_completed = percent_completed; try { if (typeof window !== 'undefined') { window.postMessage({ type: 'api_download_blob', status: 'downloading', task_id: task_id, endpoint: endpoint, filename: filename, size_total: contentLength, size_loaded: receivedLength, percent_completed: percent_completed }, '*'); } } catch (e) { console.error('Error posting message:', e); } } const blob = new Blob(chunks); if (auto_download) { const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = downloadUrl; link.setAttribute('download', filename || 'download'); document.body.appendChild(link); link.click(); link.remove(); return true; } else { return blob; } } } catch (error) { console.log(`API GET object request *fetch* error on attempt ${attempt}:`, error); if (attempt === retry_count) { console.log('Max retry attempts reached. Returning false.'); return false; } // Log retry information if (log_lvl) { console.log(`Retrying... (${attempt}/${retry_count})`); } } } };