aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/frontend/html/pages/entry.js
diff options
context:
space:
mode:
authorMartin Ridgers <[email protected]>2024-11-18 08:41:46 +0100
committerGitHub Enterprise <[email protected]>2024-11-18 08:41:46 +0100
commitcca69117b7ffac5cdd8933148ed9c94dd241528d (patch)
treeba9dfce342e86d9cbdf6cf54059e1e7d618eecee /src/zenserver/frontend/html/pages/entry.js
parentoplog prep gc fix (#216) (diff)
downloadzen-cca69117b7ffac5cdd8933148ed9c94dd241528d.tar.xz
zen-cca69117b7ffac5cdd8933148ed9c94dd241528d.zip
Dashboard: oplog tree view (#217)
* Turned tables and progress bars and friends into "widgets!" * A step to abstracting away a page's the internal DOM structure * Folded sector creation into Page and pivoted it to a widget host * Try and keep start/count as numbers regardless of input * No need for the entry table to be defined up front now * Add op count and log sixe to oplog list page * Cache left side toolbar object * Bounds count page start when building list of oplog entrie * Start/end navigation tools * Build rest of entry page while waiting for indexer to load * Consistent naming with other pages * Spacially consolidate fetching code * Hide fetch latency to speed up index generation workers * Extract dashboard structure from zen.js monolith * Fix breadcrumbs after restructuring * Add view link to actions cell of oplogs list * Generator to enumerate names of entries in indexer * Methods for simple traversal of component relations * is() to check if a component is of a certain type * Extend attr() to get and unset a component's attributes * Unsetting all styles of anchor tags was underisrable * Restore page name as id of container element * A tree view of an oplog * Move helper class out to private module scope * Small tweak to use left var that already exists * Changelog update * Updated frontend .zip archive
Diffstat (limited to 'src/zenserver/frontend/html/pages/entry.js')
-rw-r--r--src/zenserver/frontend/html/pages/entry.js195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/zenserver/frontend/html/pages/entry.js b/src/zenserver/frontend/html/pages/entry.js
new file mode 100644
index 000000000..b166d0a6f
--- /dev/null
+++ b/src/zenserver/frontend/html/pages/entry.js
@@ -0,0 +1,195 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+"use strict";
+
+import { ZenPage } from "./page.js"
+import { Fetcher } from "../util/fetcher.js"
+import { Table, PropTable, Toolbar, ProgressBar } from "../util/widgets.js"
+import { create_indexer } from "../indexer/indexer.js"
+
+////////////////////////////////////////////////////////////////////////////////
+export class Page extends ZenPage
+{
+ main()
+ {
+ this.set_title("oplog entry");
+
+ const project = this.get_param("project");
+ const oplog = this.get_param("oplog");
+ const opkey = this.get_param("opkey");
+
+ this._entry = new Fetcher()
+ .resource("prj", project, "oplog", oplog, "entries")
+ .param("opkey", opkey)
+ .cbo();
+
+ this._indexer = this.load_indexer(project, oplog);
+
+ this._build_page();
+ }
+
+ async load_indexer(project, oplog, loaded_cb)
+ {
+ const progress_bar = this.add_widget(ProgressBar);
+ progress_bar.set_progress("indexing");
+ const indexer = await create_indexer(project, oplog, (...args) => {
+ progress_bar.set_progress(...args);
+ });
+ progress_bar.destroy();
+ return indexer;
+ }
+
+ async _build_deps(section, tree)
+ {
+ const indexer = await this._indexer;
+
+ for (const dep_name in tree)
+ {
+ const dep_section = section.add_section(dep_name);
+ const table = dep_section.add_widget(Table, ["name", "id"], Table.Flag_PackRight);
+ for (const dep_id of tree[dep_name])
+ {
+ const cell_values = ["", dep_id.toString(16).padStart(16, "0")];
+ const row = table.add_row(...cell_values);
+
+ var opkey = indexer.lookup_id(dep_id);
+ row.get_cell(0).text(opkey).on_click((k) => this.view_opkey(k), opkey);
+ }
+ }
+ }
+
+ async _build_page()
+ {
+ var entry = await this._entry;
+ entry = entry.as_object().find("entry").as_object();
+
+ const name = entry.find("key").as_value();
+ var section = this.add_section(name);
+
+ // tree
+ {
+ var tree = entry.find("$tree");
+ if (tree == undefined)
+ tree = this._convert_legacy_to_tree(entry);
+
+ if (tree == undefined)
+ return this._display_unsupported(section, entry);
+
+ delete tree["$id"];
+
+ const sub_section = section.add_section("deps");
+ this._build_deps(sub_section, tree);
+ }
+
+ // data
+ {
+ const sub_section = section.add_section("data");
+ const table = sub_section.add_widget(Table, ["name", "actions"], Table.Flag_PackRight);
+ for (const field_name of ["packagedata", "bulkdata"])
+ {
+ var pkg_data = entry.find(field_name);
+ if (pkg_data == undefined)
+ continue;
+
+ for (const item of pkg_data.as_array())
+ {
+ var io_hash;
+ var file_name;
+ for (const field of item.as_object())
+ {
+ if (field.is_named("data")) io_hash = field.as_value();
+ else if (field.is_named("filename")) file_name = field.as_value();
+ }
+
+ if (io_hash instanceof Uint8Array)
+ {
+ var ret = "";
+ for (var x of io_hash)
+ ret += x.toString(16).padStart(2, "0");
+ io_hash = ret;
+ }
+
+ const row = table.add_row(file_name);
+
+ const project = this.get_param("project");
+ const oplog = this.get_param("oplog");
+ const link = row.get_cell(0).link(
+ "/" + ["prj", project, "oplog", oplog, io_hash].join("/")
+ );
+
+ const do_nothing = () => void(0);
+ const action_tb = new Toolbar(row.get_cell(-1), true);
+ action_tb.left().add("copy-hash").on_click(async (v) => {
+ await navigator.clipboard.writeText(v);
+ }, io_hash);
+ }
+ }
+ }
+
+ // props
+ {
+ const object = entry.to_js_object();
+ var sub_section = section.add_section("props");
+ sub_section.add_widget(PropTable).add_object(object);
+ }
+ }
+
+ _display_unsupported(section, entry)
+ {
+ const object = entry.to_js_object();
+ const text = JSON.stringify(object, null, " ");
+ section.tag("pre").text(text);
+ }
+
+ _convert_legacy_to_tree(entry)
+ {
+ const pkg_data = entry.find("packagedata");
+ if (pkg_data == undefined)
+ return
+
+ const tree = {};
+
+ var id = 0n;
+ for (var item of pkg_data.as_array())
+ {
+ var pkg_id = item.as_object().find("id");
+ if (pkg_id == undefined)
+ continue;
+
+ pkg_id = pkg_id.as_value().subarray(0, 8);
+ for (var i = 7; i >= 0; --i)
+ {
+ id <<= 8n;
+ id |= BigInt(pkg_id[i]);
+ }
+ break;
+ }
+ tree["$id"] = id;
+
+ const pkgst_entry = entry.find("packagestoreentry").as_object();
+
+ for (const field of pkgst_entry)
+ {
+ const field_name = field.get_name();
+ if (!field_name.endsWith("importedpackageids"))
+ continue;
+
+ var dep_name = field_name.slice(0, -18);
+ if (dep_name.length == 0)
+ dep_name = "imported";
+
+ var out = tree[dep_name] = [];
+ for (var item of field.as_array())
+ out.push(item.as_value(BigInt));
+ }
+
+ return tree;
+ }
+
+ view_opkey(opkey)
+ {
+ const params = this._params;
+ params.set("opkey", opkey);
+ window.location.search = params;
+ }
+}