Compare commits

..

4 Commits

Author SHA1 Message Date
Sindhu Balasubramanian
a6dbfc70aa Add console log 2025-09-16 09:15:23 -07:00
Sindhu Balasubramanian
9cf3c7902e Add flags for PPAF 2025-09-15 11:33:41 -07:00
Dmitry Shilov
922ca5c523 chore: Update help link in FabricHome component to point to the new documentation (#2206) 2025-09-04 11:58:07 +02:00
Dmitry Shilov
bafe002fa3 chore: Enhance accessibility (#2208)
- Add tabIndex to button
- Add aria attributes to icons and headings
2025-09-04 11:39:11 +02:00
7 changed files with 31 additions and 110 deletions

View File

@@ -124,10 +124,13 @@ export const endpoint = () => {
const location = _global.parent ? _global.parent.location : _global.location;
return configContext.EMULATOR_ENDPOINT || location.origin;
}
return (
userContext.selectedRegionalEndpoint ||
const endpoint: string = userContext.selectedRegionalEndpoint ||
userContext.endpoint ||
userContext?.databaseAccount?.properties?.documentEndpoint
userContext?.databaseAccount?.properties?.documentEndpoint;
console.log("endpoint value", endpoint);
return (
endpoint
);
};
@@ -207,6 +210,8 @@ export function client(): Cosmos.CosmosClient {
defaultHeaders: _defaultHeaders,
connectionPolicy: {
enableEndpointDiscovery: !userContext.selectedRegionalEndpoint,
enablePartitionLevelFailover: true,
enablePartitionLevelCircuitBreaker: true,
retryOptions: {
maxRetryAttemptCount: LocalStorageUtility.getEntryNumber(StorageKey.RetryAttempts),
fixedRetryIntervalInMilliseconds: LocalStorageUtility.getEntryNumber(StorageKey.RetryInterval),

View File

@@ -1,8 +1,8 @@
/**
* Accordion top class
*/
import { makeStyles, tokens } from "@fluentui/react-components";
import { DocumentAddRegular, LinkMultipleRegular } from "@fluentui/react-icons";
import { Link, makeStyles, tokens } from "@fluentui/react-components";
import { DocumentAddRegular, LinkMultipleRegular, OpenRegular } from "@fluentui/react-icons";
import { SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
import { isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
@@ -119,7 +119,7 @@ const FabricHomeScreenButton: React.FC<FabricHomeScreenButtonProps & { className
}) => {
const styles = useStyles();
return (
<div role="button" className={`${styles.buttonContainer} ${className}`} onClick={onClick}>
<div role="button" className={`${styles.buttonContainer} ${className}`} onClick={onClick} tabIndex={0}>
<div className={styles.buttonUpperPart}>{icon}</div>
<div aria-label={title} className={styles.buttonLowerPart}>
<div>{title}</div>
@@ -147,7 +147,7 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
{
title: "Sample data",
description: "Automatically load sample data in your database",
icon: <img src={CosmosDbBlackIcon} />,
icon: <img src={CosmosDbBlackIcon} alt={"Azure Cosmos DB icon"} aria-hidden="true" />,
onClick: () => setOpenSampleDataImportDialog(true),
},
{
@@ -181,16 +181,18 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
explorer={props.explorer}
databaseName={userContext.fabricContext?.databaseName}
/>
<div className={styles.title} role="heading" aria-label={title}>
<div className={styles.title} role="heading" aria-label={title} aria-level={1}>
{title}
</div>
{getSplashScreenButtons()}
{/* <div className={styles.footer}>
Need help?{" "}
<Link href="https://aka.ms/cosmosdbfabricdocs" target="_blank">
Learn more <img src={LinkIcon} alt="Learn more" />
</Link>
</div> */}
{
<div className={styles.footer}>
Need help?{" "}
<Link href="https://learn.microsoft.com/fabric/database/cosmos-db/overview" target="_blank">
Learn more <OpenRegular />
</Link>
</div>
}
</CosmosFluentProvider>
</>
);

View File

@@ -1,13 +0,0 @@
export const CLOUDSHELL_IP_RECOMMENDATIONS = {
centralindia: ["4.247.135.109", "74.225.207.63"],
southeastasia: ["4.194.56.6", "4.194.213.10", "4.194.144.127", "4.194.5.74"],
centraluseuap: ["52.158.186.182", "172.215.26.246", "134.138.154.177", "134.138.129.52", "172.215.31.177"],
eastus2euap: ["135.18.43.51", "20.252.175.33", "40.89.88.111", "135.18.17.187", "135.18.67.251"],
eastus: ["40.71.199.151", "20.42.18.188", "52.190.17.9", "20.120.96.152"],
northeurope: ["74.234.65.146", "52.169.70.113"],
southcentralus: ["4.151.247.81", "20.225.211.35", "4.151.48.133", "4.151.247.225"],
westeurope: ["52.166.126.216", "108.142.162.20", "52.178.13.125", "172.201.33.160"],
westus: ["20.245.161.131", "57.154.182.51", "40.118.133.244", "20.253.192.12", "20.43.245.209", "20.66.22.66"],
usgovarizona: ["62.10.232.179"],
usgovvirginia: ["62.10.26.85"],
};

View File

@@ -10,8 +10,6 @@ const validCloudShellRegions = new Set([
"westcentralus",
"usgovvirginia",
"usgovarizona",
"centraluseuap",
"eastus2euap",
]);
/**
@@ -43,8 +41,8 @@ export const getNormalizedRegion = (region: string, defaultCloudshellRegion: str
}
const regionMap: Record<string, string> = {
centralus: "centraluseuap",
eastus2: "eastus2euap",
centralus: "westcentralus",
eastus2: "eastus",
};
const normalizedRegion = regionMap[region.toLowerCase()] || region;

View File

@@ -1,40 +0,0 @@
import { configContext } from "ConfigContext";
import * as DataModels from "Contracts/DataModels";
import { userContext } from "UserContext";
import { armRequest } from "Utils/arm/request";
import { CLOUDSHELL_IP_RECOMMENDATIONS } from "../CloudShellTab/Utils/CloudShellIPUtils";
import { getNormalizedRegion } from "../CloudShellTab/Utils/RegionUtils";
export async function checkCloudShellIPsConfigured() {
const databaseRegion = userContext.databaseAccount?.location;
console.log("db region", databaseRegion);
const normalizedRegion = getNormalizedRegion(databaseRegion, "westus");
const cloudShellIPs = getCloudShellIPsForRegion(normalizedRegion);
console.log("CloudShell IPs for region", normalizedRegion, cloudShellIPs);
if (!cloudShellIPs || cloudShellIPs.length === 0) {
return false;
}
const firewallRules = await getFirewallRules();
console.log("firewall rules", firewallRules);
return false;
}
function getCloudShellIPsForRegion(region: string): string[] {
const regionKey = region.toLowerCase();
const ips = CLOUDSHELL_IP_RECOMMENDATIONS[regionKey as keyof typeof CLOUDSHELL_IP_RECOMMENDATIONS];
return ips ? [...ips] : [];
}
async function getFirewallRules(): Promise<DataModels.FirewallRule[]> {
const firewallRulesUri = `${userContext.databaseAccount.id}/firewallRules`;
const response: any = await armRequest({
host: configContext.ARM_ENDPOINT,
path: firewallRulesUri,
method: "GET",
apiVersion: "2023-03-01-preview",
});
return response?.data?.value || response?.value || [];
}

View File

@@ -22,19 +22,9 @@ export abstract class BaseTerminalComponentAdapter implements ReactAdapter {
protected getUsername: () => string,
protected isAllPublicIPAddressesEnabled: ko.Observable<boolean>,
protected kind: ViewModels.TerminalKind,
protected isCloudShellIPsConfigured?: ko.Observable<boolean>,
) { }
) {}
public renderComponent(): JSX.Element {
if (this.kind === ViewModels.TerminalKind.VCoreMongo && this.isCloudShellIPsConfigured && !this.isCloudShellIPsConfigured()) {
return (
<QuickstartFirewallNotification
messageType={this.getMessageType()}
screenshot={VcoreFirewallRuleScreenshot}
shellName={getShellNameForDisplay(this.kind)}
/>
);
}
if (!this.isAllPublicIPAddressesEnabled()) {
return (
<QuickstartFirewallNotification

View File

@@ -8,7 +8,6 @@ import { userContext } from "../../UserContext";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../Explorer";
import { useNotebook } from "../Notebook/useNotebook";
import { checkCloudShellIPsConfigured } from "./Shared/CloudShellIPChecker";
import { NotebookTerminalComponentAdapter } from "./ShellAdapters/NotebookTerminalComponentAdapter";
import TabsBase from "./TabsBase";
@@ -24,13 +23,11 @@ export default class TerminalTab extends TabsBase {
private container: Explorer;
private notebookTerminalComponentAdapter: ReactAdapter;
private isAllPublicIPAddressesEnabled: ko.Observable<boolean>;
private isCloudShellIPsConfigured: ko.Observable<boolean>;
constructor(options: TerminalTabOptions) {
super(options);
this.container = options.container;
this.isAllPublicIPAddressesEnabled = ko.observable(true);
this.isCloudShellIPsConfigured = ko.observable(true);
const commonArgs: [
() => DataModels.DatabaseAccount,
@@ -39,37 +36,19 @@ export default class TerminalTab extends TabsBase {
ko.Observable<boolean>,
ViewModels.TerminalKind,
] = [
() => userContext?.databaseAccount,
() => this.tabId,
() => this.getUsername(),
this.isAllPublicIPAddressesEnabled,
options.kind,
];
() => userContext?.databaseAccount,
() => this.tabId,
() => this.getUsername(),
this.isAllPublicIPAddressesEnabled,
options.kind,
];
if (userContext.features.enableCloudShell) {
this.notebookTerminalComponentAdapter = new CloudShellTerminalComponentAdapter(
() => userContext?.databaseAccount,
() => this.tabId,
() => this.getUsername(),
this.isAllPublicIPAddressesEnabled,
options.kind,
options.kind === ViewModels.TerminalKind.VCoreMongo ? this.isCloudShellIPsConfigured : undefined,
);
this.notebookTerminalComponentAdapter = new CloudShellTerminalComponentAdapter(...commonArgs);
this.notebookTerminalComponentAdapter.parameters = ko.computed<boolean>(() => {
if (options.kind === ViewModels.TerminalKind.VCoreMongo) {
const cloudShellConfigured = this.isCloudShellIPsConfigured();
return this.isTemplateReady() && cloudShellConfigured;
}
return this.isTemplateReady() && this.isAllPublicIPAddressesEnabled();
});
if (options.kind === ViewModels.TerminalKind.VCoreMongo) {
(async () => {
const result = await checkCloudShellIPsConfigured();
this.isCloudShellIPsConfigured(result);
})();
}
} else {
this.notebookTerminalComponentAdapter = new NotebookTerminalComponentAdapter(
() => this.getNotebookServerInfo(options),