Compare commits

..

2 Commits

Author SHA1 Message Date
Srinath Narayanan
f637e685be Merge branch 'master' into users/srnara/selfServeDemo 2021-05-19 09:42:09 +05:30
Srinath Narayanan
c45c5868fb demo initial changes 2021-05-19 09:41:27 +05:30
123 changed files with 3450 additions and 1470 deletions

View File

@@ -1,4 +1,3 @@
{
"JUNO_ENDPOINT": "https://tools-staging.cosmos.azure.com",
"enableSchemaAnalyzer": true
}
"JUNO_ENDPOINT": "https://tools-staging.cosmos.azure.com"
}

18
package-lock.json generated
View File

@@ -5513,6 +5513,12 @@
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA=="
},
"@types/memoize-one": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@types/memoize-one/-/memoize-one-4.1.1.tgz",
"integrity": "sha512-+9djKUUn8hOyktLCfCy4hLaIPgDNovaU36fsnZe9trFHr6ddlbIn2q0SEsnkCkNR+pBWEU440Molz/+Mpyf+gQ==",
"dev": true
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@@ -5583,6 +5589,12 @@
"integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==",
"dev": true
},
"@types/promise.prototype.finally": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/promise.prototype.finally/-/promise.prototype.finally-2.0.3.tgz",
"integrity": "sha512-hQfmCK9Hw8diRIa3KoIDY4aimdxckamHUcmaZeB9tBMyb/Shi1yCBIPfry+nqN4jILNVThY1tnTwdMhQeMjqrw==",
"dev": true
},
"@types/prop-types": {
"version": "15.5.8",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz",
@@ -10734,6 +10746,12 @@
"integrity": "sha512-uoeyx2D5LawJdziMdweOp6cnZzFOOPT9VvPG6gOh6YC7N9pU0k2KpVlRiz/Vc/fFBiGUNNeJq2Aq+9GJ65Nfrw==",
"dev": true
},
"expose-loader": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-0.7.5.tgz",
"integrity": "sha512-iPowgKUZkTPX5PznYsmifVj9Bob0w2wTHVkt/eYNPSzyebkUgIedmskf/kcfEIWpiWjg3JRjnW+a17XypySMuw==",
"dev": true
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",

View File

@@ -116,8 +116,10 @@
"@types/enzyme-adapter-react-16": "1.0.6",
"@types/hasher": "0.0.31",
"@types/jest": "26.0.20",
"@types/memoize-one": "4.1.1",
"@types/node": "12.11.1",
"@types/post-robot": "10.0.1",
"@types/promise.prototype.finally": "2.0.3",
"@types/q": "1.5.1",
"@types/react": "17.0.3",
"@types/react-dom": "17.0.3",
@@ -144,6 +146,7 @@
"eslint-plugin-prefer-arrow": "1.2.2",
"eslint-plugin-react-hooks": "4.2.0",
"expect-playwright": "0.3.3",
"expose-loader": "0.7.5",
"fast-glob": "3.2.5",
"file-loader": "2.0.0",
"fs-extra": "7.0.0",
@@ -205,7 +208,7 @@
"strict:find": "node ./strict-null-checks/find.js",
"strict:add": "node ./strict-null-checks/auto-add.js",
"compile:fullStrict": "tsc -p ./tsconfig.json --strictNullChecks",
"generateARMClients": "npx ts-node --compiler-options '{\"module\":\"commonjs\"}' utils/armClientGenerator/generator.ts"
"generateARMClients": "ts-node --compiler-options '{\"module\":\"commonjs\"}' utils/armClientGenerator/generator.ts"
},
"repository": {
"type": "git",

View File

@@ -44,7 +44,7 @@ export class ArmResourceTypes {
}
export class BackendDefaults {
public static partitionKeyKind = "Hash";
public static partitionKeyKind: string = "Hash";
public static singlePartitionStorageInGb: string = "10";
public static multiPartitionStorageInGb: string = "100";
public static maxChangeFeedRetentionDuration: number = 10;

View File

@@ -32,7 +32,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
<DatePicker
className="addEntityDatePicker"
placeholder={entityValuePlaceholder}
value={entityValue ? new Date(entityValue) : new Date()}
value={entityValue && new Date(entityValue)}
ariaLabel={entityValuePlaceholder}
onSelectDate={onSelectDate}
disabled={isEntityValueDisable}
@@ -59,7 +59,7 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
disabled={isEntityValueDisable}
type={entityValueType}
placeholder={entityValuePlaceholder}
value={typeof entityValue === "string" ? entityValue : ""}
value={typeof entityValue === "string" && entityValue}
onChange={onEntityValueChange}
/>
);

View File

@@ -19,7 +19,7 @@ import { handleError } from "./ErrorHandlingUtils";
export class QueriesClient {
private static readonly PartitionKey: DataModels.PartitionKey = {
paths: [`/${SavedQueries.PartitionKeyProperty}`],
kind: "Hash",
kind: BackendDefaults.partitionKeyKind,
version: BackendDefaults.partitionKeyVersion,
};
private static readonly FetchQuery: string = "SELECT * FROM c";

View File

@@ -10,15 +10,18 @@ import { userContext } from "../../UserContext";
import {
createUpdateCassandraTable,
getCassandraTable,
} from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { createUpdateGremlinGraph, getGremlinGraph } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
createUpdateGremlinGraph,
getGremlinGraph,
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import {
createUpdateMongoDBCollection,
getMongoDBCollection,
} from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/cosmos/tableResources";
import * as ARMTypes from "../../Utils/arm/generatedClients/cosmos/types";
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import * as ARMTypes from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -6,23 +6,23 @@ import { userContext } from "../../UserContext";
import {
createUpdateCassandraKeyspace,
getCassandraKeyspace,
} from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
createUpdateGremlinDatabase,
getGremlinDatabase,
} from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import {
createUpdateMongoDBDatabase,
getMongoDBDatabase,
} from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
CassandraKeyspaceCreateUpdateParameters,
CreateUpdateOptions,
GremlinDatabaseCreateUpdateParameters,
MongoDBDatabaseCreateUpdateParameters,
SqlDatabaseCreateUpdateParameters,
} from "../../Utils/arm/generatedClients/cosmos/types";
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -4,11 +4,11 @@ import { userContext } from "../../UserContext";
import {
createUpdateSqlStoredProcedure,
getSqlStoredProcedure,
} from "../../Utils/arm/generatedClients/cosmos/sqlResources";
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
SqlStoredProcedureCreateUpdateParameters,
SqlStoredProcedureResource,
} from "../../Utils/arm/generatedClients/cosmos/types";
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,8 +1,11 @@
import { TriggerDefinition } from "@azure/cosmos";
import { Resource, TriggerDefinition } from "@azure/cosmos";
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { SqlTriggerCreateUpdateParameters, SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
SqlTriggerCreateUpdateParameters,
SqlTriggerResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
@@ -10,8 +13,8 @@ import { handleError } from "../ErrorHandlingUtils";
export async function createTrigger(
databaseId: string,
collectionId: string,
trigger: SqlTriggerResource
): Promise<TriggerDefinition | SqlTriggerResource> {
trigger: TriggerDefinition
): Promise<TriggerDefinition & Resource> {
const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`);
try {
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
@@ -35,7 +38,7 @@ export async function createTrigger(
const createTriggerParams: SqlTriggerCreateUpdateParameters = {
properties: {
resource: trigger,
resource: trigger as SqlTriggerResource,
options: {},
},
};
@@ -48,13 +51,10 @@ export async function createTrigger(
trigger.id,
createTriggerParams
);
return rpResponse && rpResponse.properties?.resource;
return rpResponse && (rpResponse.properties?.resource as TriggerDefinition & Resource);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.triggers.create((trigger as unknown) as TriggerDefinition); // TODO: TypeScript does not like the SQL SDK trigger type
const response = await client().database(databaseId).container(collectionId).scripts.triggers.create(trigger);
return response.resource;
} catch (error) {
handleError(error, "CreateTrigger", `Error while creating trigger ${trigger.id}`);

View File

@@ -4,11 +4,11 @@ import { userContext } from "../../UserContext";
import {
createUpdateSqlUserDefinedFunction,
getSqlUserDefinedFunction,
} from "../../Utils/arm/generatedClients/cosmos/sqlResources";
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
SqlUserDefinedFunctionCreateUpdateParameters,
SqlUserDefinedFunctionResource,
} from "../../Utils/arm/generatedClients/cosmos/types";
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,10 +1,10 @@
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { deleteCassandraTable } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { deleteGremlinGraph } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { deleteMongoDBCollection } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { deleteSqlContainer } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { deleteTable } from "../../Utils/arm/generatedClients/cosmos/tableResources";
import { deleteCassandraTable } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { deleteGremlinGraph } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { deleteMongoDBCollection } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { deleteSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { deleteTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,9 +1,9 @@
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { deleteCassandraKeyspace } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { deleteGremlinDatabase } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { deleteMongoDBDatabase } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { deleteSqlDatabase } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { deleteCassandraKeyspace } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { deleteGremlinDatabase } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { deleteMongoDBDatabase } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { deleteSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,6 +1,6 @@
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { deleteSqlStoredProcedure } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { deleteSqlStoredProcedure } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,6 +1,6 @@
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { deleteSqlTrigger } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { deleteSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,6 +1,6 @@
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { deleteSqlUserDefinedFunction } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { deleteSqlUserDefinedFunction } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,11 +1,11 @@
import { AuthType } from "../../AuthType";
import { Offer, ReadCollectionOfferParams } from "../../Contracts/DataModels";
import { userContext } from "../../UserContext";
import { getCassandraTableThroughput } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { getGremlinGraphThroughput } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { getMongoDBCollectionThroughput } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { getSqlContainerThroughput } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { getTableThroughput } from "../../Utils/arm/generatedClients/cosmos/tableResources";
import { getCassandraTableThroughput } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { getGremlinGraphThroughput } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { getMongoDBCollectionThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { getSqlContainerThroughput } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { getTableThroughput } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { handleError } from "../ErrorHandlingUtils";
import { readOfferWithSDK } from "./readOfferWithSDK";

View File

@@ -1,11 +1,11 @@
import { AuthType } from "../../AuthType";
import * as DataModels from "../../Contracts/DataModels";
import { userContext } from "../../UserContext";
import { listCassandraTables } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { listGremlinGraphs } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { listMongoDBCollections } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { listSqlContainers } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { listTables } from "../../Utils/arm/generatedClients/cosmos/tableResources";
import { listCassandraTables } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { listGremlinGraphs } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { listMongoDBCollections } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { listSqlContainers } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { listTables } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,10 +1,10 @@
import { AuthType } from "../../AuthType";
import { Offer, ReadDatabaseOfferParams } from "../../Contracts/DataModels";
import { userContext } from "../../UserContext";
import { getCassandraKeyspaceThroughput } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { getGremlinDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { getSqlDatabaseThroughput } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { getCassandraKeyspaceThroughput } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { getGremlinDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { getSqlDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { handleError } from "../ErrorHandlingUtils";
import { readOfferWithSDK } from "./readOfferWithSDK";

View File

@@ -1,10 +1,10 @@
import { AuthType } from "../../AuthType";
import * as DataModels from "../../Contracts/DataModels";
import { userContext } from "../../UserContext";
import { listCassandraKeyspaces } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { listGremlinDatabases } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { listMongoDBDatabases } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { listSqlDatabases } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { listCassandraKeyspaces } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { listGremlinDatabases } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { listMongoDBDatabases } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { listSqlDatabases } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,7 +1,7 @@
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { getMongoDBCollection } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { MongoDBCollectionResource } from "../../Utils/arm/generatedClients/cosmos/types";
import { getMongoDBCollection } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { MongoDBCollectionResource } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,7 +1,7 @@
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { listSqlStoredProcedures } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { listSqlStoredProcedures } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,8 +1,7 @@
import { TriggerDefinition } from "@azure/cosmos";
import { Resource, TriggerDefinition } from "@azure/cosmos";
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { listSqlTriggers } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
import { listSqlTriggers } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
@@ -10,7 +9,7 @@ import { handleError } from "../ErrorHandlingUtils";
export async function readTriggers(
databaseId: string,
collectionId: string
): Promise<SqlTriggerResource[] | TriggerDefinition[]> {
): Promise<(TriggerDefinition & Resource)[]> {
const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`);
try {
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations && userContext.apiType === "SQL") {
@@ -21,7 +20,7 @@ export async function readTriggers(
databaseId,
collectionId
);
return rpResponse?.value?.map((trigger) => trigger.properties?.resource);
return rpResponse?.value?.map((trigger) => trigger.properties?.resource as TriggerDefinition & Resource);
}
const response = await client().database(databaseId).container(collectionId).scripts.triggers.readAll().fetchAll();

View File

@@ -1,7 +1,7 @@
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { listSqlUserDefinedFunctions } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { listSqlUserDefinedFunctions } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -6,20 +6,23 @@ import { userContext } from "../../UserContext";
import {
createUpdateCassandraTable,
getCassandraTable,
} from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { createUpdateGremlinGraph, getGremlinGraph } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
createUpdateGremlinGraph,
getGremlinGraph,
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import {
createUpdateMongoDBCollection,
getMongoDBCollection,
} from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/cosmos/tableResources";
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import {
ExtendedResourceProperties,
MongoDBCollectionCreateUpdateParameters,
SqlContainerCreateUpdateParameters,
SqlContainerResource,
} from "../../Utils/arm/generatedClients/cosmos/types";
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -10,7 +10,7 @@ import {
migrateCassandraTableToManualThroughput,
updateCassandraKeyspaceThroughput,
updateCassandraTableThroughput,
} from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
migrateGremlinDatabaseToAutoscale,
migrateGremlinDatabaseToManualThroughput,
@@ -18,7 +18,7 @@ import {
migrateGremlinGraphToManualThroughput,
updateGremlinDatabaseThroughput,
updateGremlinGraphThroughput,
} from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import {
migrateMongoDBCollectionToAutoscale,
migrateMongoDBCollectionToManualThroughput,
@@ -26,7 +26,7 @@ import {
migrateMongoDBDatabaseToManualThroughput,
updateMongoDBCollectionThroughput,
updateMongoDBDatabaseThroughput,
} from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import {
migrateSqlContainerToAutoscale,
migrateSqlContainerToManualThroughput,
@@ -34,13 +34,13 @@ import {
migrateSqlDatabaseToManualThroughput,
updateSqlContainerThroughput,
updateSqlDatabaseThroughput,
} from "../../Utils/arm/generatedClients/cosmos/sqlResources";
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
migrateTableToAutoscale,
migrateTableToManualThroughput,
updateTableThroughput,
} from "../../Utils/arm/generatedClients/cosmos/tableResources";
import { ThroughputSettingsUpdateParameters } from "../../Utils/arm/generatedClients/cosmos/types";
} from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { ThroughputSettingsUpdateParameters } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { HttpHeaders } from "../Constants";
import { client } from "../CosmosClient";

View File

@@ -4,11 +4,11 @@ import { userContext } from "../../UserContext";
import {
createUpdateSqlStoredProcedure,
getSqlStoredProcedure,
} from "../../Utils/arm/generatedClients/cosmos/sqlResources";
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
SqlStoredProcedureCreateUpdateParameters,
SqlStoredProcedureResource,
} from "../../Utils/arm/generatedClients/cosmos/types";
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -1,8 +1,11 @@
import { TriggerDefinition } from "@azure/cosmos";
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { SqlTriggerCreateUpdateParameters, SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
SqlTriggerCreateUpdateParameters,
SqlTriggerResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
@@ -10,8 +13,8 @@ import { handleError } from "../ErrorHandlingUtils";
export async function updateTrigger(
databaseId: string,
collectionId: string,
trigger: SqlTriggerResource
): Promise<SqlTriggerResource | TriggerDefinition> {
trigger: TriggerDefinition
): Promise<TriggerDefinition> {
const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`);
const { authType, useSDKOperations, apiType, subscriptionId, resourceGroup, databaseAccount } = userContext;
try {
@@ -28,7 +31,7 @@ export async function updateTrigger(
if (getResponse?.properties?.resource) {
const createTriggerParams: SqlTriggerCreateUpdateParameters = {
properties: {
resource: trigger,
resource: trigger as SqlTriggerResource,
options: {},
},
};
@@ -41,7 +44,7 @@ export async function updateTrigger(
trigger.id,
createTriggerParams
);
return rpResponse && rpResponse.properties?.resource;
return rpResponse && (rpResponse.properties?.resource as TriggerDefinition);
}
throw new Error(`Failed to update trigger: ${trigger.id} does not exist.`);
@@ -51,7 +54,7 @@ export async function updateTrigger(
.database(databaseId)
.container(collectionId)
.scripts.trigger(trigger.id)
.replace((trigger as unknown) as TriggerDefinition); // TODO: TypeScript does not like the SQL SDK trigger type
.replace(trigger);
return response?.resource;
} catch (error) {
handleError(error, "UpdateTrigger", `Error while updating trigger ${trigger.id}`);

View File

@@ -4,11 +4,11 @@ import { userContext } from "../../UserContext";
import {
createUpdateSqlUserDefinedFunction,
getSqlUserDefinedFunction,
} from "../../Utils/arm/generatedClients/cosmos/sqlResources";
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
SqlUserDefinedFunctionCreateUpdateParameters,
SqlUserDefinedFunctionResource,
} from "../../Utils/arm/generatedClients/cosmos/types";
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";

View File

@@ -27,7 +27,6 @@ export interface ConfigContext {
hostedExplorerURL: string;
armAPIVersion?: string;
allowedJunoOrigins: string[];
enableSchemaAnalyzer: boolean;
}
// Default configuration
@@ -62,7 +61,6 @@ let configContext: Readonly<ConfigContext> = {
"https://tools-staging.cosmos.azure.com",
"https://localhost",
],
enableSchemaAnalyzer: false,
};
export function resetConfigContext(): void {

View File

@@ -167,7 +167,7 @@ export interface KeyResource {
export interface IndexingPolicy {
automatic: boolean;
indexingMode: "consistent" | "lazy" | "none";
indexingMode: string;
includedPaths: any;
excludedPaths: any;
compositeIndexes?: any;
@@ -176,7 +176,7 @@ export interface IndexingPolicy {
export interface PartitionKey {
paths: string[];
kind: "Hash" | "Range" | "MultiHash";
kind: string;
version: number;
systemKey?: boolean;
}

View File

@@ -15,7 +15,6 @@ import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import Trigger from "../Explorer/Tree/Trigger";
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
import { SelfServeType } from "../SelfServe/SelfServeUtils";
import { SqlTriggerResource } from "../Utils/arm/generatedClients/cosmos/types";
import * as DataModels from "./DataModels";
import { SubscriptionType } from "./SubscriptionType";
@@ -176,7 +175,7 @@ export interface Collection extends CollectionBase {
createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure;
createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction;
createTriggerNode(data: TriggerDefinition | SqlTriggerResource): Trigger;
createTriggerNode(data: TriggerDefinition & Resource): Trigger;
findStoredProcedureWithId(sprocRid: string): StoredProcedure;
findTriggerWithId(triggerRid: string): Trigger;
findUserDefinedFunctionWithId(udfRid: string): UserDefinedFunction;

View File

@@ -3,10 +3,11 @@
*/
import * as React from "react";
import * as Constants from "../../../Common/Constants";
import AnimateHeight from "react-animate-height";
import TriangleDownIcon from "../../../../images/Triangle-down.svg";
import TriangleRightIcon from "../../../../images/Triangle-right.svg";
import * as Constants from "../../../Common/Constants";
export interface AccordionComponentProps {}
@@ -26,12 +27,12 @@ export interface AccordionItemComponentProps {
}
interface AccordionItemComponentState {
isExpanded?: boolean;
isExpanded: boolean;
}
export class AccordionItemComponent extends React.Component<AccordionItemComponentProps, AccordionItemComponentState> {
private static readonly durationMS = 500;
private isExpanded?: boolean;
private isExpanded: boolean;
constructor(props: AccordionItemComponentProps) {
super(props);
@@ -78,7 +79,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
);
}
private onHeaderClick = (_event: React.MouseEvent<HTMLDivElement>): void => {
private onHeaderClick = (event: React.MouseEvent<HTMLDivElement>): void => {
this.setState({ isExpanded: !this.state.isExpanded });
};

View File

@@ -15,22 +15,7 @@ import {
ProgressIndicator,
TextField,
} from "@fluentui/react";
import React, { FC } from "react";
import create, { UseStore } from "zustand";
export interface DialogState {
visible: boolean;
dialogProps?: DialogProps;
openDialog: (props: DialogProps) => void;
closeDialog: () => void;
}
export const useDialog: UseStore<DialogState> = create((set) => ({
visible: false,
openDialog: (props: DialogProps) => set(() => ({ visible: true, dialogProps: props })),
closeDialog: () =>
set((state) => ({ visible: false, openDialog: state.openDialog, closeDialog: state.closeDialog }), true),
}));
import React, { FunctionComponent } from "react";
export interface TextFieldProps extends ITextFieldProps {
label: string;
@@ -50,6 +35,7 @@ export interface DialogProps {
title: string;
subText: string;
isModal: boolean;
visible: boolean;
choiceGroupProps?: IChoiceGroupProps;
textFieldProps?: TextFieldProps;
linkProps?: LinkProps;
@@ -70,26 +56,24 @@ const DIALOG_TITLE_FONT_SIZE = "17px";
const DIALOG_TITLE_FONT_WEIGHT = 400;
const DIALOG_SUBTEXT_FONT_SIZE = "15px";
export const Dialog: FC = () => {
const { visible, dialogProps: props } = useDialog();
const {
title,
subText,
isModal,
choiceGroupProps,
textFieldProps,
linkProps,
progressIndicatorProps,
primaryButtonText,
secondaryButtonText,
onPrimaryButtonClick,
onSecondaryButtonClick,
primaryButtonDisabled,
type,
showCloseButton,
onDismiss,
} = props || {};
export const Dialog: FunctionComponent<DialogProps> = ({
title,
subText,
isModal,
visible,
choiceGroupProps,
textFieldProps,
linkProps,
progressIndicatorProps,
primaryButtonText,
secondaryButtonText,
onPrimaryButtonClick,
onSecondaryButtonClick,
primaryButtonDisabled,
type,
showCloseButton,
onDismiss,
}: DialogProps) => {
const dialogProps: IDialogProps = {
hidden: !visible,
dialogContentProps: {
@@ -121,7 +105,7 @@ export const Dialog: FC = () => {
}
: {};
return visible ? (
return (
<FluentDialog {...dialogProps}>
{choiceGroupProps && <ChoiceGroup {...choiceGroupProps} />}
{textFieldProps && <TextField {...textFieldProps} />}
@@ -136,7 +120,5 @@ export const Dialog: FC = () => {
{secondaryButtonProps && <DefaultButton {...secondaryButtonProps} />}
</DialogFooter>
</FluentDialog>
) : (
<></>
);
};

View File

@@ -0,0 +1,105 @@
import React from "react";
import { shallow, mount } from "enzyme";
import { DefaultDirectoryDropdownComponent, DefaultDirectoryDropdownProps } from "./DefaultDirectoryDropdownComponent";
import { Tenant } from "../../../Contracts/DataModels";
const createBlankProps = (): DefaultDirectoryDropdownProps => {
return {
defaultDirectoryId: "",
directories: [],
onDefaultDirectoryChange: jest.fn(),
};
};
const createBlankDirectory = (): Tenant => {
return {
countryCode: "",
displayName: "",
domains: [],
id: "",
tenantId: "",
};
};
describe("test render", () => {
it("renders with no directories", () => {
const props = createBlankProps();
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with directories but no default", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with directories and default", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
props.defaultDirectoryId = "asdfghjklzxcvbnm9876543210";
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with directories and last visit default", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
props.defaultDirectoryId = "lastVisited";
const wrapper = shallow(<DefaultDirectoryDropdownComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
});
describe("test function", () => {
it("on default directory change", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
props.defaultDirectoryId = "lastVisited";
const wrapper = mount(<DefaultDirectoryDropdownComponent {...props} />);
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
wrapper.find("button.ms-Dropdown-item").at(1).simulate("click");
expect(props.onDefaultDirectoryChange).toBeCalled();
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
wrapper.find("button.ms-Dropdown-item").at(0).simulate("click");
expect(props.onDefaultDirectoryChange).toBeCalled();
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,71 @@
/**
* React component for Switch Directory
*/
import { Dropdown, IDropdownOption, IDropdownProps } from "@fluentui/react";
import * as React from "react";
import _ from "underscore";
import { Tenant } from "../../../Contracts/DataModels";
export interface DefaultDirectoryDropdownProps {
directories: Array<Tenant>;
defaultDirectoryId: string;
onDefaultDirectoryChange: (newDirectory: Tenant) => void;
}
export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDirectoryDropdownProps> {
public static readonly lastVisitedKey: string = "lastVisited";
public render(): JSX.Element {
const lastVisitedOption: IDropdownOption = {
key: DefaultDirectoryDropdownComponent.lastVisitedKey,
text: "Sign in to your last visited directory",
};
const directoryOptions: Array<IDropdownOption> = this.props.directories.map(
(dirc): IDropdownOption => {
return {
key: dirc.tenantId,
text: `${dirc.displayName}(${dirc.tenantId})`,
};
}
);
const dropDownOptions: Array<IDropdownOption> = [lastVisitedOption, ...directoryOptions];
const dropDownProps: IDropdownProps = {
label: "Set your default directory",
options: dropDownOptions,
defaultSelectedKey: this.props.defaultDirectoryId ? this.props.defaultDirectoryId : lastVisitedOption.key,
onChange: this._onDropdownChange,
className: "defaultDirectoryDropdown",
};
return <Dropdown {...dropDownProps} />;
}
private _onDropdownChange = (e: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, index?: number): void => {
if (!option || !option.key) {
return;
}
if (option.key === this.props.defaultDirectoryId) {
return;
}
if (option.key === DefaultDirectoryDropdownComponent.lastVisitedKey) {
this.props.onDefaultDirectoryChange({
tenantId: option.key,
countryCode: undefined,
displayName: undefined,
domains: [],
id: undefined,
});
return;
}
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === option.key);
if (!selectedDirectory) {
return;
}
this.props.onDefaultDirectoryChange(selectedDirectory);
};
}

View File

@@ -0,0 +1,36 @@
import * as ko from "knockout";
import * as React from "react";
import { DirectoryListComponent, DirectoryListProps } from "./DirectoryListComponent";
import { DefaultDirectoryDropdownComponent, DefaultDirectoryDropdownProps } from "./DefaultDirectoryDropdownComponent";
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
export class DirectoryComponentAdapter implements ReactAdapter {
public parameters: ko.Observable<number>;
constructor(
private _dropdownProps: ko.Observable<DefaultDirectoryDropdownProps>,
private _listProps: ko.Observable<DirectoryListProps>
) {
this._dropdownProps.subscribe(() => this.forceRender());
this._listProps.subscribe(() => this.forceRender());
this.parameters = ko.observable<number>(Date.now());
}
public renderComponent(): JSX.Element {
return (
<div>
<div className="directoryDropdownContainer">
<DefaultDirectoryDropdownComponent {...this._dropdownProps()} />
</div>
<div className="directoryDivider" />
<div className="directoryListContainer">
<DirectoryListComponent {...this._listProps()} />
</div>
</div>
);
}
public forceRender(): void {
window.requestAnimationFrame(() => this.parameters(Date.now()));
}
}

View File

@@ -0,0 +1,78 @@
import React from "react";
import { shallow, mount } from "enzyme";
import { DirectoryListComponent, DirectoryListProps } from "./DirectoryListComponent";
import { Tenant } from "../../../Contracts/DataModels";
const createBlankProps = (): DirectoryListProps => {
return {
selectedDirectoryId: undefined,
directories: [],
onNewDirectorySelected: jest.fn(),
};
};
const createBlankDirectory = (): Tenant => {
return {
countryCode: undefined,
displayName: undefined,
domains: [],
id: undefined,
tenantId: undefined,
};
};
describe("test render", () => {
it("renders with no directories", () => {
const props = createBlankProps();
const wrapper = shallow(<DirectoryListComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with directories and selected", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "asdfghjklzxcvbnm9876543210";
props.directories = [tenant1, tenant2];
props.selectedDirectoryId = "asdfghjklzxcvbnm9876543210";
const wrapper = shallow(<DirectoryListComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders with filters", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "1234567890";
const tenant2 = createBlankDirectory();
tenant1.displayName = "Macrohard";
tenant1.tenantId = "9876543210";
props.directories = [tenant1, tenant2];
props.selectedDirectoryId = "9876543210";
const wrapper = mount(<DirectoryListComponent {...props} />);
wrapper.find("input.ms-TextField-field").simulate("change", { target: { value: "Macro" } });
expect(wrapper).toMatchSnapshot();
});
});
describe("test function", () => {
it("on new directory selected", () => {
const props = createBlankProps();
const tenant1 = createBlankDirectory();
tenant1.displayName = "Microsoft";
tenant1.tenantId = "asdfghjklzxcvbnm1234567890";
props.directories = [tenant1];
const wrapper = mount(<DirectoryListComponent {...props} />);
wrapper.find("button.directoryListButton").simulate("click");
expect(props.onNewDirectorySelected).toBeCalled();
expect(props.onNewDirectorySelected).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,126 @@
import {
DefaultButton,
IButtonProps,
ITextFieldProps,
List,
ScrollablePane,
Sticky,
StickyPositionType,
TextField,
} from "@fluentui/react";
import * as React from "react";
import _ from "underscore";
import { Tenant } from "../../../Contracts/DataModels";
export interface DirectoryListProps {
directories: Array<Tenant>;
selectedDirectoryId: string;
onNewDirectorySelected: (newDirectory: Tenant) => void;
}
export interface DirectoryListComponentState {
filterText: string;
}
// onRenderCell is not called when selectedDirectoryId changed, so add a selected state to force render
interface ListTenant extends Tenant {
selected?: boolean;
}
export class DirectoryListComponent extends React.Component<DirectoryListProps, DirectoryListComponentState> {
constructor(props: DirectoryListProps) {
super(props);
this.state = {
filterText: "",
};
}
public render(): JSX.Element {
const { directories: originalItems, selectedDirectoryId } = this.props;
const { filterText } = this.state;
const filteredItems =
originalItems && originalItems.length && filterText
? originalItems.filter(
(directory) =>
directory.displayName &&
directory.displayName.toLowerCase().indexOf(filterText && filterText.toLowerCase()) >= 0
)
: originalItems;
const filteredItemsSelected = filteredItems.map((t) => {
let tenant: ListTenant = t;
tenant.selected = t.tenantId === selectedDirectoryId;
return tenant;
});
const textFieldProps: ITextFieldProps = {
className: "directoryListFilterTextBox",
placeholder: "Filter by directory name",
onChange: this._onFilterChanged,
ariaLabel: "Directory filter text box",
};
// TODO: add magnify glass to search bar with onRenderSuffix
return (
<ScrollablePane data-is-scrollable="true">
<Sticky stickyPosition={StickyPositionType.Header}>
<TextField {...textFieldProps} />
</Sticky>
<List items={filteredItemsSelected} onRenderCell={this._onRenderCell} />
</ScrollablePane>
);
}
private _onFilterChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text?: string): void => {
this.setState({
filterText: text,
});
};
private _onRenderCell = (directory: ListTenant): JSX.Element => {
const buttonProps: IButtonProps = {
disabled: directory.selected || false,
className: "directoryListButton",
onClick: this._onNewDirectoryClick,
styles: {
root: {
backgroundColor: "transparent",
height: "auto",
borderBottom: "1px solid #ccc",
padding: "1px 0",
width: "100%",
},
rootDisabled: {
backgroundColor: "#f1f1f8",
},
rootHovered: {
backgroundColor: "rgba(85,179,255,.1)",
},
flexContainer: {
height: "auto",
justifyContent: "flex-start",
},
},
};
return (
<DefaultButton {...buttonProps}>
<div className="directoryListItem" data-is-focusable={true}>
<div className="directoryListItemName">{directory.displayName}</div>
<div className="directoryListItemId">{directory.tenantId}</div>
</div>
</DefaultButton>
);
};
private _onNewDirectoryClick = (e: React.MouseEvent<HTMLButtonElement>): void => {
if (!e || !e.currentTarget) {
return;
}
const buttonElement = e.currentTarget;
const selectedDirectoryId = buttonElement.getElementsByClassName("directoryListItemId")[0].textContent;
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === selectedDirectoryId);
this.props.onNewDirectorySelected(selectedDirectory);
};
}

View File

@@ -0,0 +1,93 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test render renders with directories and default 1`] = `
<Dropdown
className="defaultDirectoryDropdown"
defaultSelectedKey="asdfghjklzxcvbnm9876543210"
label="Set your default directory"
onChange={[Function]}
options={
Array [
Object {
"key": "lastVisited",
"text": "Sign in to your last visited directory",
},
Object {
"key": "asdfghjklzxcvbnm9876543210",
"text": "Macrohard(asdfghjklzxcvbnm9876543210)",
},
Object {
"key": "",
"text": "()",
},
]
}
/>
`;
exports[`test render renders with directories and last visit default 1`] = `
<Dropdown
className="defaultDirectoryDropdown"
defaultSelectedKey="lastVisited"
label="Set your default directory"
onChange={[Function]}
options={
Array [
Object {
"key": "lastVisited",
"text": "Sign in to your last visited directory",
},
Object {
"key": "asdfghjklzxcvbnm9876543210",
"text": "Macrohard(asdfghjklzxcvbnm9876543210)",
},
Object {
"key": "",
"text": "()",
},
]
}
/>
`;
exports[`test render renders with directories but no default 1`] = `
<Dropdown
className="defaultDirectoryDropdown"
defaultSelectedKey="lastVisited"
label="Set your default directory"
onChange={[Function]}
options={
Array [
Object {
"key": "lastVisited",
"text": "Sign in to your last visited directory",
},
Object {
"key": "asdfghjklzxcvbnm9876543210",
"text": "Macrohard(asdfghjklzxcvbnm9876543210)",
},
Object {
"key": "",
"text": "()",
},
]
}
/>
`;
exports[`test render renders with no directories 1`] = `
<Dropdown
className="defaultDirectoryDropdown"
defaultSelectedKey="lastVisited"
label="Set your default directory"
onChange={[Function]}
options={
Array [
Object {
"key": "lastVisited",
"text": "Sign in to your last visited directory",
},
]
}
/>
`;

View File

@@ -28,6 +28,7 @@ import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryCons
import { trace } from "../../../Shared/Telemetry/TelemetryProcessor";
import * as GalleryUtils from "../../../Utils/GalleryUtils";
import Explorer from "../../Explorer";
import { Dialog, DialogProps } from "../Dialog";
import { GalleryCardComponent, GalleryCardComponentProps } from "./Cards/GalleryCardComponent";
import { CodeOfConduct } from "./CodeOfConduct/CodeOfConduct";
import "./GalleryViewerComponent.less";
@@ -67,6 +68,7 @@ interface GalleryViewerComponentState {
selectedTab: GalleryTab;
sortBy: SortBy;
searchText: string;
dialogProps: DialogProps;
isCodeOfConductAccepted: boolean;
isFetchingPublishedNotebooks: boolean;
isFetchingFavouriteNotebooks: boolean;
@@ -117,6 +119,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
selectedTab: props.selectedTab,
sortBy: props.sortBy,
searchText: props.searchText,
dialogProps: undefined,
isCodeOfConductAccepted: undefined,
isFetchingFavouriteNotebooks: true,
isFetchingPublishedNotebooks: true,
@@ -184,6 +187,8 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
return (
<div className="galleryContainer">
<Pivot {...pivotProps}>{pivotItems}</Pivot>
{this.state.dialogProps && <Dialog {...this.state.dialogProps} />}
</div>
);
}

View File

@@ -1,25 +1,25 @@
/**
* Wrapper around Notebook Viewer Read only content
*/
import { IChoiceGroupProps, Icon, IProgressIndicatorProps, Link, ProgressIndicator } from "@fluentui/react";
import { Notebook } from "@nteract/commutable";
import { createContentRef } from "@nteract/core";
import { IChoiceGroupProps, Icon, IProgressIndicatorProps, Link, ProgressIndicator } from "@fluentui/react";
import * as React from "react";
import { contents } from "rx-jupyter";
import { getErrorMessage, getErrorStack, handleError } from "../../../Common/ErrorHandlingUtils";
import { IGalleryItem, JunoClient } from "../../../Juno/JunoClient";
import { SessionStorageUtility } from "../../../Shared/StorageUtility";
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
import * as GalleryUtils from "../../../Utils/GalleryUtils";
import { DialogHost } from "../../../Utils/GalleryUtils";
import Explorer from "../../Explorer";
import { NotebookClientV2 } from "../../Notebook/NotebookClientV2";
import { NotebookComponentBootstrapper } from "../../Notebook/NotebookComponent/NotebookComponentBootstrapper";
import NotebookReadOnlyRenderer from "../../Notebook/NotebookRenderer/NotebookReadOnlyRenderer";
import { Dialog, TextFieldProps, useDialog } from "../Dialog";
import { Dialog, DialogProps, TextFieldProps } from "../Dialog";
import { NotebookMetadataComponent } from "./NotebookMetadataComponent";
import "./NotebookViewerComponent.less";
import Explorer from "../../Explorer";
import { SessionStorageUtility } from "../../../Shared/StorageUtility";
import { DialogHost } from "../../../Utils/GalleryUtils";
import { getErrorMessage, getErrorStack, handleError } from "../../../Common/ErrorHandlingUtils";
import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
export interface NotebookViewerComponentProps {
container?: Explorer;
@@ -38,6 +38,7 @@ interface NotebookViewerComponentState {
content: Notebook;
galleryItem?: IGalleryItem;
isFavorite?: boolean;
dialogProps: DialogProps;
showProgressBar: boolean;
}
@@ -69,6 +70,7 @@ export class NotebookViewerComponent
content: undefined,
galleryItem: props.galleryItem,
isFavorite: props.isFavorite,
dialogProps: undefined,
showProgressBar: true,
};
@@ -164,7 +166,8 @@ export class NotebookViewerComponent
hideInputs: this.props.hideInputs,
hidePrompts: this.props.hidePrompts,
})}
<Dialog />
{this.state.dialogProps && <Dialog {...this.state.dialogProps} />}
</div>
);
}
@@ -190,6 +193,7 @@ export class NotebookViewerComponent
};
}
// DialogHost
showOkModalDialog(
title: string,
msg: string,
@@ -197,21 +201,25 @@ export class NotebookViewerComponent
onOk: () => void,
progressIndicatorProps?: IProgressIndicatorProps
): void {
useDialog.getState().openDialog({
isModal: true,
title,
subText: msg,
primaryButtonText: okLabel,
onPrimaryButtonClick: () => {
useDialog.getState().closeDialog();
onOk && onOk();
this.setState({
dialogProps: {
isModal: true,
visible: true,
title,
subText: msg,
primaryButtonText: okLabel,
onPrimaryButtonClick: () => {
this.setState({ dialogProps: undefined });
onOk && onOk();
},
secondaryButtonText: undefined,
onSecondaryButtonClick: undefined,
progressIndicatorProps,
},
secondaryButtonText: undefined,
onSecondaryButtonClick: undefined,
progressIndicatorProps,
});
}
// DialogHost
showOkCancelModalDialog(
title: string,
msg: string,
@@ -224,24 +232,27 @@ export class NotebookViewerComponent
textFieldProps?: TextFieldProps,
primaryButtonDisabled?: boolean
): void {
useDialog.getState().openDialog({
isModal: true,
title,
subText: msg,
primaryButtonText: okLabel,
secondaryButtonText: cancelLabel,
onPrimaryButtonClick: () => {
useDialog.getState().closeDialog();
onOk && onOk();
this.setState({
dialogProps: {
isModal: true,
visible: true,
title,
subText: msg,
primaryButtonText: okLabel,
secondaryButtonText: cancelLabel,
onPrimaryButtonClick: () => {
this.setState({ dialogProps: undefined });
onOk && onOk();
},
onSecondaryButtonClick: () => {
this.setState({ dialogProps: undefined });
onCancel && onCancel();
},
progressIndicatorProps,
choiceGroupProps,
textFieldProps,
primaryButtonDisabled,
},
onSecondaryButtonClick: () => {
useDialog.getState().closeDialog();
onCancel && onCancel();
},
progressIndicatorProps,
choiceGroupProps,
textFieldProps,
primaryButtonDisabled,
});
}

View File

@@ -0,0 +1,16 @@
@import "../../../../less/Common/Constants.less";
.radioSwitchComponent {
cursor: pointer;
display: flex;
&>span:nth-child(n+2) {
margin-left: 10px;
}
.caption {
color: @BaseDark;
padding-left: @SmallSpace;
vertical-align: top;
}
}

View File

@@ -0,0 +1,51 @@
/**
* Horizontal switch component
*/
import { Icon } from "@fluentui/react";
import * as React from "react";
import { NormalizedEventKey } from "../../../Common/Constants";
import "./RadioSwitchComponent.less";
export interface Choice {
key: string;
onSelect: () => void;
label: string;
}
export interface RadioSwitchComponentProps {
choices: Choice[];
selectedKey: string;
onSelectionKeyChange?: (newValue: string) => void;
}
export class RadioSwitchComponent extends React.Component<RadioSwitchComponentProps> {
public render(): JSX.Element {
return (
<div className="radioSwitchComponent">
{this.props.choices.map((choice: Choice) => (
<span
tabIndex={0}
key={choice.key}
onClick={() => this.onSelect(choice)}
onKeyPress={(event) => this.onKeyPress(event, choice)}
>
<Icon iconName={this.props.selectedKey === choice.key ? "RadioBtnOn" : "RadioBtnOff"} />
<span className="caption">{choice.label}</span>
</span>
))}
</div>
);
}
private onSelect(choice: Choice): void {
this.props.onSelectionKeyChange && this.props.onSelectionKeyChange(choice.key);
choice.onSelect();
}
private onKeyPress(event: React.KeyboardEvent<HTMLSpanElement>, choice: Choice): void {
if (event.key === NormalizedEventKey.Enter || event.key === NormalizedEventKey.Space) {
this.onSelect(choice);
}
}
}

View File

@@ -0,0 +1,45 @@
/**
* Generic abstract React component that senses its dimensions.
* It updates its state and re-renders if dimensions change.
*/
import * as React from "react";
import * as ResizeSensor from "css-element-queries/src/ResizeSensor";
export abstract class ResizeSensorComponent<P, S> extends React.Component<P, S> {
private isSensing: boolean = false;
private resizeSensor: any;
public constructor(props: P) {
super(props);
}
protected abstract onDimensionsChanged(width: number, height: number): void;
protected abstract getSensorTarget(): HTMLElement;
public componentDidUpdate(): void {
if (this.isSensing) {
return;
}
const bar = this.getSensorTarget();
if (bar.clientWidth > 0 || bar.clientHeight > 0) {
const oldPosition = bar.style.position;
// TODO Find a better way to use constructor
this.resizeSensor = new (ResizeSensor as any)(bar, () => {
this.onDimensionsChanged(bar.clientWidth, bar.clientHeight);
});
this.isSensing = true;
// ResizeSensor.js sets position to 'relative' which makes the dropdown menu appear clipped.
// Undoing doesn't seem to affect resize sensing functionality.
bar.style.position = oldPosition;
}
}
public componentWillUnmount(): void {
if (!!this.resizeSensor) {
this.resizeSensor.detach();
}
}
}

View File

@@ -14,7 +14,7 @@ import * as ViewModels from "../../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import { trace, traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../../UserContext";
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../../Explorer";

View File

@@ -1,12 +1,12 @@
import { shallow } from "enzyme";
import React from "react";
import * as DataModels from "../../../../Contracts/DataModels";
import { IndexingPolicyComponent, IndexingPolicyComponentProps } from "./IndexingPolicyComponent";
import * as DataModels from "../../../../Contracts/DataModels";
describe("IndexingPolicyComponent", () => {
const initialIndexingPolicyContent: DataModels.IndexingPolicy = {
automatic: false,
indexingMode: "consistent",
indexingMode: "",
includedPaths: [],
excludedPaths: [],
};

View File

@@ -1,44 +1,44 @@
import * as React from "react";
import {
DetailsList,
DetailsListLayoutMode,
IColumn,
Stack,
IconButton,
Text,
SelectionMode,
IColumn,
MessageBar,
MessageBarType,
SelectionMode,
Separator,
Spinner,
SpinnerSize,
Stack,
Text,
Separator,
} from "@fluentui/react";
import * as React from "react";
import { MongoIndex } from "../../../../../Utils/arm/generatedClients/cosmos/types";
import { CollapsibleSectionComponent } from "../../../CollapsiblePanel/CollapsibleSectionComponent";
import {
addMongoIndexStackProps,
createAndAddMongoIndexStackProps,
customDetailsListStyles,
mongoIndexingPolicyDisclaimer,
mediumWidthStackStyles,
subComponentStackProps,
createAndAddMongoIndexStackProps,
separatorStyles,
indexingPolicynUnsavedWarningMessage,
infoAndToolTipTextStyle,
mediumWidthStackStyles,
mongoCompoundIndexNotSupportedMessage,
mongoIndexingPolicyDisclaimer,
onRenderRow,
separatorStyles,
subComponentStackProps,
mongoCompoundIndexNotSupportedMessage,
} from "../../SettingsRenderUtils";
import { MongoIndex } from "../../../../../Utils/arm/generatedClients/2020-04-01/types";
import {
MongoIndexTypes,
AddMongoIndexProps,
MongoIndexIdField,
MongoNotificationType,
getMongoIndexType,
getMongoIndexTypeText,
isIndexTransforming,
MongoIndexIdField,
MongoIndexTypes,
MongoNotificationType,
} from "../../SettingsUtils";
import { IndexingPolicyRefreshComponent } from "../IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
import { AddMongoIndexComponent } from "./AddMongoIndexComponent";
import { CollapsibleSectionComponent } from "../../../CollapsiblePanel/CollapsibleSectionComponent";
import { IndexingPolicyRefreshComponent } from "../IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
export interface MongoIndexingPolicyComponentProps {
mongoIndexes: MongoIndex[];

View File

@@ -202,12 +202,10 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
}
private getFreeTierInfoMessage(): JSX.Element {
const freeTierLimits = SharedConstants.FreeTierLimits;
return (
<Text>
With free tier, you will get the first {freeTierLimits.RU} RU/s and {freeTierLimits.Storage} GB of storage in
this account for free. To keep your account free, keep the total RU/s across all resources in the account to{" "}
{freeTierLimits.RU} RU/s.
With free tier, you will get the first 400 RU/s and 5 GB of storage in this account for free. To keep your
account free, keep the total RU/s across all resources in the account to 400 RU/s.
<Link
href="https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill#billing-examples-with-free-tier-accounts"
target="_blank"

View File

@@ -155,9 +155,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
this.state = {
spendAckChecked: this.props.spendAckChecked,
exceedFreeTierThroughput:
this.props.isFreeTierAccount &&
!this.props.isAutoPilotSelected &&
this.props.throughput > SharedConstants.FreeTierLimits.RU,
this.props.isFreeTierAccount && !this.props.isAutoPilotSelected && this.props.throughput > 400,
};
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
@@ -443,9 +441,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
if (this.overrideWithAutoPilotSettings()) {
this.props.onMaxAutoPilotThroughputChange(newThroughput);
} else {
this.setState({
exceedFreeTierThroughput: this.props.isFreeTierAccount && newThroughput > SharedConstants.FreeTierLimits.RU,
});
this.setState({ exceedFreeTierThroughput: this.props.isFreeTierAccount && newThroughput > 400 });
this.props.onThroughputChange(newThroughput);
}
};
@@ -585,7 +581,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
styles={messageBarStyles}
>
{`Billing will apply if you provision more than ${SharedConstants.FreeTierLimits.RU} RU/s of manual throughput, or if the resource scales beyond ${SharedConstants.FreeTierLimits.RU} RU/s with autoscale.`}
{
"Billing will apply if you provision more than 400 RU/s of manual throughput, or if the resource scales beyond 400 RU/s with autoscale."
}
</MessageBar>
)}
{this.props.getThroughputWarningMessage() && (

View File

@@ -1,7 +1,7 @@
import * as Constants from "../../../Common/Constants";
import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels";
import { MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
import * as DataModels from "../../../Contracts/DataModels";
import * as Constants from "../../../Common/Constants";
import { MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
const zeroValue = 0;
export type isDirtyTypes = boolean | string | number | DataModels.IndexingPolicy;

View File

@@ -1,7 +1,7 @@
import ko from "knockout";
import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels";
import Explorer from "../../Explorer";
import ko from "knockout";
export const container = new Explorer();
@@ -13,7 +13,7 @@ export const collection = ({
analyticalStorageTtl: ko.observable<number>(undefined),
indexingPolicy: ko.observable<DataModels.IndexingPolicy>({
automatic: true,
indexingMode: "consistent",
indexingMode: "default",
includedPaths: [],
excludedPaths: [],
}),

View File

@@ -28,6 +28,8 @@ exports[`SettingsComponent renders 1`] = `
"changeFeedPolicy": [Function],
"conflictResolutionPolicy": [Function],
"container": Explorer {
"_closeModalDialog": [Function],
"_closeSynapseLinkModalDialog": [Function],
"_isAfecFeatureRegistered": [Function],
"_isInitializingNotebooks": false,
"_refreshSparkEnabledStateForAccount": [Function],
@@ -36,6 +38,7 @@ exports[`SettingsComponent renders 1`] = `
"arcadiaToken": [Function],
"canExceedMaximumValue": [Function],
"canSaveQueries": [Function],
"closeDialog": undefined,
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"collectionCreationDefaults": Object {
@@ -828,6 +831,7 @@ exports[`SettingsComponent renders 1`] = `
},
"hasStorageAnalyticsAfecFeature": [Function],
"isAccountReady": [Function],
"isAutoscaleDefaultEnabled": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
@@ -852,6 +856,7 @@ exports[`SettingsComponent renders 1`] = `
"onGitHubClientError": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openDialog": undefined,
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
@@ -919,6 +924,8 @@ exports[`SettingsComponent renders 1`] = `
}
container={
Explorer {
"_closeModalDialog": [Function],
"_closeSynapseLinkModalDialog": [Function],
"_isAfecFeatureRegistered": [Function],
"_isInitializingNotebooks": false,
"_refreshSparkEnabledStateForAccount": [Function],
@@ -927,6 +934,7 @@ exports[`SettingsComponent renders 1`] = `
"arcadiaToken": [Function],
"canExceedMaximumValue": [Function],
"canSaveQueries": [Function],
"closeDialog": undefined,
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"collectionCreationDefaults": Object {
@@ -1719,6 +1727,7 @@ exports[`SettingsComponent renders 1`] = `
},
"hasStorageAnalyticsAfecFeature": [Function],
"isAccountReady": [Function],
"isAutoscaleDefaultEnabled": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
@@ -1743,6 +1752,7 @@ exports[`SettingsComponent renders 1`] = `
"onGitHubClientError": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openDialog": undefined,
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
@@ -1823,6 +1833,8 @@ exports[`SettingsComponent renders 1`] = `
"changeFeedPolicy": [Function],
"conflictResolutionPolicy": [Function],
"container": Explorer {
"_closeModalDialog": [Function],
"_closeSynapseLinkModalDialog": [Function],
"_isAfecFeatureRegistered": [Function],
"_isInitializingNotebooks": false,
"_refreshSparkEnabledStateForAccount": [Function],
@@ -1831,6 +1843,7 @@ exports[`SettingsComponent renders 1`] = `
"arcadiaToken": [Function],
"canExceedMaximumValue": [Function],
"canSaveQueries": [Function],
"closeDialog": undefined,
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"collectionCreationDefaults": Object {
@@ -2623,6 +2636,7 @@ exports[`SettingsComponent renders 1`] = `
},
"hasStorageAnalyticsAfecFeature": [Function],
"isAccountReady": [Function],
"isAutoscaleDefaultEnabled": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
@@ -2647,6 +2661,7 @@ exports[`SettingsComponent renders 1`] = `
"onGitHubClientError": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openDialog": undefined,
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
@@ -2714,6 +2729,8 @@ exports[`SettingsComponent renders 1`] = `
}
container={
Explorer {
"_closeModalDialog": [Function],
"_closeSynapseLinkModalDialog": [Function],
"_isAfecFeatureRegistered": [Function],
"_isInitializingNotebooks": false,
"_refreshSparkEnabledStateForAccount": [Function],
@@ -2722,6 +2739,7 @@ exports[`SettingsComponent renders 1`] = `
"arcadiaToken": [Function],
"canExceedMaximumValue": [Function],
"canSaveQueries": [Function],
"closeDialog": undefined,
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"collectionCreationDefaults": Object {
@@ -3514,6 +3532,7 @@ exports[`SettingsComponent renders 1`] = `
},
"hasStorageAnalyticsAfecFeature": [Function],
"isAccountReady": [Function],
"isAutoscaleDefaultEnabled": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
@@ -3538,6 +3557,7 @@ exports[`SettingsComponent renders 1`] = `
"onGitHubClientError": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openDialog": undefined,
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {
@@ -3619,7 +3639,7 @@ exports[`SettingsComponent renders 1`] = `
"automatic": true,
"excludedPaths": Array [],
"includedPaths": Array [],
"indexingMode": "consistent",
"indexingMode": "default",
}
}
indexingPolicyContentBaseline={
@@ -3627,7 +3647,7 @@ exports[`SettingsComponent renders 1`] = `
"automatic": true,
"excludedPaths": Array [],
"includedPaths": Array [],
"indexingMode": "consistent",
"indexingMode": "default",
}
}
logIndexingPolicySuccessMessage={[Function]}

View File

@@ -187,8 +187,9 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
<TooltipHost
directionalHint={DirectionalHint.topLeftEdge}
content={
showFreeTierExceedThroughputTooltip && throughput > SharedConstants.FreeTierLimits.RU
? `The first ${SharedConstants.FreeTierLimits.RU} RU/s in this account are free. Billing will apply to any throughput beyond ${SharedConstants.FreeTierLimits.RU} RU/s.`
showFreeTierExceedThroughputTooltip &&
throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs400
? "The first 400 RU/s in this account are free. Billing will apply to any throughput beyond 400 RU/s."
: undefined
}
>

View File

@@ -1,13 +1,13 @@
import * as ko from "knockout";
import { KeyCodes } from "../../../Common/Constants";
import * as ViewModels from "../../../Contracts/ViewModels";
import { FreeTierLimits } from "../../../Shared/Constants";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
import * as ko from "knockout";
import * as ViewModels from "../../../Contracts/ViewModels";
import ThroughputInputComponentAutoscaleV3 from "./ThroughputInputComponentAutoscaleV3.html";
import { KeyCodes } from "../../../Common/Constants";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
import { userContext } from "../../../UserContext";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
/**
* Throughput Input:
*
@@ -236,11 +236,11 @@ export class ThroughputInputViewModel extends WaitsForTemplateViewModel {
this.freeTierExceedThroughputTooltip = options.freeTierExceedThroughputTooltip || ko.observable<string>();
this.freeTierExceedThroughputWarning = options.freeTierExceedThroughputWarning || ko.observable<string>();
this.showFreeTierExceedThroughputTooltip = ko.pureComputed<boolean>(
() => !!this.freeTierExceedThroughputTooltip() && this.value() > FreeTierLimits.RU
() => !!this.freeTierExceedThroughputTooltip() && this.value() > 400
);
this.showFreeTierExceedThroughputWarning = ko.pureComputed<boolean>(
() => !!this.freeTierExceedThroughputWarning() && this.value() > FreeTierLimits.RU
() => !!this.freeTierExceedThroughputWarning() && this.value() > 400
);
}

View File

@@ -41,7 +41,7 @@ import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/No
import * as ComponentRegisterer from "./ComponentRegisterer";
import { ArcadiaWorkspaceItem } from "./Controls/Arcadia/ArcadiaMenuPicker";
import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent";
import { DialogProps, TextFieldProps, useDialog } from "./Controls/Dialog";
import { DialogProps, TextFieldProps } from "./Controls/Dialog";
import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent";
import { CommandBarComponentAdapter } from "./Menus/CommandBar/CommandBarComponentAdapter";
import { ConsoleData } from "./Menus/NotificationConsole/NotificationConsoleComponent";
@@ -93,6 +93,8 @@ export interface ExplorerParams {
setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
openSidePanel: (headerText: string, panelContent: JSX.Element, onClose?: () => void) => void;
closeSidePanel: () => void;
closeDialog: () => void;
openDialog: (props: DialogProps) => void;
tabsManager: TabsManager;
}
@@ -155,6 +157,7 @@ export default class Explorer {
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
public isMongoIndexingEnabled: ko.Observable<boolean>;
public canExceedMaximumValue: ko.Computed<boolean>;
public isAutoscaleDefaultEnabled: ko.Observable<boolean>;
public isSchemaEnabled: ko.Computed<boolean>;
// Notebooks
@@ -171,6 +174,8 @@ export default class Explorer {
public isSynapseLinkUpdating: ko.Observable<boolean>;
public memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
public notebookManager?: NotebookManager;
public openDialog: ExplorerParams["openDialog"];
public closeDialog: ExplorerParams["closeDialog"];
public isShellEnabled: ko.Observable<boolean>;
@@ -195,6 +200,8 @@ export default class Explorer {
this.setInProgressConsoleDataIdToBeDeleted = params?.setInProgressConsoleDataIdToBeDeleted;
this.openSidePanel = params?.openSidePanel;
this.closeSidePanel = params?.closeSidePanel;
this.closeDialog = params?.closeDialog;
this.openDialog = params?.openDialog;
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
dataExplorerArea: Constants.Areas.ResourceTree,
@@ -306,6 +313,8 @@ export default class Explorer {
this.isSchemaEnabled = ko.computed<boolean>(() => userContext.features.enableSchema);
this.isAutoscaleDefaultEnabled = ko.observable<boolean>(false);
this.databases = ko.observableArray<ViewModels.Database>();
this.canSaveQueries = ko.computed<boolean>(() => {
const savedQueriesDatabase: ViewModels.Database = _.find(
@@ -533,10 +542,6 @@ export default class Explorer {
],
});
}
if (configContext.enableSchemaAnalyzer) {
userContext.features.enableSchemaAnalyzer = true;
}
}
private onGitHubClientError = (error: any): void => {
@@ -563,6 +568,7 @@ export default class Explorer {
linkUrl: "https://aka.ms/cosmosdb-synapselink",
},
isModal: true,
visible: true,
title: `Enable Azure Synapse Link on your Cosmos DB account`,
subText: `Enable Azure Synapse Link to perform near real time analytical analytics on this account, without impacting the performance of your transactional workloads.
Azure Synapse Link brings together Cosmos Db Analytical Store and Synapse Analytics`,
@@ -575,7 +581,7 @@ export default class Explorer {
"Enabling Azure Synapse Link for this account. This may take a few minutes before you can enable analytical store for this account."
);
this.isSynapseLinkUpdating(true);
useDialog.getState().closeDialog();
this._closeSynapseLinkModalDialog();
const resourceProviderClient = new ResourceProviderClientFactory().getOrCreate(userContext.databaseAccount.id);
@@ -603,11 +609,11 @@ export default class Explorer {
},
onSecondaryButtonClick: () => {
useDialog.getState().closeDialog();
this._closeSynapseLinkModalDialog();
TelemetryProcessor.traceCancel(Action.EnableAzureSynapseLink);
},
};
useDialog.getState().openDialog(addSynapseLinkDialogProps);
this.openDialog(addSynapseLinkDialogProps);
TelemetryProcessor.traceStart(Action.EnableAzureSynapseLink);
// TODO: return result
@@ -885,14 +891,15 @@ export default class Explorer {
const resetConfirmationDialogProps: DialogProps = {
isModal: true,
visible: true,
title: "Reset Workspace",
subText: "This lets you keep your notebook files and the workspace will be restored to default. Proceed anyway?",
primaryButtonText: "OK",
secondaryButtonText: "Cancel",
onPrimaryButtonClick: this._resetNotebookWorkspace,
onSecondaryButtonClick: () => useDialog.getState().closeDialog(),
onSecondaryButtonClick: this._closeModalDialog,
};
useDialog.getState().openDialog(resetConfirmationDialogProps);
this.openDialog(resetConfirmationDialogProps);
}
private async _containsDefaultNotebookWorkspace(databaseAccount: DataModels.DatabaseAccount): Promise<boolean> {
@@ -937,7 +944,7 @@ export default class Explorer {
}
private _resetNotebookWorkspace = async () => {
useDialog.getState().closeDialog();
this._closeModalDialog();
const clearInProgressMessage = logConsoleProgress("Resetting notebook workspace");
try {
await this.notebookManager?.notebookClient.resetWorkspace();
@@ -955,6 +962,14 @@ export default class Explorer {
}
};
private _closeModalDialog = () => {
this.closeDialog();
};
private _closeSynapseLinkModalDialog = () => {
this.closeDialog();
};
public findSelectedDatabase(): ViewModels.Database {
if (!this.selectedNode()) {
return null;
@@ -1007,10 +1022,26 @@ export default class Explorer {
if (inputs.defaultCollectionThroughput) {
this.collectionCreationDefaults = inputs.defaultCollectionThroughput;
}
this.setFeatureFlagsFromFlights(inputs.flights);
this.isAccountReady(true);
}
}
public setFeatureFlagsFromFlights(flights: readonly string[]): void {
if (!flights) {
return;
}
if (flights.indexOf(Constants.Flights.AutoscaleTest) !== -1) {
this.isAutoscaleDefaultEnabled(true);
}
if (flights.indexOf(Constants.Flights.MongoIndexing) !== -1) {
this.isMongoIndexingEnabled(true);
}
if (flights.indexOf(Constants.Flights.SchemaAnalyzer) !== -1) {
userContext.features.enableSchemaAnalyzer = true;
}
}
public findSelectedCollection(): ViewModels.Collection {
return (this.selectedNode().nodeKind === "Collection"
? this.selectedNode()
@@ -1236,15 +1267,14 @@ export default class Explorer {
}
public showOkModalDialog(title: string, msg: string): void {
useDialog.getState().openDialog({
this.openDialog({
isModal: true,
visible: true,
title,
subText: msg,
primaryButtonText: "Close",
secondaryButtonText: undefined,
onPrimaryButtonClick: () => {
useDialog.getState().closeDialog();
},
onPrimaryButtonClick: this._closeModalDialog,
onSecondaryButtonClick: undefined,
});
}
@@ -1260,18 +1290,19 @@ export default class Explorer {
textFieldProps?: TextFieldProps,
isPrimaryButtonDisabled?: boolean
): void {
useDialog.getState().openDialog({
this.openDialog({
isModal: true,
visible: true,
title,
subText: msg,
primaryButtonText: okLabel,
secondaryButtonText: cancelLabel,
onPrimaryButtonClick: () => {
useDialog.getState().closeDialog();
this._closeModalDialog();
onOk && onOk();
},
onSecondaryButtonClick: () => {
useDialog.getState().closeDialog();
this._closeModalDialog();
onCancel && onCancel();
},
choiceGroupProps,
@@ -1589,13 +1620,14 @@ export default class Explorer {
}
if (item.type === NotebookContentItemType.Directory && item.children && item.children.length > 0) {
useDialog.getState().openDialog({
this.openDialog({
isModal: true,
visible: true,
title: "Unable to delete file",
subText: "Directory is not empty.",
primaryButtonText: "Close",
secondaryButtonText: undefined,
onPrimaryButtonClick: () => useDialog.getState().closeDialog(),
onPrimaryButtonClick: this._closeModalDialog,
onSecondaryButtonClick: undefined,
});
return Promise.reject();

View File

@@ -12,7 +12,7 @@ export interface GremlinSimpleClientParameters {
password: string;
successCallback: (result: Result) => void;
progressCallback: (result: Result) => void;
failureCallback: (result: Result | null, error: string) => void;
failureCallback: (result: Result, error: string) => void;
infoCallback: (msg: string) => void;
}
@@ -62,6 +62,7 @@ export class GremlinSimpleClient {
private static readonly requestChargeHeader = "x-ms-request-charge";
public params: GremlinSimpleClientParameters;
private protocols: string | string[];
private ws: WebSocket;
public requestsToSend: { [requestId: string]: GremlinRequestMessage };
@@ -71,7 +72,6 @@ export class GremlinSimpleClient {
this.params = params;
this.pendingRequests = {};
this.requestsToSend = {};
this.ws = GremlinSimpleClient.createWebSocket(this.params.endpoint);
}
public connect() {
@@ -117,7 +117,7 @@ export class GremlinSimpleClient {
}
}
public decodeMessage(msg: MessageEvent): GremlinResponseMessage | null {
public decodeMessage(msg: MessageEvent): GremlinResponseMessage {
if (msg.data === null) {
return null;
}
@@ -280,7 +280,7 @@ export class GremlinSimpleClient {
public static utf8ToB64(utf8Str: string) {
return btoa(
encodeURIComponent(utf8Str).replace(/%([0-9A-F]{2})/g, function (_match, p1) {
encodeURIComponent(utf8Str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16));
})
);
@@ -305,7 +305,7 @@ export class GremlinSimpleClient {
return binaryMessage;
}
private onOpen(_event: any) {
private onOpen(event: any) {
this.executeRequestsToSend();
}

View File

@@ -1,11 +1,12 @@
/* eslint jsx-a11y/no-static-element-interactions: 0 */
/* eslint jsx-a11y/click-events-have-key-events: 0 */
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
import React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { actions, selectors, ContentRef, AppState } from "@nteract/core";
interface ComponentProps {
id: string;
contentRef: ContentRef;
@@ -69,7 +70,7 @@ export class HijackScroll extends React.Component<Props> {
}
}
const makeMapStateToProps = (_initialState: AppState, ownProps: ComponentProps) => {
const makeMapStateToProps = (initialState: AppState, ownProps: ComponentProps) => {
const mapStateToProps = (state: AppState) => {
const { id, contentRef } = ownProps;
const model = selectors.model(state, { contentRef });
@@ -86,7 +87,7 @@ const makeMapStateToProps = (_initialState: AppState, ownProps: ComponentProps)
return mapStateToProps;
};
const makeMapDispatchToProps = (_initialDispatch: Dispatch, ownProps: ComponentProps) => {
const makeMapDispatchToProps = (initialDispatch: Dispatch, ownProps: ComponentProps) => {
const mapDispatchToProps = (dispatch: Dispatch) => ({
selectCell: () => dispatch(actions.focusCell({ id: ownProps.id, contentRef: ownProps.contentRef })),
});

View File

@@ -1,10 +1,11 @@
import { CellId } from "@nteract/commutable";
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
import Immutable from "immutable";
import React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { CellId } from "@nteract/commutable";
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
interface ComponentProps {
contentRef: ContentRef;
children: React.ReactNode;
@@ -106,7 +107,7 @@ export class KeyboardShortcuts extends React.Component<Props> {
}
}
export const makeMapStateToProps = (_state: AppState, ownProps: ComponentProps) => {
export const makeMapStateToProps = (state: AppState, ownProps: ComponentProps) => {
const { contentRef } = ownProps;
const mapStateToProps = (state: AppState) => {
const model = selectors.model(state, { contentRef });

View File

@@ -13,7 +13,7 @@ import * as React from "react";
type SchemaAnalyzerHeaderProps = {
isKernelIdle: boolean;
isKernelBusy: boolean;
onSampleSizeUpdated: (sampleSize?: string) => void;
onSampleSizeUpdated: (sampleSize: string) => void;
onAnalyzeButtonClick: (filter: string, sampleSize: string) => void;
};
@@ -30,15 +30,15 @@ export const SchemaAnalyzerHeader = ({
onSampleSizeUpdated,
onAnalyzeButtonClick,
}: SchemaAnalyzerHeaderProps): JSX.Element => {
const [filter, setFilter] = React.useState<string | undefined>(DefaultFilter);
const [sampleSize, setSampleSize] = React.useState<string | undefined>(DefaultSampleSize);
const [filter, setFilter] = React.useState<string>(DefaultFilter);
const [sampleSize, setSampleSize] = React.useState<string>(DefaultSampleSize);
return (
<Stack horizontal tokens={{ childrenGap: 10 }}>
<Stack.Item grow>
<TextField
value={filter}
onChange={(_event, newValue?: string) => setFilter(newValue)}
onChange={(event, newValue) => setFilter(newValue)}
label="Filter"
placeholder={FilterPlaceholder}
disabled={!isKernelIdle}
@@ -47,7 +47,7 @@ export const SchemaAnalyzerHeader = ({
<Stack.Item>
<TextField
value={sampleSize}
onChange={(_event, newValue?: string) => {
onChange={(event, newValue) => {
const num = Number(newValue);
if (!newValue || (num >= MinSampleSize && num <= MaxSampleSize)) {
setSampleSize(newValue);

View File

@@ -20,7 +20,7 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
import { configContext, Platform } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels";
import { SubscriptionType } from "../../Contracts/SubscriptionType";
import { CollectionCreation } from "../../Shared/Constants";
import { CollectionCreation, IndexingPolicies } from "../../Shared/Constants";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
@@ -41,40 +41,6 @@ export interface AddCollectionPanelProps {
databaseId?: string;
}
const SharedDatabaseDefault: DataModels.IndexingPolicy = {
indexingMode: "consistent",
automatic: true,
includedPaths: [],
excludedPaths: [
{
path: "/*",
},
],
};
const AllPropertiesIndexed: DataModels.IndexingPolicy = {
indexingMode: "consistent",
automatic: true,
includedPaths: [
{
path: "/*",
indexes: [
{
kind: "Range",
dataType: "Number",
precision: -1,
},
{
kind: "Range",
dataType: "String",
precision: -1,
},
],
},
],
excludedPaths: [],
};
export interface AddCollectionPanelState {
createNewDatabase: boolean;
newDatabaseId: string;
@@ -989,14 +955,14 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
const partitionKey: DataModels.PartitionKey = partitionKeyString
? {
paths: [partitionKeyString],
kind: "Hash",
kind: Constants.BackendDefaults.partitionKeyKind,
version: partitionKeyVersion,
}
: undefined;
const indexingPolicy: DataModels.IndexingPolicy = this.state.enableIndexing
? AllPropertiesIndexed
: SharedDatabaseDefault;
? IndexingPolicies.AllPropertiesIndexed
: IndexingPolicies.SharedDatabaseDefault;
const telemetryData = {
database: {
@@ -1026,16 +992,13 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
let offerThroughput: number;
let autoPilotMaxThroughput: number;
if (databaseLevelThroughput) {
if (this.state.createNewDatabase) {
if (this.isNewDatabaseAutoscale) {
autoPilotMaxThroughput = this.newDatabaseThroughput;
} else {
offerThroughput = this.newDatabaseThroughput;
}
if (this.state.createNewDatabase) {
if (this.isNewDatabaseAutoscale) {
autoPilotMaxThroughput = this.newDatabaseThroughput;
} else {
offerThroughput = this.newDatabaseThroughput;
}
} else {
} else if (!databaseLevelThroughput) {
if (this.isCollectionAutoscale) {
autoPilotMaxThroughput = this.collectionThroughput;
} else {

View File

@@ -36,10 +36,10 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
AddCollectionUtility.getMaxThroughput(container.collectionCreationDefaults, container)
);
const [isAutoPilotSelected, setIsAutoPilotSelected] = useState<boolean>(userContext.features.autoscaleDefault);
const [isAutoPilotSelected, setIsAutoPilotSelected] = useState<boolean>(container.isAutoscaleDefaultEnabled());
const [isSharedAutoPilotSelected, setIsSharedAutoPilotSelected] = useState<boolean>(
userContext.features.autoscaleDefault
container.isAutoscaleDefaultEnabled()
);
const [userTableQuery, setUserTableQuery] = useState<string>(
@@ -108,7 +108,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
useEffect(() => {
if (!container.isServerlessEnabled()) {
setIsAutoPilotSelected(userContext.features.autoscaleDefault);
setIsAutoPilotSelected(container.isAutoscaleDefaultEnabled());
}
TelemetryProcessor.trace(Action.CreateCollection, ActionModifiers.Open, addCollectionPaneOpenMessage);

View File

@@ -17,6 +17,8 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
addRepoProps={
Object {
"container": Explorer {
"_closeModalDialog": [Function],
"_closeSynapseLinkModalDialog": [Function],
"_isAfecFeatureRegistered": [Function],
"_isInitializingNotebooks": false,
"_refreshSparkEnabledStateForAccount": [Function],
@@ -25,6 +27,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"arcadiaToken": [Function],
"canExceedMaximumValue": [Function],
"canSaveQueries": [Function],
"closeDialog": undefined,
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"collectionCreationDefaults": Object {
@@ -817,6 +820,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
},
"hasStorageAnalyticsAfecFeature": [Function],
"isAccountReady": [Function],
"isAutoscaleDefaultEnabled": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
@@ -841,6 +845,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"onGitHubClientError": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openDialog": undefined,
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {

View File

@@ -36,7 +36,7 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
{message}
{link && linkText && (
<Link target="_blank" href={link}>
{" " + linkText}
{linkText}
</Link>
)}
</Text>

View File

@@ -7,6 +7,8 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
errorMessage="Could not create directory "
explorer={
Explorer {
"_closeModalDialog": [Function],
"_closeSynapseLinkModalDialog": [Function],
"_isAfecFeatureRegistered": [Function],
"_isInitializingNotebooks": false,
"_refreshSparkEnabledStateForAccount": [Function],
@@ -15,6 +17,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"arcadiaToken": [Function],
"canExceedMaximumValue": [Function],
"canSaveQueries": [Function],
"closeDialog": undefined,
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"collectionCreationDefaults": Object {
@@ -807,6 +810,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
},
"hasStorageAnalyticsAfecFeature": [Function],
"isAccountReady": [Function],
"isAutoscaleDefaultEnabled": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
@@ -831,6 +835,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"onGitHubClientError": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openDialog": undefined,
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {

View File

@@ -5,6 +5,8 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
closePanel={[Function]}
explorer={
Explorer {
"_closeModalDialog": [Function],
"_closeSynapseLinkModalDialog": [Function],
"_isAfecFeatureRegistered": [Function],
"_isInitializingNotebooks": false,
"_refreshSparkEnabledStateForAccount": [Function],
@@ -13,6 +15,7 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
"arcadiaToken": [Function],
"canExceedMaximumValue": [Function],
"canSaveQueries": [Function],
"closeDialog": undefined,
"closeSidePanel": undefined,
"collapsedResourceTreeWidth": 36,
"collectionCreationDefaults": Object {
@@ -805,6 +808,7 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
},
"hasStorageAnalyticsAfecFeature": [Function],
"isAccountReady": [Function],
"isAutoscaleDefaultEnabled": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isHostedDataExplorerEnabled": [Function],
"isLastCollection": [Function],
@@ -832,6 +836,7 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
"onGitHubClientError": [Function],
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"openDialog": undefined,
"openSidePanel": undefined,
"provideFeedbackEmail": [Function],
"queriesClient": QueriesClient {

View File

@@ -193,7 +193,7 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
}
}
public abstract onSaveClick: () => void;
public abstract onSaveClick: () => Promise<any>;
public abstract onUpdateClick: () => Promise<any>;
public onDiscard = (): Q.Promise<any> => {
@@ -205,6 +205,16 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
return Q();
};
public onSaveOrUpdateClick(): Promise<any> {
if (this.saveButton.visible()) {
return this.onSaveClick();
} else if (this.updateButton.visible()) {
return this.onUpdateClick();
}
return undefined;
}
protected getTabsButtons(): CommandButtonComponentProps[] {
const buttons: CommandButtonComponentProps[] = [];
const label = "Save";

View File

@@ -1,3 +1,4 @@
import { Resource, TriggerDefinition, TriggerOperation, TriggerType } from "@azure/cosmos";
import * as Constants from "../../Common/Constants";
import { createTrigger } from "../../Common/dataAccess/createTrigger";
import { updateTrigger } from "../../Common/dataAccess/updateTrigger";
@@ -6,7 +7,6 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
import * as ViewModels from "../../Contracts/ViewModels";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
import Trigger from "../Tree/Trigger";
import ScriptTabBase from "./ScriptTabBase";
import template from "./TriggerTab.html";
@@ -30,12 +30,12 @@ export default class TriggerTab extends ScriptTabBase {
super.buildCommandBarOptions();
}
public onSaveClick = (): void => {
this._createTrigger({
public onSaveClick = (): Promise<TriggerDefinition & Resource> => {
return this._createTrigger({
id: this.id(),
body: this.editorContent(),
triggerOperation: this.triggerOperation() as SqlTriggerResource["triggerOperation"],
triggerType: this.triggerType() as SqlTriggerResource["triggerType"],
triggerOperation: this.triggerOperation() as TriggerOperation,
triggerType: this.triggerType() as TriggerType,
});
};
@@ -50,8 +50,8 @@ export default class TriggerTab extends ScriptTabBase {
return updateTrigger(this.collection.databaseId, this.collection.id(), {
id: this.id(),
body: this.editorContent(),
triggerOperation: this.triggerOperation() as SqlTriggerResource["triggerOperation"],
triggerType: this.triggerType() as SqlTriggerResource["triggerType"],
triggerOperation: this.triggerOperation() as TriggerOperation,
triggerType: this.triggerType() as TriggerType,
})
.then(
(createdResource) => {
@@ -117,7 +117,7 @@ export default class TriggerTab extends ScriptTabBase {
}
}
private _createTrigger(resource: SqlTriggerResource): void {
private _createTrigger(resource: TriggerDefinition): Promise<TriggerDefinition & Resource> {
this.isExecutionError(false);
this.isExecuting(true);
const startKey: number = TelemetryProcessor.traceStart(Action.CreateTrigger, {
@@ -125,8 +125,7 @@ export default class TriggerTab extends ScriptTabBase {
tabTitle: this.tabTitle(),
});
resource.body = String(resource.body); // Ensure trigger body is converted to string
createTrigger(this.collection.databaseId, this.collection.id(), resource)
return createTrigger(this.collection.databaseId, this.collection.id(), resource)
.then(
(createdResource) => {
this.tabTitle(createdResource.id);

View File

@@ -18,7 +18,6 @@ import { UploadDetailsRecord } from "../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
import { SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
import { logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
import Explorer from "../Explorer";
import { CassandraAPIDataClient, CassandraTableKey, CassandraTableKeys } from "../Tables/TableDataClient";
@@ -963,9 +962,7 @@ export default class Collection implements ViewModels.Collection {
public loadTriggers(): Promise<any> {
return readTriggers(this.databaseId, this.id()).then((triggers) => {
const triggerNodes: ViewModels.TreeNode[] = triggers.map(
(trigger: SqlTriggerResource | TriggerDefinition) => new Trigger(this.container, this, trigger)
);
const triggerNodes: ViewModels.TreeNode[] = triggers.map((trigger) => new Trigger(this.container, this, trigger));
const otherNodes = this.children().filter((node) => node.nodeKind !== "Trigger");
const allNodes = otherNodes.concat(triggerNodes);
this.children(allNodes);

View File

@@ -0,0 +1,16 @@
{
"PreviousCommentsLabel": "Previous Comments",
"RatingLabel": "Rating",
"ShowCommentsLabel": "Have more Comments?",
"ShowCommentsTrueLabel": "Yes",
"ShowCommentsFalseLabel": "No",
"CommentsLabel": "Comments",
"CommentsPlaceholder": "Leave your comments here",
"IntializeMesssage": "Feedback is being submitted",
"IntializeTitle": "Feedback Submission",
"SuccessMesssage": "Feedback submitted successfully",
"SuccessTitle": "Feedback Submission",
"FailureMesssage": "Feedback submission failed",
"FailureTitle": "Feedback Submission",
"UpdateInProgressMessage": "Feedback submission in progress"
}

View File

@@ -46,9 +46,8 @@
"ConnectionStringText": "To use the dedicated gateway, use the connection string shown in ",
"KeysBlade": "the keys blade.",
"MetricsString": "Metrics",
"MetricsText": "Monitor the CPU and memory usage for the dedicated gateway instances in ",
"MetricsText": "Monitor the \"DedicatedGatewayMaximumCpuUsage\" and \"DedicatedGatewayAverageMemoryUsage\" in ",
"MetricsBlade": "the metrics blade.",
"MonitorUsage": "Monitor Usage",
"ResizingDecisionText": "To understand if the dedicated gateway is the right size, ",
"ResizingDecisionLink": "learn more about dedicated gateway sizing.",
"WarningBannerOnUpdate": "Adding or modifying dedicated gateway instances may affect your bill.",

View File

@@ -30,7 +30,7 @@ import "../less/tree.less";
import { AuthType } from "./AuthType";
import "./Explorer/Controls/Accordion/AccordionComponent.less";
import "./Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.less";
import { Dialog } from "./Explorer/Controls/Dialog";
import { Dialog, DialogProps } from "./Explorer/Controls/Dialog";
import "./Explorer/Controls/DynamicList/DynamicListComponent.less";
import "./Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.less";
import "./Explorer/Controls/JsonEditor/JsonEditorComponent.less";
@@ -64,6 +64,17 @@ const App: React.FunctionComponent = () => {
//TODO: Refactor so we don't need to pass the id to remove a console data
const [inProgressConsoleDataIdToBeDeleted, setInProgressConsoleDataIdToBeDeleted] = useState("");
const [dialogProps, setDialogProps] = useState<DialogProps>();
const [showDialog, setShowDialog] = useState<boolean>(false);
const openDialog = (props: DialogProps) => {
setDialogProps(props);
setShowDialog(true);
};
const closeDialog = () => {
setShowDialog(false);
};
const { isPanelOpen, panelContent, headerText, openSidePanel, closeSidePanel } = useSidePanel();
const { tabs, activeTab, tabsManager } = useTabs();
@@ -73,6 +84,8 @@ const App: React.FunctionComponent = () => {
setInProgressConsoleDataIdToBeDeleted,
openSidePanel,
closeSidePanel,
openDialog,
closeDialog,
tabsManager,
};
@@ -85,7 +98,7 @@ const App: React.FunctionComponent = () => {
return (
<div className="flexContainer">
<div id="divExplorer" className="flexContainer hideOverflows">
<div id="divExplorer" className="flexContainer hideOverflows" style={{ display: "none" }}>
{/* Main Command Bar - Start */}
<div data-bind="react: commandBarComponentAdapter" />
{/* Collections Tree and Tabs - Begin */}
@@ -213,7 +226,7 @@ const App: React.FunctionComponent = () => {
closePanel={closeSidePanel}
isConsoleExpanded={isNotificationConsoleExpanded}
/>
<Dialog />
{showDialog && <Dialog {...dialogProps} />}
</div>
);
};

View File

@@ -9,7 +9,6 @@ export type Features = {
readonly enableRightPanelV2: boolean;
readonly enableSchema: boolean;
enableSchemaAnalyzer: boolean;
autoscaleDefault: boolean;
readonly enableSDKoperations: boolean;
readonly enableSpark: boolean;
readonly enableTtl: boolean;
@@ -69,6 +68,5 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
pr: get("pr"),
showMinRUSurvey: "true" === get("showminrusurvey"),
ttl90Days: "true" === get("ttl90days"),
autoscaleDefault: "true" === get("autoscaledefault"),
};
}

View File

@@ -0,0 +1,31 @@
import { SessionStorageUtility } from "../../Shared/StorageUtility";
import { RefreshResult } from "../SelfServeTypes";
import { Feedback } from "./Demo.types";
export const update = async (feedback: Feedback): Promise<void> => {
SessionStorageUtility.setEntry("rating", feedback.rating?.toString());
SessionStorageUtility.setEntry("comments", feedback.comments);
};
export const initialize = async (): Promise<Feedback> => {
let rating = parseInt(SessionStorageUtility.getEntry("rating"));
rating = isNaN(rating) ? undefined : rating;
const comments = SessionStorageUtility.getEntry("comments");
return {
rating,
comments,
};
};
export const refresh = async (): Promise<RefreshResult> => {
const refreshCountString = SessionStorageUtility.getEntry("refreshCount");
const refreshCount = refreshCountString ? parseInt(refreshCountString) : 0;
const progressToBeSent = refreshCount % 2 === 0 ? false : true;
SessionStorageUtility.setEntry("refreshCount", (refreshCount + 1).toString());
return {
isUpdateInProgress: progressToBeSent,
updateInProgressMessageTKey: "UpdateInProgressMessage",
};
};

105
src/SelfServe/Demo/Demo.tsx Normal file
View File

@@ -0,0 +1,105 @@
import { IsDisplayable, OnChange, RefreshOptions, Values } from "../Decorators";
import {
Description,
DescriptionType,
InputType,
NumberUiType,
OnSaveResult,
RefreshResult,
SelfServeBaseClass,
SmartUiInput,
} from "../SelfServeTypes";
import { initialize, refresh, update } from "./Demo.rp";
const onShowCommentsChanged = (
newValue: InputType,
currentValues: Map<string, SmartUiInput>
): Map<string, SmartUiInput> => {
const comments = currentValues.get("comments");
const showComments = currentValues.get("showComments");
showComments.value = newValue;
// show the comments element only when toggle is true, hide comments when toggle is false
comments.hidden = !newValue;
currentValues.set("comments", comments);
currentValues.set("showComments", showComments);
return currentValues;
};
@IsDisplayable()
@RefreshOptions({ retryIntervalInMs: 1000 })
export default class Demo extends SelfServeBaseClass {
public initialize = async (): Promise<Map<string, SmartUiInput>> => {
const initialFeedback = await initialize();
const resultMap = new Map<string, SmartUiInput>();
const previousComments = initialFeedback.comments
? { value: { textTKey: initialFeedback.comments, type: DescriptionType.Text } as Description }
: undefined;
resultMap.set("previousComments", previousComments);
resultMap.set("rating", { value: initialFeedback.rating });
resultMap.set("showComments", { value: false });
resultMap.set("comments", { value: "", hidden: true });
return resultMap;
};
public onSave = async (currentValues: Map<string, SmartUiInput>): Promise<OnSaveResult> => {
const rating = currentValues.get("rating")?.value as number;
const currentComments = currentValues.get("comments")?.value as string;
const comments = currentComments
? currentComments
: (currentValues.get("previousComments")?.value as Description).textTKey;
await update({ rating, comments });
return {
operationStatusUrl: undefined,
portalNotification: {
initialize: {
messageTKey: "IntializeMesssage",
titleTKey: "IntializeTitle",
},
success: {
messageTKey: "SuccessMesssage",
titleTKey: "SuccessTitle",
},
failure: {
messageTKey: "FailureMesssage",
titleTKey: "FailureTitle",
},
},
};
};
public onRefresh = async (): Promise<RefreshResult> => {
return refresh();
};
@Values({
labelTKey: "PreviousCommentsLabel",
isDynamicDescription: true,
})
previousComments: string;
@Values({
labelTKey: "RatingLabel",
min: 0,
max: 5,
step: 1,
uiType: NumberUiType.Slider,
})
rating: number;
@OnChange(onShowCommentsChanged)
@Values({
labelTKey: "ShowCommentsLabel",
trueLabelTKey: "ShowCommentsTrueLabel",
falseLabelTKey: "ShowCommentsFalseLabel",
})
showComments: boolean;
@Values({
labelTKey: "CommentsLabel",
placeholderTKey: "CommentsPlaceholder",
})
comments: string;
}

View File

@@ -0,0 +1,4 @@
export interface Feedback {
rating: number;
comments: string;
}

View File

@@ -1,6 +1,6 @@
import { SessionStorageUtility } from "../../Shared/StorageUtility";
import { userContext } from "../../UserContext";
import { get } from "../../Utils/arm/generatedClients/cosmos/databaseAccounts";
import { get } from "../../Utils/arm/generatedClients/2020-04-01/databaseAccounts";
import { RefreshResult } from "../SelfServeTypes";
import { AccountProps, Regions } from "./SelfServeExample.types";

View File

@@ -50,6 +50,12 @@ const getDescriptor = async (selfServeType: SelfServeType): Promise<SelfServeDes
await loadTranslations(sqlX.constructor.name);
return sqlX.toSelfServeDescriptor();
}
case SelfServeType.demo: {
const Demo = await import(/* webpackChunkName: "Demo" */ "./Demo/Demo");
const demo = new Demo.default();
await loadTranslations(demo.constructor.name);
return demo.toSelfServeDescriptor();
}
default:
return undefined;
}

View File

@@ -17,8 +17,6 @@ import * as _ from "underscore";
import { sendMessage } from "../Common/MessageHandler";
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
import { SmartUiComponent, SmartUiDescriptor } from "../Explorer/Controls/SmartUi/SmartUiComponent";
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
import { trace } from "../Shared/Telemetry/TelemetryProcessor";
import { commandBarItemStyles, commandBarStyles, containerStackTokens, separatorStyles } from "./SelfServeStyles";
import {
AnyDisplay,
@@ -29,7 +27,6 @@ import {
Node,
NumberInput,
RefreshResult,
SelfServeComponentTelemetryType,
SelfServeDescriptor,
SmartUiInput,
StringInput,
@@ -90,12 +87,6 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
}
});
this.initializeSmartUiComponent();
const telemetryData = {
selfServeClassName: this.props.descriptor.root.id,
eventType: SelfServeComponentTelemetryType.Load,
};
trace(Action.SelfServeComponent, ActionModifiers.Mark, telemetryData, SelfServeMessageTypes.TelemetryInfo);
}
constructor(props: SelfServeComponentProps) {
@@ -175,7 +166,7 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
const { baselineValues } = this.state;
for (const key of currentValues.keys()) {
const baselineValue = baselineValues.get(key);
currentValues = currentValues.set(key, baselineValue ? { ...baselineValue } : baselineValue);
currentValues = currentValues.set(key, { ...baselineValue });
}
this.setState({ currentValues });
};
@@ -271,12 +262,6 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
};
public performSave = async (): Promise<void> => {
const telemetryData = {
selfServeClassName: this.props.descriptor.root.id,
eventType: SelfServeComponentTelemetryType.Save,
};
trace(Action.SelfServeComponent, ActionModifiers.Mark, telemetryData, SelfServeMessageTypes.TelemetryInfo);
this.setState({ isSaving: true, notification: undefined });
try {
const onSaveResult = await this.props.descriptor.onSave(

View File

@@ -70,12 +70,6 @@ export interface SelfServeDescriptor {
refreshParams?: RefreshParams;
}
/**@internal */
export enum SelfServeComponentTelemetryType {
Load = "Load",
Save = "Save",
}
/**@internal */
export type AnyDisplay = NumberInput | BooleanInput | StringInput | ChoiceInput | DescriptionDisplay;

View File

@@ -31,6 +31,7 @@ export enum SelfServeType {
// Add your self serve types here
example = "example",
sqlx = "sqlx",
demo = "demo",
}
/**

View File

@@ -50,7 +50,6 @@ export const updateDedicatedGatewayResource = async (sku: string, instances: num
} catch (e) {
const failureTelemetry = { ...body, e, selfServeClassName: SqlX.name };
selfServeTraceFailure(failureTelemetry, updateTimeStamp);
throw e;
}
return armRequestResult?.operationStatusUrl;
};
@@ -71,7 +70,6 @@ export const deleteDedicatedGatewayResource = async (): Promise<string> => {
} catch (e) {
const failureTelemetry = { e, selfServeClassName: SqlX.name };
selfServeTraceFailure(failureTelemetry, deleteTimeStamp);
throw e;
}
return armRequestResult?.operationStatusUrl;
};
@@ -92,7 +90,6 @@ export const getDedicatedGatewayResource = async (): Promise<SqlxServiceResource
} catch (e) {
const failureTelemetry = { e, selfServeClassName: SqlX.name };
selfServeTraceFailure(failureTelemetry, getResourceTimeStamp);
throw e;
}
return armRequestResult?.result;
};

View File

@@ -1,10 +1,9 @@
import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators";
import { IsDisplayable, OnChange, RefreshOptions, Values } from "../Decorators";
import { selfServeTrace } from "../SelfServeTelemetryProcessor";
import {
ChoiceItem,
Description,
DescriptionType,
Info,
InputType,
NumberUiType,
OnSaveResult,
@@ -47,6 +46,15 @@ const metricsStringValue: Description = {
},
};
const resizingDecisionValue: Description = {
textTKey: "ResizingDecisionText",
type: DescriptionType.Text,
link: {
href: "https://aka.ms/cosmos-db-dedicated-gateway-size",
textTKey: "ResizingDecisionLink",
},
};
const CosmosD4s = "Cosmos.D4s";
const CosmosD8s = "Cosmos.D8s";
const CosmosD16s = "Cosmos.D16s";
@@ -96,6 +104,7 @@ const onEnableDedicatedGatewayChange = (
currentValues.set("warningBanner", baselineValues.get("warningBanner"));
currentValues.set("connectionString", baselineValues.get("connectionString"));
currentValues.set("metricsString", baselineValues.get("metricsString"));
currentValues.set("resizingDecisionString", baselineValues.get("resizingDecisionString"));
return currentValues;
}
@@ -148,6 +157,11 @@ const onEnableDedicatedGatewayChange = (
hidden: !newValue || !dedicatedGatewayOriginallyEnabled,
});
currentValues.set("resizingDecisionString", {
value: resizingDecisionValue,
hidden: !newValue || !dedicatedGatewayOriginallyEnabled,
});
return currentValues;
};
@@ -169,14 +183,6 @@ const getInstancesMax = async (): Promise<number> => {
return 5;
};
const NumberOfInstancesDropdownInfo: Info = {
messageTKey: "ResizingDecisionText",
link: {
href: "https://aka.ms/cosmos-db-dedicated-gateway-size",
textTKey: "ResizingDecisionLink",
},
};
@IsDisplayable()
@RefreshOptions({ retryIntervalInMs: 20000 })
export default class SqlX extends SelfServeBaseClass {
@@ -273,6 +279,10 @@ export default class SqlX extends SelfServeBaseClass {
value: undefined,
hidden: true,
});
defaults.set("resizingDecisionString", {
value: undefined,
hidden: true,
});
const response = await getCurrentProvisioningState();
if (response.status && response.status !== "Deleting") {
@@ -289,7 +299,13 @@ export default class SqlX extends SelfServeBaseClass {
value: metricsStringValue,
hidden: false,
});
defaults.set("resizingDecisionString", {
value: resizingDecisionValue,
hidden: false,
});
}
defaults.set("warningBanner", undefined);
return defaults;
};
@@ -328,7 +344,6 @@ export default class SqlX extends SelfServeBaseClass {
sku: ChoiceItem;
@OnChange(onNumberOfInstancesChange)
@PropertyInfo(NumberOfInstancesDropdownInfo)
@Values({
labelTKey: "NumberOfInstances",
min: getInstancesMin,
@@ -338,6 +353,16 @@ export default class SqlX extends SelfServeBaseClass {
})
instances: number;
@Values({
description: metricsStringValue,
})
metricsString: string;
@Values({
description: resizingDecisionValue,
})
resizingDecisionString: string;
@Values({
labelTKey: "Cost",
isDynamicDescription: true,
@@ -349,10 +374,4 @@ export default class SqlX extends SelfServeBaseClass {
isDynamicDescription: true,
})
connectionString: string;
@Values({
labelTKey: "MonitorUsage",
description: metricsStringValue,
})
metricsString: string;
}

View File

@@ -187,6 +187,42 @@ export const CollectionCreationDefaults = {
},
} as const;
export class IndexingPolicies {
public static SharedDatabaseDefault = {
indexingMode: "consistent",
automatic: true,
includedPaths: <any>[],
excludedPaths: [
{
path: "/*",
},
],
};
public static AllPropertiesIndexed = {
indexingMode: "consistent",
automatic: true,
includedPaths: [
{
path: "/*",
indexes: [
{
kind: "Range",
dataType: "Number",
precision: -1,
},
{
kind: "Range",
dataType: "String",
precision: -1,
},
],
},
],
excludedPaths: <any>[],
};
}
export class SubscriptionUtilMappings {
public static FreeTierSubscriptionIds: string[] = [
"b8f2ff04-0a81-4cf9-95ef-5828d16981d2",
@@ -201,8 +237,3 @@ export class SubscriptionUtilMappings {
export class AutopilotDocumentation {
public static Url: string = "https://aka.ms/cosmos-autoscale-info";
}
export class FreeTierLimits {
public static RU: number = 1000;
public static Storage: number = 25;
}

View File

@@ -117,7 +117,6 @@ export enum Action {
SelfServe,
ExpandAddCollectionPaneAdvancedSection,
SchemaAnalyzerClickAnalyze,
SelfServeComponent,
}
export const ActionModifiers = {

View File

@@ -267,11 +267,9 @@ export function getUpsellMessage(
if (isFreeTier) {
const collectionName = getCollectionName().toLocaleLowerCase();
const resourceType = isCollection ? collectionName : "database";
const freeTierMaxRU = Constants.FreeTierLimits.RU;
const freeTierMaxStorage = Constants.FreeTierLimits.Storage;
return isFirstResourceCreated
? `Your account currently has at least 1 database or ${collectionName} with provisioned RU/s. Billing will apply to this ${resourceType} if the total RU/s in your account exceeds ${freeTierMaxRU} RU/s.`
: `With free tier, you'll get the first ${freeTierMaxRU} RU/s and ${freeTierMaxStorage} GB of storage in this account for free. Billing will apply if you provision more than ${freeTierMaxRU} RU/s of manual throughput, or if the ${resourceType} scales beyond ${freeTierMaxRU} RU/s with autoscale.`;
? `The free tier discount of 400 RU/s has already been applied to a database or ${collectionName} in this account. Billing will apply to this ${resourceType} after it is created.`
: `With free tier, you'll get the first 400 RU/s and 5 GB of storage in this account for free. Billing will apply if you provision more than 400 RU/s of manual throughput, or if the ${resourceType} scales beyond 400 RU/s with autoscale.`;
} else {
let price: number = Constants.OfferPricing.MonthlyPricing.default.Standard.StartingPrice;

View File

@@ -1,6 +1,6 @@
import * as DataModels from "../Contracts/DataModels";
import * as Q from "q";
import * as sinon from "sinon";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import * as QueryUtils from "./QueryUtils";
@@ -8,7 +8,7 @@ describe("Query Utils", () => {
function generatePartitionKeyForPath(path: string): DataModels.PartitionKey {
return {
paths: [path],
kind: "Hash",
kind: "hash",
version: 2,
};
}

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Lists the Cassandra keyspaces under an existing Azure Cosmos DB database account. */
export async function listCassandraKeyspaces(
@@ -84,7 +82,7 @@ export async function migrateCassandraKeyspaceToAutoscale(
resourceGroupName: string,
accountName: string,
keyspaceName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/throughputSettings/default/migrateToAutoscale`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
@@ -95,7 +93,7 @@ export async function migrateCassandraKeyspaceToManualThroughput(
resourceGroupName: string,
accountName: string,
keyspaceName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/throughputSettings/default/migrateToManualThroughput`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
@@ -180,7 +178,7 @@ export async function migrateCassandraTableToAutoscale(
accountName: string,
keyspaceName: string,
tableName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/tables/${tableName}/throughputSettings/default/migrateToAutoscale`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
@@ -192,7 +190,7 @@ export async function migrateCassandraTableToManualThroughput(
accountName: string,
keyspaceName: string,
tableName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/tables/${tableName}/throughputSettings/default/migrateToManualThroughput`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Retrieves the metrics determined by the given filter for the given database account and collection. */
export async function listMetrics(

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Retrieves the metrics determined by the given filter for the given collection, split by partition. */
export async function listMetrics(

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Retrieves the metrics determined by the given filter for the given collection and region, split by partition. */
export async function listMetrics(

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Retrieves the metrics determined by the given filter for the given database account, collection and region. */
export async function listMetrics(

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Retrieves the metrics determined by the given filter for the given database account and database. */
export async function listMetrics(

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Retrieves the metrics determined by the given filter for the given database account and region. */
export async function listMetrics(

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Retrieves the properties of an existing Azure Cosmos DB database account. */
export async function get(

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Lists the Gremlin databases under an existing Azure Cosmos DB database account. */
export async function listGremlinDatabases(
@@ -84,7 +82,7 @@ export async function migrateGremlinDatabaseToAutoscale(
resourceGroupName: string,
accountName: string,
databaseName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/throughputSettings/default/migrateToAutoscale`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
@@ -95,7 +93,7 @@ export async function migrateGremlinDatabaseToManualThroughput(
resourceGroupName: string,
accountName: string,
databaseName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/throughputSettings/default/migrateToManualThroughput`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
@@ -180,7 +178,7 @@ export async function migrateGremlinGraphToAutoscale(
accountName: string,
databaseName: string,
graphName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/graphs/${graphName}/throughputSettings/default/migrateToAutoscale`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
@@ -192,7 +190,7 @@ export async function migrateGremlinGraphToManualThroughput(
accountName: string,
databaseName: string,
graphName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/graphs/${graphName}/throughputSettings/default/migrateToManualThroughput`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Lists the MongoDB databases under an existing Azure Cosmos DB database account. */
export async function listMongoDBDatabases(
@@ -73,7 +71,7 @@ export async function updateMongoDBDatabaseThroughput(
accountName: string,
databaseName: string,
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
@@ -84,7 +82,7 @@ export async function migrateMongoDBDatabaseToAutoscale(
resourceGroupName: string,
accountName: string,
databaseName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/throughputSettings/default/migrateToAutoscale`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
@@ -95,7 +93,7 @@ export async function migrateMongoDBDatabaseToManualThroughput(
resourceGroupName: string,
accountName: string,
databaseName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/throughputSettings/default/migrateToManualThroughput`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
@@ -180,7 +178,7 @@ export async function migrateMongoDBCollectionToAutoscale(
accountName: string,
databaseName: string,
collectionName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/collections/${collectionName}/throughputSettings/default/migrateToAutoscale`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
@@ -192,7 +190,7 @@ export async function migrateMongoDBCollectionToManualThroughput(
accountName: string,
databaseName: string,
collectionName: string
): Promise<Types.ThroughputSettingsGetResults | void | Types.CloudError> {
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/collections/${collectionName}/throughputSettings/default/migrateToManualThroughput`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Lists all of the available Cosmos DB Resource Provider operations. */
export async function list(): Promise<Types.OperationListResult> {

View File

@@ -1,15 +1,13 @@
/*
AUTOGENERATED FILE
Do not manually edit
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/stable/2021-04-15/cosmos-db.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2021-04-15";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2020-04-01";
/* Retrieves the metrics determined by the given filter for the given partition key range id. */
export async function listMetrics(

Some files were not shown because too many files have changed in this diff Show More