mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-31 23:02:29 +00:00
Compare commits
5 Commits
users/nish
...
before_opt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bd905403b | ||
|
|
bd592d07af | ||
|
|
644f5941ec | ||
|
|
9fb006a996 | ||
|
|
c2b98c3e23 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
- run: npm ci
|
||||
- run: npm run build:contracts
|
||||
- name: Restore Build Cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .cache
|
||||
key: ${{ runner.os }}-build-cache
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
UI for Azure Cosmos DB. Powers the [Azure Portal](https://portal.azure.com/), https://cosmos.azure.com/, and the [Cosmos DB Emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator)
|
||||
|
||||
|
||||

|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"EmulatorMasterKey": "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
|
||||
"Account": {
|
||||
"name": "",
|
||||
"id": "",
|
||||
"location": "",
|
||||
"type": "",
|
||||
"kind": "DocumentDB",
|
||||
"properties": {
|
||||
"documentEndpoint": "",
|
||||
"tableEndpoint": "",
|
||||
"gremlinEndpoint": "",
|
||||
"cassandraEndpoint": "",
|
||||
"capabilities": [
|
||||
{
|
||||
"name": "EnableNoSQLVectorSearch",
|
||||
"description": "Enable Vector Search on NoSQL account"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,12 @@ export enum CapacityMode {
|
||||
Serverless = "Serverless",
|
||||
}
|
||||
|
||||
export enum WorkloadType {
|
||||
Learning = "Learning",
|
||||
DevelopmentTesting = "Development/Testing",
|
||||
Production = "Production",
|
||||
None = "None",
|
||||
}
|
||||
// flight names returned from the portal are always lowercase
|
||||
export class Flights {
|
||||
public static readonly SettingsV2 = "settingsv2";
|
||||
@@ -119,6 +125,7 @@ export class AfecFeatures {
|
||||
|
||||
export class TagNames {
|
||||
public static defaultExperience: string = "defaultExperience";
|
||||
public static WorkloadType: string = "hidden-workload-type";
|
||||
}
|
||||
|
||||
export class MongoDBAccounts {
|
||||
@@ -446,6 +453,10 @@ export enum PoolIdType {
|
||||
QueryCopilot = "query-copilot",
|
||||
}
|
||||
|
||||
export const EmulatorMasterKey =
|
||||
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
||||
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
||||
|
||||
export class Notebook {
|
||||
public static readonly defaultBasePath = "./notebooks";
|
||||
public static readonly heartbeatDelayMs = 60000;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as Cosmos from "@azure/cosmos";
|
||||
import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizationTokenUsingResourceTokens";
|
||||
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
||||
import { EmulatorMasterKey } from "Platform/Emulator/emulatorAccount";
|
||||
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import { useNewPortalBackendEndpoint } from "Utils/EndpointUtils";
|
||||
@@ -12,7 +11,7 @@ import { Platform, configContext } from "../ConfigContext";
|
||||
import { updateUserContext, userContext } from "../UserContext";
|
||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
|
||||
import { HttpHeaders } from "./Constants";
|
||||
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
||||
import { getErrorMessage } from "./ErrorHandlingUtils";
|
||||
|
||||
const _global = typeof self === "undefined" ? window : self;
|
||||
|
||||
34
src/Common/DatabaseAccountUtility.test.ts
Normal file
34
src/Common/DatabaseAccountUtility.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { WorkloadType } from "Common/Constants";
|
||||
import { getWorkloadType } from "Common/DatabaseAccountUtility";
|
||||
import { DatabaseAccount, Tags } from "Contracts/DataModels";
|
||||
import { updateUserContext } from "UserContext";
|
||||
|
||||
describe("Database Account Utility", () => {
|
||||
describe("Workload Type", () => {
|
||||
beforeEach(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
tags: {} as Tags,
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
});
|
||||
|
||||
it("Workload Type should return Learning", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
tags: {
|
||||
"hidden-workload-type": WorkloadType.Learning,
|
||||
} as Tags,
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
const workloadType: WorkloadType = getWorkloadType();
|
||||
expect(workloadType).toBe(WorkloadType.Learning);
|
||||
});
|
||||
|
||||
it("Workload Type should return None", () => {
|
||||
const workloadType: WorkloadType = getWorkloadType();
|
||||
expect(workloadType).toBe(WorkloadType.None);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,5 @@
|
||||
import { TagNames, WorkloadType } from "Common/Constants";
|
||||
import { Tags } from "Contracts/DataModels";
|
||||
import { userContext } from "../UserContext";
|
||||
|
||||
function isVirtualNetworkFilterEnabled() {
|
||||
@@ -15,3 +17,12 @@ function isPrivateEndpointConnectionsEnabled() {
|
||||
export function isPublicInternetAccessAllowed(): boolean {
|
||||
return !isVirtualNetworkFilterEnabled() && !isIpRulesEnabled() && !isPrivateEndpointConnectionsEnabled();
|
||||
}
|
||||
|
||||
export function getWorkloadType(): WorkloadType {
|
||||
const tags: Tags = userContext?.databaseAccount?.tags;
|
||||
const workloadType: WorkloadType = tags && (tags[TagNames.WorkloadType] as WorkloadType);
|
||||
if (!workloadType) {
|
||||
return WorkloadType.None;
|
||||
}
|
||||
return workloadType;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface ArmEntity {
|
||||
location: string;
|
||||
type: string;
|
||||
kind: string;
|
||||
tags?: Tags;
|
||||
}
|
||||
|
||||
export interface DatabaseAccount extends ArmEntity {
|
||||
@@ -663,3 +664,5 @@ export interface FeatureRegistration {
|
||||
state: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type Tags = { [key: string]: string };
|
||||
|
||||
@@ -41,6 +41,7 @@ export enum MessageTypes {
|
||||
OpenPostgreSQLPasswordReset,
|
||||
OpenPostgresNetworkingBlade,
|
||||
OpenCosmosDBNetworkingBlade,
|
||||
DisplayNPSSurvey, // unused
|
||||
OpenVCoreMongoNetworkingBlade,
|
||||
OpenVCoreMongoConnectionStringsBlade,
|
||||
GetAuthorizationToken, // unused. Can be removed if the portal uses the same list of enums.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Checkbox, DirectionalHint, Link, Stack, Text, TextField, TooltipHost } from "@fluentui/react";
|
||||
import { getWorkloadType } from "Common/DatabaseAccountUtility";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
@@ -34,10 +35,15 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
setIsThroughputCapExceeded,
|
||||
onCostAcknowledgeChange,
|
||||
}: ThroughputInputProps) => {
|
||||
const defaultThroughput: number =
|
||||
isFreeTier ||
|
||||
isQuickstart ||
|
||||
[Constants.WorkloadType.Learning, Constants.WorkloadType.DevelopmentTesting].includes(getWorkloadType())
|
||||
? AutoPilotUtils.autoPilotThroughput1K
|
||||
: AutoPilotUtils.autoPilotThroughput4K;
|
||||
|
||||
const [isAutoscaleSelected, setIsAutoScaleSelected] = useState<boolean>(true);
|
||||
const [throughput, setThroughput] = useState<number>(
|
||||
isFreeTier || isQuickstart ? AutoPilotUtils.autoPilotThroughput1K : AutoPilotUtils.autoPilotThroughput4K,
|
||||
);
|
||||
const [throughput, setThroughput] = useState<number>(defaultThroughput);
|
||||
const [isCostAcknowledged, setIsCostAcknowledged] = useState<boolean>(false);
|
||||
const [throughputError, setThroughputError] = useState<string>("");
|
||||
const [totalThroughputUsed, setTotalThroughputUsed] = useState<number>(0);
|
||||
@@ -47,7 +53,6 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
|
||||
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
|
||||
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
|
||||
|
||||
useEffect(() => {
|
||||
// throughput cap check for the initial state
|
||||
let totalThroughput = 0;
|
||||
@@ -157,9 +162,6 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
|
||||
const handleOnChangeMode = (event: React.ChangeEvent<HTMLInputElement>, mode: string): void => {
|
||||
if (mode === "Autoscale") {
|
||||
const defaultThroughput = isFreeTier
|
||||
? AutoPilotUtils.autoPilotThroughput1K
|
||||
: AutoPilotUtils.autoPilotThroughput4K;
|
||||
setThroughput(defaultThroughput);
|
||||
setIsAutoScaleSelected(true);
|
||||
setThroughputValue(defaultThroughput);
|
||||
|
||||
@@ -26,7 +26,7 @@ import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils";
|
||||
import { Allotment, AllotmentHandle } from "allotment";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { debounce } from "lodash";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
const useSidebarStyles = makeStyles({
|
||||
sidebar: {
|
||||
@@ -109,6 +109,7 @@ interface GlobalCommand {
|
||||
icon: JSX.Element;
|
||||
onClick: () => void;
|
||||
keyboardAction?: KeyboardAction;
|
||||
ref?: React.RefObject<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
|
||||
@@ -118,6 +119,7 @@ const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
|
||||
// However, that messes with the Menu positioning, so we need to get a reference to the 'div' to pass to the Menu.
|
||||
// We can't use a ref though, because it would be set after the Menu is rendered, so we use a state value to force a re-render.
|
||||
const [globalCommandButton, setGlobalCommandButton] = useState<HTMLElement | null>(null);
|
||||
const primaryFocusableRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const actions = useMemo<GlobalCommand[]>(() => {
|
||||
if (
|
||||
@@ -177,6 +179,16 @@ const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
|
||||
);
|
||||
}, [actions, setKeyboardActions]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (primaryFocusableRef.current) {
|
||||
const timer = setTimeout(() => {
|
||||
primaryFocusableRef.current.focus();
|
||||
}, 0);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
return undefined;
|
||||
}, []);
|
||||
|
||||
if (!primaryAction) {
|
||||
return null;
|
||||
}
|
||||
@@ -184,7 +196,7 @@ const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
|
||||
return (
|
||||
<div className={styles.globalCommandsContainer} data-test="GlobalCommands">
|
||||
{actions.length === 1 ? (
|
||||
<Button icon={primaryAction.icon} onClick={onPrimaryActionClick}>
|
||||
<Button icon={primaryAction.icon} onClick={onPrimaryActionClick} ref={primaryFocusableRef}>
|
||||
{primaryAction.label}
|
||||
</Button>
|
||||
) : (
|
||||
@@ -194,7 +206,7 @@ const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
|
||||
<div ref={setGlobalCommandButton}>
|
||||
<SplitButton
|
||||
menuButton={{ ...triggerProps, "aria-label": "More commands" }}
|
||||
primaryActionButton={{ onClick: onPrimaryActionClick }}
|
||||
primaryActionButton={{ onClick: onPrimaryActionClick, ref: primaryFocusableRef }}
|
||||
className={styles.globalCommandsSplitButton}
|
||||
icon={primaryAction.icon}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
import emulatorConfig from "../../../configs/emulator.json";
|
||||
import { AccountKind } from "../../Common/Constants";
|
||||
|
||||
export const EmulatorMasterKey = emulatorConfig.EmulatorMasterKey || "";
|
||||
export const emulatorAccount = emulatorConfig.Account || {};
|
||||
export const emulatorAccount = {
|
||||
name: "",
|
||||
id: "",
|
||||
location: "",
|
||||
type: "",
|
||||
kind: AccountKind.DocumentDB,
|
||||
properties: {
|
||||
documentEndpoint: "",
|
||||
tableEndpoint: "",
|
||||
gremlinEndpoint: "",
|
||||
cassandraEndpoint: "",
|
||||
capabilities: [
|
||||
{
|
||||
name: "EnableNoSQLVectorSearch",
|
||||
description: "Enable Vector Search on NoSQL account",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const msRestNodeAuth = require("@azure/ms-rest-nodeauth");
|
||||
const { AzureCliCredential } = require("@azure/identity");
|
||||
const { CosmosDBManagementClient } = require("@azure/arm-cosmosdb");
|
||||
const ms = require("ms");
|
||||
|
||||
@@ -16,7 +16,7 @@ function friendlyTime(date) {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const credentials = await msRestNodeAuth.AzureCliCredentials.create();
|
||||
const credentials = new AzureCliCredential();
|
||||
const client = new CosmosDBManagementClient(credentials, subscriptionId);
|
||||
const accounts = await client.databaseAccounts.list(resourceGroupName);
|
||||
for (const account of accounts) {
|
||||
|
||||
Reference in New Issue
Block a user