diff --git a/.eslintignore b/.eslintignore index b37c00904..49fefb80d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -14,7 +14,6 @@ src/Common/DataAccessUtilityBase.ts src/Common/DeleteFeedback.ts src/Common/DocumentClientUtilityBase.ts src/Common/EditableUtility.ts -src/Common/EnvironmentUtility.ts src/Common/HashMap.test.ts src/Common/HashMap.ts src/Common/HeadersUtility.test.ts diff --git a/README.md b/README.md index 316975238..9057742d5 100644 --- a/README.md +++ b/README.md @@ -13,29 +13,18 @@ UI for Azure Cosmos DB. Powers the [Azure Portal](https://portal.azure.com/), ht ### Watch mode -Run `npm run watch` to start the development server and automatically rebuild on changes +Run `npm start` to start the development server and automatically rebuild on changes -### Specifying Development Platform +### Hosted Development (https://cosmos.azure.com) -Setting the environment variable `PLATFORM` during the build process will force the explorer to load the specified platform. By default in development it will run in `Hosted` mode. Valid options: - -- Hosted -- Emulator -- Portal - -`PLATFORM=Emulator npm run watch` - -### Hosted Development - -The default webpack dev server configuration will proxy requests to the production portal backend: `https://main.documentdb.ext.azure.com`. This will allow you to use production connection strings on your local machine. - -To run pure hosted mode, in `webpack.config.js` change index HtmlWebpackPlugin to use hostedExplorer.html and change entry for index to use HostedExplorer.ts. +- Visit: `https://localhost:1234/hostedExplorer.html` +- Local sign in via AAD will NOT work. Connection string only in dev mode. Use the Portal if you need AAD auth. +- The default webpack dev server configuration will proxy requests to the production portal backend: `https://main.documentdb.ext.azure.com`. This will allow you to use production connection strings on your local machine. ### Emulator Development -In a window environment, running `npm run build` will automatically copy the built files from `/dist` over to the default emulator install paths. In a non-windows environment you can specify an alternate endpoint using `EMULATOR_ENDPOINT` and webpack dev server will proxy requests for you. - -`PLATFORM=Emulator EMULATOR_ENDPOINT=https://my-vm.azure.com:8081 npm run watch` +- Start the Cosmos Emulator +- Visit: https://localhost:1234/index.html #### Setting up a Remote Emulator @@ -55,16 +44,8 @@ The Cosmos emulator currently only runs in Windows environments. You can still d ### Portal Development -The Cosmos Portal that consumes this repo is not currently open source. If you have access to this project, `npm run build` will copy the built files over to the portal where they will be loaded by the portal development environment - -You can however load a local running instance of data explorer in the production portal. - -1. Turn off browser SSL validation for localhost: chrome://flags/#allow-insecure-localhost OR Install valid SSL certs for localhost (on IE, follow these [instructions](https://www.technipages.com/ie-bypass-problem-with-this-websites-security-certificate) to install the localhost certificate in the right place) -2. Allowlist `https://localhost:1234` domain for CORS in the Azure Cosmos DB portal -3. Start the project in portal mode: `PLATFORM=Portal npm run watch` -4. Load the portal using the following link: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html - -Live reload will occur, but data explorer will not properly integrate again with the parent iframe. You will have to manually reload the page. +- Visit: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html +- You may have to manually visit https://localhost:1234/explorer.html first and click through any SSL certificate warnings ### Testing diff --git a/src/Common/EnvironmentUtility.ts b/src/Common/EnvironmentUtility.ts index acb371359..0a4516514 100644 --- a/src/Common/EnvironmentUtility.ts +++ b/src/Common/EnvironmentUtility.ts @@ -1,8 +1,6 @@ -export default class EnvironmentUtility { - public static normalizeArmEndpointUri(uri: string): string { - if (uri && uri.slice(-1) !== "/") { - return `${uri}/`; - } - return uri; +export function normalizeArmEndpoint(uri: string): string { + if (uri && uri.slice(-1) !== "/") { + return `${uri}/`; } + return uri; } diff --git a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap index bc0e13735..aef4316e1 100644 --- a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap +++ b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap @@ -731,7 +731,6 @@ exports[`SettingsComponent renders 1`] = ` "visible": [Function], }, "arcadiaToken": [Function], - "armEndpoint": [Function], "browseQueriesPane": BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -1023,7 +1022,6 @@ exports[`SettingsComponent renders 1`] = ` "onRefreshResourcesClick": [Function], "onSwitchToConnectionString": [Function], "onToggleKeyDown": [Function], - "parentFrameDataExplorerVersion": [Function], "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -1049,7 +1047,6 @@ exports[`SettingsComponent renders 1`] = ` "titleLabel": "Select Columns", "visible": [Function], }, - "quotaId": [Function], "refreshDatabaseAccount": [Function], "refreshNotebookList": [Function], "refreshTreeTitle": [Function], @@ -2005,7 +2002,6 @@ exports[`SettingsComponent renders 1`] = ` "visible": [Function], }, "arcadiaToken": [Function], - "armEndpoint": [Function], "browseQueriesPane": BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -2297,7 +2293,6 @@ exports[`SettingsComponent renders 1`] = ` "onRefreshResourcesClick": [Function], "onSwitchToConnectionString": [Function], "onToggleKeyDown": [Function], - "parentFrameDataExplorerVersion": [Function], "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -2323,7 +2318,6 @@ exports[`SettingsComponent renders 1`] = ` "titleLabel": "Select Columns", "visible": [Function], }, - "quotaId": [Function], "refreshDatabaseAccount": [Function], "refreshNotebookList": [Function], "refreshTreeTitle": [Function], @@ -3292,7 +3286,6 @@ exports[`SettingsComponent renders 1`] = ` "visible": [Function], }, "arcadiaToken": [Function], - "armEndpoint": [Function], "browseQueriesPane": BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -3584,7 +3577,6 @@ exports[`SettingsComponent renders 1`] = ` "onRefreshResourcesClick": [Function], "onSwitchToConnectionString": [Function], "onToggleKeyDown": [Function], - "parentFrameDataExplorerVersion": [Function], "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -3610,7 +3602,6 @@ exports[`SettingsComponent renders 1`] = ` "titleLabel": "Select Columns", "visible": [Function], }, - "quotaId": [Function], "refreshDatabaseAccount": [Function], "refreshNotebookList": [Function], "refreshTreeTitle": [Function], @@ -4566,7 +4557,6 @@ exports[`SettingsComponent renders 1`] = ` "visible": [Function], }, "arcadiaToken": [Function], - "armEndpoint": [Function], "browseQueriesPane": BrowseQueriesPane { "canSaveQueries": [Function], "container": [Circular], @@ -4858,7 +4848,6 @@ exports[`SettingsComponent renders 1`] = ` "onRefreshResourcesClick": [Function], "onSwitchToConnectionString": [Function], "onToggleKeyDown": [Function], - "parentFrameDataExplorerVersion": [Function], "provideFeedbackEmail": [Function], "queriesClient": QueriesClient { "container": [Circular], @@ -4884,7 +4873,6 @@ exports[`SettingsComponent renders 1`] = ` "titleLabel": "Select Columns", "visible": [Function], }, - "quotaId": [Function], "refreshDatabaseAccount": [Function], "refreshNotebookList": [Function], "refreshTreeTitle": [Function], diff --git a/src/Explorer/Explorer.ts b/src/Explorer/Explorer.ts index 0c9f91661..58490c8d1 100644 --- a/src/Explorer/Explorer.ts +++ b/src/Explorer/Explorer.ts @@ -18,7 +18,7 @@ import DeleteDatabaseConfirmationPane from "./Panes/DeleteDatabaseConfirmationPa import { readCollection } from "../Common/dataAccess/readCollection"; import { readDatabases } from "../Common/dataAccess/readDatabases"; import EditTableEntityPane from "./Panes/Tables/EditTableEntityPane"; -import EnvironmentUtility from "../Common/EnvironmentUtility"; +import { normalizeArmEndpoint } from "../Common/EnvironmentUtility"; import GraphStylingPane from "./Panes/GraphStylingPane"; import hasher from "hasher"; import NewVertexPane from "./Panes/NewVertexPane"; @@ -121,7 +121,6 @@ export default class Explorer { public databaseAccount: ko.Observable; public collectionCreationDefaults: ViewModels.CollectionCreationDefaults = SharedConstants.CollectionCreationDefaults; public subscriptionType: ko.Observable; - public quotaId: ko.Observable; public defaultExperience: ko.Observable; public isPreferredApiDocumentDB: ko.Computed; public isPreferredApiCassandra: ko.Computed; @@ -135,12 +134,10 @@ export default class Explorer { public canSaveQueries: ko.Computed; public features: ko.Observable; public serverId: ko.Observable; - public armEndpoint: ko.Observable; public isTryCosmosDBSubscription: ko.Observable; public queriesClient: QueriesClient; public tableDataClient: TableDataClient; public splitter: Splitter; - public parentFrameDataExplorerVersion: ko.Observable = ko.observable(""); public mostRecentActivity: MostRecentActivity.MostRecentActivity; // Notification Console @@ -278,7 +275,6 @@ export default class Explorer { this.databaseAccount = ko.observable(); this.subscriptionType = ko.observable(SharedConstants.CollectionCreation.DefaultSubscriptionType); - this.quotaId = ko.observable(""); let firstInitialization = true; this.isRefreshingExplorer = ko.observable(true); this.isRefreshingExplorer.subscribe((isRefreshing: boolean) => { @@ -318,9 +314,9 @@ export default class Explorer { if (isAccountReady) { this.isAuthWithResourceToken() ? this.refreshDatabaseForResourceToken() : this.refreshAllDatabases(true); RouteHandler.getInstance().initHandler(); - this.notebookWorkspaceManager = new NotebookWorkspaceManager(this.armEndpoint()); + this.notebookWorkspaceManager = new NotebookWorkspaceManager(); this.arcadiaWorkspaces = ko.observableArray(); - this._arcadiaManager = new ArcadiaResourceManager(this.armEndpoint()); + this._arcadiaManager = new ArcadiaResourceManager(); this._isAfecFeatureRegistered(Constants.AfecFeatures.StorageAnalytics).then(isRegistered => this.hasStorageAnalyticsAfecFeature(isRegistered) ); @@ -370,7 +366,6 @@ export default class Explorer { this.features = ko.observable(); this.serverId = ko.observable(); - this.armEndpoint = ko.observable(undefined); this.queriesClient = new QueriesClient(this); this.isTryCosmosDBSubscription = ko.observable(false); @@ -1012,9 +1007,7 @@ export default class Explorer { this.isSynapseLinkUpdating(true); this._closeSynapseLinkModalDialog(); - const resourceProviderClient = new ResourceProviderClientFactory(this.armEndpoint()).getOrCreate( - this.databaseAccount().id - ); + const resourceProviderClient = new ResourceProviderClientFactory().getOrCreate(this.databaseAccount().id); try { const databaseAccount: DataModels.DatabaseAccount = await resourceProviderClient.patchAsync( @@ -1754,61 +1747,59 @@ export default class Explorer { inputs.extensionEndpoint = configContext.PROXY_PATH; } - const initPromise: Q.Promise = inputs ? this.initDataExplorerWithFrameInputs(inputs) : Q(); + this.initDataExplorerWithFrameInputs(inputs); - initPromise.then(() => { - const openAction: ActionContracts.DataExplorerAction = message.openAction; - if (!!openAction) { - if (this.isRefreshingExplorer()) { - const subscription = this.databases.subscribe((databases: ViewModels.Database[]) => { - handleOpenAction(openAction, this.nonSystemDatabases(), this); - subscription.dispose(); - }); - } else { + const openAction: ActionContracts.DataExplorerAction = message.openAction; + if (!!openAction) { + if (this.isRefreshingExplorer()) { + const subscription = this.databases.subscribe((databases: ViewModels.Database[]) => { handleOpenAction(openAction, this.nonSystemDatabases(), this); - } + subscription.dispose(); + }); + } else { + handleOpenAction(openAction, this.nonSystemDatabases(), this); } - if (message.actionType === ActionContracts.ActionType.TransmitCachedData) { - handleCachedDataMessage(message); - return; - } - if (message.type) { - switch (message.type) { - case MessageTypes.UpdateLocationHash: - if (!message.locationHash) { - break; - } - hasher.replaceHash(message.locationHash); - RouteHandler.getInstance().parseHash(message.locationHash); - break; - case MessageTypes.SendNotification: - if (!message.message) { - break; - } - NotificationConsoleUtils.logConsoleMessage( - message.consoleDataType || ConsoleDataType.Info, - message.message, - message.id - ); - break; - case MessageTypes.ClearNotification: - if (!message.id) { - break; - } - NotificationConsoleUtils.clearInProgressMessageWithId(message.id); - break; - case MessageTypes.LoadingStatus: - if (!message.text) { - break; - } - this._setLoadingStatusText(message.text, message.title); - break; - } - return; + } + if (message.actionType === ActionContracts.ActionType.TransmitCachedData) { + handleCachedDataMessage(message); + return; + } + if (message.type) { + switch (message.type) { + case MessageTypes.UpdateLocationHash: + if (!message.locationHash) { + break; + } + hasher.replaceHash(message.locationHash); + RouteHandler.getInstance().parseHash(message.locationHash); + break; + case MessageTypes.SendNotification: + if (!message.message) { + break; + } + NotificationConsoleUtils.logConsoleMessage( + message.consoleDataType || ConsoleDataType.Info, + message.message, + message.id + ); + break; + case MessageTypes.ClearNotification: + if (!message.id) { + break; + } + NotificationConsoleUtils.clearInProgressMessageWithId(message.id); + break; + case MessageTypes.LoadingStatus: + if (!message.text) { + break; + } + this._setLoadingStatusText(message.text, message.title); + break; } + return; + } - this.splashScreenAdapter.forceRender(); - }); + this.splashScreenAdapter.forceRender(); } public findSelectedDatabase(): ViewModels.Database { @@ -1848,8 +1839,14 @@ export default class Explorer { return false; } - public initDataExplorerWithFrameInputs(inputs: ViewModels.DataExplorerInputsFrame): Q.Promise { + public initDataExplorerWithFrameInputs(inputs: ViewModels.DataExplorerInputsFrame): void { if (inputs != null) { + // In development mode, save the iframe message from the portal in session storage. + // This allows webpack hot reload to funciton properly + if (process.env.NODE_ENV === "development") { + sessionStorage.setItem("portalDataExplorerInitMessage", JSON.stringify(inputs)); + } + const authorizationToken = inputs.authorizationToken || ""; const masterKey = inputs.masterKey || ""; const databaseAccount = inputs.databaseAccount || null; @@ -1858,25 +1855,18 @@ export default class Explorer { } this.features(inputs.features); this.serverId(inputs.serverId); - this.armEndpoint(EnvironmentUtility.normalizeArmEndpointUri(inputs.csmEndpoint || configContext.ARM_ENDPOINT)); this.databaseAccount(databaseAccount); this.subscriptionType(inputs.subscriptionType); - this.quotaId(inputs.quotaId); this.hasWriteAccess(inputs.hasWriteAccess); this.flight(inputs.addCollectionDefaultFlight); this.isTryCosmosDBSubscription(inputs.isTryCosmosDBSubscription); this.isAuthWithResourceToken(inputs.isAuthWithresourceToken); this.setFeatureFlagsFromFlights(inputs.flights); - - if (!!inputs.dataExplorerVersion) { - this.parentFrameDataExplorerVersion(inputs.dataExplorerVersion); - } - this._importExplorerConfigComplete = true; updateConfigContext({ BACKEND_ENDPOINT: inputs.extensionEndpoint || "", - ARM_ENDPOINT: this.armEndpoint() + ARM_ENDPOINT: normalizeArmEndpoint(inputs.csmEndpoint || configContext.ARM_ENDPOINT) }); updateUserContext({ @@ -1885,7 +1875,8 @@ export default class Explorer { databaseAccount, resourceGroup: inputs.resourceGroup, subscriptionId: inputs.subscriptionId, - subscriptionType: inputs.subscriptionType + subscriptionType: inputs.subscriptionType, + quotaId: inputs.quotaId }); TelemetryProcessor.traceSuccess( Action.LoadDatabaseAccount, @@ -1899,7 +1890,6 @@ export default class Explorer { this.isAccountReady(true); } - return Q(); } public setFeatureFlagsFromFlights(flights: readonly string[]): void { @@ -2562,7 +2552,7 @@ export default class Explorer { public _refreshSparkEnabledStateForAccount = async (): Promise => { const subscriptionId = userContext.subscriptionId; - const armEndpoint = this.armEndpoint(); + const armEndpoint = configContext.ARM_ENDPOINT; const authType = window.authType as AuthType; if (!subscriptionId || !armEndpoint || authType === AuthType.EncryptedToken) { // explorer is not aware of the database account yet @@ -2571,7 +2561,7 @@ export default class Explorer { } const featureUri = `subscriptions/${subscriptionId}/providers/Microsoft.Features/providers/Microsoft.DocumentDb/features/${Constants.AfecFeatures.Spark}`; - const resourceProviderClient = new ResourceProviderClientFactory(this.armEndpoint()).getOrCreate(featureUri); + const resourceProviderClient = new ResourceProviderClientFactory().getOrCreate(featureUri); try { const sparkNotebooksFeature: DataModels.AfecFeature = await resourceProviderClient.getAsync( featureUri, @@ -2591,7 +2581,7 @@ export default class Explorer { public _isAfecFeatureRegistered = async (featureName: string): Promise => { const subscriptionId = userContext.subscriptionId; - const armEndpoint = this.armEndpoint(); + const armEndpoint = configContext.ARM_ENDPOINT; const authType = window.authType as AuthType; if (!featureName || !subscriptionId || !armEndpoint || authType === AuthType.EncryptedToken) { // explorer is not aware of the database account yet @@ -2599,7 +2589,7 @@ export default class Explorer { } const featureUri = `subscriptions/${subscriptionId}/providers/Microsoft.Features/providers/Microsoft.DocumentDb/features/${featureName}`; - const resourceProviderClient = new ResourceProviderClientFactory(this.armEndpoint()).getOrCreate(featureUri); + const resourceProviderClient = new ResourceProviderClientFactory().getOrCreate(featureUri); try { const featureStatus: DataModels.AfecFeature = await resourceProviderClient.getAsync( featureUri, diff --git a/src/Explorer/Panes/AddCollectionPane.ts b/src/Explorer/Panes/AddCollectionPane.ts index cf37da3ca..08bd372f1 100644 --- a/src/Explorer/Panes/AddCollectionPane.ts +++ b/src/Explorer/Panes/AddCollectionPane.ts @@ -16,6 +16,7 @@ import { ContextualPaneBase } from "./ContextualPaneBase"; import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent"; import { createCollection } from "../../Common/dataAccess/createCollection"; import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; +import { userContext } from "../../UserContext"; export interface AddCollectionPaneOptions extends ViewModels.PaneOptions { isPreferredApiTable: ko.Computed; @@ -668,7 +669,7 @@ export default class AddCollectionPane extends ContextualPaneBase { databaseId: this.databaseId() }), subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { storage: this.storage() === Constants.BackendDefaults.singlePartitionStorageInGb ? "f" : "u", throughput: this._getThroughput(), @@ -770,7 +771,7 @@ export default class AddCollectionPane extends ContextualPaneBase { collectionWithThroughputInShared: this.collectionWithThroughputInShared() }), subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { storage: this.storage() === Constants.BackendDefaults.singlePartitionStorageInGb ? "f" : "u", throughput: offerThroughput, @@ -844,7 +845,7 @@ export default class AddCollectionPane extends ContextualPaneBase { collectionWithThroughputInShared: this.collectionWithThroughputInShared() }), subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { storage: this.storage() === Constants.BackendDefaults.singlePartitionStorageInGb ? "f" : "u", throughput: offerThroughput, @@ -878,7 +879,7 @@ export default class AddCollectionPane extends ContextualPaneBase { collectionWithThroughputInShared: this.collectionWithThroughputInShared() }, subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { storage: this.storage() === Constants.BackendDefaults.singlePartitionStorageInGb ? "f" : "u", throughput: offerThroughput, diff --git a/src/Explorer/Panes/AddDatabasePane.ts b/src/Explorer/Panes/AddDatabasePane.ts index 5f198d9a2..16f800923 100644 --- a/src/Explorer/Panes/AddDatabasePane.ts +++ b/src/Explorer/Panes/AddDatabasePane.ts @@ -13,6 +13,7 @@ import { createDatabase } from "../../Common/dataAccess/createDatabase"; import { configContext, Platform } from "../../ConfigContext"; import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; import { SubscriptionType } from "../../Contracts/SubscriptionType"; +import { userContext } from "../../UserContext"; export default class AddDatabasePane extends ContextualPaneBase { public defaultExperience: ko.Computed; @@ -250,7 +251,7 @@ export default class AddDatabasePane extends ContextualPaneBase { databaseAccountName: this.container.databaseAccount().name, defaultExperience: this.container.defaultExperience(), subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { throughput: this.throughput(), flight: this.container.flight() @@ -278,7 +279,7 @@ export default class AddDatabasePane extends ContextualPaneBase { }), offerThroughput, subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { flight: this.container.flight() }, @@ -342,7 +343,7 @@ export default class AddDatabasePane extends ContextualPaneBase { }), offerThroughput: offerThroughput, subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { flight: this.container.flight() }, @@ -366,7 +367,7 @@ export default class AddDatabasePane extends ContextualPaneBase { }), offerThroughput: offerThroughput, subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { flight: this.container.flight() }, diff --git a/src/Explorer/Panes/CassandraAddCollectionPane.ts b/src/Explorer/Panes/CassandraAddCollectionPane.ts index b72dec1ea..092f032a7 100644 --- a/src/Explorer/Panes/CassandraAddCollectionPane.ts +++ b/src/Explorer/Panes/CassandraAddCollectionPane.ts @@ -15,6 +15,7 @@ import { HashMap } from "../../Common/HashMap"; import { configContext, Platform } from "../../ConfigContext"; import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; import { SubscriptionType } from "../../Contracts/SubscriptionType"; +import { userContext } from "../../UserContext"; export default class CassandraAddCollectionPane extends ContextualPaneBase { public createTableQuery: ko.Observable; @@ -299,7 +300,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase { databaseId: this.keyspaceId() }), subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { storage: "u", throughput: this.throughput(), @@ -353,7 +354,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase { }), keyspaceHasSharedOffer: this.keyspaceHasSharedOffer(), subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { storage: "u", throughput: this.throughput(), @@ -399,7 +400,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase { }), keyspaceHasSharedOffer: this.keyspaceHasSharedOffer(), subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { storage: "u", throughput: this.throughput(), @@ -429,7 +430,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase { }, keyspaceHasSharedOffer: this.keyspaceHasSharedOffer(), subscriptionType: SubscriptionType[this.container.subscriptionType()], - subscriptionQuotaId: this.container.quotaId(), + subscriptionQuotaId: userContext.quotaId, defaultsCheck: { storage: "u", throughput: this.throughput(), diff --git a/src/NotebookWorkspaceManager/NotebookWorkspaceManager.ts b/src/NotebookWorkspaceManager/NotebookWorkspaceManager.ts index 563645f25..6b3d36f3f 100644 --- a/src/NotebookWorkspaceManager/NotebookWorkspaceManager.ts +++ b/src/NotebookWorkspaceManager/NotebookWorkspaceManager.ts @@ -12,8 +12,8 @@ import { getErrorMessage } from "../Common/ErrorHandlingUtils"; export class NotebookWorkspaceManager { private resourceProviderClientFactory: IResourceProviderClientFactory; - constructor(private _armEndpoint: string) { - this.resourceProviderClientFactory = new ResourceProviderClientFactory(this._armEndpoint); + constructor() { + this.resourceProviderClientFactory = new ResourceProviderClientFactory(); } public async getNotebookWorkspacesAsync(cosmosdbResourceId: string): Promise { diff --git a/src/Platform/Emulator/Main.ts b/src/Platform/Emulator/Main.ts index 7cc62b094..d3f5783a1 100644 --- a/src/Platform/Emulator/Main.ts +++ b/src/Platform/Emulator/Main.ts @@ -19,6 +19,7 @@ export function initializeExplorer(): Explorer { cassandraEndpoint: "" } }); + explorer.isAccountReady(true); return explorer; } diff --git a/src/Platform/Hosted/Main.ts b/src/Platform/Hosted/Main.ts index e218a136e..672efba95 100644 --- a/src/Platform/Hosted/Main.ts +++ b/src/Platform/Hosted/Main.ts @@ -268,7 +268,7 @@ export default class Main { masterKey?: string /* master key extracted from connection string if available */, account?: DatabaseAccount, authorizationToken?: string /* access key */ - ): Q.Promise { + ): void { const serverId: string = AuthHeadersUtil.serverId; const authType: string = (window).authType; const accountResourceId = @@ -373,7 +373,7 @@ export default class Main { }); } - return Q.reject(`Unsupported AuthType ${authType}`); + throw new Error(`Unsupported AuthType ${authType}`); } private static _instantiateExplorer(): Explorer { diff --git a/src/Platform/Portal/Main.ts b/src/Platform/Portal/Main.ts index 1e9af9e29..5d0b652ab 100644 --- a/src/Platform/Portal/Main.ts +++ b/src/Platform/Portal/Main.ts @@ -1,9 +1,23 @@ import "../../Explorer/Tables/DataTable/DataTableBindingManager"; import Explorer from "../../Explorer/Explorer"; +import { handleMessage } from "../../Controls/Heatmap/Heatmap"; export function initializeExplorer(): Explorer { const explorer = new Explorer(); + // In development mode, try to load the iframe message from session storage. + // This allows webpack hot reload to funciton properly + if (process.env.NODE_ENV === "development") { + const initMessage = sessionStorage.getItem("portalDataExplorerInitMessage"); + if (initMessage) { + const message = JSON.parse(initMessage); + console.warn("Loaded cached portal iframe message from session storage"); + console.dir(message); + explorer.initDataExplorerWithFrameInputs(message); + } + } + window.addEventListener("message", explorer.handleMessage.bind(explorer), false); + return explorer; } diff --git a/src/ResourceProvider/ResourceProviderClientFactory.ts b/src/ResourceProvider/ResourceProviderClientFactory.ts index 89c31e0f2..07b71a34e 100644 --- a/src/ResourceProvider/ResourceProviderClientFactory.ts +++ b/src/ResourceProvider/ResourceProviderClientFactory.ts @@ -1,10 +1,14 @@ +import { configContext } from "../ConfigContext"; import { IResourceProviderClientFactory, IResourceProviderClient } from "./IResourceProviderClient"; import { ResourceProviderClient } from "./ResourceProviderClient"; export class ResourceProviderClientFactory implements IResourceProviderClientFactory { + private armEndpoint: string; private cachedClients: { [url: string]: IResourceProviderClient } = {}; - constructor(private armEndpoint: string) {} + constructor() { + this.armEndpoint = configContext.ARM_ENDPOINT; + } public getOrCreate(url: string): IResourceProviderClient { if (!url) { diff --git a/src/Shared/Constants.ts b/src/Shared/Constants.ts index 212ff41ca..7aae90063 100644 --- a/src/Shared/Constants.ts +++ b/src/Shared/Constants.ts @@ -144,10 +144,6 @@ export class OfferPricing { }; } -export class GeneralResources { - public static loadingText: string = "Loading..."; -} - export class CollectionCreation { // TODO generate these values based on Product\Services\Documents\ImageStore\GatewayApplication\Settings.xml public static readonly MinRUPerPartitionBelow7Partitions: number = 400; @@ -228,32 +224,6 @@ export class IndexingPolicies { } export class SubscriptionUtilMappings { - // TODO: Expose this through a web API from the portal - public static SubscriptionTypeMap: { [key: string]: SubscriptionType } = { - "AAD_2015-09-01": SubscriptionType.Free, - "AzureDynamics_2014-09-01": SubscriptionType.Free, - "AzureInOpen_2014-09-01": SubscriptionType.EA, - "AzurePass_2014-09-01": SubscriptionType.Free, - "BackupStorage_2014-09-01": SubscriptionType.PAYG, - "BizSpark_2014-09-01": SubscriptionType.Benefits, - "BizSparkPlus_2014-09-01": SubscriptionType.Benefits, - "CSP_2015-05-01": SubscriptionType.EA, - "Default_2014-09-01": SubscriptionType.PAYG, - "DevEssentials_2016-01-01": SubscriptionType.Benefits, - "DreamSpark_2015-02-01": SubscriptionType.Benefits, - "EnterpriseAgreement_2014-09-01": SubscriptionType.EA, - "FreeTrial_2014-09-01": SubscriptionType.Free, - "Internal_2014-09-01": SubscriptionType.Internal, - "LegacyMonetaryCommitment_2014-09-01": SubscriptionType.EA, - "LightweightTrial_2016-09-01": SubscriptionType.Free, - "MonetaryCommitment_2015-05-01": SubscriptionType.EA, - "MPN_2014-09-01": SubscriptionType.Benefits, - "MSDN_2014-09-01": SubscriptionType.Benefits, - "MSDNDevTest_2014-09-01": SubscriptionType.Benefits, - "PayAsYouGo_2014-09-01": SubscriptionType.PAYG, - "Sponsored_2016-01-01": SubscriptionType.Benefits - }; - public static FreeTierSubscriptionIds: string[] = [ "b8f2ff04-0a81-4cf9-95ef-5828d16981d2", "39b1fdff-e5b2-4f83-adb4-33cb3aabf5ea", @@ -264,57 +234,6 @@ export class SubscriptionUtilMappings { ]; } -export class Offers { - public static offerTypeS1: string = "S1"; - public static offerTypeS2: string = "S2"; - public static offerTypeS3: string = "S3"; - public static offerTypeStandard: string = "Standard"; -} - -export class OfferThoughput { - public static offerS1Throughput: number = 250; - public static offerS2Throughput: number = 1000; - public static offerS3Throughput: number = 2500; -} - -export class OfferVersions { - public static offerV1: string = "V1"; - public static offerV2: string = "V2"; -} - -export class InvalidOffers { - public static offerTypeInvalid: string = "Invalid"; - public static offerTypeError: string = "Loading Error"; -} - -export class SpecTypes { - public static collection: string = "DocumentDbCollection"; -} - -export class CurrencyCodes { - public static usd: string = "USD"; - public static rmb: string = "RMB"; -} - -export class ColorSchemes { - public static standard: string = "mediumBlue"; - public static legacy: string = "yellowGreen"; -} - -export class FeatureIds { - public static storage: string = "storage"; - public static sla: string = "sla"; - public static partitioned: string = "partitioned"; - public static singlePartitioned: string = "singlePartition"; - public static legacySinglePartitioned: string = "legacySinglePartition"; -} - -export class FeatureIconNames { - public static storage: string = "SSD"; - public static sla: string = "Monitoring"; - public static productionReady: string = "ProductionReadyDb"; -} - export class AutopilotDocumentation { public static Url: string = "https://aka.ms/cosmos-autoscale-info"; } diff --git a/src/SparkClusterManager/ArcadiaResourceManager.ts b/src/SparkClusterManager/ArcadiaResourceManager.ts index 68fa2318b..b16121ec0 100644 --- a/src/SparkClusterManager/ArcadiaResourceManager.ts +++ b/src/SparkClusterManager/ArcadiaResourceManager.ts @@ -8,14 +8,13 @@ import { ArmApiVersions, ArmResourceTypes } from "../Common/Constants"; import { IResourceProviderClient, IResourceProviderClientFactory } from "../ResourceProvider/IResourceProviderClient"; import * as Logger from "../Common/Logger"; import { ResourceProviderClientFactory } from "../ResourceProvider/ResourceProviderClientFactory"; -import { configContext } from "../ConfigContext"; import { getErrorMessage } from "../Common/ErrorHandlingUtils"; export class ArcadiaResourceManager { private resourceProviderClientFactory: IResourceProviderClientFactory; - constructor(private armEndpoint = configContext.ARM_ENDPOINT) { - this.resourceProviderClientFactory = new ResourceProviderClientFactory(this.armEndpoint); + constructor() { + this.resourceProviderClientFactory = new ResourceProviderClientFactory(); } public async getWorkspacesAsync(arcadiaResourceId: string): Promise { diff --git a/src/UserContext.ts b/src/UserContext.ts index d7ef737f2..ea76afe86 100644 --- a/src/UserContext.ts +++ b/src/UserContext.ts @@ -14,6 +14,7 @@ interface UserContext { defaultExperience?: DefaultAccountExperienceType; useSDKOperations?: boolean; subscriptionType?: SubscriptionType; + quotaId?: string; } const userContext: Readonly = {} as const;