import { ChoiceGroup, IChoiceGroupOption, Label, Link, MessageBar, Stack, Text, TextField } from "@fluentui/react"; import * as React from "react"; import * as ViewModels from "../../../../Contracts/ViewModels"; import { userContext } from "../../../../UserContext"; import { Int32 } from "../../../Panes/Tables/Validators/EntityPropertyValidationCommon"; import { changeFeedPolicyToolTip, getChoiceGroupStyles, getTextFieldStyles, messageBarStyles, subComponentStackProps, titleAndInputStackProps, ttlWarning, } from "../SettingsRenderUtils"; import { ChangeFeedPolicyState, GeospatialConfigType, IsComponentDirtyResult, TtlOff, TtlOn, TtlOnNoDefault, TtlType, getSanitizedInputValue, isDirty, } from "../SettingsUtils"; import { ToolTipLabelComponent } from "./ToolTipLabelComponent"; export interface SubSettingsComponentProps { collection: ViewModels.Collection; timeToLive: TtlType; timeToLiveBaseline: TtlType; onTtlChange: (newTtl: TtlType) => void; timeToLiveSeconds: number; timeToLiveSecondsBaseline: number; onTimeToLiveSecondsChange: (newTimeToLiveSeconds: number) => void; displayedTtlSeconds: string; onDisplayedTtlSecondsChange: (newDisplayedTtlSeconds: string) => void; geospatialConfigType: GeospatialConfigType; geospatialConfigTypeBaseline: GeospatialConfigType; onGeoSpatialConfigTypeChange: (newGeoSpatialConfigType: GeospatialConfigType) => void; isAnalyticalStorageEnabled: boolean; analyticalStorageTtlSelection: TtlType; analyticalStorageTtlSelectionBaseline: TtlType; onAnalyticalStorageTtlSelectionChange: (newAnalyticalStorageTtlSelection: TtlType) => void; analyticalStorageTtlSeconds: number; analyticalStorageTtlSecondsBaseline: number; onAnalyticalStorageTtlSecondsChange: (newAnalyticalStorageTtlSeconds: number) => void; changeFeedPolicyVisible: boolean; changeFeedPolicy: ChangeFeedPolicyState; changeFeedPolicyBaseline: ChangeFeedPolicyState; onChangeFeedPolicyChange: (newChangeFeedPolicyState: ChangeFeedPolicyState) => void; onSubSettingsSaveableChange: (isSubSettingsSaveable: boolean) => void; onSubSettingsDiscardableChange: (isSubSettingsDiscardable: boolean) => void; } export class SubSettingsComponent extends React.Component { private shouldCheckComponentIsDirty = true; private geospatialVisible: boolean; private partitionKeyValue: string; private partitionKeyName: string; constructor(props: SubSettingsComponentProps) { super(props); this.geospatialVisible = userContext.apiType === "SQL"; this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key"; this.partitionKeyValue = this.getPartitionKeyValue(); } componentDidMount(): void { this.onComponentUpdate(); } componentDidUpdate(prevProps: SubSettingsComponentProps): void { if ( (prevProps.timeToLive === TtlType.Off || prevProps.timeToLive === TtlType.OnNoDefault) && this.props.timeToLive === TtlType.On && this.props.timeToLiveBaseline !== TtlType.On ) { this.props.onDisplayedTtlSecondsChange(""); } this.onComponentUpdate(); } private onComponentUpdate = (): void => { if (!this.shouldCheckComponentIsDirty) { this.shouldCheckComponentIsDirty = true; return; } const isComponentDirtyResult = this.IsComponentDirty(); this.props.onSubSettingsSaveableChange(isComponentDirtyResult.isSaveable); this.props.onSubSettingsDiscardableChange(isComponentDirtyResult.isDiscardable); this.shouldCheckComponentIsDirty = false; }; public IsComponentDirty = (): IsComponentDirtyResult => { if ( (this.props.timeToLive === TtlType.On && !this.props.timeToLiveSeconds) || (this.props.analyticalStorageTtlSelection === TtlType.On && !this.props.analyticalStorageTtlSeconds) || (this.props.timeToLive === TtlType.On && this.props.displayedTtlSeconds === "") ) { return { isSaveable: false, isDiscardable: true }; } else if ( isDirty(this.props.timeToLive, this.props.timeToLiveBaseline) || (this.props.timeToLive === TtlType.On && isDirty(this.props.timeToLiveSeconds, this.props.timeToLiveSecondsBaseline)) || isDirty(this.props.analyticalStorageTtlSelection, this.props.analyticalStorageTtlSelectionBaseline) || (this.props.analyticalStorageTtlSelection === TtlType.On && isDirty(this.props.analyticalStorageTtlSeconds, this.props.analyticalStorageTtlSecondsBaseline)) || isDirty(this.props.geospatialConfigType, this.props.geospatialConfigTypeBaseline) || isDirty(this.props.changeFeedPolicy, this.props.changeFeedPolicyBaseline) ) { return { isSaveable: true, isDiscardable: true }; } return { isSaveable: false, isDiscardable: false }; }; private ttlChoiceGroupOptions: IChoiceGroupOption[] = [ { key: TtlType.Off, text: "Off" }, { key: TtlType.OnNoDefault, text: "On (no default)" }, { key: TtlType.On, text: "On" }, ]; public getTtlValue = (value: string): TtlType => { switch (value) { case TtlOn: return TtlType.On; case TtlOff: return TtlType.Off; case TtlOnNoDefault: return TtlType.OnNoDefault; } return undefined; }; private onTtlChange = (ev?: React.FormEvent, option?: IChoiceGroupOption): void => this.props.onTtlChange(this.getTtlValue(option.key)); private onTimeToLiveSecondsChange = ( event: React.FormEvent, newValue?: string, ): void => { const newTimeToLiveSeconds = getSanitizedInputValue(newValue, Int32.Max); this.props.onDisplayedTtlSecondsChange(newTimeToLiveSeconds.toString()); this.props.onTimeToLiveSecondsChange(newTimeToLiveSeconds); }; private onGeoSpatialConfigTypeChange = ( ev?: React.FormEvent, option?: IChoiceGroupOption, ): void => this.props.onGeoSpatialConfigTypeChange(GeospatialConfigType[option.key as keyof typeof GeospatialConfigType]); private onAnalyticalStorageTtlSelectionChange = ( ev?: React.FormEvent, option?: IChoiceGroupOption, ): void => this.props.onAnalyticalStorageTtlSelectionChange(this.getTtlValue(option.key)); private onAnalyticalStorageTtlSecondsChange = ( event: React.FormEvent, newValue?: string, ): void => { const newAnalyticalStorageTtlSeconds = getSanitizedInputValue(newValue, Int32.Max); this.props.onAnalyticalStorageTtlSecondsChange(newAnalyticalStorageTtlSeconds); }; private onChangeFeedPolicyChange = ( ev?: React.FormEvent, option?: IChoiceGroupOption, ): void => this.props.onChangeFeedPolicyChange(ChangeFeedPolicyState[option.key as keyof typeof ChangeFeedPolicyState]); private getTtlComponent = (): JSX.Element => userContext.apiType === "Mongo" ? ( To enable time-to-live (TTL) for your collection/documents, create a TTL index . ) : ( {isDirty(this.props.timeToLive, this.props.timeToLiveBaseline) && this.props.timeToLive === TtlType.On && ( {ttlWarning} )} {this.props.timeToLive === TtlType.On && ( )} ); private analyticalTtlChoiceGroupOptions: IChoiceGroupOption[] = [ { key: TtlType.Off, text: "Off", disabled: true }, { key: TtlType.OnNoDefault, text: "On (no default)" }, { key: TtlType.On, text: "On" }, ]; private getAnalyticalStorageTtlComponent = (): JSX.Element => ( {this.props.analyticalStorageTtlSelection === TtlType.On && ( )} ); private geoSpatialConfigTypeChoiceGroupOptions: IChoiceGroupOption[] = [ { key: GeospatialConfigType.Geography, text: "Geography" }, { key: GeospatialConfigType.Geometry, text: "Geometry" }, ]; private getGeoSpatialComponent = (): JSX.Element => ( ); private changeFeedChoiceGroupOptions: IChoiceGroupOption[] = [ { key: ChangeFeedPolicyState.Off, text: "Off" }, { key: ChangeFeedPolicyState.On, text: "On" }, ]; private getChangeFeedComponent = (): JSX.Element => { const labelId = "settingsV2ChangeFeedLabelId"; return ( ); }; private getPartitionKeyValue = (): string => { if (userContext.apiType === "Mongo") { return this.props.collection.partitionKeyProperties?.[0] || ""; } return (this.props.collection.partitionKeyProperties || []).map((property) => "/" + property).join(", "); }; private getPartitionKeyComponent = (): JSX.Element => ( {this.getPartitionKeyVisible() && ( )} {userContext.apiType === "SQL" && this.isLargePartitionKeyEnabled() && ( Large {this.partitionKeyName.toLowerCase()} has been enabled. )} {userContext.apiType === "SQL" && (this.isHierarchicalPartitionedContainer() ? ( Hierarchically partitioned container. ) : ( Non-hierarchically partitioned container. ))} ); public getPartitionKeyVisible = (): boolean => { if ( userContext.apiType === "Cassandra" || userContext.apiType === "Tables" || !this.props.collection.partitionKeyProperties || this.props.collection.partitionKeyProperties.length === 0 || (userContext.apiType === "Mongo" && this.props.collection.partitionKey.systemKey) ) { return false; } return true; }; public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2; public isHierarchicalPartitionedContainer = (): boolean => this.props.collection.partitionKey?.kind === "MultiHash"; public render(): JSX.Element { return ( {userContext.apiType !== "Cassandra" && this.getTtlComponent()} {this.geospatialVisible && this.getGeoSpatialComponent()} {this.props.isAnalyticalStorageEnabled && this.getAnalyticalStorageTtlComponent()} {this.props.changeFeedPolicyVisible && this.getChangeFeedComponent()} {this.getPartitionKeyComponent()} ); } }