From 7af1d41ff321cf97b2dea9e53b082fe6394aa329 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 7 May 2025 18:46:39 -0400 Subject: [PATCH] The POST function no longer needs Axios. It will now retry X times if it fails. --- package.json | 2 +- src/lib/ae_api/api_post_object.ts | 435 +++++++++++------- src/routes/journals/[journal_id]/+layout.ts | 4 +- .../entry/[journal_entry_id]/+page.ts | 4 +- 4 files changed, 282 insertions(+), 163 deletions(-) diff --git a/package.json b/package.json index bf7460ff..feab4843 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ae-app-svelte5", - "version": "0.2.430", + "version": "0.2.507", "private": true, "scripts": { "dev": "vite dev", diff --git a/src/lib/ae_api/api_post_object.ts b/src/lib/ae_api/api_post_object.ts index 284265fa..b1615452 100644 --- a/src/lib/ae_api/api_post_object.ts +++ b/src/lib/ae_api/api_post_object.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +// import axios from 'axios'; export let temp_post_blob_percent_completed = 0; export let post_blob_percent_completed = temp_post_blob_percent_completed; @@ -8,18 +8,19 @@ export let post_object_percent_completed = temp_post_object_percent_completed; // Updated 2024-05-23 export let post_object = async function post_object( { - api_cfg=null, - endpoint='', - params={}, - data={}, - form_data=null, - return_meta=false, - return_blob=false, - filename='', - auto_download=false, + api_cfg = null, + endpoint = '', + params = {}, + data = {}, + form_data = null, + return_meta = false, + return_blob = false, + filename = '', + auto_download = false, // 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 + task_id = crypto.randomUUID(), + log_lvl = 0, + retry_count = 5 }: { api_cfg: any, endpoint: string, @@ -31,9 +32,10 @@ export let post_object = async function post_object( filename?: string, auto_download?: boolean, task_id?: string, - log_lvl?: number + log_lvl?: number, + retry_count?: number } - ) { + ) { if (log_lvl) { console.log(`*** post_object() *** Endpoint: ${endpoint} Task ID: ${task_id}`); @@ -50,175 +52,288 @@ export let post_object = async function post_object( console.log(`Filename: ${filename}`); console.log(`Auto Download: ${auto_download}`); } - // console.log(return_meta); } - let axios_api = axios.create({ - baseURL: api_cfg['base_url'], - /* other custom settings */ - }); - axios_api.defaults.headers = api_cfg['headers']; - console.log('Axios API', axios_api); - // console.log('Axios API POST', axios_api.post); + if (!api_cfg) { + console.error('No API Config was provided. Returning false.'); + return false; + } + + // Construct the URL with query parameters + const url = new URL(endpoint, api_cfg['base_url']); + Object.keys(params).forEach(key => url.searchParams.append(key, params[key])); + + // Clean the headers + let headers_cleaned: Record = {}; + for (const prop in api_cfg['headers']) { + let prop_cleaned = prop.replaceAll('_', '-'); + headers_cleaned[prop_cleaned] = api_cfg['headers'][prop]; + } - // if (typeof data == 'FormData') { if (form_data) { - axios_api.defaults.headers['content-type'] = 'multipart/form-data'; - data = form_data; + headers_cleaned['Content-Type'] = 'multipart/form-data'; } else { - axios_api.defaults.headers['content-type'] = 'application/json'; + headers_cleaned['Content-Type'] = 'application/json'; } + if (log_lvl > 1) { + console.log('Cleaned Headers:', headers_cleaned); + } - if (!return_blob) { - let response_data = await axios_api.post( - endpoint, - data, - { - params: params, - onUploadProgress: (progressEvent) => { - let percent_completed = Math.round( - (progressEvent.loaded * 100) / progressEvent.total - ); - console.log('POST Progress:', progressEvent.progress, 'Total:', progressEvent.total, 'Loaded:', progressEvent.loaded, 'Percent Completed', percent_completed); + for (let attempt = 1; attempt <= retry_count; attempt++) { + try { + const controller = new AbortController(); + const fetchOptions: RequestInit = { + method: 'POST', + headers: headers_cleaned, + body: form_data ? form_data : JSON.stringify(data), + signal: controller.signal + }; - temp_post_object_percent_completed = percent_completed; - - try { - window.postMessage({ - type: 'api_post_json_form', - status: 'uploading', - task_id: task_id, - endpoint: endpoint, - size_total: progressEvent.total, - size_loaded: progressEvent.loaded, - percent_completed: percent_completed, - progress: progressEvent.progress, - rate: progressEvent.rate, - }, - '*' - ); - } catch (error) { - console.log('Error posting message to window:', error); - } - } - } - ) - .then(function (response) { - console.log('POST Response Data:', response.data); - try { - window.postMessage({ - type: 'api_post_json_form', - status: 'complete', - task_id: task_id, - endpoint: endpoint, - size_total: 0, - size_loaded: 0, - percent_completed: 100, - progress: 100, - rate: 0, - }, - '*' - ); - } catch (error) { - console.log('Error posting message to window:', error); + if (log_lvl > 1) { + console.log('Fetch Options:', fetchOptions); } - if (response.data['data'].result === null) { - // This should mean that the request was successful, but a result of None/null was returned from Aether API. - // console.log('Returning null after POST'); - return null; - } else { - // This should mean that the request was successful, and a result with data was returned from Aether API. - // console.log('Returning data after POST'); - return response.data['data']; - } - //return response.data; - }) - .catch(function (error) { - if (error.response && error.response.status === 404) { - return null; // Returning null since there were no results - } - console.log(error); - return false; // Returning false since something may have gone wrong. Also more in line with what the API returns. - // return error; - }); + const response = await fetch(url.toString(), fetchOptions); - if (log_lvl > 1) { - console.log('Response Data:', response_data); - } - axios_api.defaults.headers['content-type'] = 'application/json'; - return response_data; - - } else { - // console.log('Expecting a Blob to be returned...'); - - let response_data_promise = await axios_api.post( - endpoint, - data, - { - params: params, - responseType: 'blob', - onDownloadProgress: (progressEvent) => { - let percent_completed = Math.round( - (progressEvent.loaded * 100) / progressEvent.total - ); - console.log('POST Blob Progress:', progressEvent.progress, 'Total:', progressEvent.total, 'Loaded:', progressEvent.loaded, 'Percent Completed', percent_completed); - - temp_post_blob_percent_completed = percent_completed; - } - } - ) - .then(function (response) { if (log_lvl) { - console.log(response); + console.log(`Response: status=${response.status} attempt=${attempt}`); } - const { data, headers } = response - console.log(headers); + if (!response.ok) { + if (response.status === 404) { + console.warn('404 Not Found. Returning null.'); + return null; // Returning null since there were no results + } + throw new Error(`HTTP error! status: ${response.status}`); + } - if (filename) { - } else if (headers['content-disposition']) { - filename = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1'); + if (!return_blob) { + const json = await response.json(); + + if (log_lvl > 1) { + console.log('Response JSON:', json); + } + + // Post a message to the window indicating the upload is complete + try { + window.postMessage({ + type: 'api_post_json_form', + status: 'complete', + task_id: task_id, + endpoint: endpoint, + size_total: 0, + size_loaded: 0, + percent_completed: 100, + progress: 100, + rate: 0 + }, '*'); + } catch (error) { + console.error('Error posting message to window:', error); + } + + // Return the response data or metadata + return return_meta ? json : json.data; } else { - filename = 'unknown_file.ext'; + const blob = await response.blob(); + + 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.error(`API POST error on attempt ${attempt}:`, error); + + // If this is the last attempt, return false + if (attempt === retry_count) { + console.error('Max retry attempts reached. Returning false.'); + return false; } - if (auto_download) { - const url = window.URL.createObjectURL(new Blob([response.data])); - const link = document.createElement('a'); - link.href = url; - // link.setAttribute('download', 'event_exhibit_tracking_export.xlsx'); //or any other extension - link.setAttribute('download', filename); //or any other extension - document.body.appendChild(link); - link.click(); - return true; - } else { - return response; + // Log retry information + if (log_lvl) { + console.log(`Retrying... (${attempt}/${retry_count})`); } - }); - - if (response_data_promise) { - // The most common and expected response. - // console.log('Returning result. This is generally expected.'); - // let test_blob = new Blob([response_data_promise.data]); - // console.log(test_blob); - // return test_blob; - // console.log(response_data_promise.blob()); - return response_data_promise; - } else { - // This generally should not happen. It likely means the query was bad or an API issue. - console.log('Returning unknown. This should not happen in most cases.'); - Promise.reject(new Error('fail')).then(resolved, rejected); } } + + + + // let axios_api = axios.create({ + // baseURL: api_cfg['base_url'], + // /* other custom settings */ + // }); + // axios_api.defaults.headers = api_cfg['headers']; + // console.log('Axios API', axios_api); + // // console.log('Axios API POST', axios_api.post); + + // // if (typeof data == 'FormData') { + // if (form_data) { + // axios_api.defaults.headers['content-type'] = 'multipart/form-data'; + // data = form_data; + // } else { + // axios_api.defaults.headers['content-type'] = 'application/json'; + // } + + + // if (!return_blob) { + // let response_data = await axios_api.post( + // endpoint, + // data, + // { + // params: params, + // onUploadProgress: (progressEvent) => { + // let percent_completed = Math.round( + // (progressEvent.loaded * 100) / progressEvent.total + // ); + // console.log('POST Progress:', progressEvent.progress, 'Total:', progressEvent.total, 'Loaded:', progressEvent.loaded, 'Percent Completed', percent_completed); + + // temp_post_object_percent_completed = percent_completed; + + // try { + // window.postMessage({ + // type: 'api_post_json_form', + // status: 'uploading', + // task_id: task_id, + // endpoint: endpoint, + // size_total: progressEvent.total, + // size_loaded: progressEvent.loaded, + // percent_completed: percent_completed, + // progress: progressEvent.progress, + // rate: progressEvent.rate, + // }, + // '*' + // ); + // } catch (error) { + // console.log('Error posting message to window:', error); + // } + // } + // } + // ) + // .then(function (response) { + // console.log('POST Response Data:', response.data); + // try { + // window.postMessage({ + // type: 'api_post_json_form', + // status: 'complete', + // task_id: task_id, + // endpoint: endpoint, + // size_total: 0, + // size_loaded: 0, + // percent_completed: 100, + // progress: 100, + // rate: 0, + // }, + // '*' + // ); + // } catch (error) { + // console.log('Error posting message to window:', error); + // } + + // if (response.data['data'].result === null) { + // // This should mean that the request was successful, but a result of None/null was returned from Aether API. + // // console.log('Returning null after POST'); + // return null; + // } else { + // // This should mean that the request was successful, and a result with data was returned from Aether API. + // // console.log('Returning data after POST'); + // return response.data['data']; + // } + // //return response.data; + // }) + // .catch(function (error) { + // if (error.response && error.response.status === 404) { + // return null; // Returning null since there were no results + // } + // console.log(error); + // return false; // Returning false since something may have gone wrong. Also more in line with what the API returns. + // // return error; + // }); + + // if (log_lvl > 1) { + // console.log('Response Data:', response_data); + // } + // axios_api.defaults.headers['content-type'] = 'application/json'; + // return response_data; + + // } else { + // // console.log('Expecting a Blob to be returned...'); + + // let response_data_promise = await axios_api.post( + // endpoint, + // data, + // { + // params: params, + // responseType: 'blob', + // onDownloadProgress: (progressEvent) => { + // let percent_completed = Math.round( + // (progressEvent.loaded * 100) / progressEvent.total + // ); + // console.log('POST Blob Progress:', progressEvent.progress, 'Total:', progressEvent.total, 'Loaded:', progressEvent.loaded, 'Percent Completed', percent_completed); + + // temp_post_blob_percent_completed = percent_completed; + // } + // } + // ) + // .then(function (response) { + // if (log_lvl) { + // console.log(response); + // } + + // const { data, headers } = response + // console.log(headers); + + // if (filename) { + // } else if (headers['content-disposition']) { + // filename = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1'); + // } else { + // filename = 'unknown_file.ext'; + // } + + // if (auto_download) { + // const url = window.URL.createObjectURL(new Blob([response.data])); + // const link = document.createElement('a'); + // link.href = url; + // // link.setAttribute('download', 'event_exhibit_tracking_export.xlsx'); //or any other extension + // link.setAttribute('download', filename); //or any other extension + // document.body.appendChild(link); + // link.click(); + // return true; + // } else { + // return response; + // } + // }); + + // if (response_data_promise) { + // // The most common and expected response. + // // console.log('Returning result. This is generally expected.'); + // // let test_blob = new Blob([response_data_promise.data]); + // // console.log(test_blob); + // // return test_blob; + // // console.log(response_data_promise.blob()); + // return response_data_promise; + // } else { + // // This generally should not happen. It likely means the query was bad or an API issue. + // console.log('Returning unknown. This should not happen in most cases.'); + // Promise.reject(new Error('fail')).then(resolved, rejected); + // } + // } } -function resolved(result: any) { - console.log('Resolved'); -} +// function resolved(result: any) { +// console.log('Resolved'); +// } -function rejected(result: any) { - console.error(result); -} \ No newline at end of file +// function rejected(result: any) { +// console.error(result); +// } \ No newline at end of file diff --git a/src/routes/journals/[journal_id]/+layout.ts b/src/routes/journals/[journal_id]/+layout.ts index 2ccda5e6..812297a1 100644 --- a/src/routes/journals/[journal_id]/+layout.ts +++ b/src/routes/journals/[journal_id]/+layout.ts @@ -21,7 +21,9 @@ export async function load({ params, parent }) { }); } ae_acct.slct.journal_id = journal_id; - console.log(`ae_journals journals [journal_id] +page.ts: journal_id = `, ae_acct.slct.journal_id); + if (log_lvl) { + console.log(`ae_journals journals [journal_id] +page.ts: journal_id = `, journal_id); + } if (browser) { if (log_lvl) { diff --git a/src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.ts b/src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.ts index fafe86a1..832bde94 100644 --- a/src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.ts +++ b/src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.ts @@ -25,7 +25,9 @@ export async function load({ params, parent }) { // route }); } ae_acct.slct.journal_entry_id = journal_entry_id; - console.log(`ae_journals journals [journal_entry_id] +page.ts: journal_entry_id = `, ae_acct.slct.journal_entry_id); + if (log_lvl) { + console.log(`ae_journals journals [journal_entry_id] +page.ts: journal_entry_id = `, journal_entry_id); + } if (browser) { if (log_lvl) {