_wrapperVersion = '1.0.0'; _minApiVersion = '1.0.0'; _maxApiVersion = '1.0.0'; _defaultTTL = 60000; _apiConfig = { basePath: '/api/v1/' }; if (!window.localStorage) { console.warn('Local Storage is not available, some features may not work'); } // Generic driver functions let _api = { get: async function (path) { const options = { headers: new Headers({ 'content-type': 'application/json' }) }; const response = await fetch(_apiConfig.basePath + path, options); // Handle the response if (!response.ok) { console.error('Failed to fetch:', response.statusText); _testPageFail(response.statusText); return; } const result = await response.json(); // Handle the result, was json valid? if (!result) { // Is it a number instead? if (typeof result === 'number') { return result; } console.error('Invalid JSON response'); _testPageFail('Invalid JSON response'); return; } return result; }, post: async function (path, data) { const options = { method: 'POST', headers: new Headers({ 'content-type': 'application/json' }), body: JSON.stringify(data) }; const response = await fetch(_apiConfig.basePath + path, options); // Handle the response if (!response.ok) { _testPageFail(response.statusText); return; } const result = await response.json(); // Handle the result, was json valid? if (!result) { _testPageFail('Invalid JSON response'); return; } return result; }, delete: async function (path, data) { const options = { method: 'DELETE', headers: new Headers({ 'content-type': 'application/json' }), body: JSON.stringify(data) }; const response = await fetch(_apiConfig.basePath + path, options); // Handle the response if (!response.ok) { _testPageFail(response.statusText); return; } const result = await response.json(); // Handle the result, was json valid? if (!result) { _testPageFail('Invalid JSON response'); return; } return result; }, patch: async function (path, data) { const options = { method: 'PATCH', headers: new Headers({ 'content-type': 'application/json' }), body: JSON.stringify(data) }; const response = await fetch(_apiConfig.basePath + path, options); // Handle the response if (!response.ok) { _testPageFail(response.statusText); return; } const result = await response.json(); // Handle the result, was json valid? if (!result) { _testPageFail('Invalid JSON response'); return; } return result; } }; function updateRow(tableName, id, data) { invalidateCache(tableName); return _api.patch(`${tableName}`, { id: id, ...data }); } function deleteRow(tableName, id) { invalidateCache(tableName); return _api.delete(`${tableName}`, { id: id }); } function getApiDescriptionByTable(tableName) { const keyDesc = `desc:${tableName}`; const keyTime = `${keyDesc}:time`; const keyTTL = `${keyDesc}:ttl`; // Retrieve cached data const description = JSON.parse(localStorage.getItem(keyDesc)); const timeCreated = parseInt(localStorage.getItem(keyTime)); const ttl = parseInt(localStorage.getItem(keyTTL)); // Check if valid cached data exists if (description && timeCreated && ttl) { const currentTime = Date.now(); const age = currentTime - parseInt(timeCreated, 10); if (age < parseInt(ttl, 10)) { // Return cached data immediately return Promise.resolve(description); } else { console.warn('Cached description expired; fetching new data'); // Fetch new data, update cache, and return it return fetchAndUpdateCache(tableName); } } else { console.warn('No cached description; fetching from server'); // Fetch data, update cache, and return it return fetchAndUpdateCache(tableName); } function fetchAndUpdateCache(tableName) { return _api .get(`${tableName}/describe`) .then((data) => { if (data) { // Update local storage with new data localStorage.setItem(keyDesc, JSON.stringify(data)); localStorage.setItem(keyTime, Date.now().toString()); localStorage.setItem(keyTTL, '60000'); // 60 seconds TTL } return data; // Return the fetched data }) .catch((error) => { console.error('Failed to fetch description:', error); // Fallback to cached data if available (even if expired) return description || null; }); } } function returnTableDataByTableName(tableName, search="", orderBy="asc", sort="", take=-1, skip=0) { var orderBy = orderBy.toLowerCase(); if(orderBy == "") { orderBy = "asc"; } var baseString = tableName + "?order=" + orderBy; if(sort && sort.length > 0) { baseString += "&sort=" + sort; } if(take > 0) { baseString += "&take=" + take; } if(skip > 0) { baseString += "&skip=" + skip; } if (search && search.length > 0) { return _api.get(baseString + '&search=' + search); } else { return _api.get(baseString); } } async function getCountByTable(tableName, search="") { let baseString = tableName + '?count=true'; if (search && search.length > 0) { baseString += '&search=' + search; } // Stored in `data:count:${tableName}` let result = await _api.get(baseString); console.debug('Count result:', result); if (typeof result !== 'number') { _testPageWarn('Count was not a number, was: ' + result); console.warn('Count was not a number, was: ' + result); return -1; } return result; } function _testPageFail(reason) { document.getElementById('heroStatus').classList.remove('is-success'); document.getElementById('heroStatus').classList.add('is-danger'); document.getElementById('heroExplainer').innerHTML = 'API Wrapper Test Failed, reason: ' + reason; } function _testPageWarn(reason) { document.getElementById('heroStatus').classList.remove('is-success'); document.getElementById('heroStatus').classList.add('is-warning'); document.getElementById('heroExplainer').innerHTML = 'API Wrapper Test Warning, reason: ' + reason; } function getServerVersion() { return _api.get('version'); } function createEntry(tableName, data) { invalidateCache(tableName); return _api.post(tableName, data); } function invalidateCache(tableName) { const keyDesc = `desc:${tableName}`; const keyTime = `${keyDesc}:time`; const keyTTL = `${keyDesc}:ttl`; localStorage.removeItem(keyDesc); localStorage.removeItem(keyTime); localStorage.removeItem(keyTTL); }