diff options
| author | Martin Ridgers <[email protected]> | 2024-11-18 08:41:46 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2024-11-18 08:41:46 +0100 |
| commit | cca69117b7ffac5cdd8933148ed9c94dd241528d (patch) | |
| tree | ba9dfce342e86d9cbdf6cf54059e1e7d618eecee /src/zenserver/frontend/html/pages/entry.js | |
| parent | oplog prep gc fix (#216) (diff) | |
| download | zen-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.js | 195 |
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; + } +} |