mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-30 14:22:05 +00:00
Compare commits
1 Commits
user/matre
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdf1e6385b |
14
package-lock.json
generated
14
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@azure/arm-cosmosdb": "9.1.0",
|
||||
"@azure/cosmos": "3.16.2",
|
||||
"@azure/cosmos": "3.16.1",
|
||||
"@azure/cosmos-language-service": "0.0.5",
|
||||
"@azure/identity": "1.2.1",
|
||||
"@azure/ms-rest-nodeauth": "3.0.7",
|
||||
@@ -396,9 +396,9 @@
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||
},
|
||||
"node_modules/@azure/cosmos": {
|
||||
"version": "3.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.16.2.tgz",
|
||||
"integrity": "sha512-sceY5LWj0BHGj8PSyaVCfDRQLVZyoCfIY78kyIROJVEw0k+p9XFs8fhpykN8JklkCftL0WlaVY+X25SQwnhZsw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.16.1.tgz",
|
||||
"integrity": "sha512-9J76EMiF+ypZwO/Z7OS4PjrU/3hdRLe18dQh/Hj+jwXG2gKJ1NwgkYqcwu1HiCiX73A8+NCeA3PRDbegEnuuNQ==",
|
||||
"dependencies": {
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-rest-pipeline": "^1.2.0",
|
||||
@@ -31458,9 +31458,9 @@
|
||||
}
|
||||
},
|
||||
"@azure/cosmos": {
|
||||
"version": "3.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.16.2.tgz",
|
||||
"integrity": "sha512-sceY5LWj0BHGj8PSyaVCfDRQLVZyoCfIY78kyIROJVEw0k+p9XFs8fhpykN8JklkCftL0WlaVY+X25SQwnhZsw==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.16.1.tgz",
|
||||
"integrity": "sha512-9J76EMiF+ypZwO/Z7OS4PjrU/3hdRLe18dQh/Hj+jwXG2gKJ1NwgkYqcwu1HiCiX73A8+NCeA3PRDbegEnuuNQ==",
|
||||
"requires": {
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-rest-pipeline": "^1.2.0",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@azure/arm-cosmosdb": "9.1.0",
|
||||
"@azure/cosmos": "3.16.2",
|
||||
"@azure/cosmos": "3.16.1",
|
||||
"@azure/cosmos-language-service": "0.0.5",
|
||||
"@azure/identity": "1.2.1",
|
||||
"@azure/ms-rest-nodeauth": "3.0.7",
|
||||
|
||||
@@ -354,10 +354,6 @@ export enum ContainerStatusType {
|
||||
Disconnected = "Disconnected",
|
||||
}
|
||||
|
||||
export enum PoolIdType {
|
||||
DefaultPoolId = "default",
|
||||
}
|
||||
|
||||
export const EmulatorMasterKey =
|
||||
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
||||
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
||||
|
||||
@@ -24,7 +24,7 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
|
||||
);
|
||||
try {
|
||||
let collection: DataModels.Collection;
|
||||
if (userContext.authType === AuthType.AAD && !userContext.features.enableSDKoperations) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
if (params.createNewDatabase) {
|
||||
const createDatabaseParams: DataModels.CreateDatabaseParams = {
|
||||
autoPilotMaxThroughput: params.autoPilotMaxThroughput,
|
||||
|
||||
@@ -25,8 +25,7 @@ export async function createDatabase(params: DataModels.CreateDatabaseParams): P
|
||||
if (userContext.apiType === "Tables") {
|
||||
throw new Error("Creating database resources is not allowed for tables accounts");
|
||||
}
|
||||
const database: DataModels.Database = await (userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations
|
||||
const database: DataModels.Database = await (userContext.authType === AuthType.AAD && !userContext.useSDKOperations
|
||||
? createDatabaseWithARM(params)
|
||||
: createDatabaseWithSDK(params));
|
||||
|
||||
|
||||
@@ -20,11 +20,7 @@ export async function createStoredProcedure(
|
||||
): Promise<StoredProcedureDefinition & Resource> {
|
||||
const clearMessage = logConsoleProgress(`Creating stored procedure ${storedProcedure.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
try {
|
||||
const getResponse = await getSqlStoredProcedure(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -14,11 +14,7 @@ export async function createTrigger(
|
||||
): Promise<TriggerDefinition | SqlTriggerResource> {
|
||||
const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
try {
|
||||
const getResponse = await getSqlTrigger(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -20,11 +20,7 @@ export async function createUserDefinedFunction(
|
||||
): Promise<UserDefinedFunctionDefinition & Resource> {
|
||||
const clearMessage = logConsoleProgress(`Creating user defined function ${userDefinedFunction.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
try {
|
||||
const getResponse = await getSqlUserDefinedFunction(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { handleError } from "../ErrorHandlingUtils";
|
||||
export async function deleteCollection(databaseId: string, collectionId: string): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting container ${collectionId}`);
|
||||
try {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.features.enableSDKoperations) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
await deleteCollectionWithARM(databaseId, collectionId);
|
||||
} else {
|
||||
await client().database(databaseId).container(collectionId).delete();
|
||||
|
||||
@@ -12,7 +12,10 @@ export async function deleteDatabase(databaseId: string): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting database ${databaseId}`);
|
||||
|
||||
try {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.features.enableSDKoperations) {
|
||||
if (userContext.apiType === "Tables") {
|
||||
throw new Error("Deleting database resources is not allowed for tables accounts");
|
||||
}
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
await deleteDatabaseWithARM(databaseId);
|
||||
} else {
|
||||
await client().database(databaseId).delete();
|
||||
|
||||
@@ -12,11 +12,7 @@ export async function deleteStoredProcedure(
|
||||
): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting stored procedure ${storedProcedureId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
await deleteSqlStoredProcedure(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -8,11 +8,7 @@ import { handleError } from "../ErrorHandlingUtils";
|
||||
export async function deleteTrigger(databaseId: string, collectionId: string, triggerId: string): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting trigger ${triggerId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
await deleteSqlTrigger(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -8,11 +8,7 @@ import { handleError } from "../ErrorHandlingUtils";
|
||||
export async function deleteUserDefinedFunction(databaseId: string, collectionId: string, id: string): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting user defined function ${id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
await deleteSqlUserDefinedFunction(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -14,11 +14,7 @@ export const readCollectionOffer = async (params: ReadCollectionOfferParams): Pr
|
||||
const clearMessage = logConsoleProgress(`Querying offer for collection ${params.collectionId}`);
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType !== "Tables"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
return await readCollectionOfferWithARM(params.databaseId, params.collectionId);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,7 @@ import { handleError } from "../ErrorHandlingUtils";
|
||||
export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType !== "Tables"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
return await readCollectionsWithARM(databaseId);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,7 @@ export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promis
|
||||
const clearMessage = logConsoleProgress(`Querying offer for database ${params.databaseId}`);
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType !== "Tables"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
return await readDatabaseOfferWithARM(params.databaseId);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,7 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
|
||||
let databases: DataModels.Database[];
|
||||
const clearMessage = logConsoleProgress(`Querying databases`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType !== "Tables"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
databases = await readDatabasesWithARM();
|
||||
} else {
|
||||
const sdkResponse = await client().databases.readAll().fetchAll();
|
||||
|
||||
@@ -12,11 +12,7 @@ export async function readStoredProcedures(
|
||||
): Promise<(StoredProcedureDefinition & Resource)[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying stored procedures for container ${collectionId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
const rpResponse = await listSqlStoredProcedures(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -13,11 +13,7 @@ export async function readTriggers(
|
||||
): Promise<SqlTriggerResource[] | TriggerDefinition[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType === "SQL"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
|
||||
const rpResponse = await listSqlTriggers(
|
||||
userContext.subscriptionId,
|
||||
userContext.resourceGroup,
|
||||
|
||||
@@ -11,9 +11,9 @@ export async function readUserDefinedFunctions(
|
||||
collectionId: string
|
||||
): Promise<(UserDefinedFunctionDefinition & Resource)[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying user defined functions for container ${collectionId}`);
|
||||
const { authType, apiType, subscriptionId, resourceGroup, databaseAccount } = userContext;
|
||||
const { authType, useSDKOperations, apiType, subscriptionId, resourceGroup, databaseAccount } = userContext;
|
||||
try {
|
||||
if (authType === AuthType.AAD && !userContext.features.enableSDKoperations && apiType === "SQL") {
|
||||
if (authType === AuthType.AAD && !useSDKOperations && apiType === "SQL") {
|
||||
const rpResponse = await listSqlUserDefinedFunctions(
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
|
||||
@@ -33,11 +33,7 @@ export async function updateCollection(
|
||||
const clearMessage = logConsoleProgress(`Updating container ${collectionId}`);
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
!userContext.features.enableSDKoperations &&
|
||||
userContext.apiType !== "Tables"
|
||||
) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType !== "Tables") {
|
||||
collection = await updateCollectionWithARM(databaseId, collectionId, newCollection);
|
||||
} else {
|
||||
const sdkResponse = await client()
|
||||
|
||||
@@ -56,7 +56,7 @@ export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> =>
|
||||
const clearMessage = logConsoleProgress(`Updating offer for ${offerResourceText}`);
|
||||
|
||||
try {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.features.enableSDKoperations) {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
if (params.collectionId) {
|
||||
updatedOffer = await updateCollectionOfferWithARM(params);
|
||||
} else if (userContext.apiType === "Tables") {
|
||||
|
||||
@@ -20,9 +20,9 @@ export async function updateStoredProcedure(
|
||||
): Promise<StoredProcedureDefinition & Resource> {
|
||||
const clearMessage = logConsoleProgress(`Updating stored procedure ${storedProcedure.id}`);
|
||||
try {
|
||||
const { authType, apiType, subscriptionId, resourceGroup, databaseAccount } = userContext;
|
||||
const { authType, useSDKOperations, apiType, subscriptionId, resourceGroup, databaseAccount } = userContext;
|
||||
|
||||
if (authType === AuthType.AAD && !userContext.features.enableSDKoperations && apiType === "SQL") {
|
||||
if (authType === AuthType.AAD && !useSDKOperations && apiType === "SQL") {
|
||||
const getResponse = await getSqlStoredProcedure(
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
|
||||
@@ -13,9 +13,9 @@ export async function updateTrigger(
|
||||
trigger: SqlTriggerResource
|
||||
): Promise<SqlTriggerResource | TriggerDefinition> {
|
||||
const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`);
|
||||
const { authType, apiType, subscriptionId, resourceGroup, databaseAccount } = userContext;
|
||||
const { authType, useSDKOperations, apiType, subscriptionId, resourceGroup, databaseAccount } = userContext;
|
||||
try {
|
||||
if (authType === AuthType.AAD && !userContext.features.enableSDKoperations && apiType === "SQL") {
|
||||
if (authType === AuthType.AAD && !useSDKOperations && apiType === "SQL") {
|
||||
const getResponse = await getSqlTrigger(
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
|
||||
@@ -19,9 +19,9 @@ export async function updateUserDefinedFunction(
|
||||
userDefinedFunction: UserDefinedFunctionDefinition
|
||||
): Promise<UserDefinedFunctionDefinition & Resource> {
|
||||
const clearMessage = logConsoleProgress(`Updating user defined function ${userDefinedFunction.id}`);
|
||||
const { authType, apiType, subscriptionId, resourceGroup, databaseAccount } = userContext;
|
||||
const { authType, useSDKOperations, apiType, subscriptionId, resourceGroup, databaseAccount } = userContext;
|
||||
try {
|
||||
if (authType === AuthType.AAD && !userContext.features.enableSDKoperations && apiType === "SQL") {
|
||||
if (authType === AuthType.AAD && !useSDKOperations && apiType === "SQL") {
|
||||
const getResponse = await getSqlUserDefinedFunction(
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
|
||||
@@ -438,25 +438,18 @@ export interface NotebookWorkspaceConnectionInfo {
|
||||
|
||||
export interface ContainerInfo {
|
||||
durationLeftInMinutes: number;
|
||||
phoenixServerInfo: NotebookWorkspaceConnectionInfo;
|
||||
notebookServerInfo: NotebookWorkspaceConnectionInfo;
|
||||
status: ContainerStatusType;
|
||||
}
|
||||
|
||||
export interface IProvisionData {
|
||||
cosmosEndpoint: string;
|
||||
poolId: string;
|
||||
}
|
||||
|
||||
export interface IContainerData {
|
||||
forwardingId: string;
|
||||
}
|
||||
|
||||
export interface IDbAccountAllow {
|
||||
status: number;
|
||||
message?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export interface IResponse<T> {
|
||||
status: number;
|
||||
data: T;
|
||||
@@ -481,8 +474,8 @@ export interface IMaxUsersPerDbAccountExceeded extends IPhoenixError {
|
||||
}
|
||||
|
||||
export interface IPhoenixConnectionInfoResult {
|
||||
readonly authToken?: string;
|
||||
readonly phoenixServiceUrl?: string;
|
||||
readonly notebookAuthToken?: string;
|
||||
readonly notebookServerUrl?: string;
|
||||
readonly forwardingId?: string;
|
||||
}
|
||||
|
||||
@@ -563,7 +556,6 @@ export interface ContainerConnectionInfo {
|
||||
}
|
||||
|
||||
export enum PhoenixErrorType {
|
||||
UserMissingPermissionsError = "UserMissingPermissionsError",
|
||||
MaxAllocationTimeExceeded = "MaxAllocationTimeExceeded",
|
||||
MaxDbAccountsPerUserExceeded = "MaxDbAccountsPerUserExceeded",
|
||||
MaxUsersPerDbAccountExceeded = "MaxUsersPerDbAccountExceeded",
|
||||
@@ -571,5 +563,4 @@ export enum PhoenixErrorType {
|
||||
RegionNotServicable = "RegionNotServicable",
|
||||
SubscriptionNotAllowed = "SubscriptionNotAllowed",
|
||||
UnknownError = "UnknownError",
|
||||
PhoenixFlightFallback = "PhoenixFlightFallback",
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
|
||||
},
|
||||
];
|
||||
|
||||
if (userContext.apiType !== "Tables" || userContext.features.enableSDKoperations) {
|
||||
if (userContext.apiType !== "Tables") {
|
||||
items.push({
|
||||
iconSrc: DeleteDatabaseIcon,
|
||||
onClick: () =>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ import shallow from "zustand/shallow";
|
||||
import { AuthType } from "../AuthType";
|
||||
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
|
||||
import * as Constants from "../Common/Constants";
|
||||
import { Areas, ConnectionStatusType, HttpStatusCodes, Notebook, PoolIdType } from "../Common/Constants";
|
||||
import { Areas, ConnectionStatusType, HttpStatusCodes, Notebook } from "../Common/Constants";
|
||||
import { readCollection } from "../Common/dataAccess/readCollection";
|
||||
import { readDatabases } from "../Common/dataAccess/readDatabases";
|
||||
import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHandlingUtils";
|
||||
@@ -357,7 +357,6 @@ export default class Explorer {
|
||||
) {
|
||||
const provisionData: IProvisionData = {
|
||||
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
|
||||
poolId: PoolIdType.DefaultPoolId,
|
||||
};
|
||||
const connectionStatus: ContainerConnectionInfo = {
|
||||
status: ConnectionStatusType.Connecting,
|
||||
@@ -370,8 +369,8 @@ export default class Explorer {
|
||||
});
|
||||
useNotebook.getState().setIsAllocating(true);
|
||||
connectionInfo = await this.phoenixClient.allocateContainer(provisionData);
|
||||
if (!connectionInfo?.data?.phoenixServiceUrl) {
|
||||
throw new Error(`PhoenixServiceUrl is invalid!`);
|
||||
if (!connectionInfo?.data?.notebookServerUrl) {
|
||||
throw new Error(`NotebookServerUrl is invalid!`);
|
||||
}
|
||||
await this.setNotebookInfo(connectionInfo, connectionStatus);
|
||||
TelemetryProcessor.traceSuccess(Action.PhoenixConnection, {
|
||||
@@ -422,8 +421,8 @@ export default class Explorer {
|
||||
notebookServerEndpoint:
|
||||
(validateEndpoint(userContext.features.notebookServerUrl, allowedNotebookServerUrls) &&
|
||||
userContext.features.notebookServerUrl) ||
|
||||
connectionInfo.data.phoenixServiceUrl,
|
||||
authToken: userContext.features.notebookServerToken || connectionInfo.data.authToken,
|
||||
connectionInfo.data.notebookServerUrl,
|
||||
authToken: userContext.features.notebookServerToken || connectionInfo.data.notebookAuthToken,
|
||||
forwardingId: connectionInfo.data.forwardingId,
|
||||
});
|
||||
this.notebookManager?.notebookClient
|
||||
@@ -498,8 +497,8 @@ export default class Explorer {
|
||||
if (connectionInfo?.status !== HttpStatusCodes.OK) {
|
||||
throw new Error(`Reset Workspace: Received status code- ${connectionInfo?.status}`);
|
||||
}
|
||||
if (!connectionInfo?.data?.phoenixServiceUrl) {
|
||||
throw new Error(`Reset Workspace: PhoenixServiceUrl is invalid!`);
|
||||
if (!connectionInfo?.data?.notebookServerUrl) {
|
||||
throw new Error(`Reset Workspace: NotebookServerUrl is invalid!`);
|
||||
}
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
await this.setNotebookInfo(connectionInfo, connectionStatus);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useDialog } from "Explorer/Controls/Dialog";
|
||||
import promiseRetry, { AbortError } from "p-retry";
|
||||
import { PhoenixClient } from "Phoenix/PhoenixClient";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import { ConnectionStatusType, HttpHeaders, HttpStatusCodes, Notebook, PoolIdType } from "../../Common/Constants";
|
||||
import { ConnectionStatusType, HttpHeaders, HttpStatusCodes, Notebook } from "../../Common/Constants";
|
||||
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||
import * as Logger from "../../Common/Logger";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
@@ -154,7 +154,6 @@ export class NotebookContainerClient {
|
||||
if (useNotebook.getState().isPhoenixNotebooks) {
|
||||
const provisionData: IProvisionData = {
|
||||
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
|
||||
poolId: PoolIdType.DefaultPoolId,
|
||||
};
|
||||
return await this.phoenixClient.resetContainer(provisionData);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import Immutable from "immutable";
|
||||
import * as React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Dispatch } from "redux";
|
||||
import * as Logger from "../../../Common/Logger";
|
||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import loadTransform from "../NotebookComponent/loadTransform";
|
||||
@@ -101,7 +100,6 @@ export class SchemaAnalyzer extends React.Component<SchemaAnalyzerProps, SchemaA
|
||||
// Only in cases where CosmosMongoKernel runs into an error we get a single output
|
||||
if (outputs.size === 1) {
|
||||
traceFailure(Action.SchemaAnalyzerClickAnalyze, data, this.clickAnalyzeTelemetryStartKey);
|
||||
Logger.logError(`Failed to analyze schema: ${JSON.stringify(data)}`, "SchemaAnalyzer/traceClickAnalyzeComplete");
|
||||
} else {
|
||||
traceSuccess(Action.SchemaAnalyzerClickAnalyze, data, this.clickAnalyzeTelemetryStartKey);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ import { PhoenixClient } from "Phoenix/PhoenixClient";
|
||||
import create, { UseStore } from "zustand";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import { ConnectionStatusType, HttpStatusCodes } from "../../Common/Constants";
|
||||
import { ConnectionStatusType } from "../../Common/Constants";
|
||||
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||
import * as Logger from "../../Common/Logger";
|
||||
import { configContext } from "../../ConfigContext";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { ContainerConnectionInfo, ContainerInfo, PhoenixErrorType } from "../../Contracts/DataModels";
|
||||
import { ContainerConnectionInfo, ContainerInfo } from "../../Contracts/DataModels";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
import { IPinnedRepo } from "../../Juno/JunoClient";
|
||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
@@ -96,7 +96,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
containerStatus: {
|
||||
status: undefined,
|
||||
durationLeftInMinutes: undefined,
|
||||
phoenixServerInfo: undefined,
|
||||
notebookServerInfo: undefined,
|
||||
},
|
||||
isPhoenixNotebooks: undefined,
|
||||
isPhoenixFeatures: undefined,
|
||||
@@ -296,30 +296,22 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
useNotebook.getState().setContainerStatus({
|
||||
status: undefined,
|
||||
durationLeftInMinutes: undefined,
|
||||
phoenixServerInfo: undefined,
|
||||
notebookServerInfo: undefined,
|
||||
});
|
||||
},
|
||||
setIsRefreshed: (isRefreshed: boolean) => set({ isRefreshed }),
|
||||
setContainerStatus: (containerStatus: ContainerInfo) => set({ containerStatus }),
|
||||
getPhoenixStatus: async () => {
|
||||
if (get().isPhoenixNotebooks === undefined || get().isPhoenixFeatures === undefined) {
|
||||
let isPhoenixNotebooks = false;
|
||||
let isPhoenixFeatures = false;
|
||||
|
||||
const isPublicInternetAllowed = isPublicInternetAccessAllowed();
|
||||
const phoenixClient = new PhoenixClient();
|
||||
const dbAccountAllowedInfo = await phoenixClient.getDbAccountAllowedStatus();
|
||||
|
||||
if (dbAccountAllowedInfo.status === HttpStatusCodes.OK) {
|
||||
if (dbAccountAllowedInfo?.type === PhoenixErrorType.PhoenixFlightFallback) {
|
||||
isPhoenixNotebooks = isPublicInternetAllowed && userContext.features.phoenixNotebooks;
|
||||
isPhoenixFeatures = isPublicInternetAllowed && userContext.features.phoenixFeatures;
|
||||
} else {
|
||||
isPhoenixNotebooks = isPhoenixFeatures = isPublicInternetAllowed;
|
||||
}
|
||||
} else {
|
||||
isPhoenixNotebooks = isPhoenixFeatures = false;
|
||||
let isPhoenix = false;
|
||||
if (userContext.features.phoenixNotebooks || userContext.features.phoenixFeatures) {
|
||||
const phoenixClient = new PhoenixClient();
|
||||
isPhoenix = isPublicInternetAccessAllowed() && (await phoenixClient.isDbAcountWhitelisted());
|
||||
}
|
||||
|
||||
const isPhoenixNotebooks = userContext.features.phoenixNotebooks && isPhoenix;
|
||||
const isPhoenixFeatures = userContext.features.phoenixFeatures && isPhoenix;
|
||||
|
||||
set({ isPhoenixNotebooks: isPhoenixNotebooks });
|
||||
set({ isPhoenixFeatures: isPhoenixFeatures });
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,25 +3,35 @@
|
||||
*/
|
||||
import { Coachmark, DirectionalHint, Image, Link, Stack, TeachingBubbleContent, Text } from "@fluentui/react";
|
||||
import { useCarousel } from "hooks/useCarousel";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import * as React from "react";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import AddDatabaseIcon from "../../../images/AddDatabase.svg";
|
||||
import NewQueryIcon from "../../../images/AddSqlQuery_16x16.svg";
|
||||
import NewStoredProcedureIcon from "../../../images/AddStoredProcedure.svg";
|
||||
import OpenQueryIcon from "../../../images/BrowseQuery.svg";
|
||||
import ConnectIcon from "../../../images/Connect_color.svg";
|
||||
import ContainersIcon from "../../../images/Containers.svg";
|
||||
import LinkIcon from "../../../images/Link_blue.svg";
|
||||
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
||||
import NotebookColorIcon from "../../../images/Notebooks.svg";
|
||||
import QuickStartIcon from "../../../images/Quickstart_Lightning.svg";
|
||||
import ScaleAndSettingsIcon from "../../../images/Scale_15x15.svg";
|
||||
import CollectionIcon from "../../../images/tree-collection.svg";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as Constants from "../../Common/Constants";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { useSidePanel } from "../../hooks/useSidePanel";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { getCollectionName } from "../../Utils/APITypeUtils";
|
||||
import { getCollectionName, getDatabaseName } from "../../Utils/APITypeUtils";
|
||||
import { FeaturePanelLauncher } from "../Controls/FeaturePanel/FeaturePanelLauncher";
|
||||
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
|
||||
import Explorer from "../Explorer";
|
||||
import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity";
|
||||
import { useNotebook } from "../Notebook/useNotebook";
|
||||
import { AddDatabasePanel } from "../Panes/AddDatabasePanel/AddDatabasePanel";
|
||||
import { BrowseQueriesPane } from "../Panes/BrowseQueriesPane/BrowseQueriesPane";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
|
||||
@@ -224,13 +234,103 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
iconSrc: ConnectIcon,
|
||||
title: "Connect",
|
||||
description: "Prefer using your own choice of tooling? Find the connection string you need to connect",
|
||||
onClick: () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect),
|
||||
onClick: () => useTabs.getState().openAndActivateConnectTab(),
|
||||
};
|
||||
heroes.push(connectBtn);
|
||||
|
||||
return heroes;
|
||||
}
|
||||
|
||||
private createCommonTaskItems(): SplashScreenItem[] {
|
||||
const items: SplashScreenItem[] = [];
|
||||
|
||||
if (userContext.authType === AuthType.ResourceToken) {
|
||||
return items;
|
||||
}
|
||||
|
||||
if (!useSelectedNode.getState().isDatabaseNodeOrNoneSelected()) {
|
||||
if (userContext.apiType === "SQL" || userContext.apiType === "Gremlin") {
|
||||
items.push({
|
||||
iconSrc: NewQueryIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, undefined);
|
||||
},
|
||||
title: "New SQL Query",
|
||||
description: undefined,
|
||||
});
|
||||
} else if (userContext.apiType === "Mongo") {
|
||||
items.push({
|
||||
iconSrc: NewQueryIcon,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, undefined);
|
||||
},
|
||||
title: "New Query",
|
||||
description: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
if (userContext.apiType === "SQL") {
|
||||
items.push({
|
||||
iconSrc: OpenQueryIcon,
|
||||
title: "Open Query",
|
||||
description: undefined,
|
||||
onClick: () =>
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={this.container} />),
|
||||
});
|
||||
}
|
||||
|
||||
if (userContext.apiType !== "Cassandra") {
|
||||
items.push({
|
||||
iconSrc: NewStoredProcedureIcon,
|
||||
title: "New Stored Procedure",
|
||||
description: undefined,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, undefined);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/* Scale & Settings */
|
||||
const isShared = useDatabases.getState().findSelectedDatabase()?.isDatabaseShared();
|
||||
|
||||
const label = isShared ? "Settings" : "Scale & Settings";
|
||||
items.push({
|
||||
iconSrc: ScaleAndSettingsIcon,
|
||||
title: label,
|
||||
description: undefined,
|
||||
onClick: () => {
|
||||
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
|
||||
selectedCollection && selectedCollection.onSettingsClick();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
items.push({
|
||||
iconSrc: AddDatabaseIcon,
|
||||
title: "New " + getDatabaseName(),
|
||||
description: undefined,
|
||||
onClick: async () => {
|
||||
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
|
||||
if (throughputCap && throughputCap !== -1) {
|
||||
await useDatabases.getState().loadAllOffers();
|
||||
}
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"New " + getDatabaseName(),
|
||||
<AddDatabasePanel explorer={this.container} buttonElement={document.activeElement as HTMLElement} />
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private decorateOpenCollectionActivity({ databaseId, collectionId }: MostRecentActivity.OpenCollectionItem) {
|
||||
return {
|
||||
iconSrc: CollectionIcon,
|
||||
@@ -272,6 +372,29 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
||||
});
|
||||
}
|
||||
|
||||
private createTipsItems(): SplashScreenItem[] {
|
||||
return [
|
||||
{
|
||||
iconSrc: undefined,
|
||||
title: "Data Modeling",
|
||||
description: "Learn more about modeling",
|
||||
onClick: () => window.open(SplashScreen.dataModelingUrl),
|
||||
},
|
||||
{
|
||||
iconSrc: undefined,
|
||||
title: "Cost & Throughput Calculation",
|
||||
description: "Learn more about cost calculation",
|
||||
onClick: () => window.open(SplashScreen.throughputEstimatorUrl),
|
||||
},
|
||||
{
|
||||
iconSrc: undefined,
|
||||
title: "Configure automatic failover",
|
||||
description: "Learn more about Cosmos DB high-availability",
|
||||
onClick: () => window.open(SplashScreen.failoverUrl),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
private onSplashScreenItemKeyPress(event: React.KeyboardEvent, callback: () => void) {
|
||||
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
|
||||
callback();
|
||||
|
||||
@@ -600,43 +600,56 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
metricsMap.forEach((queryMetrics) => {
|
||||
if (queryMetrics) {
|
||||
aggregatedMetrics.documentLoadTime =
|
||||
queryMetrics.documentLoadTime &&
|
||||
this._normalize(queryMetrics.documentLoadTime.totalMilliseconds()) +
|
||||
this._normalize(aggregatedMetrics.documentLoadTime);
|
||||
this._normalize(aggregatedMetrics.documentLoadTime);
|
||||
aggregatedMetrics.documentWriteTime =
|
||||
queryMetrics.documentWriteTime &&
|
||||
this._normalize(queryMetrics.documentWriteTime.totalMilliseconds()) +
|
||||
this._normalize(aggregatedMetrics.documentWriteTime);
|
||||
this._normalize(aggregatedMetrics.documentWriteTime);
|
||||
aggregatedMetrics.indexHitDocumentCount =
|
||||
queryMetrics.indexHitDocumentCount &&
|
||||
this._normalize(queryMetrics.indexHitDocumentCount) +
|
||||
this._normalize(aggregatedMetrics.indexHitDocumentCount);
|
||||
this._normalize(aggregatedMetrics.indexHitDocumentCount);
|
||||
aggregatedMetrics.outputDocumentCount =
|
||||
queryMetrics.outputDocumentCount &&
|
||||
this._normalize(queryMetrics.outputDocumentCount) + this._normalize(aggregatedMetrics.outputDocumentCount);
|
||||
aggregatedMetrics.outputDocumentSize =
|
||||
queryMetrics.outputDocumentSize &&
|
||||
this._normalize(queryMetrics.outputDocumentSize) + this._normalize(aggregatedMetrics.outputDocumentSize);
|
||||
aggregatedMetrics.indexLookupTime =
|
||||
queryMetrics.indexLookupTime &&
|
||||
this._normalize(queryMetrics.indexLookupTime.totalMilliseconds()) +
|
||||
this._normalize(aggregatedMetrics.indexLookupTime);
|
||||
this._normalize(aggregatedMetrics.indexLookupTime);
|
||||
aggregatedMetrics.retrievedDocumentCount =
|
||||
queryMetrics.retrievedDocumentCount &&
|
||||
this._normalize(queryMetrics.retrievedDocumentCount) +
|
||||
this._normalize(aggregatedMetrics.retrievedDocumentCount);
|
||||
this._normalize(aggregatedMetrics.retrievedDocumentCount);
|
||||
aggregatedMetrics.retrievedDocumentSize =
|
||||
queryMetrics.retrievedDocumentSize &&
|
||||
this._normalize(queryMetrics.retrievedDocumentSize) +
|
||||
this._normalize(aggregatedMetrics.retrievedDocumentSize);
|
||||
this._normalize(aggregatedMetrics.retrievedDocumentSize);
|
||||
aggregatedMetrics.vmExecutionTime =
|
||||
queryMetrics.vmExecutionTime &&
|
||||
this._normalize(queryMetrics.vmExecutionTime.totalMilliseconds()) +
|
||||
this._normalize(aggregatedMetrics.vmExecutionTime);
|
||||
this._normalize(aggregatedMetrics.vmExecutionTime);
|
||||
aggregatedMetrics.totalQueryExecutionTime =
|
||||
queryMetrics.totalQueryExecutionTime &&
|
||||
this._normalize(queryMetrics.totalQueryExecutionTime.totalMilliseconds()) +
|
||||
this._normalize(aggregatedMetrics.totalQueryExecutionTime);
|
||||
this._normalize(aggregatedMetrics.totalQueryExecutionTime);
|
||||
|
||||
aggregatedMetrics.runtimeExecutionTimes.queryEngineExecutionTime =
|
||||
aggregatedMetrics.runtimeExecutionTimes &&
|
||||
this._normalize(queryMetrics.runtimeExecutionTimes.queryEngineExecutionTime.totalMilliseconds()) +
|
||||
this._normalize(aggregatedMetrics.runtimeExecutionTimes.queryEngineExecutionTime);
|
||||
this._normalize(aggregatedMetrics.runtimeExecutionTimes.queryEngineExecutionTime);
|
||||
aggregatedMetrics.runtimeExecutionTimes.systemFunctionExecutionTime =
|
||||
aggregatedMetrics.runtimeExecutionTimes &&
|
||||
this._normalize(queryMetrics.runtimeExecutionTimes.systemFunctionExecutionTime.totalMilliseconds()) +
|
||||
this._normalize(aggregatedMetrics.runtimeExecutionTimes.systemFunctionExecutionTime);
|
||||
this._normalize(aggregatedMetrics.runtimeExecutionTimes.systemFunctionExecutionTime);
|
||||
aggregatedMetrics.runtimeExecutionTimes.userDefinedFunctionExecutionTime =
|
||||
aggregatedMetrics.runtimeExecutionTimes &&
|
||||
this._normalize(queryMetrics.runtimeExecutionTimes.userDefinedFunctionExecutionTime.totalMilliseconds()) +
|
||||
this._normalize(aggregatedMetrics.runtimeExecutionTimes.userDefinedFunctionExecutionTime);
|
||||
this._normalize(aggregatedMetrics.runtimeExecutionTimes.userDefinedFunctionExecutionTime);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { CollectionTabKind } from "Contracts/ViewModels";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { SplashScreen } from "Explorer/SplashScreen/SplashScreen";
|
||||
import { ConnectTab } from "Explorer/Tabs/ConnectTab";
|
||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||
import ko from "knockout";
|
||||
@@ -8,33 +6,28 @@ import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||
import loadingIcon from "../../../images/circular_loader_black_16x16.gif";
|
||||
import errorIcon from "../../../images/close-black.svg";
|
||||
import { useObservable } from "../../hooks/useObservable";
|
||||
import { ReactTabKind, useTabs } from "../../hooks/useTabs";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
import TabsBase from "./TabsBase";
|
||||
|
||||
type Tab = TabsBase | (TabsBase & { render: () => JSX.Element });
|
||||
|
||||
interface TabsProps {
|
||||
explorer: Explorer;
|
||||
}
|
||||
|
||||
export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
||||
const { openedTabs, openedReactTabs, activeTab, activeReactTab } = useTabs();
|
||||
|
||||
export const Tabs = (): JSX.Element => {
|
||||
const { openedTabs, activeTab } = useTabs();
|
||||
const isConnectTabOpen = useTabs((state) => state.isConnectTabOpen);
|
||||
const isConnectTabActive = useTabs((state) => state.isConnectTabActive);
|
||||
return (
|
||||
<div className="tabsManagerContainer">
|
||||
<div id="content" className="flexContainer hideOverflows">
|
||||
<div className="nav-tabs-margin">
|
||||
<ul className="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
|
||||
{openedReactTabs.map((tab) => (
|
||||
<TabNav key={ReactTabKind[tab]} active={activeReactTab === tab} tabKind={tab} />
|
||||
))}
|
||||
{isConnectTabOpen && <TabNav key="connect" tab={undefined} active={isConnectTabActive} />}
|
||||
{openedTabs.map((tab) => (
|
||||
<TabNav key={tab.tabId} tab={tab} active={activeTab === tab} />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="tabPanesContainer">
|
||||
{activeReactTab !== undefined && getReactTabContent(activeReactTab, explorer)}
|
||||
{isConnectTabActive && <ConnectTab />}
|
||||
{openedTabs.map((tab) => (
|
||||
<TabPane key={tab.tabId} tab={tab} active={activeTab === tab} />
|
||||
))}
|
||||
@@ -44,7 +37,7 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
||||
);
|
||||
};
|
||||
|
||||
function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?: ReactTabKind }) {
|
||||
function TabNav({ tab, active }: { tab: Tab; active: boolean }) {
|
||||
const [hovering, setHovering] = useState(false);
|
||||
const focusTab = useRef<HTMLLIElement>() as MutableRefObject<HTMLLIElement>;
|
||||
const tabId = tab ? tab.tabId : "connect";
|
||||
@@ -58,20 +51,8 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
|
||||
<li
|
||||
onMouseOver={() => setHovering(true)}
|
||||
onMouseLeave={() => setHovering(false)}
|
||||
onClick={() => {
|
||||
if (tab) {
|
||||
tab.onTabClick();
|
||||
} else if (tabKind !== undefined) {
|
||||
useTabs.getState().activateReactTab(tabKind);
|
||||
}
|
||||
}}
|
||||
onKeyPress={({ nativeEvent: e }) => {
|
||||
if (tab) {
|
||||
tab.onKeyPressActivate(undefined, e);
|
||||
} else if (tabKind !== undefined) {
|
||||
onKeyPressReactTab(e, tabKind);
|
||||
}
|
||||
}}
|
||||
onClick={() => (tab ? tab.onTabClick() : useTabs.getState().activateConnectTab())}
|
||||
onKeyPress={({ nativeEvent: e }) => (tab ? tab.onKeyPressActivate(undefined, e) : onKeyPressConnectTab(e))}
|
||||
className={active ? "active tabList" : "tabList"}
|
||||
title={useObservable(tab?.tabPath || ko.observable(""))}
|
||||
aria-selected={active}
|
||||
@@ -84,18 +65,16 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
|
||||
<span className="tabNavContentContainer">
|
||||
<a data-toggle="tab" href={"#" + tabId} tabIndex={-1}>
|
||||
<div className="tab_Content">
|
||||
<span className="statusIconContainer" style={{ width: tabKind === ReactTabKind.Home ? 0 : 18 }}>
|
||||
<span className="statusIconContainer">
|
||||
{useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />}
|
||||
{useObservable(tab?.isExecuting || ko.observable(false)) && (
|
||||
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" />
|
||||
)}
|
||||
</span>
|
||||
<span className="tabNavText">{useObservable(tab?.tabTitle || ko.observable(ReactTabKind[tabKind]))}</span>
|
||||
{tabKind !== ReactTabKind.Home && (
|
||||
<span className="tabIconSection">
|
||||
<CloseButton tab={tab} active={active} hovering={hovering} tabKind={tabKind} />
|
||||
</span>
|
||||
)}
|
||||
<span className="tabNavText">{useObservable(tab?.tabTitle || ko.observable("Connect"))}</span>
|
||||
<span className="tabIconSection">
|
||||
<CloseButton tab={tab} active={active} hovering={hovering} />
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</span>
|
||||
@@ -103,24 +82,14 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
|
||||
);
|
||||
}
|
||||
|
||||
const CloseButton = ({
|
||||
tab,
|
||||
active,
|
||||
hovering,
|
||||
tabKind,
|
||||
}: {
|
||||
tab: Tab;
|
||||
active: boolean;
|
||||
hovering: boolean;
|
||||
tabKind?: ReactTabKind;
|
||||
}) => (
|
||||
const CloseButton = ({ tab, active, hovering }: { tab: Tab; active: boolean; hovering: boolean }) => (
|
||||
<span
|
||||
style={{ display: hovering || active ? undefined : "none" }}
|
||||
title="Close"
|
||||
role="button"
|
||||
aria-label="Close Tab"
|
||||
className="cancelButton"
|
||||
onClick={() => (tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind))}
|
||||
onClick={() => (tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeConnectTab())}
|
||||
tabIndex={active ? 0 : undefined}
|
||||
onKeyPress={({ nativeEvent: e }) => tab.onKeyPressClose(undefined, e)}
|
||||
>
|
||||
@@ -175,20 +144,9 @@ function TabPane({ tab, active }: { tab: Tab; active: boolean }) {
|
||||
return <div {...attrs} ref={ref} data-bind="html:html" />;
|
||||
}
|
||||
|
||||
const onKeyPressReactTab = (e: KeyboardEvent, tabKind: ReactTabKind): void => {
|
||||
const onKeyPressConnectTab = (e: KeyboardEvent): void => {
|
||||
if (e.key === "Enter" || e.key === "Space") {
|
||||
useTabs.getState().activateReactTab(tabKind);
|
||||
useTabs.getState().activateConnectTab();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
|
||||
switch (activeReactTab) {
|
||||
case ReactTabKind.Connect:
|
||||
return <ConnectTab />;
|
||||
case ReactTabKind.Home:
|
||||
return <SplashScreen explorer={explorer} />;
|
||||
default:
|
||||
throw Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Link, Stack, TeachingBubble, Text } from "@fluentui/react";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||
import React from "react";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceCancel, traceSuccess } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { traceCancel } from "Shared/Telemetry/TelemetryProcessor";
|
||||
|
||||
export const QuickstartTutorial: React.FC = (): JSX.Element => {
|
||||
const { step, isSampleDBExpanded, isDocumentsTabOpened, sampleCollection, setStep } = useTeachingBubble();
|
||||
@@ -146,10 +146,7 @@ export const QuickstartTutorial: React.FC = (): JSX.Element => {
|
||||
hasCloseButton
|
||||
primaryButtonProps={{
|
||||
text: "Launch connect",
|
||||
onClick: () => {
|
||||
traceSuccess(Action.CompleteUITour);
|
||||
useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect);
|
||||
},
|
||||
onClick: () => useTabs.getState().openAndActivateConnectTab(),
|
||||
}}
|
||||
secondaryButtonProps={{
|
||||
text: "Previous",
|
||||
|
||||
@@ -2,7 +2,6 @@ import { initializeIcons, Link, Text } from "@fluentui/react";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import { userContext } from "UserContext";
|
||||
import { initializeConfiguration } from "../ConfigContext";
|
||||
import { GalleryHeaderComponent } from "../Explorer/Controls/Header/GalleryHeaderComponent";
|
||||
import {
|
||||
@@ -26,9 +25,7 @@ const onInit = async () => {
|
||||
|
||||
const props: GalleryAndNotebookViewerComponentProps = {
|
||||
junoClient: new JunoClient(),
|
||||
selectedTab:
|
||||
galleryViewerProps.selectedTab ||
|
||||
(userContext.features.publicGallery ? GalleryTab.PublicGallery : GalleryTab.OfficialSamples),
|
||||
selectedTab: galleryViewerProps.selectedTab || GalleryTab.PublicGallery,
|
||||
sortBy: galleryViewerProps.sortBy || SortBy.MostRecent,
|
||||
searchText: galleryViewerProps.searchText,
|
||||
};
|
||||
|
||||
@@ -46,10 +46,12 @@ import "./Explorer/Menus/NotificationConsole/NotificationConsole.less";
|
||||
import { NotificationConsole } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import "./Explorer/Panes/PanelComponent.less";
|
||||
import { SidePanel } from "./Explorer/Panes/PanelContainerComponent";
|
||||
import { SplashScreen } from "./Explorer/SplashScreen/SplashScreen";
|
||||
import "./Explorer/SplashScreen/SplashScreen.less";
|
||||
import { Tabs } from "./Explorer/Tabs/Tabs";
|
||||
import { useConfig } from "./hooks/useConfig";
|
||||
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
||||
import { useTabs } from "./hooks/useTabs";
|
||||
import "./Libs/jquery";
|
||||
import "./Shared/appInsights";
|
||||
|
||||
@@ -57,6 +59,8 @@ initializeIcons();
|
||||
|
||||
const App: React.FunctionComponent = () => {
|
||||
const [isLeftPaneExpanded, setIsLeftPaneExpanded] = useState<boolean>(true);
|
||||
const openedTabs = useTabs((state) => state.openedTabs);
|
||||
const isConnectTabOpen = useTabs((state) => state.isConnectTabOpen);
|
||||
const isCarouselOpen = useCarousel((state) => state.shouldOpen);
|
||||
|
||||
const config = useConfig();
|
||||
@@ -100,7 +104,9 @@ const App: React.FunctionComponent = () => {
|
||||
{/* Collections Tree Collapsed - End */}
|
||||
</div>
|
||||
</div>
|
||||
<Tabs explorer={explorer} />
|
||||
{/* Collections Tree - End */}
|
||||
{openedTabs.length === 0 && !isConnectTabOpen && <SplashScreen explorer={explorer} />}
|
||||
<Tabs />
|
||||
</div>
|
||||
{/* Collections Tree and Tabs - End */}
|
||||
<div
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
ContainerConnectionInfo,
|
||||
ContainerInfo,
|
||||
IContainerData,
|
||||
IDbAccountAllow,
|
||||
IMaxAllocationTimeExceeded,
|
||||
IMaxDbAccountsPerUserExceeded,
|
||||
IMaxUsersPerDbAccountExceeded,
|
||||
@@ -104,7 +103,7 @@ export class PhoenixClient {
|
||||
const containerStatus = await response.json();
|
||||
return {
|
||||
durationLeftInMinutes: containerStatus?.durationLeftInMinutes,
|
||||
phoenixServerInfo: containerStatus?.phoenixServerInfo,
|
||||
notebookServerInfo: containerStatus?.notebookServerInfo,
|
||||
status: ContainerStatusType.Active,
|
||||
};
|
||||
} else if (response.status === HttpStatusCodes.NotFound) {
|
||||
@@ -148,7 +147,7 @@ export class PhoenixClient {
|
||||
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
|
||||
return {
|
||||
durationLeftInMinutes: undefined,
|
||||
phoenixServerInfo: undefined,
|
||||
notebookServerInfo: undefined,
|
||||
status: ContainerStatusType.Disconnected,
|
||||
};
|
||||
}
|
||||
@@ -162,17 +161,15 @@ export class PhoenixClient {
|
||||
}
|
||||
}
|
||||
|
||||
public async getDbAccountAllowedStatus(): Promise<IDbAccountAllow> {
|
||||
public async isDbAcountWhitelisted(): Promise<boolean> {
|
||||
const startKey = TelemetryProcessor.traceStart(Action.PhoenixDBAccountAllowed, {
|
||||
dataExplorerArea: Areas.Notebook,
|
||||
});
|
||||
let responseJson;
|
||||
try {
|
||||
const response = await window.fetch(`${this.getPhoenixControlPlanePathPrefix()}`, {
|
||||
method: "GET",
|
||||
headers: PhoenixClient.getHeaders(),
|
||||
});
|
||||
responseJson = await response?.json();
|
||||
if (response.status !== HttpStatusCodes.OK) {
|
||||
throw new Error(`Received status code: ${response?.status}`);
|
||||
}
|
||||
@@ -183,11 +180,7 @@ export class PhoenixClient {
|
||||
},
|
||||
startKey
|
||||
);
|
||||
return {
|
||||
status: response.status,
|
||||
message: responseJson?.message,
|
||||
type: responseJson?.type,
|
||||
};
|
||||
return response.status === HttpStatusCodes.OK;
|
||||
} catch (error) {
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.PhoenixDBAccountAllowed,
|
||||
@@ -199,11 +192,7 @@ export class PhoenixClient {
|
||||
startKey
|
||||
);
|
||||
Logger.logError(getErrorMessage(error), "PhoenixClient/IsDbAcountWhitelisted");
|
||||
return {
|
||||
status: HttpStatusCodes.Forbidden,
|
||||
message: responseJson?.message,
|
||||
type: responseJson?.type,
|
||||
};
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +255,6 @@ export class PhoenixClient {
|
||||
"."
|
||||
);
|
||||
}
|
||||
case PhoenixErrorType.UserMissingPermissionsError:
|
||||
case PhoenixErrorType.AllocationValidationResult:
|
||||
case PhoenixErrorType.RegionNotServicable:
|
||||
case PhoenixErrorType.SubscriptionNotAllowed: {
|
||||
|
||||
@@ -7,7 +7,6 @@ import SqlX from "./SqlX";
|
||||
import {
|
||||
FetchPricesResponse,
|
||||
PriceMapAndCurrencyCode,
|
||||
RegionItem,
|
||||
RegionsResponse,
|
||||
SqlxServiceResource,
|
||||
UpdateDedicatedGatewayRequestParameters,
|
||||
@@ -140,7 +139,7 @@ const getGeneralPath = (subscriptionId: string, resourceGroup: string, name: str
|
||||
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${name}`;
|
||||
};
|
||||
|
||||
export const getRegions = async (): Promise<Array<RegionItem>> => {
|
||||
export const getRegions = async (): Promise<Array<string>> => {
|
||||
const telemetryData = {
|
||||
feature: "Calculate approximate cost",
|
||||
function: "getRegions",
|
||||
@@ -150,6 +149,8 @@ export const getRegions = async (): Promise<Array<RegionItem>> => {
|
||||
const getRegionsTimestamp = selfServeTraceStart(telemetryData);
|
||||
|
||||
try {
|
||||
const regions = new Array<string>();
|
||||
|
||||
const response = await armRequestWithoutPolling<RegionsResponse>({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path: getGeneralPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name),
|
||||
@@ -157,12 +158,20 @@ export const getRegions = async (): Promise<Array<RegionItem>> => {
|
||||
apiVersion: "2021-04-01-preview",
|
||||
});
|
||||
|
||||
if (response.result.location !== undefined) {
|
||||
regions.push(response.result.location.split(" ").join("").toLowerCase());
|
||||
} else {
|
||||
for (const location of response.result.locations) {
|
||||
regions.push(location.locationName.split(" ").join("").toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
selfServeTraceSuccess(telemetryData, getRegionsTimestamp);
|
||||
return response.result.properties.locations;
|
||||
return regions;
|
||||
} catch (err) {
|
||||
const failureTelemetry = { err, selfServeClassName: SqlX.name };
|
||||
selfServeTraceFailure(failureTelemetry, getRegionsTimestamp);
|
||||
return new Array<RegionItem>();
|
||||
return new Array<string>();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -170,7 +179,7 @@ const getFetchPricesPathForRegion = (subscriptionId: string): string => {
|
||||
return `/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/fetchPrices`;
|
||||
};
|
||||
|
||||
export const getPriceMapAndCurrencyCode = async (regions: Array<RegionItem>): Promise<PriceMapAndCurrencyCode> => {
|
||||
export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promise<PriceMapAndCurrencyCode> => {
|
||||
const telemetryData = {
|
||||
feature: "Calculate approximate cost",
|
||||
function: "getPriceMapAndCurrencyCode",
|
||||
@@ -182,7 +191,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array<RegionItem>): Pr
|
||||
try {
|
||||
const priceMap = new Map<string, Map<string, number>>();
|
||||
let currencyCode;
|
||||
for (const regionItem of regions) {
|
||||
for (const region of regions) {
|
||||
const regionPriceMap = new Map<string, number>();
|
||||
|
||||
const response = await armRequestWithoutPolling<FetchPricesResponse>({
|
||||
@@ -193,7 +202,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array<RegionItem>): Pr
|
||||
queryParams: {
|
||||
filter:
|
||||
"armRegionName eq '" +
|
||||
regionItem.locationName.split(" ").join("").toLowerCase() +
|
||||
region +
|
||||
"' and serviceFamily eq 'Databases' and productName eq 'Azure Cosmos DB Dedicated Gateway - General Purpose'",
|
||||
},
|
||||
});
|
||||
@@ -206,7 +215,7 @@ export const getPriceMapAndCurrencyCode = async (regions: Array<RegionItem>): Pr
|
||||
}
|
||||
regionPriceMap.set(item.skuName, item.retailPrice);
|
||||
}
|
||||
priceMap.set(regionItem.locationName, regionPriceMap);
|
||||
priceMap.set(region, regionPriceMap);
|
||||
}
|
||||
|
||||
selfServeTraceSuccess(telemetryData, getPriceMapAndCurrencyCodeTimestamp);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { RegionItem } from "SelfServe/SqlX/SqlxTypes";
|
||||
import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators";
|
||||
import {
|
||||
selfServeTrace,
|
||||
@@ -209,7 +208,7 @@ const ApproximateCostDropDownInfo: Info = {
|
||||
|
||||
let priceMap: Map<string, Map<string, number>>;
|
||||
let currencyCode: string;
|
||||
let regions: Array<RegionItem>;
|
||||
let regions: Array<string>;
|
||||
|
||||
const calculateCost = (skuName: string, instanceCount: number): Description => {
|
||||
const telemetryData = {
|
||||
@@ -222,47 +221,27 @@ const calculateCost = (skuName: string, instanceCount: number): Description => {
|
||||
|
||||
try {
|
||||
let costPerHour = 0;
|
||||
let costBreakdown = "";
|
||||
for (const regionItem of regions) {
|
||||
const incrementalCost = priceMap.get(regionItem.locationName).get(skuName.replace("Cosmos.", ""));
|
||||
for (const region of regions) {
|
||||
const incrementalCost = priceMap.get(region).get(skuName.replace("Cosmos.", ""));
|
||||
if (incrementalCost === undefined) {
|
||||
throw new Error(`${regionItem.locationName} not found in price map.`);
|
||||
} else if (incrementalCost === 0) {
|
||||
throw new Error(`${regionItem.locationName} cost per hour = 0`);
|
||||
throw new Error("Value not found in map.");
|
||||
}
|
||||
|
||||
let regionalInstanceCount = instanceCount;
|
||||
if (regionItem.isZoneRedundant) {
|
||||
regionalInstanceCount = Math.ceil(instanceCount * 1.5);
|
||||
}
|
||||
|
||||
const regionalCostPerHour = incrementalCost * regionalInstanceCount;
|
||||
costBreakdown += `
|
||||
${regionItem.locationName} ${regionItem.isZoneRedundant ? "(AZ)" : ""}
|
||||
${regionalCostPerHour} ${currencyCode} (${regionalInstanceCount} instances * ${incrementalCost} ${currencyCode})\
|
||||
`;
|
||||
|
||||
if (regionalCostPerHour === 0) {
|
||||
throw new Error(`${regionItem.locationName} Cost per hour = 0`);
|
||||
}
|
||||
|
||||
costPerHour += regionalCostPerHour;
|
||||
costPerHour += incrementalCost;
|
||||
}
|
||||
|
||||
if (costPerHour === 0) {
|
||||
throw new Error("Cost per hour = 0");
|
||||
}
|
||||
|
||||
costPerHour *= instanceCount;
|
||||
costPerHour = Math.round(costPerHour * 100) / 100;
|
||||
|
||||
selfServeTraceSuccess(telemetryData, calculateCostTimestamp);
|
||||
return {
|
||||
textTKey: `${costPerHour} ${currencyCode}
|
||||
${costBreakdown}`,
|
||||
textTKey: `${costPerHour} ${currencyCode}`,
|
||||
type: DescriptionType.Text,
|
||||
};
|
||||
} catch (err) {
|
||||
alert(err);
|
||||
const failureTelemetry = { err, regions, priceMap, selfServeClassName: SqlX.name };
|
||||
selfServeTraceFailure(failureTelemetry, calculateCostTimestamp);
|
||||
|
||||
|
||||
@@ -48,14 +48,10 @@ export type PriceItem = {
|
||||
};
|
||||
|
||||
export type RegionsResponse = {
|
||||
properties: RegionsProperties;
|
||||
};
|
||||
|
||||
export type RegionsProperties = {
|
||||
locations: Array<RegionItem>;
|
||||
location: string;
|
||||
};
|
||||
|
||||
export type RegionItem = {
|
||||
locationName: string;
|
||||
isZoneRedundant: boolean;
|
||||
};
|
||||
|
||||
@@ -130,7 +130,6 @@ export enum Action {
|
||||
CompleteCarousel,
|
||||
LaunchUITour,
|
||||
CancelUITour,
|
||||
CompleteUITour,
|
||||
}
|
||||
|
||||
export const ActionModifiers = {
|
||||
|
||||
@@ -36,6 +36,7 @@ interface UserContext {
|
||||
readonly accessToken?: string;
|
||||
readonly authorizationToken?: string;
|
||||
readonly resourceToken?: string;
|
||||
readonly useSDKOperations: boolean;
|
||||
readonly subscriptionType?: SubscriptionType;
|
||||
readonly quotaId?: string;
|
||||
// API Type is not yet provided by ARM. You need to manually inspect all the capabilities+kind so we abstract that logic in userContext
|
||||
@@ -60,6 +61,7 @@ export type PortalEnv = "localhost" | "blackforest" | "fairfax" | "mooncake" | "
|
||||
const ONE_WEEK_IN_MS = 604800000;
|
||||
|
||||
const features = extractFeatures();
|
||||
const { enableSDKoperations: useSDKOperations } = features;
|
||||
|
||||
const userContext: UserContext = {
|
||||
apiType: "SQL",
|
||||
@@ -67,6 +69,7 @@ const userContext: UserContext = {
|
||||
isTryCosmosDBSubscription: false,
|
||||
portalEnv: "prod",
|
||||
features,
|
||||
useSDKOperations,
|
||||
addCollectionFlight: CollectionCreation.DefaultAddCollectionDefaultFlight,
|
||||
subscriptionType: CollectionCreation.DefaultSubscriptionType,
|
||||
collectionCreationDefaults: CollectionCreationDefaults,
|
||||
|
||||
@@ -6,43 +6,37 @@ import TabsBase from "../Explorer/Tabs/TabsBase";
|
||||
|
||||
interface TabsState {
|
||||
openedTabs: TabsBase[];
|
||||
openedReactTabs: ReactTabKind[];
|
||||
activeTab: TabsBase;
|
||||
activeReactTab: ReactTabKind;
|
||||
isConnectTabOpen: boolean;
|
||||
isConnectTabActive: boolean;
|
||||
activateTab: (tab: TabsBase) => void;
|
||||
activateNewTab: (tab: TabsBase) => void;
|
||||
activateReactTab: (tabkind: ReactTabKind) => void;
|
||||
updateTab: (tab: TabsBase) => void;
|
||||
getTabs: (tabKind: ViewModels.CollectionTabKind, comparator?: (tab: TabsBase) => boolean) => TabsBase[];
|
||||
refreshActiveTab: (comparator: (tab: TabsBase) => boolean) => void;
|
||||
closeTabsByComparator: (comparator: (tab: TabsBase) => boolean) => void;
|
||||
closeTab: (tab: TabsBase) => void;
|
||||
closeAllNotebookTabs: (hardClose: boolean) => void;
|
||||
openAndActivateReactTab: (tabKind: ReactTabKind) => void;
|
||||
closeReactTab: (tabKind: ReactTabKind) => void;
|
||||
}
|
||||
|
||||
export enum ReactTabKind {
|
||||
Connect,
|
||||
Home,
|
||||
activateConnectTab: () => void;
|
||||
openAndActivateConnectTab: () => void;
|
||||
closeConnectTab: () => void;
|
||||
}
|
||||
|
||||
export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||
openedTabs: [],
|
||||
openedReactTabs: [ReactTabKind.Home],
|
||||
activeTab: undefined,
|
||||
activeReactTab: ReactTabKind.Home,
|
||||
isConnectTabOpen: false,
|
||||
isConnectTabActive: false,
|
||||
activateTab: (tab: TabsBase): void => {
|
||||
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
|
||||
set({ activeTab: tab, activeReactTab: undefined });
|
||||
set({ activeTab: tab, isConnectTabActive: false });
|
||||
tab.onActivate();
|
||||
}
|
||||
},
|
||||
activateNewTab: (tab: TabsBase): void => {
|
||||
set((state) => ({ openedTabs: [...state.openedTabs, tab], activeTab: tab, activeReactTab: undefined }));
|
||||
set((state) => ({ openedTabs: [...state.openedTabs, tab], activeTab: tab, isConnectTabActive: false }));
|
||||
tab.onActivate();
|
||||
},
|
||||
activateReactTab: (tabKind: ReactTabKind): void => set({ activeTab: undefined, activeReactTab: tabKind }),
|
||||
updateTab: (tab: TabsBase) => {
|
||||
if (get().activeTab?.tabId === tab.tabId) {
|
||||
set({ activeTab: tab });
|
||||
@@ -79,7 +73,7 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||
return true;
|
||||
});
|
||||
if (updatedTabs.length === 0) {
|
||||
set({ activeTab: undefined, activeReactTab: ReactTabKind.Home });
|
||||
set({ activeTab: undefined, isConnectTabActive: get().isConnectTabOpen });
|
||||
}
|
||||
|
||||
if (tab.tabId === activeTab.tabId && tabIndex !== -1) {
|
||||
@@ -117,27 +111,21 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||
});
|
||||
|
||||
if (get().openedTabs.length === 0) {
|
||||
set({ activeTab: undefined, activeReactTab: ReactTabKind.Home });
|
||||
set({ activeTab: undefined, isConnectTabActive: get().isConnectTabOpen });
|
||||
}
|
||||
}
|
||||
},
|
||||
openAndActivateReactTab: (tabKind: ReactTabKind) => {
|
||||
if (get().openedReactTabs.indexOf(tabKind) === -1) {
|
||||
set((state) => ({
|
||||
openedReactTabs: [...state.openedReactTabs, tabKind],
|
||||
}));
|
||||
activateConnectTab: () => {
|
||||
if (get().isConnectTabOpen) {
|
||||
set({ isConnectTabActive: true, activeTab: undefined });
|
||||
}
|
||||
|
||||
set({ activeTab: undefined, activeReactTab: tabKind });
|
||||
},
|
||||
closeReactTab: (tabKind: ReactTabKind) => {
|
||||
const { activeReactTab, openedTabs, openedReactTabs } = get();
|
||||
if (activeReactTab === tabKind) {
|
||||
openedTabs?.length > 0
|
||||
? set({ activeTab: openedTabs[0], activeReactTab: undefined })
|
||||
: set({ activeTab: undefined, activeReactTab: openedReactTabs[0] });
|
||||
openAndActivateConnectTab: () => set({ isConnectTabActive: true, isConnectTabOpen: true, activeTab: undefined }),
|
||||
closeConnectTab: () => {
|
||||
const { isConnectTabActive, openedTabs } = get();
|
||||
if (isConnectTabActive && openedTabs?.length > 0) {
|
||||
set({ activeTab: openedTabs[0] });
|
||||
}
|
||||
|
||||
set({ openedReactTabs: openedReactTabs.filter((tab: ReactTabKind) => tabKind !== tab) });
|
||||
set({ isConnectTabActive: false, isConnectTabOpen: false });
|
||||
},
|
||||
}));
|
||||
|
||||
28
utils/deployment-status/package-lock.json
generated
28
utils/deployment-status/package-lock.json
generated
@@ -51,9 +51,12 @@
|
||||
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.1.0",
|
||||
@@ -62,6 +65,25 @@
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.0",
|
||||
"moment": "^2.27.0",
|
||||
"node-fetch": "^2.6.1"
|
||||
"node-fetch": "^2.6.7"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user