From b60d68e12ee03fcd75e53c3d05df8e4250554434 Mon Sep 17 00:00:00 2001
From: Dominic Kempf
Date: Mon, 15 Jul 2024 17:23:25 +0200
Subject: [PATCH] Attempt at making on demand instance data queries
---
.gitignore | 3 +
index.html | 1 +
src/App.svelte | 9 ++-
src/components/EasyDBDetailView.svelte | 18 +++--
src/generate.js | 96 +++-----------------------
src/lib/apiaccess.js | 5 +-
src/lib/easydbData.js | 92 ++++++++++++++++++++++++
src/lib/easydbHelpers.js | 11 +--
src/lib/easydbPregen.js | 4 ++
src/lib/stores.js | 43 +++++++++++-
10 files changed, 181 insertions(+), 101 deletions(-)
create mode 100644 src/lib/easydbData.js
create mode 100644 src/lib/easydbPregen.js
diff --git a/.gitignore b/.gitignore
index a547bf3..88266cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+# This file is regularyl overwritten for pregeneration
+src/lib/easydb.js
+
# Logs
logs
*.log
diff --git a/index.html b/index.html
index 6e3676f..4e9fb70 100644
--- a/index.html
+++ b/index.html
@@ -3,6 +3,7 @@
+
EasyDB Detail View Demo
diff --git a/src/App.svelte b/src/App.svelte
index 76f0cb8..ad90597 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -3,6 +3,7 @@
import EasyDbDetailView from "./components/EasyDBDetailView.svelte";
let uuid = "859e2318-32f6-4013-8468-ef8cec0b581b";
+ let instance = "https://heidicon.ub.uni-heidelberg.de";
let languages = [
{ value: 'de-DE', name: 'Deutsch'},
@@ -21,6 +22,12 @@
explain what can be interactively controlled from the JavaScript side.
On the right hand side, we see the detail view component result.
+
+
+
+
-
+
diff --git a/src/components/EasyDBDetailView.svelte b/src/components/EasyDBDetailView.svelte
index 4cb6fd8..68b9cf5 100644
--- a/src/components/EasyDBDetailView.svelte
+++ b/src/components/EasyDBDetailView.svelte
@@ -1,20 +1,28 @@
-{#await easydb_api_object(uuid) }
- Waiting for API response...
-{:then data }
-
+{#await $easydbDataPromiseStore }
+ Accessing the EasyDB instance...
+{:then}
+ {#await easydb_api_object(uuid) }
+ Waiting for API response...
+ {:then data }
+
+ {/await}
{/await}
diff --git a/src/generate.js b/src/generate.js
index b37774b..9890c99 100644
--- a/src/generate.js
+++ b/src/generate.js
@@ -1,7 +1,5 @@
import { writeFile } from 'fs/promises';
-
-// TODO: In the reorganization functions below we could also cull data that we don't need
-// in order to reduce the bundle size. This is a low priority performance optimization.
+import { accessInstance } from './lib/easydbData.js';
// This "trick" is super-unfortunate, but Svelte Preprocessing is fully regex-based
// and any attempts to pass stringified data that contains backslashes will result in
@@ -11,96 +9,18 @@ function escape(str) {
return str.replaceAll("\\\"", "__ESCAPED_QUOTES__").replaceAll("\\", "__BACKSLASH__");
}
-// We massage the API response a bit to fit our needs better. This makes it more convenient
-// to work with the data in the Svelte component.
-function reorganize_masks(maskdata) {
- let newdata = {};
- for (let mask of maskdata.masks) {
- newdata[mask.name] = mask;
- }
- return newdata;
-}
-
-// We do the same for the schema data, where we want to access the tables directly by
-// their name in the Svelte component.
-function reorganize_schemas(schemadata) {
- let newdata = {};
- for (let table of schemadata.tables) {
- newdata[table.name] = table;
- console.log(table.name);
- }
- return newdata;
-}
-
-// Get a session token from the EasyDB instance
-const token_response = await fetch('https://heidicon.ub.uni-heidelberg.de/api/session');
-if(token_response.status != 200) {
- throw new Error("Could not get a session token from the EasyDB instance.");
-}
-const token_json = await token_response.json();
-const token = token_json.token;
-
-// Authenticcate the session token
-const auth_response = await fetch(
- 'https://heidicon.ub.uni-heidelberg.de/api/session/authenticate?' +
- new URLSearchParams({
- token: token,
- method: 'anonymous',
- }),
- {
- method: 'POST',
- }
-);
-if(auth_response.status != 200) {
- throw new Error("Could not authenticate the session token.");
-}
-
-// Fetch all the masks for this instance
-const mask_response = await fetch(
- 'https://heidicon.ub.uni-heidelberg.de/api/v1/mask/HEAD?' +
- new URLSearchParams({
- "token": token,
- "format": "json",
- }),
-);
-if(mask_response.status != 200) {
- throw new Error("Could not fetch the masks for this instance");
-}
-const masks = await mask_response.json();
-
-// Fetch all the l10n data for this instance
-const l10n_response = await fetch(
- 'https://heidicon.ub.uni-heidelberg.de/api/v1/l10n/user/HEAD?' +
- new URLSearchParams({
- "token": token,
- }),
-);
-if(l10n_response.status != 200) {
- throw new Error("Could not fetch the l10n data for this instance");
-}
-const l10n = await l10n_response.json();
-
-// Fetch the schema data for this instance
-const schema_response = await fetch(
- 'https://heidicon.ub.uni-heidelberg.de/api/v1/schema/user/HEAD?' +
- new URLSearchParams({
- "token": token,
- "format": "json",
- }),
-);
-if(schema_response.status != 200) {
- throw new Error("Could not fetch the schema data for this instance");
-}
-const schema = await schema_response.json();
+const instance = "https://heidicon.ub.uni-heidelberg.de";
+const data = await accessInstance(instance);
// Inject the data into the Svelte component as a constant
const content =
- `export const masks = JSON.parse('${escape(JSON.stringify(reorganize_masks(masks)))}'.replaceAll("__ESCAPED_QUOTES__", "\\\\\\"").replaceAll("__BACKSLASH__", "\\\\"));\n` +
- `export const l10n = JSON.parse('${escape(JSON.stringify(l10n))}'.replaceAll("__ESCAPED_QUOTES__", "\\\\\\"").replaceAll("__BACKSLASH__", "\\\\"));\n` +
- `export const schemas = JSON.parse('${escape(JSON.stringify(reorganize_schemas(schema)))}'.replaceAll("__ESCAPED_QUOTES__", "\\\\\\"").replaceAll("__BACKSLASH__", "\\\\"));\n`;
+ `export const pregen_instance = '${instance}';\n` +
+ `export const pregen_masks = JSON.parse('${escape(JSON.stringify(data.masks))}'.replaceAll("__ESCAPED_QUOTES__", "\\\\\\"").replaceAll("__BACKSLASH__", "\\\\"));\n` +
+ `export const pregen_l10n = JSON.parse('${escape(JSON.stringify(data.l10n))}'.replaceAll("__ESCAPED_QUOTES__", "\\\\\\"").replaceAll("__BACKSLASH__", "\\\\"));\n` +
+ `export const pregen_schemas = JSON.parse('${escape(JSON.stringify(data.schemas))}'.replaceAll("__ESCAPED_QUOTES__", "\\\\\\"").replaceAll("__BACKSLASH__", "\\\\"));\n`;
try {
- await writeFile('src/lib/easydb.js', content);
+ await writeFile('src/lib/easydbPregen.js', content);
console.log('Successfully regenerated the EasyDB data.');
} catch (error) {
console.error('Error writing the EasyDB data:', error);
diff --git a/src/lib/apiaccess.js b/src/lib/apiaccess.js
index acc60d7..bc86440 100644
--- a/src/lib/apiaccess.js
+++ b/src/lib/apiaccess.js
@@ -1,10 +1,13 @@
+import { get } from 'svelte/store';
+import { easydbInstanceStore } from './stores';
+
export async function easydb_api_object(uuid) {
if (!uuid) {
return {}
}
// Fetch the schema data for this instance
- const response = await fetch('http://localhost:8080/https://heidicon.ub.uni-heidelberg.de/api/objects/uuid/' + uuid);
+ const response = await fetch(`${get(easydbInstanceStore)}/api/objects/uuid/${uuid}`);
if(response.status != 200) {
throw new Error(`Could not fetch the data for uuid: ${uuid}`);
}
diff --git a/src/lib/easydbData.js b/src/lib/easydbData.js
new file mode 100644
index 0000000..395ee75
--- /dev/null
+++ b/src/lib/easydbData.js
@@ -0,0 +1,92 @@
+// TODO: In the reorganization functions below we could also cull data that we don't need
+// in order to reduce the bundle size. This is a low priority performance optimization.
+
+// We massage the API response a bit to fit our needs better. This makes it more convenient
+// to work with the data in the Svelte component.
+function reorganize_masks(maskdata) {
+ let newdata = {};
+ for (let mask of maskdata.masks) {
+ newdata[mask.name] = mask;
+ }
+ return newdata;
+}
+
+// We do the same for the schema data, where we want to access the tables directly by
+// their name in the Svelte component.
+function reorganize_schemas(schemadata) {
+ let newdata = {};
+ for (let table of schemadata.tables) {
+ newdata[table.name] = table;
+ }
+ return newdata;
+}
+
+export async function accessInstance(instance) {
+ // Get a session token from the EasyDB instance
+ const token_response = await fetch(`${instance}/api/session`);
+ if(token_response.status != 200) {
+ throw new Error("Could not get a session token from the EasyDB instance.");
+ }
+ const token_json = await token_response.json();
+ const token = token_json.token;
+
+ // Authenticcate the session token
+ const auth_response = await fetch(
+ `${instance}/api/session/authenticate?` +
+ new URLSearchParams({
+ token: token,
+ method: 'anonymous',
+ }),
+ {
+ method: 'POST',
+ }
+ );
+ if(auth_response.status != 200) {
+ throw new Error("Could not authenticate the session token.");
+ }
+
+ // Fetch all the masks for this instance
+ const mask_response = await fetch(
+ `${instance}/api/v1/mask/HEAD?` +
+ new URLSearchParams({
+ "token": token,
+ "format": "json",
+ }),
+ );
+ if(mask_response.status != 200) {
+ throw new Error("Could not fetch the masks for this instance");
+ }
+ const masks = await mask_response.json();
+
+ // Fetch all the l10n data for this instance
+ const l10n_response = await fetch(
+ `${instance}/api/v1/l10n/user/HEAD?` +
+ new URLSearchParams({
+ "token": token,
+ }),
+ );
+ if(l10n_response.status != 200) {
+ throw new Error("Could not fetch the l10n data for this instance");
+ }
+ const l10n = await l10n_response.json();
+
+ // Fetch the schema data for this instance
+ const schema_response = await fetch(
+ `${instance}/api/v1/schema/user/HEAD?` +
+ new URLSearchParams({
+ "token": token,
+ "format": "json",
+ }),
+ );
+ if(schema_response.status != 200) {
+ throw new Error("Could not fetch the schema data for this instance");
+ }
+ const schema = await schema_response.json();
+
+ return {
+ instance: instance,
+ masks: reorganize_masks(masks),
+ l10n: l10n,
+ schemas: reorganize_schemas(schema),
+ }
+}
\ No newline at end of file
diff --git a/src/lib/easydbHelpers.js b/src/lib/easydbHelpers.js
index 775eb6c..436bb3d 100644
--- a/src/lib/easydbHelpers.js
+++ b/src/lib/easydbHelpers.js
@@ -1,18 +1,19 @@
-import { l10n, masks, schemas } from './easydb';
+import { easydbDataStore } from './stores';
+import { get } from 'svelte/store';
import { bestLanguage } from './l10n';
export function maskObj(data) {
- return masks[data._mask];
+ return get(easydbDataStore).masks[data._mask];
}
// Given a data JSON object and a field from a mask, return the schema for that column
export function findSchemaColumn(table, field) {
- return schemas[table].columns.find((column) => column.name === field.column_name_hint);
+ return get(easydbDataStore).schemas[table].columns.find((column) => column.name === field.column_name_hint);
}
// Given a data JSON object and the field definition from a mask, return the label of the field with the language code lang
export function fieldLabel(table, field, lang) {
- return bestLanguage(l10n[`schema.${table}.column.${field.column_name_hint}`], lang);
+ return bestLanguage(get(easydbDataStore).l10n[`schema.${table}.column.${field.column_name_hint}`], lang);
}
// Given a data JSON object and the field definition from a mask, return a boolean whether it exists in the data
@@ -48,7 +49,7 @@ export function reverseLinkedSubData(data, table, field) {
}
export function splitterTitle(data, table, options, lang) {
- return bestLanguage(l10n[`mask.${schemas[table].table_id}.${maskObj(data).name}.splitter.${String(options.splitterIdx)}`], lang);
+ return bestLanguage(get(easydbDataStore).l10n[`mask.${get(easydbDataStore).schemas[table].table_id}.${maskObj(data).name}.splitter.${String(options.splitterIdx)}`], lang);
}
export function hasContent(data, table, fields) {
diff --git a/src/lib/easydbPregen.js b/src/lib/easydbPregen.js
new file mode 100644
index 0000000..b733d0a
--- /dev/null
+++ b/src/lib/easydbPregen.js
@@ -0,0 +1,4 @@
+export const pregen_instance = null;
+export const pregen_masks = null;
+export const pregen_l10n = null;
+export const pregen_schemas = null;
diff --git a/src/lib/stores.js b/src/lib/stores.js
index ca80010..9966ad2 100644
--- a/src/lib/stores.js
+++ b/src/lib/stores.js
@@ -1,3 +1,44 @@
-import { writable } from "svelte/store";
+import { derived, writable } from "svelte/store";
+import { accessInstance } from "./easydbData";
+import { pregen_instance, pregen_l10n, pregen_masks, pregen_schemas } from "./easydbPregen";
+// A derived store that resolves a promise
+function derivedPromise(store) {
+ return derived(store, ($store, set) => {
+ $store.then(value => {
+ set(value);
+ });
+ });
+}
+
+// Our pregenerated defaults wrapped in a Promise
+async function pregenDefaults() {
+ return {
+ instance: pregen_instance,
+ masks: pregen_masks,
+ schemas: pregen_schemas,
+ l10n: pregen_l10n,
+ };
+}
+
+// This manages the global state of the current language
export const l10nStore = writable(null);
+
+// This manages the global state of the EasyDB instance we are talking to
+export const easydbInstanceStore = writable(null);
+
+// A derived store that delivers a promise.
+export const easydbDataPromiseStore = derived(
+ easydbInstanceStore,
+ ($instance) => {
+ if ($instance === pregen_instance) {
+ return pregenDefaults();
+ }
+ return accessInstance($instance);
+ }
+);
+
+// A derived store that awaits above promise. It would be better to not even
+// expose the promise store, but I did not get this to work without the explicit
+// await in EasyDBDetailView.svelte.
+export const easydbDataStore = derivedPromise(easydbDataPromiseStore);