diff --git a/src/Explorer/Tabs/CloudShellTab/CloudShellTerminalCore.tsx b/src/Explorer/Tabs/CloudShellTab/CloudShellTerminalCore.tsx index 8dff70d08..996afc351 100644 --- a/src/Explorer/Tabs/CloudShellTab/CloudShellTerminalCore.tsx +++ b/src/Explorer/Tabs/CloudShellTab/CloudShellTerminalCore.tsx @@ -43,13 +43,37 @@ export const startCloudShellTerminal = async (terminal: Terminal, shellType: Ter await ensureCloudShellProviderRegistered(); resolvedRegion = determineCloudShellRegion(); - // Ask for user consent for region - const consentGranted = await askConfirmation( - terminal, - formatWarningMessage( - "The shell environment may be operating in a region different from that of the database, which could impact performance or data compliance. Do you wish to proceed?", + + resolvedRegion = determineCloudShellRegion(); + + terminal.writeln(formatWarningMessage("⚠️ IMPORTANT: Azure Cloud Shell Region Notice ⚠️")); + terminal.writeln( + formatInfoMessage( + "The Cloud Shell environment will operate in a region that may differ from your database's region.", ), ); + terminal.writeln(formatInfoMessage("This has two potential implications:")); + terminal.writeln(formatInfoMessage("1. Performance Impact:")); + terminal.writeln( + formatInfoMessage(" Commands may experience higher latency due to geographic distance between regions."), + ); + terminal.writeln(formatInfoMessage("2. Data Compliance Considerations:")); + terminal.writeln( + formatInfoMessage( + " Data processed through this shell could temporarily reside in a different geographic region,", + ), + ); + terminal.writeln( + formatInfoMessage(" which may affect compliance with data residency requirements or regulations specific"), + ); + terminal.writeln(formatInfoMessage(" to your organization.")); + terminal.writeln(""); + + terminal.writeln("\x1b[94mFor more information on Azure Cosmos DB data governance and compliance, please visit:"); + terminal.writeln("\x1b[94mhttps://learn.microsoft.com/en-us/azure/cosmos-db/data-residency\x1b[0m"); + + // Ask for user consent for region + const consentGranted = await askConfirmation(terminal, formatWarningMessage("Do you wish to proceed?")); // Track user decision TelemetryProcessor.trace( diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler.tsx index 7d09000ae..c6ba7b8f0 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler.tsx @@ -22,6 +22,12 @@ export const EXIT_COMMAND = ` printf "\\033[1;31mSession ended. Please close thi * the required methods. */ export abstract class AbstractShellHandler { + /** + * The name of the application using this shell handler. + * This is used for telemetry and logging purposes. + */ + protected APP_NAME = "CosmosExplorerTerminal"; + abstract getShellName(): string; abstract getSetUpCommands(): string[]; abstract getConnectionCommand(): string; diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/CassandraShellHandler.test.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/CassandraShellHandler.test.tsx index ea5310a27..9bf4b3aa7 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/CassandraShellHandler.test.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/CassandraShellHandler.test.tsx @@ -87,7 +87,7 @@ describe("CassandraShellHandler", () => { }); test("should return correct connection command", () => { - const expectedCommand = "cqlsh test-endpoint.cassandra.cosmos.azure.com 10350 -u test-account -p test-key --ssl"; + const expectedCommand = `cqlsh test-endpoint.cassandra.cosmos.azure.com 10350 -u test-account -p test-key --ssl`; expect(handler.getConnectionCommand()).toBe(expectedCommand); expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-endpoint.cassandra.cosmos.azure.com:443/"); diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.test.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.test.tsx index ba6168f96..25395322f 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.test.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.test.tsx @@ -91,7 +91,7 @@ describe("MongoShellHandler", () => { const command = mongoShellHandler.getConnectionCommand(); expect(command).toBe( - "mongosh --host test-mongo.documents.azure.com --port 10255 --username test-account --password test-key --tls --tlsAllowInvalidCertificates", + "mongosh mongodb://test-mongo.documents.azure.com:10255?appName=CosmosExplorerTerminal --username test-account --password test-key --tls --tlsAllowInvalidCertificates", ); expect(CommonUtils.getHostFromUrl).toHaveBeenCalledWith("https://test-mongo.documents.azure.com:443/"); diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.tsx index 9da24717c..d15c42017 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.tsx @@ -28,9 +28,17 @@ export class MongoShellHandler extends AbstractShellHandler { if (!dbName) { return "echo 'Database name not found.'"; } - return `mongosh --host ${getHostFromUrl(this._endpoint)} --port 10255 --username ${dbName} --password ${ - this._key - } --tls --tlsAllowInvalidCertificates`; + return ( + "mongosh mongodb://" + + getHostFromUrl(this._endpoint) + + ":10255?appName=" + + this.APP_NAME + + " --username " + + dbName + + " --password " + + this._key + + " --tls --tlsAllowInvalidCertificates" + ); } public getTerminalSuppressedData(): string { diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/PostgresShellHandler.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/PostgresShellHandler.tsx index d2b235eca..c22d9d972 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/PostgresShellHandler.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/PostgresShellHandler.tsx @@ -54,7 +54,7 @@ export class PostgresShellHandler extends AbstractShellHandler { // All Azure Cosmos DB PostgreSQL deployments follow this convention. // Ref. https://learn.microsoft.com/en-us/azure/cosmos-db/postgresql/reference-limits#database-creation const loginName = userContext.postgresConnectionStrParams.adminLogin; - return `psql -h "${this._endpoint}" -p 5432 -d "citus" -U "${loginName}" --set=sslmode=require`; + return `psql -h "${this._endpoint}" -p 5432 -d "citus" -U "${loginName}" --set=sslmode=require --set=application_name=${this.APP_NAME}`; } public getTerminalSuppressedData(): string { diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.tsx index 3c6c859a8..85641c5ff 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.tsx @@ -23,7 +23,7 @@ export class VCoreMongoShellHandler extends AbstractShellHandler { } const userName = userContext.vcoreMongoConnectionParams.adminLogin; - return `mongosh "mongodb+srv://${userName}:@${this._endpoint}/?authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000"`; + return `mongosh "mongodb+srv://${userName}:@${this._endpoint}/?authMechanism=SCRAM-SHA-256&retrywrites=false&maxIdleTimeMS=120000&appName=${this.APP_NAME}"`; } public getTerminalSuppressedData(): string {