From a33429fd85535091f0315ff51eb90c5448e14b5e Mon Sep 17 00:00:00 2001 From: asier-isayas Date: Wed, 26 Nov 2025 13:07:18 -0500 Subject: [PATCH] Add Session Id (#2263) * adding sessionId to UserContext * add session id * add session id to settings pane and fix npm run compile * Add conditional for Portal * set default session id on userContext init * fix tests --------- Co-authored-by: Asier Isayas --- package-lock.json | 88 ++++++++++++++++++- package.json | 5 +- src/Common/Constants.ts | 1 + src/Common/MongoProxyClient.ts | 1 + src/Contracts/ViewModels.ts | 1 + .../Panes/SettingsPane/SettingsPane.test.tsx | 6 ++ .../Panes/SettingsPane/SettingsPane.tsx | 7 ++ .../__snapshots__/SettingsPane.test.tsx.snap | 32 +++++++ src/Explorer/Tables/TableDataClient.ts | 11 +-- src/UserContext.ts | 3 + src/hooks/useKnockoutExplorer.ts | 2 + 11 files changed, 148 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 045e28f44..3969be269 100644 --- a/package-lock.json +++ b/package-lock.json @@ -116,6 +116,7 @@ "tinykeys": "2.1.0", "underscore": "1.12.1", "utility-types": "3.10.0", + "uuid": "9.0.0", "zustand": "3.5.0" }, "devDependencies": { @@ -626,6 +627,14 @@ } } }, + "node_modules/@azure/ms-rest-js/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@azure/ms-rest-js/node_modules/xml2js": { "version": "0.5.0", "license": "MIT", @@ -685,6 +694,14 @@ "node": ">=0.8.0" } }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -7595,6 +7612,14 @@ "uuid": "^8.0.0" } }, + "node_modules/@nteract/commutable/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nteract/connected-components": { "version": "6.8.2", "license": "BSD-3-Clause", @@ -9125,6 +9150,14 @@ "uuid": "^8.0.0" } }, + "node_modules/@nteract/fixtures/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nteract/iron-icons": { "version": "1.0.0", "license": "BSD-3-Clause", @@ -9282,6 +9315,14 @@ "uuid": "^8.0.0" } }, + "node_modules/@nteract/messaging/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nteract/monaco-editor": { "version": "3.2.2", "license": "BSD-3-Clause", @@ -9397,6 +9438,14 @@ "version": "0.18.1", "license": "MIT" }, + "node_modules/@nteract/monaco-editor/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nteract/mythic-configuration": { "version": "1.0.12", "license": "BSD-3-Clause", @@ -9665,6 +9714,14 @@ "uuid": "^8.0.0" } }, + "node_modules/@nteract/reducers/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nteract/selectors": { "version": "3.2.0", "license": "BSD-3-Clause", @@ -9888,6 +9945,14 @@ "uuid": "^8.0.0" } }, + "node_modules/@nteract/types/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@octokit/auth-token": { "version": "4.0.0", "license": "MIT", @@ -26419,6 +26484,15 @@ "xmlbuilder": "^15.1.0" } }, + "node_modules/jest-trx-results-processor/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/jest-util": { "version": "24.9.0", "license": "MIT", @@ -33753,6 +33827,15 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/source-map": { "version": "0.5.7", "license": "BSD-3-Clause", @@ -35619,8 +35702,9 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "license": "MIT", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { "uuid": "dist/bin/uuid" } diff --git a/package.json b/package.json index 451da877d..ad7fd5e19 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,8 @@ "@types/mkdirp": "1.0.1", "@types/node-fetch": "2.5.7", "@xmldom/xmldom": "0.7.13", - "@xterm/xterm": "5.5.0", "@xterm/addon-fit": "0.10.0", + "@xterm/xterm": "5.5.0", "allotment": "1.20.2", "applicationinsights": "1.8.0", "bootstrap": "3.4.1", @@ -111,6 +111,7 @@ "tinykeys": "2.1.0", "underscore": "1.12.1", "utility-types": "3.10.0", + "uuid": "9.0.0", "zustand": "3.5.0" }, "devDependencies": { @@ -248,4 +249,4 @@ "printWidth": 120, "endOfLine": "auto" } -} \ No newline at end of file +} diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index 802bfd590..65b3f8d51 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -297,6 +297,7 @@ export class HttpHeaders { public static migrateOfferToManualThroughput: string = "x-ms-cosmos-migrate-offer-to-manual-throughput"; public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot"; public static xAPIKey: string = "X-API-Key"; + public static sessionId: string = "x-ms-client-session-id"; } export class ContentType { diff --git a/src/Common/MongoProxyClient.ts b/src/Common/MongoProxyClient.ts index 298b9268d..48eef3a48 100644 --- a/src/Common/MongoProxyClient.ts +++ b/src/Common/MongoProxyClient.ts @@ -17,6 +17,7 @@ const defaultHeaders = { [HttpHeaders.apiType]: ApiType.MongoDB.toString(), [CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100", [CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15", + [HttpHeaders.sessionId]: userContext.sessionId, }; function authHeaders() { diff --git a/src/Contracts/ViewModels.ts b/src/Contracts/ViewModels.ts index 1ea0f2075..99ff76f9d 100644 --- a/src/Contracts/ViewModels.ts +++ b/src/Contracts/ViewModels.ts @@ -446,6 +446,7 @@ export interface DataExplorerInputsFrame { feedbackPolicies?: any; aadToken?: string; containerCopyEnabled?: boolean; + sessionId?: string; } export interface SelfServeFrameInputs { diff --git a/src/Explorer/Panes/SettingsPane/SettingsPane.test.tsx b/src/Explorer/Panes/SettingsPane/SettingsPane.test.tsx index 25b0167f5..f3b72870b 100644 --- a/src/Explorer/Panes/SettingsPane/SettingsPane.test.tsx +++ b/src/Explorer/Panes/SettingsPane/SettingsPane.test.tsx @@ -5,6 +5,12 @@ import { updateUserContext } from "../../../UserContext"; import { SettingsPane } from "./SettingsPane"; describe("Settings Pane", () => { + beforeEach(() => { + updateUserContext({ + sessionId: "1234-5678", + }); + }); + it("should render Default properly", () => { const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); diff --git a/src/Explorer/Panes/SettingsPane/SettingsPane.tsx b/src/Explorer/Panes/SettingsPane/SettingsPane.tsx index 2e89418d8..bbf4c1438 100644 --- a/src/Explorer/Panes/SettingsPane/SettingsPane.tsx +++ b/src/Explorer/Panes/SettingsPane/SettingsPane.tsx @@ -212,6 +212,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ const styles = useStyles(); const explorerVersion = configContext.gitSha; + const sessionId: string = userContext.sessionId; const isEmulator = configContext.platform === Platform.Emulator; const shouldShowQueryPageOptions = userContext.apiType === "SQL"; const showRetrySettings = @@ -1227,6 +1228,12 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{explorerVersion}
+
+
+
Session ID
+
{sessionId}
+
+
); diff --git a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap index 022cc8647..ca0243ec7 100644 --- a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap +++ b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap @@ -649,6 +649,22 @@ exports[`Settings Pane should render Default properly 1`] = `
+
+
+
+ Session ID +
+
+ 1234-5678 +
+
+
`; @@ -958,6 +974,22 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
+
+
+
+ Session ID +
+
+ 1234-5678 +
+
+
`; diff --git a/src/Explorer/Tables/TableDataClient.ts b/src/Explorer/Tables/TableDataClient.ts index ff2d9a5ea..631e19e88 100644 --- a/src/Explorer/Tables/TableDataClient.ts +++ b/src/Explorer/Tables/TableDataClient.ts @@ -286,7 +286,7 @@ export class CassandraAPIDataClient extends TableDataClient { query, paginationToken, }), - beforeSend: this.setAuthorizationHeader as any, + beforeSend: this.setCommonHeaders as any, cache: false, }); shouldNotify && @@ -440,7 +440,7 @@ export class CassandraAPIDataClient extends TableDataClient { keyspaceId: collection.databaseId, tableId: collection.id(), }), - beforeSend: this.setAuthorizationHeader as any, + beforeSend: this.setCommonHeaders as any, cache: false, }) .then( @@ -482,7 +482,7 @@ export class CassandraAPIDataClient extends TableDataClient { keyspaceId: collection.databaseId, tableId: collection.id(), }), - beforeSend: this.setAuthorizationHeader as any, + beforeSend: this.setCommonHeaders as any, cache: false, }) .then( @@ -518,7 +518,7 @@ export class CassandraAPIDataClient extends TableDataClient { resourceId: resourceId, query: query, }), - beforeSend: this.setAuthorizationHeader as any, + beforeSend: this.setCommonHeaders as any, cache: false, }).then( (data: any) => { @@ -547,7 +547,7 @@ export class CassandraAPIDataClient extends TableDataClient { return cassandraEndpoint; } - private setAuthorizationHeader: (xhr: XMLHttpRequest) => boolean = (xhr: XMLHttpRequest): boolean => { + private setCommonHeaders: (xhr: XMLHttpRequest) => boolean = (xhr: XMLHttpRequest): boolean => { const authorizationHeaderMetadata: ViewModels.AuthorizationTokenHeaderMetadata = getAuthorizationHeader(); xhr.setRequestHeader(authorizationHeaderMetadata.header, authorizationHeaderMetadata.token); @@ -555,6 +555,7 @@ export class CassandraAPIDataClient extends TableDataClient { xhr.setRequestHeader(Constants.HttpHeaders.entraIdToken, userContext.aadToken); } + xhr.setRequestHeader(Constants.HttpHeaders.sessionId, userContext.sessionId); return true; }; diff --git a/src/UserContext.ts b/src/UserContext.ts index ecc4f5807..a495b9971 100644 --- a/src/UserContext.ts +++ b/src/UserContext.ts @@ -4,6 +4,7 @@ import { Action } from "Shared/Telemetry/TelemetryConstants"; import { traceOpen } from "Shared/Telemetry/TelemetryProcessor"; import { useCarousel } from "hooks/useCarousel"; import { usePostgres } from "hooks/usePostgres"; +import { v4 as uuidv4 } from "uuid"; import { AuthType } from "./AuthType"; import { DatabaseAccount } from "./Contracts/DataModels"; import { SubscriptionType } from "./Contracts/SubscriptionType"; @@ -118,6 +119,7 @@ export interface UserContext { readonly dataPlaneRbacEnabled?: boolean; readonly refreshCosmosClient?: boolean; throughputBucketsEnabled?: boolean; + readonly sessionId: string; } export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra" | "Postgres" | "VCoreMongo"; @@ -135,6 +137,7 @@ const userContext: UserContext = { features, subscriptionType: CollectionCreation.DefaultSubscriptionType, collectionCreationDefaults: CollectionCreationDefaults, + sessionId: uuidv4(), // Default sessionId - will be overwritten if provided by host }; export function isAccountNewerThanThresholdInMs(createdAt: string, threshold: number) { diff --git a/src/hooks/useKnockoutExplorer.ts b/src/hooks/useKnockoutExplorer.ts index 3e5546f80..b2aa588be 100644 --- a/src/hooks/useKnockoutExplorer.ts +++ b/src/hooks/useKnockoutExplorer.ts @@ -85,6 +85,7 @@ export function useKnockoutExplorer(platform: Platform): Explorer { userContext.features.phoenixNotebooks = true; userContext.features.phoenixFeatures = true; } + let explorer: Explorer; if (platform === Platform.Hosted) { explorer = await configureHosted(); @@ -927,6 +928,7 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) { collectionCreationDefaults: inputs.defaultCollectionThroughput, isTryCosmosDBSubscription: inputs.isTryCosmosDBSubscription, feedbackPolicies: inputs.feedbackPolicies, + ...(inputs.sessionId && { sessionId: inputs.sessionId }), // Remove conditional once Portal sends sessionId }); if (inputs.isPostgresAccount) {