Files
gem5/util/gem5-resources-manager/static/js/editor.js
KUNAL PAI 9b9dc09f6e resources: Add the gem5 Resources Manager
A GUI web-based tool to manage gem5 Resources.

Can manage in two data sources,
a MongoDB database or a JSON file.

The JSON file can be both local or remote.

JSON files are written to a temporary file before
writing to the local file.

The Manager supports the following functions
on a high-level:
- searching for a resource by ID
- navigating to a resource version
- adding a new resource
- adding a new version to a resource
- editing any information within a searched resource
(while enforcing the gem5 Resources schema
found at: https://resources.gem5.org/gem5-resources-schema.json)
- deleting a resource version
- undo and redo up to the last 10 operations

The Manager also allows a user to save a session
through localStorage and re-access it through a password securely.

This patch also provides a
Command Line Interface tool mainly for
MongoDB-related functions.

This CLI tool can currently:
- backup a MongoDB collection to a JSON file
- restore a JSON file to a MongoDB collection
- search for a resource through its ID and
view its JSON object
- make a JSON file that is compliant with the
gem5 Resources Schema

Co-authored-by: Parth Shah <helloparthshah@gmail.com>
Co-authored-by: Harshil2107 <harshilp2107@gmail.com>
Co-authored-by: aarsli <arsli@ucdavis.edu>
Change-Id: I8107f609c869300b5323d4942971a7ce7c28d6b5
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/71218
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
Maintainer: Bobby Bruce <bbruce@ucdavis.edu>
2023-07-08 02:01:02 +00:00

590 lines
17 KiB
JavaScript

const diffEditorContainer = document.getElementById("diff-editor");
var diffEditor;
var originalModel;
var modifiedModel;
const schemaEditorContainer = document.getElementById("schema-editor");
var schemaEditor;
var schemaModel;
const schemaButton = document.getElementById("schema-toggle");
const editingActionsButtons = Array.from(
document.querySelectorAll("#editing-actions button")
);
var editingActionsState;
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipTriggerList.forEach(tooltip => {
tooltip.setAttribute("data-bs-trigger", "hover");
});
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
require.config({
paths: {
vs: "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs",
},
});
require(["vs/editor/editor.main"], () => {
originalModel = monaco.editor.createModel(`{\n}`, "json");
modifiedModel = monaco.editor.createModel(`{\n}`, "json");
diffEditor = monaco.editor.createDiffEditor(diffEditorContainer, {
theme: "vs-dark",
language: "json",
automaticLayout: true,
});
diffEditor.setModel({
original: originalModel,
modified: modifiedModel,
});
fetch("/schema")
.then((res) => res.json())
.then((data) => {
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
trailingCommas: "error",
comments: "error",
validate: true,
schemas: [
{
uri: "http://json-schema.org/draft-07/schema",
fileMatch: ["*"],
schema: data,
},
],
});
schemaEditor = monaco.editor.create(schemaEditorContainer, {
theme: "vs-dark",
language: "json",
automaticLayout: true,
readOnly: true,
});
schemaModel = monaco.editor.createModel(`{\n}`, "json");
schemaEditor.setModel(schemaModel);
schemaModel.setValue(JSON.stringify(data, null, 4));
schemaEditorContainer.style.display = "none";
});
});
let clientType = document.getElementById('client-type');
clientType.textContent = clientType.textContent === "mongodb" ? "MongoDB" : clientType.textContent.toUpperCase();
const revisionButtons = [document.getElementById("undo-operation"), document.getElementById("redo-operation")];
revisionButtons.forEach(btn => {
btn.disabled = true;
});
const editorGroupIds = [];
document.querySelectorAll(".editorButtonGroup button, .revisionButtonGroup button")
.forEach(btn => {
editorGroupIds.push(btn.id);
});
function checkErrors() {
let errors = monaco.editor.getModelMarkers({ resource: modifiedModel.uri });
if (errors.length > 0) {
console.log(errors);
let str = "";
errors.forEach((error) => {
str += error.message + "\n";
});
appendAlert('Error!', 'schemaError', { str }, 'danger');
return true;
}
return false;
}
let didChange = false;
function update(e) {
e.preventDefault();
if (checkErrors()) {
return;
}
let json = JSON.parse(modifiedModel.getValue());
let original_json = JSON.parse(originalModel.getValue());
console.log(json);
fetch("/update", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
resource: json,
original_resource: original_json,
alias: document.getElementById("alias").innerText,
}),
})
.then((res) => res.json())
.then(async (data) => {
console.log(data);
await addVersions();
//Select last option
document.getElementById("version-dropdown").value =
json["resource_version"];
console.log(document.getElementById("version-dropdown").value);
find(e);
});
}
function addNewResource(e) {
e.preventDefault();
if (checkErrors()) {
return;
}
let json = JSON.parse(modifiedModel.getValue());
console.log(json);
fetch("/insert", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
resource: json,
alias: document.getElementById("alias").innerText,
}),
})
.then((res) => res.json())
.then(async (data) => {
console.log(data);
await addVersions();
//Select last option
document.getElementById("version-dropdown").value =
json["resource_version"];
console.log(document.getElementById("version-dropdown").value);
find(e);
});
}
function addVersion(e) {
e.preventDefault();
console.log("add version");
if (checkErrors()) {
return;
}
let json = JSON.parse(modifiedModel.getValue());
console.log(json["resource_version"]);
fetch("/checkExists", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: json["id"],
resource_version: json["resource_version"],
alias: document.getElementById("alias").innerText,
}),
})
.then((res) => res.json())
.then((data) => {
console.log(data["exists"]);
if (data["exists"] == true) {
appendAlert("Error!", "existingResourceVersion", "Resource version already exists!", "danger");
return;
} else {
fetch("/insert", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
resource: json,
alias: document.getElementById("alias").innerText,
}),
})
.then((res) => res.json())
.then(async (data) => {
console.log("added version");
console.log(data);
await addVersions();
//Select last option
document.getElementById("version-dropdown").value =
json["resource_version"];
console.log(document.getElementById("version-dropdown").value);
find(e);
});
}
});
}
function deleteRes(e) {
e.preventDefault();
console.log("delete");
let id = document.getElementById("id").value;
let resource_version = JSON.parse(originalModel.getValue())[
"resource_version"
];
let json = JSON.parse(originalModel.getValue());
console.log(resource_version);
fetch("/delete", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
resource: json,
alias: document.getElementById("alias").innerText,
}),
})
.then((res) => res.json())
.then(async (data) => {
console.log(data);
await addVersions();
//Select first option
document.getElementById("version-dropdown").value =
document.getElementById("version-dropdown").options[0].value;
console.log(document.getElementById("version-dropdown").value);
find(e);
});
}
document.getElementById("id").onchange = function () {
console.log("id changed");
didChange = true;
};
async function addVersions() {
let select = document.getElementById("version-dropdown");
select.innerHTML = "Latest";
await fetch("/versions", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: document.getElementById("id").value,
alias: document.getElementById("alias").innerText,
}),
})
.then((res) => res.json())
.then((data) => {
let select = document.getElementById("version-dropdown");
if (data.length == 0) {
data = [{ resource_version: "Latest" }];
}
data.forEach((version) => {
let option = document.createElement("option");
option.value = version["resource_version"];
option.innerText = version["resource_version"];
select.appendChild(option);
});
});
}
function find(e) {
e.preventDefault();
if (didChange) {
addVersions();
didChange = false;
}
closeSchema();
toggleInteractables(true, editorGroupIds, () => {
diffEditor.updateOptions({ readOnly: true });
updateRevisionBtnsDisabledAttr();
});
fetch("/find", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: document.getElementById("id").value,
resource_version: document.getElementById("version-dropdown").value,
alias: document.getElementById("alias").innerText,
}),
})
.then((res) => res.json())
.then((data) => {
console.log(data);
toggleInteractables(false, editorGroupIds, () => {
diffEditor.updateOptions({ readOnly: false });
updateRevisionBtnsDisabledAttr();
});
if (data["exists"] == false) {
fetch("/keys", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
category: document.getElementById("category").value,
id: document.getElementById("id").value,
}),
})
.then((res) => res.json())
.then((data) => {
console.log(data)
data["id"] = document.getElementById("id").value;
data["category"] = document.getElementById("category").value;
originalModel.setValue(JSON.stringify(data, null, 4));
modifiedModel.setValue(JSON.stringify(data, null, 4));
document.getElementById("add_new_resource").disabled = false;
document.getElementById("add_version").disabled = true;
document.getElementById("delete").disabled = true;
document.getElementById("update").disabled = true;
});
} else {
console.log(data);
originalModel.setValue(JSON.stringify(data, null, 4));
modifiedModel.setValue(JSON.stringify(data, null, 4));
document.getElementById("version-dropdown").value =
data.resource_version;
document.getElementById("category").value = data.category;
document.getElementById("add_new_resource").disabled = true;
document.getElementById("add_version").disabled = false;
document.getElementById("delete").disabled = false;
document.getElementById("update").disabled = false;
}
});
}
window.onload = () => {
let ver_dropdown = document.getElementById("version-dropdown");
let option = document.createElement("option");
option.value = "Latest";
option.innerHTML = "Latest";
ver_dropdown.appendChild(option);
fetch("/categories")
.then((res) => res.json())
.then((data) => {
console.log(data);
let select = document.getElementById("category");
data.forEach((category) => {
let option = document.createElement("option");
option.value = category;
option.innerHTML = category;
select.appendChild(option);
});
fetch("/keys", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
category: document.getElementById("category").value,
id: "",
}),
})
.then((res) => res.json())
.then((data) => {
data["id"] = "";
data["category"] = document.getElementById("category").value;
originalModel.setValue(JSON.stringify(data, null, 4));
modifiedModel.setValue(JSON.stringify(data, null, 4));
document.getElementById("add_new_resource").disabled = false;
});
});
checkExistingSavedSession();
};
const myModal = new bootstrap.Modal("#ConfirmModal", {
keyboard: false,
});
let confirmButton = document.getElementById("confirm");
function showModal(event, callback) {
event.preventDefault();
myModal.show();
confirmButton.onclick = () => {
callback(event);
myModal.hide();
};
}
let editorTitle = document.getElementById("editor-title");
function showSchema() {
if (diffEditorContainer.style.display !== "none") {
diffEditorContainer.style.display = "none";
schemaEditorContainer.classList.add("editor-sizing");
schemaEditor.setPosition({ column: 1, lineNumber: 1 });
schemaEditor.revealPosition({ column: 1, lineNumber: 1 });
schemaEditorContainer.style.display = "block";
editingActionsState = editingActionsButtons.map(
(button) => button.disabled
);
editingActionsButtons.forEach((btn) => {
btn.disabled = true;
});
editorTitle.children[0].style.display = "none";
editorTitle.children[1].textContent = "Schema (Read Only)";
schemaButton.textContent = "Close Schema";
schemaButton.onclick = closeSchema;
}
}
function closeSchema() {
if (schemaEditorContainer.style.display !== "none") {
schemaEditorContainer.style.display = "none";
diffEditorContainer.style.display = "block";
editingActionsButtons.forEach((btn, i) => {
btn.disabled = editingActionsState[i];
});
editorTitle.children[0].style.display = "unset";
editorTitle.children[1].textContent = "Edited";
schemaButton.textContent = "Show Schema";
schemaButton.onclick = showSchema;
}
}
const saveSessionBtn = document.getElementById("saveSession");
saveSessionBtn.disabled = true;
let password = document.getElementById("session-password");
password.addEventListener("input", () => {
saveSessionBtn.disabled = password.value === "";
});
function showSaveSessionModal() {
const saveSessionModal = new bootstrap.Modal(document.getElementById('saveSessionModal'), {
focus: true, keyboard: false
});
saveSessionModal.show();
}
function saveSession() {
alias = document.getElementById("alias").innerText;
bootstrap.Modal.getInstance(document.getElementById("saveSessionModal")).hide();
let preserveDisabled = [];
document.querySelectorAll(".editorButtonGroup button, .revisionButtonGroup button")
.forEach(btn => {
btn.disabled === true ? preserveDisabled.push(btn.id) : null;
});
toggleInteractables(true);
fetch("/saveSession", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
alias: alias,
password: document.getElementById("session-password").value
}),
})
.then((res) => {
document.getElementById("saveSessionForm").reset();
toggleInteractables(false, preserveDisabled);
res.json()
.then((data) => {
if (res.status === 400) {
appendAlert('Error!', 'saveSessionError', `${data["error"]}`, 'danger');
return;
}
let sessions = JSON.parse(localStorage.getItem("sessions")) || {};
sessions[alias] = data["ciphertext"];
localStorage.setItem("sessions", JSON.stringify(sessions));
document.getElementById("showSaveSessionModal").innerText = "Session Saved";
checkExistingSavedSession();
})
})
}
function executeRevision(event, operation) {
if (!["undo", "redo"].includes(operation)) {
appendAlert("Error!", "invalidRevOp", "Fatal! Invalid Revision Operation!", "danger");
return;
}
toggleInteractables(true, editorGroupIds, () => {
diffEditor.updateOptions({ readOnly: true });
updateRevisionBtnsDisabledAttr();
});
fetch(`/${operation}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
alias: document.getElementById("alias").innerText,
}),
})
.then(() => {
toggleInteractables(false, editorGroupIds, () => {
diffEditor.updateOptions({ readOnly: false });
updateRevisionBtnsDisabledAttr();
});
find(event);
})
}
function updateRevisionBtnsDisabledAttr() {
fetch("/getRevisionStatus", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
alias: document.getElementById("alias").innerText,
}),
})
.then((res) => res.json())
.then((data) => {
revisionButtons[0].disabled = data.undo;
revisionButtons[1].disabled = data.redo;
})
}
function logout() {
toggleInteractables(true);
fetch("/logout", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
alias: document.getElementById("alias").innerText,
}),
})
.then((res) => {
toggleInteractables(false);
if (res.status !== 302) {
res.json()
.then((data) => {
appendAlert('Error!', 'logoutError', `${data["error"]}`, 'danger');
return;
})
}
window.location = res.url;
})
}
function checkExistingSavedSession() {
document.getElementById("existing-session-warning").style.display =
document.getElementById("alias").innerText in JSON.parse(localStorage.getItem("sessions") || "{}")
? "flex"
: "none";
}
document.getElementById("close-save-session-modal").addEventListener("click", () => {
document.getElementById("saveSessionModal").querySelector("form").reset();
saveSessionBtn.disabled = password.value === "";
});