bug fixes

This commit is contained in:
Sourabh Jain 2025-04-10 13:43:45 +05:30
parent 2014dcd03d
commit d6c0edf20d
7 changed files with 105 additions and 48 deletions

View File

@ -257,6 +257,7 @@ export class Areas {
public static ShareDialog: string = "Share Access Dialog"; public static ShareDialog: string = "Share Access Dialog";
public static Notebook: string = "Notebook"; public static Notebook: string = "Notebook";
public static Copilot: string = "Copilot"; public static Copilot: string = "Copilot";
public static CloudShell: string = "Cloud Shell";
} }
export class HttpHeaders { export class HttpHeaders {

View File

@ -17,7 +17,6 @@ export const CloudShellTerminalComponent: React.FC<CloudShellTerminalComponentPr
const terminalRef = useRef(null); // Reference for terminal container const terminalRef = useRef(null); // Reference for terminal container
const xtermRef = useRef(null); // Reference for XTerm instance const xtermRef = useRef(null); // Reference for XTerm instance
const socketRef = useRef(null); // Reference for WebSocket const socketRef = useRef(null); // Reference for WebSocket
const fitAddon = new FitAddon();
useEffect(() => { useEffect(() => {
// Initialize XTerm instance // Initialize XTerm instance
@ -34,18 +33,33 @@ export const CloudShellTerminalComponent: React.FC<CloudShellTerminalComponentPr
scrollback: 1000 scrollback: 1000
}); });
const fitAddon = new FitAddon();
term.loadAddon(fitAddon); term.loadAddon(fitAddon);
fitAddon.fit(); // Fit the terminal to the container size
// Attach terminal to the DOM // Attach terminal to the DOM
if (terminalRef.current) { if (terminalRef.current) {
term.open(terminalRef.current); term.open(terminalRef.current);
xtermRef.current = term; xtermRef.current = term;
} }
setTimeout(() => {
fitAddon.fit();
}, 0);
// Adjust terminal size on window resize // Use ResizeObserver instead of window resize
const handleResize = () => fitAddon.fit(); const resizeObserver = new ResizeObserver(() => {
window.addEventListener('resize', handleResize); const container = terminalRef.current;
if (
container &&
container.offsetWidth > 0 &&
container.offsetHeight > 0
) {
try {
fitAddon.fit();
} catch (e) {
console.warn("Fit failed on resize:", e);
}
}
});
resizeObserver.observe(terminalRef.current);
socketRef.current = startCloudShellTerminal(term, props.shellType); socketRef.current = startCloudShellTerminal(term, props.shellType);
@ -55,10 +69,11 @@ export const CloudShellTerminalComponent: React.FC<CloudShellTerminalComponentPr
if (socketRef.current && socketRef.current.readyState && socketRef.current.readyState === WebSocket.OPEN) { if (socketRef.current && socketRef.current.readyState && socketRef.current.readyState === WebSocket.OPEN) {
socketRef.current.close(); // Close WebSocket connection socketRef.current.close(); // Close WebSocket connection
} }
window.removeEventListener('resize', handleResize); if (resizeObserver && terminalRef.current) {
resizeObserver.unobserve(terminalRef.current);
}
term.dispose(); // Clean up XTerm instance term.dispose(); // Clean up XTerm instance
}; };
}, []); }, []);
return <div ref={terminalRef} style={{ width: "100%", height: "500px"}} />; return <div ref={terminalRef} style={{ width: "100%", height: "500px"}} />;

View File

@ -3,8 +3,12 @@
* Core functionality for CloudShell terminal management * Core functionality for CloudShell terminal management
*/ */
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
import { Terminal } from "xterm"; import { Terminal } from "xterm";
import { Areas } from "../../../Common/Constants";
import { TerminalKind } from "../../../Contracts/ViewModels"; import { TerminalKind } from "../../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../../UserContext"; import { userContext } from "../../../UserContext";
import { import {
authorizeSession, authorizeSession,
@ -14,7 +18,7 @@ import {
registerCloudShellProvider, registerCloudShellProvider,
verifyCloudShellProviderRegistration verifyCloudShellProviderRegistration
} from "./Data/CloudShellClient"; } from "./Data/CloudShellClient";
import { START_MARKER, AbstractShellHandler } from "./ShellTypes/AbstractShellHandler"; import { AbstractShellHandler, START_MARKER } from "./ShellTypes/AbstractShellHandler";
import { ShellTypeHandlerFactory } from "./ShellTypes/ShellTypeFactory"; import { ShellTypeHandlerFactory } from "./ShellTypes/ShellTypeFactory";
import { AttachAddon } from "./Utils/AttachAddOn"; import { AttachAddon } from "./Utils/AttachAddOn";
import { askConfirmation, wait } from "./Utils/CommonUtils"; import { askConfirmation, wait } from "./Utils/CommonUtils";
@ -37,42 +41,72 @@ const MAX_PING_COUNT = 20 * 60; // 20 minutes (60 seconds/minute)
export const startCloudShellTerminal = export const startCloudShellTerminal =
async (terminal: Terminal, shellType: TerminalKind): Promise<WebSocket> => { async (terminal: Terminal, shellType: TerminalKind): Promise<WebSocket> => {
await ensureCloudShellProviderRegistered(); const startKey = TelemetryProcessor.traceStart(Action.CloudShellTerminalSession, {
shellType: TerminalKind[shellType],
dataExplorerArea: Areas.CloudShell
});
const resolvedRegion = determineCloudShellRegion(); try {
// Ask for user consent for region await ensureCloudShellProviderRegistered();
const consentGranted = await askConfirmation(terminal, formatWarningMessage("This shell might be in a different region than the database region. Do you want to proceed?"));
if (!consentGranted) { const resolvedRegion = determineCloudShellRegion();
return null; // Exit if user declined // Ask for user consent for region
const consentGranted = await askConfirmation(terminal, formatWarningMessage("This shell might be in a different region than the database region. Do you want to proceed?"));
// Track user decision
TelemetryProcessor.trace(Action.CloudShellUserConsent,
consentGranted ? ActionModifiers.Success : ActionModifiers.Cancel,
{ dataExplorerArea: Areas.CloudShell }
);
if (!consentGranted) {
return null; // Exit if user declined
}
terminal.writeln(formatInfoMessage("Connecting to CloudShell....."));
let sessionDetails: {
socketUri?: string;
provisionConsoleResponse?: any;
targetUri?: string;
};
sessionDetails = await provisionCloudShellSession(resolvedRegion, terminal);
if (!sessionDetails.socketUri) {
terminal.writeln(formatErrorMessage("Failed to establish a connection. Please try again later."));
return null;
}
// Get the shell handler for this type
const shellHandler = await ShellTypeHandlerFactory.getHandler(shellType);
// Configure WebSocket connection with shell-specific commands
const socket = await establishTerminalConnection(
terminal,
shellHandler,
sessionDetails.socketUri,
sessionDetails.provisionConsoleResponse,
sessionDetails.targetUri
);
TelemetryProcessor.traceSuccess(Action.CloudShellTerminalSession, {
shellType: TerminalKind[shellType],
dataExplorerArea: Areas.CloudShell,
region: resolvedRegion
}, startKey);
return socket;
} }
catch (err) {
TelemetryProcessor.traceFailure(Action.CloudShellTerminalSession, {
shellType: TerminalKind[shellType],
dataExplorerArea: Areas.CloudShell,
error: getErrorMessage(error),
errorStack: getErrorStack(error)
}, startKey);
terminal.writeln(formatInfoMessage("Connecting to CloudShell.....")); terminal.writeln(formatErrorMessage(`Failed with error.${getErrorMessage(error)}`));
}
let sessionDetails: {
socketUri?: string;
provisionConsoleResponse?: any;
targetUri?: string;
};
sessionDetails = await provisionCloudShellSession(resolvedRegion, terminal);
if (!sessionDetails.socketUri) {
terminal.writeln(formatErrorMessage("Failed to establish a connection. Please try again later."));
return null;
}
// Get the shell handler for this type
const shellHandler = await ShellTypeHandlerFactory.getHandler(shellType);
// Configure WebSocket connection with shell-specific commands
const socket = await establishTerminalConnection(
terminal,
shellHandler,
sessionDetails.socketUri,
sessionDetails.provisionConsoleResponse,
sessionDetails.targetUri
);
return socket;
}; };
/** /**

View File

@ -1,4 +1,5 @@
export const START_MARKER = `echo "START INITIALIZATION" > /dev/null`; export const START_MARKER = `echo "START INITIALIZATION" > /dev/null`;
export const DISABLE_HISTORY = `set +o history`;
export abstract class AbstractShellHandler { export abstract class AbstractShellHandler {
@ -14,6 +15,7 @@ export abstract class AbstractShellHandler {
const allCommands = [ const allCommands = [
START_MARKER, START_MARKER,
DISABLE_HISTORY,
...setupCommands, ...setupCommands,
connectionCommand connectionCommand
]; ];

View File

@ -33,7 +33,7 @@ export class PostgresShellHandler extends AbstractShellHandler {
} }
public getConnectionCommand(): string { public getConnectionCommand(): string {
return `psql 'read -p "Enter Database Name: " dbname && read -p "Enter Username: " username && host=${this.getEndpoint()} port=5432 dbname=$dbname user=$username sslmode=require'`; return `read -p "Enter Database Name: " dbname && read -p "Enter Username: " username && psql -h "${this.getEndpoint()}" -p 5432 -d "$dbname" -U "$username" --set=sslmode=require`;
} }
public getTerminalSuppressedData(): string { public getTerminalSuppressedData(): string {

View File

@ -83,11 +83,14 @@ export class AttachAddon implements ITerminalAddon {
this._allowTerminalWrite = false; this._allowTerminalWrite = false;
terminal.write(`Preparing ${this._shellHandler.getShellName()} environment...\r\n`); terminal.write(`Preparing ${this._shellHandler.getShellName()} environment...\r\n`);
} }
if (this._allowTerminalWrite &&
this._shellHandler?.getTerminalSuppressedData() && if (this._allowTerminalWrite) {
this._shellHandler?.getTerminalSuppressedData().length > 0 && const suppressedData = this._shellHandler?.getTerminalSuppressedData();
!data.includes(this._shellHandler?.getTerminalSuppressedData())) { const hasSuppressedData = suppressedData && suppressedData.length > 0;
terminal.write(data);
if (!hasSuppressedData || !data.includes(suppressedData)) {
terminal.write(data);
}
} }
if (data.includes(this._shellHandler.getConnectionCommand())) { if (data.includes(this._shellHandler.getConnectionCommand())) {

View File

@ -143,6 +143,8 @@ export enum Action {
ReadPersistedTabState, ReadPersistedTabState,
SavePersistedTabState, SavePersistedTabState,
DeletePersistedTabState, DeletePersistedTabState,
CloudShellUserConsent,
CloudShellTerminalSession
} }
export const ActionModifiers = { export const ActionModifiers = {