import { Checkbox, Text, TextField } from "office-ui-fabric-react"; import React, { FunctionComponent, useEffect, useState } from "react"; import * as Constants from "../../../Common/Constants"; import { createDatabase } from "../../../Common/dataAccess/createDatabase"; import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; import { Tooltip } from "../../../Common/Tooltip"; import { configContext, Platform } from "../../../ConfigContext"; import * as DataModels from "../../../Contracts/DataModels"; import { SubscriptionType } from "../../../Contracts/SubscriptionType"; import * as SharedConstants from "../../../Shared/Constants"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import { userContext } from "../../../UserContext"; import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils"; import * as PricingUtils from "../../../Utils/PricingUtils"; import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput"; import Explorer from "../../Explorer"; import { GenericRightPaneComponent, GenericRightPaneProps } from "../GenericRightPaneComponent"; import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent"; export interface AddDatabasePaneProps { explorer: Explorer; closePanel: () => void; openNotificationConsole: () => void; } export const AddDatabasePane: FunctionComponent = ({ explorer: container, closePanel, openNotificationConsole, }: AddDatabasePaneProps) => { const getSharedThroughputDefault = (): boolean => { const { subscriptionType } = userContext; if (subscriptionType === SubscriptionType.EA || container.isServerlessEnabled()) { return false; } return true; }; const _isAutoPilotSelectedAndWhatTier = (): DataModels.AutoPilotCreationSettings => { if (isAutoPilotSelected && maxAutoPilotThroughputSet) { return { maxThroughput: maxAutoPilotThroughputSet * 1, }; } return undefined; }; const isCassandraAccount: boolean = userContext.apiType === "Cassandra"; const databaseLabel: string = isCassandraAccount ? "keyspace" : "database"; const collectionsLabel: string = isCassandraAccount ? "tables" : "collections"; const databaseIdLabel: string = isCassandraAccount ? "Keyspace id" : "Database id"; const databaseIdPlaceHolder: string = isCassandraAccount ? "Type a new keyspace id" : "Type a new database id"; const [databaseId, setDatabaseId] = useState(""); const databaseIdTooltipText = `A ${ isCassandraAccount ? "keyspace" : "database" } is a logical container of one or more ${isCassandraAccount ? "tables" : "collections"}`; const databaseLevelThroughputTooltipText = `Provisioned throughput at the ${databaseLabel} level will be shared across all ${collectionsLabel} within the ${databaseLabel}.`; const [databaseCreateNewShared, setDatabaseCreateNewShared] = useState(getSharedThroughputDefault()); const [formErrorsDetails, setFormErrorsDetails] = useState(); const [formErrors, setFormErrors] = useState(""); const throughputDefaults = container.collectionCreationDefaults.throughput; const [throughput, setThroughput] = useState(throughputDefaults.shared); const [maxThroughputRU, setMaxThroughputRU] = useState(throughputDefaults.unlimitedmax); const maxThroughputRUText: string = maxThroughputRU?.toLocaleString(); const [isAutoPilotSelected, setIsAutoPilotSelected] = useState(container.isAutoscaleDefaultEnabled()); const [throughputSpendAck, setThroughputSpendAck] = useState(false); const canRequestSupport = () => { if ( configContext.platform !== Platform.Emulator && !userContext.isTryCosmosDBSubscription && configContext.platform !== Platform.Portal ) { const offerThroughput: number = throughput; return offerThroughput <= 100000; } return false; }; const isFreeTierAccount: boolean = userContext.databaseAccount?.properties?.enableFreeTier; const upsellMessage: string = PricingUtils.getUpsellMessage( userContext.portalEnv, isFreeTierAccount, container.isFirstResourceCreated(), userContext.defaultExperience, false ); const upsellAnchorUrl: string = isFreeTierAccount ? Constants.Urls.freeTierInformation : Constants.Urls.cosmosPricing; const upsellAnchorText: string = isFreeTierAccount ? "Learn more" : "More details"; const [maxAutoPilotThroughputSet, setMaxAutoPilotThroughputSet] = useState( AutoPilotUtils.minAutoPilotThroughput ); const canConfigureThroughput = !container.isServerlessEnabled(); const showUpsellMessage = () => { if (container.isServerlessEnabled()) { return false; } if (isFreeTierAccount) { return databaseCreateNewShared; } return true; }; const title: string = container?.addDatabaseText() || "New Database"; const [isExecuting, setIsExecuting] = useState(false); const _updateThroughputLimitByDatabase = () => { const throughputDefaults = container.collectionCreationDefaults.throughput; setThroughput(throughputDefaults.shared); setMaxThroughputRU(throughputDefaults.unlimitedmax); }; useEffect(() => { _updateThroughputLimitByDatabase(); }, [databaseCreateNewShared]); useEffect(() => { setDatabaseCreateNewShared(getSharedThroughputDefault()); }, [userContext.subscriptionType]); useEffect(() => { setDatabaseId(""); setDatabaseCreateNewShared(getSharedThroughputDefault()); setIsAutoPilotSelected(container.isAutoscaleDefaultEnabled()); setMaxAutoPilotThroughputSet(AutoPilotUtils.minAutoPilotThroughput); _updateThroughputLimitByDatabase(); setThroughputSpendAck(false); }, [container.flight]); useEffect(() => { const addDatabasePaneOpenMessage = { subscriptionType: SubscriptionType[userContext.subscriptionType], subscriptionQuotaId: userContext.quotaId, defaultsCheck: { throughput: throughput, flight: container.flight(), }, dataExplorerArea: Constants.Areas.ContextualPane, }; TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage); }, []); const submit = () => { if (!_isValid()) { return; } const offerThroughput: number = _computeOfferThroughput(); const addDatabasePaneStartMessage = { database: { id: databaseId, shared: databaseCreateNewShared, }, offerThroughput, subscriptionType: SubscriptionType[userContext.subscriptionType], subscriptionQuotaId: userContext.quotaId, defaultsCheck: { flight: container.flight(), }, dataExplorerArea: Constants.Areas.ContextualPane, }; const startKey: number = TelemetryProcessor.traceStart(Action.CreateDatabase, addDatabasePaneStartMessage); setFormErrors(""); setIsExecuting(true); const createDatabaseParams: DataModels.CreateDatabaseParams = { databaseId: addDatabasePaneStartMessage.database.id, databaseLevelThroughput: addDatabasePaneStartMessage.database.shared, }; if (isAutoPilotSelected) { createDatabaseParams.autoPilotMaxThroughput = maxAutoPilotThroughputSet; } else { createDatabaseParams.offerThroughput = addDatabasePaneStartMessage.offerThroughput; } createDatabase(createDatabaseParams).then( () => { _onCreateDatabaseSuccess(offerThroughput, startKey); }, (error: string) => { _onCreateDatabaseFailure(error, offerThroughput, startKey); } ); }; const _onCreateDatabaseSuccess = (offerThroughput: number, startKey: number): void => { setIsExecuting(false); closePanel(); container.refreshAllDatabases(); const addDatabasePaneSuccessMessage = { database: { id: databaseId, shared: databaseCreateNewShared, }, offerThroughput: offerThroughput, subscriptionType: SubscriptionType[userContext.subscriptionType], subscriptionQuotaId: userContext.quotaId, defaultsCheck: { flight: container.flight(), }, dataExplorerArea: Constants.Areas.ContextualPane, }; TelemetryProcessor.traceSuccess(Action.CreateDatabase, addDatabasePaneSuccessMessage, startKey); }; const _onCreateDatabaseFailure = (error: string, offerThroughput: number, startKey: number): void => { setIsExecuting(false); const errorMessage = getErrorMessage(error); setFormErrors(errorMessage); setFormErrorsDetails(errorMessage); const addDatabasePaneFailedMessage = { database: { id: databaseId, shared: databaseCreateNewShared, }, offerThroughput: offerThroughput, subscriptionType: SubscriptionType[userContext.subscriptionType], subscriptionQuotaId: userContext.quotaId, defaultsCheck: { flight: container.flight(), }, dataExplorerArea: Constants.Areas.ContextualPane, error: errorMessage, errorStack: getErrorStack(error), }; TelemetryProcessor.traceFailure(Action.CreateDatabase, addDatabasePaneFailedMessage, startKey); }; const _getThroughput = (): number => { return isNaN(throughput) ? 0 : Number(throughput); }; const _computeOfferThroughput = (): number => { if (!canConfigureThroughput) { return undefined; } if (isAutoPilotSelected) { return undefined; } return _getThroughput(); }; const _isValid = (): boolean => { // TODO add feature flag that disables validation for customers with custom accounts if (isAutoPilotSelected) { const autoPilot = _isAutoPilotSelectedAndWhatTier(); if ( !autoPilot || !autoPilot.maxThroughput || !AutoPilotUtils.isValidAutoPilotThroughput(autoPilot.maxThroughput) ) { setFormErrors( `Please enter a value greater than ${AutoPilotUtils.minAutoPilotThroughput} for autopilot throughput` ); return false; } } const throughput = _getThroughput(); if (throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && !throughputSpendAck) { setFormErrors(`Please acknowledge the estimated daily spend.`); return false; } const autoscaleThroughput = maxAutoPilotThroughputSet * 1; if ( isAutoPilotSelected && autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && !throughputSpendAck ) { setFormErrors(`Please acknowledge the estimated monthly spend.`); return false; } return true; }; const handleonChangeDBId = React.useCallback( (event: React.FormEvent, newValue?: string) => { setDatabaseId(newValue || ""); }, [] ); const genericPaneProps: GenericRightPaneProps = { container, formError: formErrors, formErrorDetail: formErrorsDetails, id: "copynotebookpane", isExecuting, title, submitButtonText: "OK", onClose: closePanel, onSubmit: submit, }; return (
{showUpsellMessage && formErrors === "" && ( )}

* {databaseIdLabel} {databaseIdTooltipText}

setDatabaseCreateNewShared(!databaseCreateNewShared)} />{" "} {databaseLevelThroughputTooltipText}
{databaseCreateNewShared && (
setThroughput(throughput)} setIsAutoscale={(isAutoscale: boolean) => setIsAutoPilotSelected(isAutoscale)} onCostAcknowledgeChange={(isAcknowledged: boolean) => setThroughputSpendAck(isAcknowledged)} /> {canRequestSupport && (

Contact support{" "} for more than {maxThroughputRUText} RU/s.

)}
)}
); };