Initial work on finally creating and implementing more generic and standardized CRUD functions for my Aether objects. It should work very well for Delete, Create, and Update. Load and Load List will need more work.
This commit is contained in:
443
src/lib/ae_core/core__crud_generic.ts
Normal file
443
src/lib/ae_core/core__crud_generic.ts
Normal file
@@ -0,0 +1,443 @@
|
||||
import { marked } from 'marked';
|
||||
|
||||
import type { key_val } from '$lib/ae_stores';
|
||||
import { api } from '$lib/api';
|
||||
|
||||
import { db_save_ae_obj_li__ae_obj } from "$lib/ae_core/core__idb_dexie";
|
||||
|
||||
// Define generic CRUD args
|
||||
export interface GenericCrudArgs {
|
||||
api_cfg: any;
|
||||
obj_type: string;
|
||||
obj_id?: string;
|
||||
for_obj_type?: string;
|
||||
for_obj_id?: string;
|
||||
|
||||
db_instance?: any; // Optional DB instance for caching
|
||||
db_field_li?: string[]; // Optional list of fields to save in DB
|
||||
|
||||
// Flags to include related core object models
|
||||
inc_account_li?: boolean;
|
||||
inc_address_li?: boolean;
|
||||
inc_contact_li?: boolean;
|
||||
inc_person_li?: boolean;
|
||||
inc_site_li?: boolean;
|
||||
inc_site_domain_li?: boolean;
|
||||
inc_user_li?: boolean;
|
||||
|
||||
|
||||
// Flags to include related other object models
|
||||
inc_archive_li?: boolean;
|
||||
inc_archive_entry_li?: boolean;
|
||||
inc_event_li?: boolean;
|
||||
inc_event_session_li?: boolean;
|
||||
inc_post_li?: boolean;
|
||||
inc_post_comment_li?: boolean;
|
||||
inc_journal_li?: boolean;
|
||||
inc_journal_entry_li?: boolean;
|
||||
|
||||
inc_obj_type_li?: string[]; // Optional list of object types to include
|
||||
|
||||
data_kv?: key_val;
|
||||
enabled?: 'enabled' | 'disabled' | 'all';
|
||||
hidden?: 'not_hidden' | 'hidden' | 'all';
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
order_by_li?: key_val;
|
||||
params?: key_val;
|
||||
try_cache?: boolean;
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
// Generic function: Load single object by ID
|
||||
export async function load_ae_obj_id(
|
||||
args: GenericCrudArgs
|
||||
): Promise<any> {
|
||||
const { api_cfg, obj_type, obj_id, db_instance, db_field_li, inc_obj_type_li, try_cache = true, log_lvl = 0 } = args;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_id() *** obj_type=${obj_type} obj_id=${obj_id}`);
|
||||
}
|
||||
|
||||
let result = await api.get_ae_obj_id_crud({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
obj_id,
|
||||
params: {},
|
||||
log_lvl
|
||||
});
|
||||
|
||||
let idb_db_instance = null; // You must inject the correct DB instance per module
|
||||
let idb_tbl_name = obj_type;
|
||||
let properties_to_save: string[] = [];
|
||||
if (obj_type === 'journal') {
|
||||
idb_db_instance = 'ae_journals_db';
|
||||
// idb_tbl_name = 'journal';
|
||||
|
||||
result.id = result.journal_id_random; // Ensure we use the correct ID field
|
||||
result.journal_id = result.journal_id_random;
|
||||
result.account_id = result.account_id_random;
|
||||
result.person_id = result.person_id_random;
|
||||
|
||||
// WARNING: This works to populate most of the IDB table fields but it is not ideal. It is sort of a safety net.
|
||||
properties_to_save = Object.keys(db_instance.table('journal').schema.idxByName);
|
||||
}
|
||||
if (obj_type === 'journal_entry') {
|
||||
idb_db_instance = 'ae_journals_db';
|
||||
// idb_tbl_name = 'entry';
|
||||
|
||||
result.id = result.journal_entry_id_random; // Ensure we use the correct ID field
|
||||
result.journal_entry_id = result.journal_entry_id_random; // Ensure we use the correct ID field
|
||||
result.journal_id = result.journal_id_random;
|
||||
|
||||
// WARNING: This works to populate most of the IDB table fields but it is not ideal. It is sort of a safety net.
|
||||
properties_to_save = Object.keys(db_instance.table('journal_entry').schema.idxByName);
|
||||
|
||||
}
|
||||
properties_to_save = [
|
||||
...db_field_li
|
||||
];
|
||||
|
||||
// if (log_lvl) {
|
||||
// console.log('IDB DB Instance:', db_instance);
|
||||
// console.log(db_instance.journal_entry.core.schema);
|
||||
// console.log(db_instance.table('journal_entry'));
|
||||
// console.log(db_instance.table('journal_entry').schema.idxByName);
|
||||
// // Show only the keys of the indexes
|
||||
// console.log(Object.keys(db_instance.table('journal_entry').schema.idxByName));
|
||||
// }
|
||||
|
||||
if (try_cache && result) {
|
||||
// Process and save to DB
|
||||
const processed = await process_ae_obj__props({ obj_li: [result], log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_instance, // You must inject the correct DB instance per module
|
||||
table_name: idb_tbl_name,
|
||||
obj_li: processed,
|
||||
properties_to_save: properties_to_save,
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
if (inc_obj_type_li) {
|
||||
// Load related objects if specified
|
||||
for (const inc_obj_type of inc_obj_type_li) {
|
||||
if (log_lvl) {
|
||||
console.log(`Loading related objects of type: ${inc_obj_type}`);
|
||||
}
|
||||
const related_objects = await load_ae_obj_li({
|
||||
api_cfg,
|
||||
obj_type: inc_obj_type,
|
||||
for_obj_type: obj_type,
|
||||
for_obj_id: obj_id,
|
||||
enabled: 'enabled',
|
||||
hidden: 'not_hidden',
|
||||
limit: 99,
|
||||
try_cache,
|
||||
log_lvl
|
||||
});
|
||||
result[`${inc_obj_type}_li`] = related_objects;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generic function: Load list of objects
|
||||
export async function load_ae_obj_li(
|
||||
args: GenericCrudArgs
|
||||
): Promise<any> {
|
||||
const {
|
||||
api_cfg,
|
||||
obj_type,
|
||||
for_obj_type = '',
|
||||
for_obj_id,
|
||||
db_instance,
|
||||
db_field_li,
|
||||
inc_obj_type_li,
|
||||
enabled = 'enabled',
|
||||
hidden = 'not_hidden',
|
||||
limit = 99,
|
||||
offset = 0,
|
||||
order_by_li = {},
|
||||
params = {},
|
||||
try_cache = true,
|
||||
log_lvl = 0
|
||||
} = args;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** load_ae_obj_li() *** obj_type=${obj_type} for_obj_type=${for_obj_type} for_obj_id=${for_obj_id}`);
|
||||
}
|
||||
|
||||
let params_json: key_val = {};
|
||||
|
||||
let result = await api.get_ae_obj_li_for_obj_id_crud_v2({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
for_obj_type,
|
||||
for_obj_id,
|
||||
enabled,
|
||||
hidden,
|
||||
order_by_li,
|
||||
limit,
|
||||
offset,
|
||||
params_json: {},
|
||||
params,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
// Loop through results a
|
||||
let idb_db_instance = null; // You must inject the correct DB instance per module
|
||||
let idb_tbl_name = obj_type;
|
||||
let properties_to_save: string[] = [];
|
||||
if (obj_type === 'journal') {
|
||||
idb_db_instance = 'ae_journals_db';
|
||||
// idb_tbl_name = 'journal';
|
||||
|
||||
result.id = result.journal_id_random; // Ensure we use the correct ID field
|
||||
result.journal_id = result.journal_id_random;
|
||||
result.account_id = result.account_id_random;
|
||||
result.person_id = result.person_id_random;
|
||||
|
||||
// WARNING: This works to populate most of the IDB table fields but it is not ideal. It is sort of a safety net.
|
||||
properties_to_save = Object.keys(db_instance.table('journal').schema.idxByName);
|
||||
}
|
||||
if (obj_type === 'journal_entry') {
|
||||
idb_db_instance = 'ae_journals_db';
|
||||
// idb_tbl_name = 'entry';
|
||||
|
||||
result.id = result.journal_entry_id_random; // Ensure we use the correct ID field
|
||||
result.journal_entry_id = result.journal_entry_id_random; // Ensure we use the correct ID field
|
||||
result.journal_id = result.journal_id_random;
|
||||
|
||||
// WARNING: This works to populate most of the IDB table fields but it is not ideal. It is sort of a safety net.
|
||||
properties_to_save = Object.keys(db_instance.table('journal_entry').schema.idxByName);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (try_cache && result) {
|
||||
// Process and save to DB
|
||||
const processed = await process_ae_obj__props({ obj_li: result, log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: db_instance,
|
||||
table_name: idb_tbl_name,
|
||||
obj_li: processed,
|
||||
properties_to_save: [],
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generic function: Create object
|
||||
export async function create_ae_obj(
|
||||
args: GenericCrudArgs
|
||||
): Promise<any> {
|
||||
const { api_cfg, obj_type, data_kv, try_cache = true, log_lvl = 0 } = args;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** create_ae_obj() *** obj_type=${obj_type}`, data_kv);
|
||||
}
|
||||
|
||||
let result = await api.create_ae_obj_crud({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
fields: data_kv,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
params: {},
|
||||
return_obj: true,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (try_cache && result) {
|
||||
const processed = await process_ae_obj__props({ obj_li: [result], log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: null,
|
||||
table_name: obj_type,
|
||||
obj_li: processed,
|
||||
properties_to_save: [],
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generic function: Update object
|
||||
export async function update_ae_obj(
|
||||
args: GenericCrudArgs
|
||||
): Promise<any> {
|
||||
const { api_cfg, obj_type, obj_id, data_kv, try_cache = true, log_lvl = 0 } = args;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** update_ae_obj() *** obj_type=${obj_type} obj_id=${obj_id}`, data_kv);
|
||||
}
|
||||
|
||||
let result = await api.update_ae_obj_id_crud({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
obj_id,
|
||||
fields: data_kv,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
params: {},
|
||||
return_obj: true,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (try_cache && result) {
|
||||
const processed = await process_ae_obj__props({ obj_li: [result], log_lvl });
|
||||
await db_save_ae_obj_li__ae_obj({
|
||||
db_instance: null,
|
||||
table_name: obj_type,
|
||||
obj_li: processed,
|
||||
properties_to_save: [],
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generic function: Delete object
|
||||
export async function delete_ae_obj_id(
|
||||
args: GenericCrudArgs
|
||||
): Promise<any> {
|
||||
const { api_cfg, obj_type, obj_id, method = 'delete', try_cache = true, log_lvl = 0 } = args;
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`*** delete_ae_obj_id() *** obj_type=${obj_type} obj_id=${obj_id}`);
|
||||
}
|
||||
|
||||
let result = await api.delete_ae_obj_id_crud({
|
||||
api_cfg,
|
||||
obj_type,
|
||||
obj_id,
|
||||
key: api_cfg.api_crud_super_key,
|
||||
params: {},
|
||||
method,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (try_cache) {
|
||||
// Remove from DB regardless of success/failure
|
||||
try {
|
||||
// db_<module>.<obj_type>.delete(obj_id); // You must inject the correct DB here
|
||||
} catch (err) {
|
||||
console.error(`Failed to delete ${obj_id}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Generic processor with dynamic field detection
|
||||
export async function process_ae_obj__props({
|
||||
obj_li,
|
||||
log_lvl = 0,
|
||||
}: {
|
||||
obj_li: any[];
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** process_ae_obj__props() ***`, obj_li);
|
||||
}
|
||||
|
||||
if (!obj_li || obj_li.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const processed = obj_li.map(obj => {
|
||||
// Extract fields with fallbacks and detect existence
|
||||
const group = obj.group || '';
|
||||
const priority = obj.priority !== undefined ? obj.priority : 0;
|
||||
const sort = obj.sort !== undefined ? obj.sort : 0;
|
||||
const created_on = obj.created_on || '';
|
||||
const updated_on = obj.updated_on || '';
|
||||
const name = obj.name || '';
|
||||
|
||||
// const description = obj.description || '';
|
||||
// // Handle special case with description that has markdown
|
||||
// // Remove the most common zerowidth characters from the start of the file
|
||||
// const description_cleaned: string = obj.description.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,"");
|
||||
// const description_md_html: null|string = marked.parse(description_cleaned ?? '') ?? null;
|
||||
// // Add processed description
|
||||
// obj.description_md_html = description_md_html;
|
||||
|
||||
// Handle special case with content that has markdown
|
||||
// Remove the most common zerowidth characters from the start of the file
|
||||
const has_content = obj.content !== undefined && obj.content !== null;
|
||||
if (has_content) {
|
||||
const content_cleaned: string = obj.content.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,"");
|
||||
const content_md_html: null|string = marked.parse(content_cleaned ?? '') ?? null;
|
||||
// Add processed content
|
||||
obj.content_md_html = content_md_html;
|
||||
}
|
||||
|
||||
// Check which fields actually exist for this object
|
||||
const has_group = obj.group !== undefined && obj.group !== null;
|
||||
const has_priority = obj.priority !== undefined && obj.priority !== null;
|
||||
const has_sort = obj.sort !== undefined && obj.sort !== null;
|
||||
const has_created = obj.created_on !== undefined && obj.created_on !== null;
|
||||
const has_updated = obj.updated_on !== undefined && obj.updated_on !== null;
|
||||
const has_name = obj.name !== undefined && obj.name !== null;
|
||||
|
||||
// Generate standardized tmp_sort fields with proper zero-padding
|
||||
const sort_str = sort?.toString().padStart(3, '0') ?? '000';
|
||||
|
||||
// Build dynamic sorting combinations based on what exists
|
||||
let tmp_sort_1 = '';
|
||||
let tmp_sort_2 = '';
|
||||
let tmp_sort_3 = '';
|
||||
|
||||
// Build based on existing fields
|
||||
if (has_group && has_priority && has_sort) {
|
||||
tmp_sort_1 = `${group}_${priority}_${sort_str}`;
|
||||
tmp_sort_2 = `${group}_${priority}_${sort_str}_${name || ''}`;
|
||||
tmp_sort_3 = `${priority}_${sort_str}_${group}`;
|
||||
} else if (has_priority && has_sort) {
|
||||
tmp_sort_1 = `${priority}_${sort_str}`;
|
||||
tmp_sort_2 = `${priority}_${sort_str}_${name || ''}`;
|
||||
tmp_sort_3 = `${priority}_${sort_str}`;
|
||||
} else if (has_sort) {
|
||||
tmp_sort_1 = `${sort_str}`;
|
||||
tmp_sort_2 = `${sort_str}_${name || ''}`;
|
||||
tmp_sort_3 = `${sort_str}`;
|
||||
} else {
|
||||
// Fallback for minimal data
|
||||
tmp_sort_1 = `${created_on || ''}`;
|
||||
tmp_sort_2 = `${created_on || ''}_${name || ''}`;
|
||||
tmp_sort_3 = `${updated_on || ''}`;
|
||||
}
|
||||
|
||||
// Add time-based sorting for better ordering
|
||||
if (has_created && has_updated) {
|
||||
tmp_sort_1 += `_${created_on}_${updated_on}`;
|
||||
tmp_sort_2 += `_${created_on}_${updated_on}`;
|
||||
tmp_sort_3 += `_${updated_on}`;
|
||||
} else if (has_created) {
|
||||
tmp_sort_1 += `_${created_on}`;
|
||||
tmp_sort_2 += `_${created_on}`;
|
||||
tmp_sort_3 += `_${created_on}`;
|
||||
}
|
||||
|
||||
// Return processed object with standardized fields
|
||||
return {
|
||||
...obj,
|
||||
tmp_sort_1,
|
||||
tmp_sort_2,
|
||||
tmp_sort_3
|
||||
};
|
||||
});
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`Processed objects:`, processed);
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export async function db_save_ae_obj_li__ae_obj({
|
||||
|
||||
const db_table = db_instance[table_name];
|
||||
if (!db_table) {
|
||||
console.error(`Table not found: ${table_name}`);
|
||||
console.error(`Table not found in ${db_instance}: ${table_name}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user