import { Checkbox, Text, TextField } from "@fluentui/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/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 { PanelInfoErrorComponent } from "../PanelInfoErrorComponent"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; export interface AddDatabasePaneProps { explorer: Explorer; closePanel: () => void; openNotificationConsole: () => void; } export const AddDatabasePanel: FunctionComponent = ({ explorer: container, closePanel, openNotificationConsole, }: AddDatabasePaneProps) => { const { subscriptionType } = userContext; const getSharedThroughputDefault = !(subscriptionType === SubscriptionType.EA || container.isServerlessEnabled()); 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 [isAutoPilotSelected, setIsAutoPilotSelected] = useState(container.isAutoscaleDefaultEnabled()); const throughputDefaults = container.collectionCreationDefaults.throughput; const [throughput, setThroughput] = useState( isAutoPilotSelected ? AutoPilotUtils.minAutoPilotThroughput : throughputDefaults.shared ); 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(), false ); const upsellAnchorUrl: string = isFreeTierAccount ? Constants.Urls.freeTierInformation : Constants.Urls.cosmosPricing; const upsellAnchorText: string = isFreeTierAccount ? "Learn more" : "More details"; const maxAutoPilotThroughputSet = 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); useEffect(() => { setDatabaseCreateNewShared(getSharedThroughputDefault); }, [subscriptionType]); const addDatabasePaneMessage = { database: { id: databaseId, shared: databaseCreateNewShared, }, subscriptionType: SubscriptionType[subscriptionType], subscriptionQuotaId: userContext.quotaId, defaultsCheck: { flight: userContext.addCollectionFlight, }, dataExplorerArea: Constants.Areas.ContextualPane, }; useEffect(() => { const addDatabasePaneOpenMessage = { subscriptionType: SubscriptionType[subscriptionType], subscriptionQuotaId: userContext.quotaId, defaultsCheck: { throughput: throughput, flight: userContext.addCollectionFlight, }, dataExplorerArea: Constants.Areas.ContextualPane, }; TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage); }, []); const submit = () => { if (!_isValid()) { return; } const offerThroughput: number = _computeOfferThroughput(); const addDatabasePaneStartMessage = { ...addDatabasePaneMessage, offerThroughput, }; 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 = addDatabasePaneStartMessage.offerThroughput; } 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 = { ...addDatabasePaneMessage, offerThroughput, }; 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 = { ...addDatabasePaneMessage, offerThroughput, 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; } 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: RightPaneFormProps = { expandConsole: container.expandConsole, formError: formErrors, formErrorDetail: formErrorsDetails, id: "add-database-inputs", 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 {throughputDefaults.unlimitedmax?.toLocaleString()} RU/s.

)}
)}
); };