API V3: Implement Structured Error Handling and Validation Tests
- Updated api_get_object and api_post_object to extract rich metadata (meta.details) from 400/500 responses. - Enables frontend to bubble up specific DB schema, validation, and constraint errors for better debugging. - Added 'V3 Hardening' section to /testing dashboard with automated tests for Permissive Mode and Structured Error extraction.
This commit is contained in:
@@ -184,7 +184,21 @@ export const get_object = async function get_object({
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('The response was not ok. Throwing an error!');
|
||||
// Structured Error Handling (V3): Attempt to get rich error metadata
|
||||
let error_json: any = null;
|
||||
try {
|
||||
error_json = await response.json();
|
||||
} catch (e) {
|
||||
// Not JSON
|
||||
}
|
||||
|
||||
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
|
||||
|
||||
if (error_json?.meta?.details) {
|
||||
// Return the rich error object so caller can handle specific categories (database_schema, etc)
|
||||
return error_json;
|
||||
}
|
||||
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -164,19 +164,34 @@ export const post_object = async function post_object({
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
console.warn('404 Not Found. Returning null.');
|
||||
if (log_lvl) {
|
||||
console.log('The response was a 404 not found "error". Returning null.');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const errorBody = await response.text();
|
||||
console.error(`HTTP error! status: ${response.status}`, errorBody);
|
||||
|
||||
// Don't retry on client errors (400, 401, 403)
|
||||
if (response.status >= 400 && response.status < 404) {
|
||||
// FAIL FAST (Section 2D): Do not retry on Auth/Permission failures
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
console.error(`API Auth Failure (${response.status}). Failing fast as per Section 2D protocol.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new Error(`HTTP error! status: ${response.status} - ${errorBody}`);
|
||||
// Structured Error Handling (V3): Attempt to get rich error metadata
|
||||
let error_json: any = null;
|
||||
try {
|
||||
error_json = await response.json();
|
||||
} catch (e) {
|
||||
// Not JSON
|
||||
}
|
||||
|
||||
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
|
||||
|
||||
if (error_json?.meta?.details) {
|
||||
// Return the rich error object so caller can handle specific categories (database_schema, etc)
|
||||
return error_json;
|
||||
}
|
||||
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
if (!return_blob) {
|
||||
|
||||
@@ -176,6 +176,53 @@
|
||||
return await response.json();
|
||||
});
|
||||
|
||||
// V3 Schema & Error Validation
|
||||
const test_permissive_mode = () => run_test('Permissive Mode Test', async () => {
|
||||
const endpoint = `/v3/crud/account/${$ae_loc.account_id || 'ghost'}`;
|
||||
const data = {
|
||||
name: $ae_loc.account_name,
|
||||
_non_existent_field: 'This should be ignored by the API'
|
||||
};
|
||||
// Use post_object for a patch-like operation if needed, or update_ae_obj_v3 style
|
||||
// For testing purposes, we'll use a standard fetch to see raw response
|
||||
const url = new URL(endpoint, $ae_api.base_url);
|
||||
const headers = {
|
||||
...$ae_api.headers,
|
||||
'x-account-id': $ae_loc.account_id || 'ghost',
|
||||
'Authorization': `Bearer ${$ae_loc.jwt}`
|
||||
};
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: 'PATCH',
|
||||
headers,
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return { status: response.status, result };
|
||||
});
|
||||
|
||||
const test_structured_error = () => run_test('Structured Error Validation (Deliberate 400)', async () => {
|
||||
const endpoint = `/v3/crud/account/${$ae_loc.account_id || 'ghost'}`;
|
||||
// To trigger a 400 error despite permissive mode, we'll try to set a read-only field or invalid type if possible
|
||||
// Actually, the easiest way to test structured error is to send a malformed filter to /search
|
||||
const url = new URL(`${endpoint.split('/account')[0]}/account/search`, $ae_api.base_url);
|
||||
const headers = {
|
||||
...$ae_api.headers,
|
||||
'x-account-id': $ae_loc.account_id || 'ghost',
|
||||
'Authorization': `Bearer ${$ae_loc.jwt}`
|
||||
};
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({ and: [{ field: "non_existent", op: "eq", value: "fail" }] })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return { status: response.status, structured_details: result.meta?.details || 'MISSING' };
|
||||
});
|
||||
|
||||
// Environment Diagnostics
|
||||
let is_native = $derived(typeof window !== 'undefined' && !!(window as any).native_app);
|
||||
let app_mode = $derived($events_loc?.launcher?.app_mode || 'web');
|
||||
@@ -443,6 +490,18 @@
|
||||
<Building2 size={16}/> Load Accounts
|
||||
</button>
|
||||
</div>
|
||||
<div class="card p-6 space-y-4 border border-gray-500 shadow-lg transition-all group hover:bg-surface-500/5">
|
||||
<header class="flex items-center gap-2 text-warning-500 border-b border-gray-500 pb-2">
|
||||
<ShieldAlert size={20}/>
|
||||
<h4 class="h4 font-bold uppercase tracking-widest">V3 Hardening</h4>
|
||||
</header>
|
||||
<button class="btn variant-filled-warning p-4 w-full shadow-md transition-all hover:scale-[1.02] flex items-center justify-center gap-2" disabled={!$ae_loc.jwt} onclick={test_permissive_mode} title="Verifies the API ignores unknown fields (x-ae-ignore-extra-fields).">
|
||||
<Zap size={16}/> Permissive Mode Test
|
||||
</button>
|
||||
<button class="btn variant-filled-error p-4 w-full shadow-md transition-all hover:scale-[1.02] flex items-center justify-center gap-2" disabled={!$ae_loc.jwt} onclick={test_structured_error} title="Deliberately triggers a 400 error to verify rich metadata extraction.">
|
||||
<ShieldAlert size={16}/> Structured Error Test
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Maintenance -->
|
||||
|
||||
Reference in New Issue
Block a user