Added samples for Svelte side API code and a new CRUD V3 API guide.
This commit is contained in:
297
documentation/Aether_Svelte_API_TS_code_samples.md
Normal file
297
documentation/Aether_Svelte_API_TS_code_samples.md
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
```ts
|
||||||
|
export async function get_ae_obj_id_crud({
|
||||||
|
api_cfg,
|
||||||
|
no_account_id = false,
|
||||||
|
obj_type,
|
||||||
|
obj_id,
|
||||||
|
use_alt_table = false,
|
||||||
|
use_alt_base = false,
|
||||||
|
inc = {},
|
||||||
|
enabled = 'enabled',
|
||||||
|
hidden = 'not_hidden',
|
||||||
|
limit = 999999,
|
||||||
|
offset = 0,
|
||||||
|
data = {},
|
||||||
|
// key,
|
||||||
|
// jwt = null,
|
||||||
|
headers = {},
|
||||||
|
params = {},
|
||||||
|
timeout = 25000,
|
||||||
|
return_meta = false,
|
||||||
|
log_lvl = 0
|
||||||
|
}: {
|
||||||
|
api_cfg: any;
|
||||||
|
no_account_id?: boolean;
|
||||||
|
obj_type: string;
|
||||||
|
obj_id: string;
|
||||||
|
use_alt_table?: boolean;
|
||||||
|
use_alt_base?: boolean;
|
||||||
|
inc?: any;
|
||||||
|
enabled?: 'enabled' | 'all' | 'not_enabled' | undefined;
|
||||||
|
hidden?: 'hidden' | 'all' | 'not_hidden' | undefined;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
data?: any;
|
||||||
|
// key: string,
|
||||||
|
// jwt?: string,
|
||||||
|
headers?: any;
|
||||||
|
params?: key_val;
|
||||||
|
timeout?: number;
|
||||||
|
return_meta?: boolean;
|
||||||
|
log_lvl?: number;
|
||||||
|
}) {
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Updated 2024-05-23
|
||||||
|
export const patch_object = async function patch_object({
|
||||||
|
api_cfg = null,
|
||||||
|
endpoint = '',
|
||||||
|
params = {},
|
||||||
|
data = {},
|
||||||
|
return_meta = false,
|
||||||
|
log_lvl = 0,
|
||||||
|
retry_count = 5 // Number of retry attempts
|
||||||
|
}: {
|
||||||
|
api_cfg: any;
|
||||||
|
endpoint: string;
|
||||||
|
params?: any;
|
||||||
|
data?: any;
|
||||||
|
return_meta?: boolean;
|
||||||
|
log_lvl?: number;
|
||||||
|
retry_count?: number;
|
||||||
|
}) {
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Updated 2024-05-23
|
||||||
|
export const 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,
|
||||||
|
// 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
|
||||||
|
}: {
|
||||||
|
api_cfg: any;
|
||||||
|
endpoint: string;
|
||||||
|
params?: any;
|
||||||
|
data?: any;
|
||||||
|
form_data?: any;
|
||||||
|
return_meta?: boolean;
|
||||||
|
return_blob?: boolean;
|
||||||
|
filename?: string;
|
||||||
|
auto_download?: boolean;
|
||||||
|
task_id?: string;
|
||||||
|
log_lvl?: number;
|
||||||
|
retry_count?: number;
|
||||||
|
}) {
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Updated 2024-05-23
|
||||||
|
export const delete_object = async function delete_object({
|
||||||
|
api_cfg = null,
|
||||||
|
endpoint = '',
|
||||||
|
params = {},
|
||||||
|
data = {},
|
||||||
|
return_meta = false,
|
||||||
|
log_lvl = 0,
|
||||||
|
retry_count = 5 // Number of retry attempts
|
||||||
|
}: {
|
||||||
|
api_cfg: any;
|
||||||
|
endpoint: string;
|
||||||
|
params?: any;
|
||||||
|
data?: any;
|
||||||
|
return_meta?: boolean;
|
||||||
|
log_lvl?: number;
|
||||||
|
retry_count?: number;
|
||||||
|
}) {
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { key_val } from '$lib/stores/ae_stores';
|
||||||
|
import { get_object } from './api_get_object';
|
||||||
|
|
||||||
|
// Refactored 2025-11-13 to use a lookup map for endpoints.
|
||||||
|
const objTypeToEndpointMap: Record<string, string> = {
|
||||||
|
account: '/crud/account/list',
|
||||||
|
address: '/crud/address/list',
|
||||||
|
archive: '/crud/archive/list',
|
||||||
|
archive_content: '/crud/archive/content/list',
|
||||||
|
activity_log: '/crud/activity_log/list',
|
||||||
|
contact: '/crud/contact/list',
|
||||||
|
data_store: '/crud/data_store/list',
|
||||||
|
event: '/crud/event/list',
|
||||||
|
event_abstract: '/crud/event/abstract/list',
|
||||||
|
event_badge: '/crud/event/badge/list',
|
||||||
|
event_badge_template: '/crud/event/badge/template/list',
|
||||||
|
event_device: '/crud/event/device/list',
|
||||||
|
event_exhibit: '/crud/event/exhibit/list',
|
||||||
|
event_exhibit_tracking: '/crud/event/exhibit/tracking/list',
|
||||||
|
event_file: '/crud/event/file/list',
|
||||||
|
event_location: '/crud/event/location/list',
|
||||||
|
event_person: '/crud/event/person/list',
|
||||||
|
event_presentation: '/crud/event/presentation/list',
|
||||||
|
event_presenter: '/crud/event/presenter/list',
|
||||||
|
event_session: '/crud/event/session/list',
|
||||||
|
event_track: '/crud/event/track/list',
|
||||||
|
grant: '/crud/grant/list',
|
||||||
|
hosted_file: '/crud/hosted_file/list',
|
||||||
|
journal: '/crud/journal/list',
|
||||||
|
journal_entry: '/crud/journal/entry/list',
|
||||||
|
order: '/crud/order/list',
|
||||||
|
order_line: '/crud/order/line/list',
|
||||||
|
page: '/crud/page/list',
|
||||||
|
person: '/crud/person/list',
|
||||||
|
post: '/crud/post/list',
|
||||||
|
post_comment: '/crud/post/comment/list',
|
||||||
|
site: '/crud/site/list',
|
||||||
|
sponsorship_cfg: '/crud/sponsorship/cfg/list',
|
||||||
|
sponsorship: '/crud/sponsorship/list',
|
||||||
|
// user: '/crud/user/list',
|
||||||
|
'lu-country_subdivision': '/crud/lu/country_subdivision/list',
|
||||||
|
'lu-country': '/crud/lu/country/list',
|
||||||
|
'lu-time_zone': '/crud/lu/time_zone/list'
|
||||||
|
};
|
||||||
|
|
||||||
|
function getEndpointForObjType(obj_type: string, for_obj_type?: string): string {
|
||||||
|
if (obj_type === 'lu' && for_obj_type) {
|
||||||
|
const key = `lu-${for_obj_type}`;
|
||||||
|
const endpoint = objTypeToEndpointMap[key];
|
||||||
|
if (endpoint) return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
const endpoint = objTypeToEndpointMap[obj_type];
|
||||||
|
if (endpoint) return endpoint;
|
||||||
|
|
||||||
|
throw new Error(`Unknown object type: ${obj_type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderBy = { [key: string]: 'ASC' | 'DESC' };
|
||||||
|
|
||||||
|
interface GetAeObjLiForObjIdCrudV2Params {
|
||||||
|
api_cfg: any; // Consider defining a specific type for api_cfg
|
||||||
|
obj_type: string;
|
||||||
|
for_obj_type: string;
|
||||||
|
for_obj_id?: string;
|
||||||
|
use_alt_tbl?: boolean | string;
|
||||||
|
use_alt_mdl?: boolean | string;
|
||||||
|
use_alt_exp?: boolean | string;
|
||||||
|
inc?: key_val;
|
||||||
|
enabled?: 'all' | 'enabled' | 'not_enabled';
|
||||||
|
hidden?: 'all' | 'hidden' | 'not_hidden';
|
||||||
|
order_by_li?: OrderBy[] | null;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
params_json?: any;
|
||||||
|
params?: key_val;
|
||||||
|
log_lvl?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_ae_obj_li_for_obj_id_crud_v2({
|
||||||
|
api_cfg,
|
||||||
|
obj_type,
|
||||||
|
for_obj_type,
|
||||||
|
for_obj_id,
|
||||||
|
use_alt_tbl = false,
|
||||||
|
use_alt_mdl = false,
|
||||||
|
use_alt_exp = false,
|
||||||
|
enabled = 'enabled',
|
||||||
|
hidden = 'not_hidden',
|
||||||
|
order_by_li = null,
|
||||||
|
limit = 999999,
|
||||||
|
offset = 0,
|
||||||
|
headers = {},
|
||||||
|
params_json = null,
|
||||||
|
params = {},
|
||||||
|
log_lvl = 0
|
||||||
|
}: GetAeObjLiForObjIdCrudV2Params) {
|
||||||
|
if (log_lvl) {
|
||||||
|
console.log('*** get_ae_obj_li_for_obj_id_crud_v2() ***');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const endpoint = `/v2${getEndpointForObjType(obj_type, for_obj_type)}`;
|
||||||
|
if (log_lvl) {
|
||||||
|
console.log('Endpoint:', endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to remove a few parameters from the params object that are not allowed.
|
||||||
|
delete params['qry__enabled'];
|
||||||
|
delete params['qry__hidden'];
|
||||||
|
delete params['qry__limit'];
|
||||||
|
delete params['qry__offset'];
|
||||||
|
|
||||||
|
if (for_obj_type) params['for_obj_type'] = for_obj_type;
|
||||||
|
if (for_obj_id) params['for_obj_id'] = for_obj_id;
|
||||||
|
|
||||||
|
if (use_alt_tbl === true) params['tbl_alt'] = 'alt';
|
||||||
|
if (use_alt_mdl === true) params['mdl_alt'] = 'alt';
|
||||||
|
if (use_alt_exp === true) params['exp_alt'] = 'alt';
|
||||||
|
|
||||||
|
const allowed_enabled_list = ['all', 'enabled', 'not_enabled'];
|
||||||
|
if (allowed_enabled_list.includes(enabled)) {
|
||||||
|
params['enabled'] = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allowed_hidden_list = ['all', 'hidden', 'not_hidden'];
|
||||||
|
if (allowed_hidden_list.includes(hidden)) {
|
||||||
|
params['hidden'] = hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: The order_by_li variable is in the "headers" because URL GET params do not handle complex objects very well.
|
||||||
|
if (order_by_li) {
|
||||||
|
headers['order_by_li'] = JSON.stringify(order_by_li);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit > 0) params['limit'] = limit;
|
||||||
|
if (offset > 0) params['offset'] = offset;
|
||||||
|
|
||||||
|
if (params_json) {
|
||||||
|
// NOTE: "jp" stands for "JSON Params". This is a JSON object that needs to be safely converted to a string for the params.
|
||||||
|
// Max characters for a GET request is ~2000. This is a limitation of the browser.
|
||||||
|
const json_params_str = encodeURIComponent(JSON.stringify(params_json));
|
||||||
|
if (json_params_str.length > 2083) {
|
||||||
|
// Using console.error instead of throwing an error to avoid crashing the app for a known limitation.
|
||||||
|
console.error(
|
||||||
|
`The JSON object is too large to be used as a GET parameter. Max length is 2083 characters. Length = ${json_params_str.length}`
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
params['jp'] = json_params_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_lvl) {
|
||||||
|
console.log('Params:', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
const object_li_get_promise = await get_object({
|
||||||
|
api_cfg: api_cfg,
|
||||||
|
endpoint: endpoint,
|
||||||
|
headers: headers,
|
||||||
|
params: params,
|
||||||
|
log_lvl: log_lvl
|
||||||
|
});
|
||||||
|
|
||||||
|
if (log_lvl > 1) {
|
||||||
|
console.log(object_li_get_promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
return object_li_get_promise;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in get_ae_obj_li_for_obj_id_crud_v2:', error);
|
||||||
|
return false; // Or handle the error as appropriate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
131
documentation/V3_FRONTEND_API_GUIDE.md
Normal file
131
documentation/V3_FRONTEND_API_GUIDE.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# Aether API V3 Frontend Integration Guide (Svelte/TypeScript)
|
||||||
|
|
||||||
|
This guide explains how to update or create frontend functions to interact with the new **Aether API V3 CRUD** endpoints. V3 introduces a nested URL structure, a powerful POST-based search, and improved performance.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Key Differences (V2 vs V3)
|
||||||
|
|
||||||
|
| Feature | CRUD V2 (Legacy) | CRUD V3 (Modern) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **Base Prefix** | `/v2/crud` | `/v3/crud` |
|
||||||
|
| **List Suffix** | Uses `/list` (e.g., `/v2/crud/journal/list`) | **No suffix** (e.g., `/v3/crud/journal/`) |
|
||||||
|
| **Nested Path** | Not supported in URL path | **Supported** (e.g., `/v3/crud/journal/{id}/journal_entry/`) |
|
||||||
|
| **Order By** | Passed in **Headers** (`order_by_li`) | Passed as **Query Parameter** (`order_by_li`) |
|
||||||
|
| **Complex Search**| Limited to GET `jp` (~2KB limit) | **POST `/search`** (Unlimited size) |
|
||||||
|
| **Latency Simulation**| Not standardized | `X-Delay-ms` (Header) or `delay_ms` (Query) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Implementing V3 CRUD Functions
|
||||||
|
|
||||||
|
### A. List Top-Level Objects (GET)
|
||||||
|
Use this for simple lists or filtering by a parent via query parameters.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export async function get_ae_obj_li_v3({
|
||||||
|
api_cfg,
|
||||||
|
obj_type,
|
||||||
|
for_obj_type,
|
||||||
|
for_obj_id,
|
||||||
|
enabled = 'enabled',
|
||||||
|
hidden = 'not_hidden',
|
||||||
|
limit = 100,
|
||||||
|
offset = 0,
|
||||||
|
order_by_li = null, // e.g., {"created_on": "DESC"}
|
||||||
|
delay_ms = 0
|
||||||
|
}) {
|
||||||
|
// 1. Build V3 Endpoint (Note: No /list suffix)
|
||||||
|
const endpoint = `/v3/crud/${obj_type}/`;
|
||||||
|
|
||||||
|
// 2. Build Query Params
|
||||||
|
const params: any = {
|
||||||
|
enabled,
|
||||||
|
hidden,
|
||||||
|
limit,
|
||||||
|
offset
|
||||||
|
};
|
||||||
|
|
||||||
|
if (for_obj_type) params.for_obj_type = for_obj_type;
|
||||||
|
if (for_obj_id) params.for_obj_id = for_obj_id;
|
||||||
|
if (order_by_li) params.order_by_li = JSON.stringify(order_by_li);
|
||||||
|
if (delay_ms) params.delay_ms = delay_ms;
|
||||||
|
|
||||||
|
return await get_object({ api_cfg, endpoint, params });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### B. Access Nested Objects (GET)
|
||||||
|
V3 allows you to enforce parent-child relationships directly in the URL.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Example: Get all entries for a specific journal
|
||||||
|
// GET /v3/crud/journal/{journal_id}/journal_entry/
|
||||||
|
export async function get_nested_obj_li_v3({
|
||||||
|
api_cfg,
|
||||||
|
parent_type,
|
||||||
|
parent_id,
|
||||||
|
child_type,
|
||||||
|
limit = 100
|
||||||
|
}) {
|
||||||
|
const endpoint = `/v3/crud/${parent_type}/${parent_id}/${child_type}/`;
|
||||||
|
return await get_object({ api_cfg, endpoint, params: { limit } });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### C. Advanced Search (POST)
|
||||||
|
Use the new `/search` endpoint for complex logic (AND/OR, LIKE, IN) that would exceed URL length limits.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export async function search_ae_obj_v3({
|
||||||
|
api_cfg,
|
||||||
|
obj_type,
|
||||||
|
search_query, // Complex SearchQuery object
|
||||||
|
order_by_li = null
|
||||||
|
}) {
|
||||||
|
const endpoint = `/v3/crud/${obj_type}/search`;
|
||||||
|
|
||||||
|
const params: any = {};
|
||||||
|
if (order_by_li) params.order_by_li = JSON.stringify(order_by_li);
|
||||||
|
|
||||||
|
// Note: search_query is sent in the BODY via POST
|
||||||
|
return await post_object({
|
||||||
|
api_cfg,
|
||||||
|
endpoint,
|
||||||
|
params,
|
||||||
|
data: search_query
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example search_query usage:
|
||||||
|
* {
|
||||||
|
* "and": [
|
||||||
|
* { "field": "enable", "op": "eq", "value": true },
|
||||||
|
* {
|
||||||
|
* "or": [
|
||||||
|
* { "field": "name", "op": "like", "value": "%Meeting%" },
|
||||||
|
* { "field": "priority", "op": "gt", "value": 5 }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Best Practices for V3
|
||||||
|
|
||||||
|
1. **Stop using Headers for Sorting**: In V2, we used `headers['order_by_li']`. In V3, always use `params['order_by_li']`.
|
||||||
|
2. **Prefer `/search` for Filters**: If your query involves more than a simple `for_obj_id`, use the `POST .../search` endpoint. It is safer, more readable, and bypasses the 2083-character URL limit.
|
||||||
|
3. **Non-Blocking UI**: Use the `delay_ms` parameter during development to test how your Svelte components handle loading states and skeleton screens.
|
||||||
|
4. **Singular Nouns**: Always use singular names for `obj_type` (e.g., `journal`, not `journals`), following the backend's `obj_type_kv_li` registry.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Special Case: Account Context
|
||||||
|
V3 strictly enforces account context. Ensure your `api_cfg` logic correctly sets the `X-Account-ID` header.
|
||||||
|
|
||||||
|
* If a request doesn't need an account (e.g., public site data), set the `X-No-Account-ID: bypass` header.
|
||||||
|
* If using a temporary token, use the `x_no_account_id_token` query parameter.
|
||||||
Reference in New Issue
Block a user