Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DLT-1110] Mapped Collection Endpoint Browse (1/4) #1208

Merged
merged 10 commits into from
Jan 14, 2025
Next Next commit
[DLT-1110] Update files with prettier
[DLT-1110] Add controller unit tests
[DLT-1110] Remove debug
[DLT-1110] Correct import statements, endpoint list
[DLT-1110] Refactor out into MVC
[DLT-1110] Add debug
[DLT-1110] Functioning modal, needs refactoring
[DLT-1110] Refactor dlg start transfer using OOO programming. Should be MVC cased on what I'm seeing but we'll see
  • Loading branch information
AronPerez committed Jan 14, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 20657425a2f598b49a76c4ed5b573c7fee203b39
1 change: 1 addition & 0 deletions web/package.json.in
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@
"homepage": "https://github.com/ORNL/DataFed#readme",
"devDependencies": {
"chai": "^4",
"sinon": "^15.2.0"
"esm": "^3.2.25",
"mocha": "^10.8.2",
"pug": "^3.0.3"
26 changes: 26 additions & 0 deletions web/static/components/transfer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as dialogs from "../../dialogs.js";
import { TransferDialogController } from "./transfer-dialog-controller.js";

export class TransferDialog {
constructor() {
this.currentDialog = null;
}

/**
* Show transfer dialog
* @param {number|null} mode - Transfer mode (GET/PUT)
* @param {Array<Object>|null} records - Data records
* @param {Function} callback - Completion callback
*/
show(mode, records, callback) {
try {
this.currentDialog = new TransferDialogController(mode, records, callback);
this.currentDialog.show();
} catch (error) {
console.error("Error showing transfer dialog:", error);
dialogs.dlgAlert("Error", "Failed to open transfer dialog");
}
}
}

export const transferDialog = new TransferDialog();
35 changes: 35 additions & 0 deletions web/static/components/transfer/transfer-dialog-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as dialogs from "../../dialogs.js";
import { TransferEndpointManager } from "./transfer-endpoint-manager.js";
import { TransferModel } from "../../models/transfer-model.js";
import { TransferUIManager } from "./transfer-ui-manager.js";

/**
* TransferDialogController class manages the UI and logic for data transfers
*/
export class TransferDialogController {
/**
* @param {number} mode - Transfer mode (GET/PUT)
* @param {Array<Object>} ids - Records to transfer
* @param {Function} callback - Completion callback
*/
constructor(mode, ids, callback) {
this.model = new TransferModel(mode, ids);
this.endpointManager = new TransferEndpointManager(this);
this.uiManager = new TransferUIManager(this);
this.ids = ids;
this.callback = callback;
}

show() {
try {
this.uiManager.createDialog(this.uiManager.getDialogLabels());
this.uiManager.initializeComponents();
this.uiManager.attachMatchesHandler();
this.endpointManager.initialized = true;
this.uiManager.showDialog();
} catch (error) {
console.error("Failed to show transfer dialog:", error);
dialogs.dlgAlert("Error", "Failed to open transfer dialog");
}
}
}
133 changes: 133 additions & 0 deletions web/static/components/transfer/transfer-endpoint-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import * as api from "../../api.js";
import * as dialogs from "../../dialogs.js";

export class TransferEndpointManager {
constructor(dialog) {
this.initialized = false;
this.controller = dialog;
this.currentEndpoint = null;
this.endpointManagerList = null;
this.searchCounter = 0;
this.currentSearchToken = null;
}

/**
* ------------GET------------
*/

getEndpointStatus(endpoint) {
if (!endpoint.activated && endpoint.expires_in === -1) return "active";
if (endpoint.activated) return `${Math.floor(endpoint.expires_in / 3600)} hrs`;
return "inactive";
}

searchEndpointAutocomplete(endpoint, searchToken) {
api.epAutocomplete(endpoint, (ok, data) => {
// Only proceed if this is still the current search
if (searchToken !== this.currentSearchToken) {
return;
}

if (ok && data.DATA && data.DATA.length) {
this.endpointManagerList = data.DATA;
// Process endpoints and update UI
data.DATA.forEach((ep) => {
ep.name = ep.canonical_name || ep.id;
});
this.updateMatchesList(data.DATA);
} else {
console.warn("No matches found");
this.endpointManagerList = null;
this.updateMatchesList([]);
if (data.code) {
console.error("Autocomplete error:", data);
dialogs.dlgAlert("Globus Error", data.code);
}
}
});
}

searchEndpoint(endpoint, searchToken) {
console.log("Searching for endpoint:", endpoint);

try {
return api.epView(endpoint, (ok, data) => {
if (searchToken !== this.currentSearchToken) {
console.warn("Ignoring stale epView response");
return;
}

if (ok && !data.code) {
console.log("Direct endpoint match found:", data);
this.controller.uiManager.updateEndpoint(data);
this.controller.uiManager.state.endpointOk = true;
this.controller.uiManager.updateButtonStates();
} else {
console.warn("No direct match, trying autocomplete");
this.searchEndpointAutocomplete(endpoint, searchToken);
}
});
} catch (error) {
dialogs.dlgAlert("Globus Error", error);
}
}

/**
* ------------UPDATE------------
*/

updateMatchesList(endpoints = []) {
const matches = $("#matches", this.controller.uiManager.frame);
if (!endpoints.length) {
matches.html("<option disabled selected>No Matches</option>");
matches.prop("disabled", true);
return;
}

const html = this.controller.uiManager.createMatchesHtml(endpoints);
matches.html(html);
matches.prop("disabled", false);
}

/**
* ------------HANDLERS------------
*/

handlePathInput(searchToken) {
if (!this.initialized) {
console.warn("Dialog not yet initialized - delaying path input handling");
setTimeout(() => this.handlePathInput(searchToken), 100);
return;
}

if (searchToken !== this.currentSearchToken) {
console.log("Token mismatch - ignoring stale request");
return;
}

const path = $("#path", this.controller.uiManager.frame).val().trim();
console.log("Processing path:", path);

if (!path.length) {
console.log("Empty path - disabling endpoint");
this.endpointManagerList = null;
this.updateMatchesList([]);
this.controller.uiManager.updateButtonStates();
return;
}

const endpoint = path.split("/")[0];
console.log(
"Extracted endpoint:",
endpoint,
"Current endpoint:",
this.currentEndpoint?.name,
);

if (!this.currentEndpoint || endpoint !== this.currentEndpoint.name) {
console.log("Endpoint changed or not set - searching for new endpoint");
this.controller.uiManager.updateButtonStates();
this.searchEndpoint(endpoint, searchToken);
}
}
}
Loading