From 2752d6af004d991b737e9cc6b15880f555dbcc96 Mon Sep 17 00:00:00 2001 From: Steve Faulkner Date: Thu, 13 Aug 2020 13:26:52 -0500 Subject: [PATCH 01/15] Create Cassandra Keyspace via ARM (#142) --- src/Explorer/Panes/AddDatabasePane.ts | 5 +- src/Shared/AddDatabaseUtility.ts | 15 +- .../2020-04-01/cassandraResources.ts | 8 +- .../2020-04-01/databaseAccounts.ts | 18 +- .../2020-04-01/gremlinResources.ts | 8 +- .../2020-04-01/mongoDBResources.ts | 8 +- .../2020-04-01/sqlResources.ts | 14 +- .../2020-04-01/tableResources.ts | 4 +- .../arm/generatedClients/2020-04-01/types.ts | 568 +++++++++--------- utils/armClientGenerator/generator.ts | 20 +- 10 files changed, 340 insertions(+), 328 deletions(-) diff --git a/src/Explorer/Panes/AddDatabasePane.ts b/src/Explorer/Panes/AddDatabasePane.ts index c489f19c0..8355d7dcf 100644 --- a/src/Explorer/Panes/AddDatabasePane.ts +++ b/src/Explorer/Panes/AddDatabasePane.ts @@ -421,7 +421,7 @@ export default class AddDatabasePane extends ContextualPaneBase { startKey: number ): void { if (EnvironmentUtility.isAadUser()) { - this._createKeyspaceUsingRP(this.container.armEndpoint(), createDatabaseParameters, autoPilotSettings, startKey); + this._createKeyspaceUsingRP(createDatabaseParameters, autoPilotSettings, startKey); } else { this._createKeyspaceUsingProxy(createDatabaseParameters.offerThroughput, startKey); } @@ -450,12 +450,11 @@ export default class AddDatabasePane extends ContextualPaneBase { } private _createKeyspaceUsingRP( - armEndpoint: string, createKeyspaceParameters: DataModels.RpParameters, autoPilotSettings: DataModels.RpOptions, startKey: number ): void { - AddDbUtilities.createCassandraKeyspace(armEndpoint, createKeyspaceParameters, autoPilotSettings).then(() => { + AddDbUtilities.createCassandraKeyspace(createKeyspaceParameters, autoPilotSettings).then(() => { Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => { this._onCreateDatabaseSuccess(createKeyspaceParameters.offerThroughput, startKey); }); diff --git a/src/Shared/AddDatabaseUtility.ts b/src/Shared/AddDatabaseUtility.ts index b73462837..a1197c732 100644 --- a/src/Shared/AddDatabaseUtility.ts +++ b/src/Shared/AddDatabaseUtility.ts @@ -8,6 +8,7 @@ import { MessageTypes } from "../Contracts/ExplorerContracts"; import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils"; import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient"; import { userContext } from "../UserContext"; +import { createUpdateCassandraKeyspace } from "../Utils/arm/generatedClients/2020-04-01/cassandraResources"; export class AddDbUtilities { // todo - remove any @@ -47,7 +48,6 @@ export class AddDbUtilities { // todo - remove any public static async createCassandraKeyspace( - armEndpoint: string, params: DataModels.RpParameters, rpOptions: DataModels.RpOptions ): Promise { @@ -70,9 +70,11 @@ export class AddDbUtilities { } try { - await AddDbUtilities.getRpClient(armEndpoint).putAsync( - AddDbUtilities._getCassandraKeyspaceUri(params), - DataExplorerConstants.ArmApiVersions.publicVersion, + await createUpdateCassandraKeyspace( + userContext.subscriptionId, + userContext.resourceGroup, + userContext.databaseAccount?.name, + params.db, rpPayloadToCreateKeyspace ); } catch (reason) { @@ -159,10 +161,7 @@ export class AddDbUtilities { } private static _handleCreationError(reason: any, params: DataModels.RpParameters, dbType: string = "database") { - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Error, - `Error creating ${dbType}: ${JSON.stringify(reason)}, Payload: ${params}` - ); + NotificationConsoleUtils.logConsoleError(`Error creating ${dbType}: ${JSON.stringify(reason)}, Payload: ${params}`); if (reason.status === HttpStatusCodes.Forbidden) { sendMessage({ type: MessageTypes.ForbiddenError }); return; diff --git a/src/Utils/arm/generatedClients/2020-04-01/cassandraResources.ts b/src/Utils/arm/generatedClients/2020-04-01/cassandraResources.ts index 7477e79e5..924f30bb6 100644 --- a/src/Utils/arm/generatedClients/2020-04-01/cassandraResources.ts +++ b/src/Utils/arm/generatedClients/2020-04-01/cassandraResources.ts @@ -39,7 +39,7 @@ export async function createUpdateCassandraKeyspace( body: Types.CassandraKeyspaceCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB Cassandra keyspace. */ @@ -73,7 +73,7 @@ export async function updateCassandraKeyspaceThroughput( body: Types.ThroughputSettingsUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/throughputSettings/default`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Migrate an Azure Cosmos DB Cassandra Keyspace from manual throughput to autoscale */ @@ -131,7 +131,7 @@ export async function createUpdateCassandraTable( body: Types.CassandraTableCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/tables/${tableName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB Cassandra table. */ @@ -168,7 +168,7 @@ export async function updateCassandraTableThroughput( body: Types.ThroughputSettingsUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/tables/${tableName}/throughputSettings/default`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Migrate an Azure Cosmos DB Cassandra table from manual throughput to autoscale */ diff --git a/src/Utils/arm/generatedClients/2020-04-01/databaseAccounts.ts b/src/Utils/arm/generatedClients/2020-04-01/databaseAccounts.ts index d98fb51e4..499213664 100644 --- a/src/Utils/arm/generatedClients/2020-04-01/databaseAccounts.ts +++ b/src/Utils/arm/generatedClients/2020-04-01/databaseAccounts.ts @@ -27,13 +27,7 @@ export async function update( body: Types.DatabaseAccountUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}`; - return armRequest({ - host: configContext.ARM_ENDPOINT, - path, - method: "PATCH", - apiVersion, - body: JSON.stringify(body) - }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PATCH", apiVersion, body }); } /* Creates or updates an Azure Cosmos DB database account. The "Update" method is preferred when performing updates on an account. */ @@ -44,7 +38,7 @@ export async function createOrUpdate( body: Types.DatabaseAccountCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB database account. */ @@ -61,7 +55,7 @@ export async function failoverPriorityChange( body: Types.FailoverPolicies ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/failoverPriorityChange`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body }); } /* Lists all the Azure Cosmos DB database accounts available under the subscription. */ @@ -107,7 +101,7 @@ export async function offlineRegion( body: Types.RegionForOnlineOffline ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/offlineRegion`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body }); } /* Online the specified region for the specified Azure Cosmos DB database account. */ @@ -118,7 +112,7 @@ export async function onlineRegion( body: Types.RegionForOnlineOffline ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/onlineRegion`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body }); } /* Lists the read-only access keys for the specified Azure Cosmos DB database account. */ @@ -149,7 +143,7 @@ export async function regenerateKey( body: Types.DatabaseAccountRegenerateKeyParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/regenerateKey`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body }); } /* Checks that the Azure Cosmos DB account name already exists. A valid account name may contain only lowercase letters, numbers, and the '-' character, and must be between 3 and 50 characters. */ diff --git a/src/Utils/arm/generatedClients/2020-04-01/gremlinResources.ts b/src/Utils/arm/generatedClients/2020-04-01/gremlinResources.ts index 831e8cc6c..26a1fa5b2 100644 --- a/src/Utils/arm/generatedClients/2020-04-01/gremlinResources.ts +++ b/src/Utils/arm/generatedClients/2020-04-01/gremlinResources.ts @@ -39,7 +39,7 @@ export async function createUpdateGremlinDatabase( body: Types.GremlinDatabaseCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB Gremlin database. */ @@ -73,7 +73,7 @@ export async function updateGremlinDatabaseThroughput( body: Types.ThroughputSettingsUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/throughputSettings/default`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Migrate an Azure Cosmos DB Gremlin database from manual throughput to autoscale */ @@ -131,7 +131,7 @@ export async function createUpdateGremlinGraph( body: Types.GremlinGraphCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/graphs/${graphName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB Gremlin graph. */ @@ -168,7 +168,7 @@ export async function updateGremlinGraphThroughput( body: Types.ThroughputSettingsUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/graphs/${graphName}/throughputSettings/default`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Migrate an Azure Cosmos DB Gremlin graph from manual throughput to autoscale */ diff --git a/src/Utils/arm/generatedClients/2020-04-01/mongoDBResources.ts b/src/Utils/arm/generatedClients/2020-04-01/mongoDBResources.ts index 7592effbb..e3a3e9439 100644 --- a/src/Utils/arm/generatedClients/2020-04-01/mongoDBResources.ts +++ b/src/Utils/arm/generatedClients/2020-04-01/mongoDBResources.ts @@ -39,7 +39,7 @@ export async function createUpdateMongoDBDatabase( body: Types.MongoDBDatabaseCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB MongoDB database. */ @@ -73,7 +73,7 @@ export async function updateMongoDBDatabaseThroughput( body: Types.ThroughputSettingsUpdateParameters ): Promise { 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: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Migrate an Azure Cosmos DB MongoDB database from manual throughput to autoscale */ @@ -131,7 +131,7 @@ export async function createUpdateMongoDBCollection( body: Types.MongoDBCollectionCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/collections/${collectionName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB MongoDB Collection. */ @@ -168,7 +168,7 @@ export async function updateMongoDBCollectionThroughput( body: Types.ThroughputSettingsUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/collections/${collectionName}/throughputSettings/default`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Migrate an Azure Cosmos DB MongoDB collection from manual throughput to autoscale */ diff --git a/src/Utils/arm/generatedClients/2020-04-01/sqlResources.ts b/src/Utils/arm/generatedClients/2020-04-01/sqlResources.ts index b12187bc6..7755731c2 100644 --- a/src/Utils/arm/generatedClients/2020-04-01/sqlResources.ts +++ b/src/Utils/arm/generatedClients/2020-04-01/sqlResources.ts @@ -39,7 +39,7 @@ export async function createUpdateSqlDatabase( body: Types.SqlDatabaseCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB SQL database. */ @@ -73,7 +73,7 @@ export async function updateSqlDatabaseThroughput( body: Types.ThroughputSettingsUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/throughputSettings/default`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Migrate an Azure Cosmos DB SQL database from manual throughput to autoscale */ @@ -131,7 +131,7 @@ export async function createUpdateSqlContainer( body: Types.SqlContainerCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB SQL container. */ @@ -168,7 +168,7 @@ export async function updateSqlContainerThroughput( body: Types.ThroughputSettingsUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}/throughputSettings/default`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Migrate an Azure Cosmos DB SQL container from manual throughput to autoscale */ @@ -231,7 +231,7 @@ export async function createUpdateSqlStoredProcedure( body: Types.SqlStoredProcedureCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}/storedProcedures/${storedProcedureName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB SQL storedProcedure. */ @@ -283,7 +283,7 @@ export async function createUpdateSqlUserDefinedFunction( body: Types.SqlUserDefinedFunctionCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}/userDefinedFunctions/${userDefinedFunctionName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB SQL userDefinedFunction. */ @@ -335,7 +335,7 @@ export async function createUpdateSqlTrigger( body: Types.SqlTriggerCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}/triggers/${triggerName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB SQL trigger. */ diff --git a/src/Utils/arm/generatedClients/2020-04-01/tableResources.ts b/src/Utils/arm/generatedClients/2020-04-01/tableResources.ts index 526fae4ad..ede7fbcb4 100644 --- a/src/Utils/arm/generatedClients/2020-04-01/tableResources.ts +++ b/src/Utils/arm/generatedClients/2020-04-01/tableResources.ts @@ -39,7 +39,7 @@ export async function createUpdateTable( body: Types.TableCreateUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/tables/${tableName}`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Deletes an existing Azure Cosmos DB Table. */ @@ -73,7 +73,7 @@ export async function updateTableThroughput( body: Types.ThroughputSettingsUpdateParameters ): Promise { const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/tables/${tableName}/throughputSettings/default`; - return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) }); + return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body }); } /* Migrate an Azure Cosmos DB Table from manual throughput to autoscale */ diff --git a/src/Utils/arm/generatedClients/2020-04-01/types.ts b/src/Utils/arm/generatedClients/2020-04-01/types.ts index bf23c75b4..5f311c688 100644 --- a/src/Utils/arm/generatedClients/2020-04-01/types.ts +++ b/src/Utils/arm/generatedClients/2020-04-01/types.ts @@ -7,87 +7,87 @@ /* The List operation response, that contains the database accounts and their properties. */ export interface DatabaseAccountsListResult { /* List of database account and their properties. */ - readonly value: DatabaseAccountGetResults[]; + readonly value?: DatabaseAccountGetResults[]; } /* The List operation response, that contains the SQL databases and their properties. */ export interface SqlDatabaseListResult { /* List of SQL databases and their properties. */ - readonly value: SqlDatabaseGetResults[]; + readonly value?: SqlDatabaseGetResults[]; } /* The List operation response, that contains the containers and their properties. */ export interface SqlContainerListResult { /* List of containers and their properties. */ - readonly value: SqlContainerGetResults[]; + readonly value?: SqlContainerGetResults[]; } /* The List operation response, that contains the storedProcedures and their properties. */ export interface SqlStoredProcedureListResult { /* List of storedProcedures and their properties. */ - readonly value: SqlStoredProcedureGetResults[]; + readonly value?: SqlStoredProcedureGetResults[]; } /* The List operation response, that contains the userDefinedFunctions and their properties. */ export interface SqlUserDefinedFunctionListResult { /* List of userDefinedFunctions and their properties. */ - readonly value: SqlUserDefinedFunctionGetResults[]; + readonly value?: SqlUserDefinedFunctionGetResults[]; } /* The List operation response, that contains the triggers and their properties. */ export interface SqlTriggerListResult { /* List of triggers and their properties. */ - readonly value: SqlTriggerGetResults[]; + readonly value?: SqlTriggerGetResults[]; } /* The List operation response, that contains the MongoDB databases and their properties. */ export interface MongoDBDatabaseListResult { /* List of MongoDB databases and their properties. */ - readonly value: MongoDBDatabaseGetResults[]; + readonly value?: MongoDBDatabaseGetResults[]; } /* The List operation response, that contains the MongoDB collections and their properties. */ export interface MongoDBCollectionListResult { /* List of MongoDB collections and their properties. */ - readonly value: MongoDBCollectionGetResults[]; + readonly value?: MongoDBCollectionGetResults[]; } /* The List operation response, that contains the Table and their properties. */ export interface TableListResult { /* List of Table and their properties. */ - readonly value: TableGetResults[]; + readonly value?: TableGetResults[]; } /* The List operation response, that contains the Cassandra keyspaces and their properties. */ export interface CassandraKeyspaceListResult { /* List of Cassandra keyspaces and their properties. */ - readonly value: CassandraKeyspaceGetResults[]; + readonly value?: CassandraKeyspaceGetResults[]; } /* The List operation response, that contains the Cassandra tables and their properties. */ export interface CassandraTableListResult { /* List of Cassandra tables and their properties. */ - readonly value: CassandraTableGetResults[]; + readonly value?: CassandraTableGetResults[]; } /* The List operation response, that contains the Gremlin databases and their properties. */ export interface GremlinDatabaseListResult { /* List of Gremlin databases and their properties. */ - readonly value: GremlinDatabaseGetResults[]; + readonly value?: GremlinDatabaseGetResults[]; } /* The List operation response, that contains the graphs and their properties. */ export interface GremlinGraphListResult { /* List of graphs and their properties. */ - readonly value: GremlinGraphGetResults[]; + readonly value?: GremlinGraphGetResults[]; } /* Error Response. */ export interface ErrorResponse { /* Error code. */ - code: string; + code?: string; /* Error message indicating why the operation failed. */ - message: string; + message?: string; } /* The list of new failover policies for the failover priority change. */ @@ -99,11 +99,11 @@ export interface FailoverPolicies { /* The failover policy for a given region of a database account. */ export interface FailoverPolicy { /* The unique identifier of the region in which the database account replicates to. Example: <accountName>-<locationName>. */ - readonly id: string; + readonly id?: string; /* The name of the region in which the database account exists. */ - locationName: string; + locationName?: string; /* The failover priority of the region. A failover priority of 0 indicates a write region. The maximum value for a failover priority = (total number of regions - 1). Failover priority values must be unique for each of the regions in which the database account exists. */ - failoverPriority: number; + failoverPriority?: number; } /* Cosmos DB region to online or offline. */ @@ -115,59 +115,59 @@ export interface RegionForOnlineOffline { /* A region in which the Azure Cosmos DB database account is deployed. */ export interface Location { /* The unique identifier of the region within the database account. Example: <accountName>-<locationName>. */ - readonly id: string; + readonly id?: string; /* The name of the region. */ - locationName: string; + locationName?: string; /* The connection endpoint for the specific region. Example: https://<accountName>-<locationName>.documents.azure.com:443/ */ - readonly documentEndpoint: string; + readonly documentEndpoint?: string; /* undocumented */ - provisioningState: ProvisioningState; + provisioningState?: ProvisioningState; /* The failover priority of the region. A failover priority of 0 indicates a write region. The maximum value for a failover priority = (total number of regions - 1). Failover priority values must be unique for each of the regions in which the database account exists. */ - failoverPriority: number; + failoverPriority?: number; /* Flag to indicate whether or not this region is an AvailabilityZone region */ - isZoneRedundant: boolean; + isZoneRedundant?: boolean; } /* The core properties of ARM resources. */ export interface ARMResourceProperties { /* The unique resource identifier of the ARM resource. */ - readonly id: string; + readonly id?: string; /* The name of the ARM resource. */ - readonly name: string; + readonly name?: string; /* The type of Azure resource. */ - readonly type: string; + readonly type?: string; /* The location of the resource group to which the resource belongs. */ - location: string; + location?: string; /* undocumented */ - tags: Tags; + tags?: Tags; } /* The resource model definition for a ARM proxy resource. It will have everything other than required location and tags */ export interface ARMProxyResource { /* The unique resource identifier of the database account. */ - readonly id: string; + readonly id?: string; /* The name of the database account. */ - readonly name: string; + readonly name?: string; /* The type of Azure resource. */ - readonly type: string; + readonly type?: string; } /* An Azure Cosmos DB database account. */ export type DatabaseAccountGetResults = ARMResourceProperties & { /* Indicates the type of database account. This can only be set at database account creation. */ - kind: string; + kind?: string; /* undocumented */ - properties: DatabaseAccountGetProperties; + properties?: DatabaseAccountGetProperties; }; /* The system generated resource properties associated with SQL databases, SQL containers, Gremlin databases and Gremlin graphs. */ // TODO: ExtendedResourceProperties was missing some properties such as _self which was manually added. Need to fix this in the RP spec. export interface ExtendedResourceProperties { /* A system generated property. A unique identifier. */ - readonly _rid: string; + readonly _rid?: string; /* A system generated property that denotes the last updated timestamp of the resource. */ - readonly _ts: unknown; + readonly _ts?: unknown; /* A system generated property representing the resource etag required for optimistic concurrency control. */ readonly _etag: string; // TODO: This property was manually added. It should be auto-generated like the other properties. @@ -177,175 +177,175 @@ export interface ExtendedResourceProperties { /* An Azure Cosmos DB resource throughput. */ export type ThroughputSettingsGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB resource throughput */ - properties: ThroughputSettingsGetProperties; + properties?: ThroughputSettingsGetProperties; }; /* The properties of an Azure Cosmos DB resource throughput */ export interface ThroughputSettingsGetProperties { /* undocumented */ - resource: ThroughputSettingsResource & ExtendedResourceProperties; + resource?: ThroughputSettingsResource & ExtendedResourceProperties; } /* An Azure Cosmos DB SQL database. */ export type SqlDatabaseGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB SQL database */ - properties: SqlDatabaseGetProperties; + properties?: SqlDatabaseGetProperties; }; /* The properties of an Azure Cosmos DB SQL database */ export interface SqlDatabaseGetProperties { /* undocumented */ - resource: SqlDatabaseResource & ExtendedResourceProperties; + resource?: SqlDatabaseResource & ExtendedResourceProperties; /* undocumented */ - options: OptionsResource; + options?: OptionsResource; } /* An Azure Cosmos DB container. */ export type SqlContainerGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB container */ - properties: SqlContainerGetProperties; + properties?: SqlContainerGetProperties; }; /* The properties of an Azure Cosmos DB container */ export interface SqlContainerGetProperties { /* undocumented */ - resource: SqlContainerResource & ExtendedResourceProperties; + resource?: SqlContainerResource & ExtendedResourceProperties; /* undocumented */ - options: OptionsResource; + options?: OptionsResource; } /* An Azure Cosmos DB storedProcedure. */ export type SqlStoredProcedureGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB storedProcedure */ - properties: SqlStoredProcedureGetProperties; + properties?: SqlStoredProcedureGetProperties; }; /* The properties of an Azure Cosmos DB StoredProcedure */ export interface SqlStoredProcedureGetProperties { /* undocumented */ - resource: SqlStoredProcedureResource & ExtendedResourceProperties; + resource?: SqlStoredProcedureResource & ExtendedResourceProperties; } /* An Azure Cosmos DB userDefinedFunction. */ export type SqlUserDefinedFunctionGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB userDefinedFunction */ - properties: SqlUserDefinedFunctionGetProperties; + properties?: SqlUserDefinedFunctionGetProperties; }; /* The properties of an Azure Cosmos DB userDefinedFunction */ export interface SqlUserDefinedFunctionGetProperties { /* undocumented */ - resource: SqlUserDefinedFunctionResource & ExtendedResourceProperties; + resource?: SqlUserDefinedFunctionResource & ExtendedResourceProperties; } /* An Azure Cosmos DB trigger. */ export type SqlTriggerGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB trigger */ - properties: SqlTriggerGetProperties; + properties?: SqlTriggerGetProperties; }; /* The properties of an Azure Cosmos DB trigger */ export interface SqlTriggerGetProperties { /* undocumented */ - resource: SqlTriggerResource & ExtendedResourceProperties; + resource?: SqlTriggerResource & ExtendedResourceProperties; } /* An Azure Cosmos DB MongoDB database. */ export type MongoDBDatabaseGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB MongoDB database */ - properties: MongoDBDatabaseGetProperties; + properties?: MongoDBDatabaseGetProperties; }; /* The properties of an Azure Cosmos DB MongoDB database */ export interface MongoDBDatabaseGetProperties { /* undocumented */ - resource: MongoDBDatabaseResource & ExtendedResourceProperties; + resource?: MongoDBDatabaseResource & ExtendedResourceProperties; /* undocumented */ - options: OptionsResource; + options?: OptionsResource; } /* An Azure Cosmos DB MongoDB collection. */ export type MongoDBCollectionGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB MongoDB collection */ - properties: MongoDBCollectionGetProperties; + properties?: MongoDBCollectionGetProperties; }; /* The properties of an Azure Cosmos DB MongoDB collection */ export interface MongoDBCollectionGetProperties { /* undocumented */ - resource: MongoDBCollectionResource & ExtendedResourceProperties; + resource?: MongoDBCollectionResource & ExtendedResourceProperties; /* undocumented */ - options: OptionsResource; + options?: OptionsResource; } /* An Azure Cosmos DB Table. */ export type TableGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB Table */ - properties: TableGetProperties; + properties?: TableGetProperties; }; /* The properties of an Azure Cosmos Table */ export interface TableGetProperties { /* undocumented */ - resource: TableResource & ExtendedResourceProperties; + resource?: TableResource & ExtendedResourceProperties; /* undocumented */ - options: OptionsResource; + options?: OptionsResource; } /* An Azure Cosmos DB Cassandra keyspace. */ export type CassandraKeyspaceGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB Cassandra keyspace */ - properties: CassandraKeyspaceGetProperties; + properties?: CassandraKeyspaceGetProperties; }; /* The properties of an Azure Cosmos DB Cassandra keyspace */ export interface CassandraKeyspaceGetProperties { /* undocumented */ - resource: CassandraKeyspaceResource & ExtendedResourceProperties; + resource?: CassandraKeyspaceResource & ExtendedResourceProperties; /* undocumented */ - options: OptionsResource; + options?: OptionsResource; } /* An Azure Cosmos DB Cassandra table. */ export type CassandraTableGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB Cassandra table */ - properties: CassandraTableGetProperties; + properties?: CassandraTableGetProperties; }; /* The properties of an Azure Cosmos DB Cassandra table */ export interface CassandraTableGetProperties { /* undocumented */ - resource: CassandraTableResource & ExtendedResourceProperties; + resource?: CassandraTableResource & ExtendedResourceProperties; /* undocumented */ - options: OptionsResource; + options?: OptionsResource; } /* An Azure Cosmos DB Gremlin database. */ export type GremlinDatabaseGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB SQL database */ - properties: GremlinDatabaseGetProperties; + properties?: GremlinDatabaseGetProperties; }; /* The properties of an Azure Cosmos DB SQL database */ export interface GremlinDatabaseGetProperties { /* undocumented */ - resource: GremlinDatabaseResource & ExtendedResourceProperties; + resource?: GremlinDatabaseResource & ExtendedResourceProperties; /* undocumented */ - options: OptionsResource; + options?: OptionsResource; } /* An Azure Cosmos DB Gremlin graph. */ export type GremlinGraphGetResults = ARMResourceProperties & { /* The properties of an Azure Cosmos DB Gremlin graph */ - properties: GremlinGraphGetProperties; + properties?: GremlinGraphGetProperties; }; /* The properties of an Azure Cosmos DB Gremlin graph */ export interface GremlinGraphGetProperties { /* undocumented */ - resource: GremlinGraphResource & ExtendedResourceProperties; + resource?: GremlinGraphResource & ExtendedResourceProperties; /* undocumented */ - options: OptionsResource; + options?: OptionsResource; } /* The consistency policy for the Cosmos DB database account. */ @@ -353,79 +353,95 @@ export interface ConsistencyPolicy { /* The default consistency level and configuration settings of the Cosmos DB account. */ defaultConsistencyLevel: string; /* When used with the Bounded Staleness consistency level, this value represents the number of stale requests tolerated. Accepted range for this value is 1 – 2,147,483,647. Required when defaultConsistencyPolicy is set to 'BoundedStaleness'. */ - maxStalenessPrefix: number; + maxStalenessPrefix?: number; /* When used with the Bounded Staleness consistency level, this value represents the time amount of staleness (in seconds) tolerated. Accepted range for this value is 5 - 86400. Required when defaultConsistencyPolicy is set to 'BoundedStaleness'. */ - maxIntervalInSeconds: number; + maxIntervalInSeconds?: number; +} + +/* The CORS policy for the Cosmos DB database account. */ +export interface CorsPolicy { + /* The origin domains that are permitted to make a request against the service via CORS. */ + allowedOrigins: string; + /* The methods (HTTP request verbs) that the origin domain may use for a CORS request. */ + allowedMethods?: string; + /* The request headers that the origin domain may specify on the CORS request. */ + allowedHeaders?: string; + /* The response headers that may be sent in the response to the CORS request and exposed by the browser to the request issuer. */ + exposedHeaders?: string; + /* The maximum amount time that a browser should cache the preflight OPTIONS request. */ + maxAgeInSeconds?: number; } /* Properties for the database account. */ export interface DatabaseAccountGetProperties { /* undocumented */ - provisioningState: ProvisioningState; + provisioningState?: ProvisioningState; /* The connection endpoint for the Cosmos DB database account. */ - readonly documentEndpoint: string; + readonly documentEndpoint?: string; /* The offer type for the Cosmos DB database account. Default value: Standard. */ - readonly databaseAccountOfferType: DatabaseAccountOfferType; + readonly databaseAccountOfferType?: DatabaseAccountOfferType; /* List of IpRules. */ - ipRules: IPRules; + ipRules?: IPRules; /* Flag to indicate whether to enable/disable Virtual Network ACL rules. */ - isVirtualNetworkFilterEnabled: boolean; + isVirtualNetworkFilterEnabled?: boolean; /* Enables automatic failover of the write region in the rare event that the region is unavailable due to an outage. Automatic failover will result in a new write region for the account and is chosen based on the failover priorities configured for the account. */ - enableAutomaticFailover: boolean; + enableAutomaticFailover?: boolean; /* The consistency policy for the Cosmos DB database account. */ - consistencyPolicy: ConsistencyPolicy; + consistencyPolicy?: ConsistencyPolicy; /* List of Cosmos DB capabilities for the account */ - capabilities: Capability[]; + capabilities?: Capability[]; /* An array that contains the write location for the Cosmos DB account. */ - readonly writeLocations: Location[]; + readonly writeLocations?: Location[]; /* An array that contains of the read locations enabled for the Cosmos DB account. */ - readonly readLocations: Location[]; + readonly readLocations?: Location[]; /* An array that contains all of the locations enabled for the Cosmos DB account. */ - readonly locations: Location[]; + readonly locations?: Location[]; /* An array that contains the regions ordered by their failover priorities. */ - readonly failoverPolicies: FailoverPolicy[]; + readonly failoverPolicies?: FailoverPolicy[]; /* List of Virtual Network ACL rules configured for the Cosmos DB account. */ - virtualNetworkRules: VirtualNetworkRule[]; + virtualNetworkRules?: VirtualNetworkRule[]; /* List of Private Endpoint Connections configured for the Cosmos DB account. */ - readonly privateEndpointConnections: PrivateEndpointConnection[]; + readonly privateEndpointConnections?: PrivateEndpointConnection[]; /* Enables the account to write in multiple locations */ - enableMultipleWriteLocations: boolean; + enableMultipleWriteLocations?: boolean; /* Enables the cassandra connector on the Cosmos DB C* account */ - enableCassandraConnector: boolean; + enableCassandraConnector?: boolean; /* The cassandra connector offer type for the Cosmos DB database C* account. */ - connectorOffer: ConnectorOffer; + connectorOffer?: ConnectorOffer; /* Disable write operations on metadata resources (databases, containers, throughput) via account keys */ - disableKeyBasedMetadataWriteAccess: boolean; + disableKeyBasedMetadataWriteAccess?: boolean; /* The URI of the key vault */ - keyVaultKeyUri: string; + keyVaultKeyUri?: string; /* Whether requests from Public Network are allowed */ - publicNetworkAccess: PublicNetworkAccess; + publicNetworkAccess?: PublicNetworkAccess; /* Flag to indicate whether Free Tier is enabled. */ - enableFreeTier: boolean; + enableFreeTier?: boolean; /* API specific properties. */ - apiProperties: ApiProperties; + apiProperties?: ApiProperties; /* Flag to indicate whether to enable storage analytics. */ - enableAnalyticalStorage: boolean; + enableAnalyticalStorage?: boolean; + /* The CORS policy for the Cosmos DB database account. */ + cors?: CorsPolicy[]; } /* Properties to create and update Azure Cosmos DB database accounts. */ export interface DatabaseAccountCreateUpdateProperties { /* The consistency policy for the Cosmos DB account. */ - consistencyPolicy: ConsistencyPolicy; + consistencyPolicy?: ConsistencyPolicy; /* An array that contains the georeplication locations enabled for the Cosmos DB account. */ locations: Location[]; @@ -434,45 +450,47 @@ export interface DatabaseAccountCreateUpdateProperties { databaseAccountOfferType: DatabaseAccountOfferType; /* List of IpRules. */ - ipRules: IPRules; + ipRules?: IPRules; /* Flag to indicate whether to enable/disable Virtual Network ACL rules. */ - isVirtualNetworkFilterEnabled: boolean; + isVirtualNetworkFilterEnabled?: boolean; /* Enables automatic failover of the write region in the rare event that the region is unavailable due to an outage. Automatic failover will result in a new write region for the account and is chosen based on the failover priorities configured for the account. */ - enableAutomaticFailover: boolean; + enableAutomaticFailover?: boolean; /* List of Cosmos DB capabilities for the account */ - capabilities: Capability[]; + capabilities?: Capability[]; /* List of Virtual Network ACL rules configured for the Cosmos DB account. */ - virtualNetworkRules: VirtualNetworkRule[]; + virtualNetworkRules?: VirtualNetworkRule[]; /* Enables the account to write in multiple locations */ - enableMultipleWriteLocations: boolean; + enableMultipleWriteLocations?: boolean; /* Enables the cassandra connector on the Cosmos DB C* account */ - enableCassandraConnector: boolean; + enableCassandraConnector?: boolean; /* The cassandra connector offer type for the Cosmos DB database C* account. */ - connectorOffer: ConnectorOffer; + connectorOffer?: ConnectorOffer; /* Disable write operations on metadata resources (databases, containers, throughput) via account keys */ - disableKeyBasedMetadataWriteAccess: boolean; + disableKeyBasedMetadataWriteAccess?: boolean; /* The URI of the key vault */ - keyVaultKeyUri: string; + keyVaultKeyUri?: string; /* Whether requests from Public Network are allowed */ - publicNetworkAccess: PublicNetworkAccess; + publicNetworkAccess?: PublicNetworkAccess; /* Flag to indicate whether Free Tier is enabled. */ - enableFreeTier: boolean; + enableFreeTier?: boolean; /* API specific properties. Currently, supported only for MongoDB API. */ - apiProperties: ApiProperties; + apiProperties?: ApiProperties; /* Flag to indicate whether to enable storage analytics. */ - enableAnalyticalStorage: boolean; + enableAnalyticalStorage?: boolean; + /* The CORS policy for the Cosmos DB database account. */ + cors?: CorsPolicy[]; } /* Parameters to create and update Cosmos DB database accounts. */ export type DatabaseAccountCreateUpdateParameters = ARMResourceProperties & { /* Indicates the type of database account. This can only be set at database account creation. */ - kind: string; + kind?: string; /* undocumented */ properties: DatabaseAccountCreateUpdateProperties; }; @@ -480,86 +498,88 @@ export type DatabaseAccountCreateUpdateParameters = ARMResourceProperties & { /* Properties to update Azure Cosmos DB database accounts. */ export interface DatabaseAccountUpdateProperties { /* The consistency policy for the Cosmos DB account. */ - consistencyPolicy: ConsistencyPolicy; + consistencyPolicy?: ConsistencyPolicy; /* An array that contains the georeplication locations enabled for the Cosmos DB account. */ - locations: Location[]; + locations?: Location[]; /* List of IpRules. */ - ipRules: IPRules; + ipRules?: IPRules; /* Flag to indicate whether to enable/disable Virtual Network ACL rules. */ - isVirtualNetworkFilterEnabled: boolean; + isVirtualNetworkFilterEnabled?: boolean; /* Enables automatic failover of the write region in the rare event that the region is unavailable due to an outage. Automatic failover will result in a new write region for the account and is chosen based on the failover priorities configured for the account. */ - enableAutomaticFailover: boolean; + enableAutomaticFailover?: boolean; /* List of Cosmos DB capabilities for the account */ - capabilities: Capability[]; + capabilities?: Capability[]; /* List of Virtual Network ACL rules configured for the Cosmos DB account. */ - virtualNetworkRules: VirtualNetworkRule[]; + virtualNetworkRules?: VirtualNetworkRule[]; /* Enables the account to write in multiple locations */ - enableMultipleWriteLocations: boolean; + enableMultipleWriteLocations?: boolean; /* Enables the cassandra connector on the Cosmos DB C* account */ - enableCassandraConnector: boolean; + enableCassandraConnector?: boolean; /* The cassandra connector offer type for the Cosmos DB database C* account. */ - connectorOffer: ConnectorOffer; + connectorOffer?: ConnectorOffer; /* Disable write operations on metadata resources (databases, containers, throughput) via account keys */ - disableKeyBasedMetadataWriteAccess: boolean; + disableKeyBasedMetadataWriteAccess?: boolean; /* The URI of the key vault */ - keyVaultKeyUri: string; + keyVaultKeyUri?: string; /* Whether requests from Public Network are allowed */ - publicNetworkAccess: PublicNetworkAccess; + publicNetworkAccess?: PublicNetworkAccess; /* Flag to indicate whether Free Tier is enabled. */ - enableFreeTier: boolean; + enableFreeTier?: boolean; /* API specific properties. Currently, supported only for MongoDB API. */ - apiProperties: ApiProperties; + apiProperties?: ApiProperties; /* Flag to indicate whether to enable storage analytics. */ - enableAnalyticalStorage: boolean; + enableAnalyticalStorage?: boolean; + /* The CORS policy for the Cosmos DB database account. */ + cors?: CorsPolicy[]; } /* Parameters for patching Azure Cosmos DB database account properties. */ export interface DatabaseAccountUpdateParameters { /* undocumented */ - tags: Tags; + tags?: Tags; /* The location of the resource group to which the resource belongs. */ - location: string; + location?: string; /* undocumented */ - properties: DatabaseAccountUpdateProperties; + properties?: DatabaseAccountUpdateProperties; } /* The read-only access keys for the given database account. */ export interface DatabaseAccountListReadOnlyKeysResult { /* Base 64 encoded value of the primary read-only key. */ - readonly primaryReadonlyMasterKey: string; + readonly primaryReadonlyMasterKey?: string; /* Base 64 encoded value of the secondary read-only key. */ - readonly secondaryReadonlyMasterKey: string; + readonly secondaryReadonlyMasterKey?: string; } /* The access keys for the given database account. */ export type DatabaseAccountListKeysResult = DatabaseAccountListReadOnlyKeysResult & { /* Base 64 encoded value of the primary read-write key. */ - readonly primaryMasterKey: string; + readonly primaryMasterKey?: string; /* Base 64 encoded value of the secondary read-write key. */ - readonly secondaryMasterKey: string; + readonly secondaryMasterKey?: string; }; /* Connection string for the Cosmos DB account */ export interface DatabaseAccountConnectionString { /* Value of the connection string */ - readonly connectionString: string; + readonly connectionString?: string; /* Description of the connection string */ - readonly description: string; + readonly description?: string; } /* The connection strings for the given database account. */ export interface DatabaseAccountListConnectionStringsResult { /* An array that contains the connection strings for the Cosmos DB account. */ - connectionStrings: DatabaseAccountConnectionString[]; + connectionStrings?: DatabaseAccountConnectionString[]; } /* Parameters to regenerate the keys within the database account. */ @@ -766,14 +786,14 @@ export interface GremlinGraphCreateUpdateProperties { /* Cosmos DB resource throughput object. Either throughput is required or autoscaleSettings is required, but not both. */ export interface ThroughputSettingsResource { /* Value of the Cosmos DB resource throughput. Either throughput is required or autoscaleSettings is required, but not both. */ - throughput: number; + throughput?: number; /* Cosmos DB resource for autoscale settings. Either throughput is required or autoscaleSettings is required, but not both. */ - autoscaleSettings: AutoscaleSettingsResource; + autoscaleSettings?: AutoscaleSettingsResource; /* The minimum throughput of the resource */ - readonly minimumThroughput: string; + readonly minimumThroughput?: string; /* The throughput replace is pending */ - readonly offerReplacePending: string; + readonly offerReplacePending?: string; } /* Cosmos DB provisioned throughput settings object */ @@ -781,32 +801,32 @@ export interface AutoscaleSettingsResource { /* Represents maximum throughput container can scale up to. */ maxThroughput: number; /* Cosmos DB resource auto-upgrade policy */ - autoUpgradePolicy: AutoUpgradePolicyResource; + autoUpgradePolicy?: AutoUpgradePolicyResource; /* Represents target maximum throughput container can scale up to once offer is no longer in pending state. */ - readonly targetMaxThroughput: number; + readonly targetMaxThroughput?: number; } /* Cosmos DB resource auto-upgrade policy */ export interface AutoUpgradePolicyResource { /* Represents throughput policy which service must adhere to for auto-upgrade */ - throughputPolicy: ThroughputPolicyResource; + throughputPolicy?: ThroughputPolicyResource; } /* Cosmos DB resource throughput policy */ export interface ThroughputPolicyResource { /* Determines whether the ThroughputPolicy is active or not */ - isEnabled: boolean; + isEnabled?: boolean; /* Represents the percentage by which throughput can increase every time throughput policy kicks in. */ - incrementPercent: number; + incrementPercent?: number; } /* Cosmos DB options resource object */ export interface OptionsResource { /* Value of the Cosmos DB resource throughput or autoscaleSettings. Use the ThroughputSetting resource when retrieving offer details. */ - throughput: number; + throughput?: number; /* Specifies the Autoscale settings. */ - autoscaleSettings: AutoscaleSettings; + autoscaleSettings?: AutoscaleSettings; } /* Cosmos DB SQL database resource object */ @@ -820,61 +840,61 @@ export interface SqlContainerResource { /* Name of the Cosmos DB SQL container */ id: string; /* The configuration of the indexing policy. By default, the indexing is automatic for all document paths within the container */ - indexingPolicy: IndexingPolicy; + indexingPolicy?: IndexingPolicy; /* The configuration of the partition key to be used for partitioning data into multiple partitions */ - partitionKey: ContainerPartitionKey; + partitionKey?: ContainerPartitionKey; /* Default time to live */ - defaultTtl: number; + defaultTtl?: number; /* The unique key policy configuration for specifying uniqueness constraints on documents in the collection in the Azure Cosmos DB service. */ - uniqueKeyPolicy: UniqueKeyPolicy; + uniqueKeyPolicy?: UniqueKeyPolicy; /* The conflict resolution policy for the container. */ - conflictResolutionPolicy: ConflictResolutionPolicy; + conflictResolutionPolicy?: ConflictResolutionPolicy; } /* Cosmos DB indexing policy */ export interface IndexingPolicy { /* Indicates if the indexing policy is automatic */ - automatic: boolean; + automatic?: boolean; /* Indicates the indexing mode. */ - indexingMode: string; + indexingMode?: string; /* List of paths to include in the indexing */ - includedPaths: IncludedPath[]; + includedPaths?: IncludedPath[]; /* List of paths to exclude from indexing */ - excludedPaths: ExcludedPath[]; + excludedPaths?: ExcludedPath[]; /* List of composite path list */ - compositeIndexes: CompositePathList[]; + compositeIndexes?: CompositePathList[]; /* List of spatial specifics */ - spatialIndexes: SpatialSpec[]; + spatialIndexes?: SpatialSpec[]; } /* undocumented */ export interface ExcludedPath { /* The path for which the indexing behavior applies to. Index paths typically start with root and end with wildcard (/path/*) */ - path: string; + path?: string; } /* The paths that are included in indexing */ export interface IncludedPath { /* The path for which the indexing behavior applies to. Index paths typically start with root and end with wildcard (/path/*) */ - path: string; + path?: string; /* List of indexes for this path */ - indexes: Indexes[]; + indexes?: Indexes[]; } /* The indexes for the path. */ export interface Indexes { /* The datatype for which the indexing behavior is applied to. */ - dataType: string; + dataType?: string; /* The precision of the index. -1 is maximum precision. */ - precision: number; + precision?: number; /* Indicates the type of index. */ - kind: string; + kind?: string; } /* List of composite path */ @@ -883,17 +903,17 @@ export type CompositePathList = CompositePath[]; /* undocumented */ export interface CompositePath { /* The path for which the indexing behavior applies to. Index paths typically start with root and end with wildcard (/path/*) */ - path: string; + path?: string; /* Sort order for composite paths. */ - order: string; + order?: string; } /* undocumented */ export interface SpatialSpec { /* The path for which the indexing behavior applies to. Index paths typically start with root and end with wildcard (/path/*) */ - path: string; + path?: string; /* List of path's spatial type */ - types: SpatialType[]; + types?: SpatialType[]; } /* Indicates the spatial type of index. */ @@ -902,12 +922,12 @@ export type SpatialType = "Point" | "LineString" | "Polygon" | "MultiPolygon"; /* The configuration of the partition key to be used for partitioning data into multiple partitions */ export interface ContainerPartitionKey { /* List of paths using which data within the container can be partitioned */ - paths: Path[]; + paths?: Path[]; /* Indicates the kind of algorithm used for partitioning */ - kind: string; + kind?: string; /* Indicates the version of the partition key definition */ - version: number; + version?: number; } /* A path. These typically start with root (/path) */ @@ -916,23 +936,23 @@ export type Path = string; /* The unique key policy configuration for specifying uniqueness constraints on documents in the collection in the Azure Cosmos DB service. */ export interface UniqueKeyPolicy { /* List of unique keys on that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service. */ - uniqueKeys: UniqueKey[]; + uniqueKeys?: UniqueKey[]; } /* The unique key on that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service. */ export interface UniqueKey { /* List of paths must be unique for each document in the Azure Cosmos DB service */ - paths: Path[]; + paths?: Path[]; } /* The conflict resolution policy for the container. */ export interface ConflictResolutionPolicy { /* Indicates the conflict resolution mode. */ - mode: string; + mode?: string; /* The conflict resolution path in the case of LastWriterWins mode. */ - conflictResolutionPath: string; + conflictResolutionPath?: string; /* The procedure to resolve conflicts in the case of custom mode. */ - conflictResolutionProcedure: string; + conflictResolutionProcedure?: string; } /* Cosmos DB SQL storedProcedure resource object */ @@ -940,7 +960,7 @@ export interface SqlStoredProcedureResource { /* Name of the Cosmos DB SQL storedProcedure */ id: string; /* Body of the Stored Procedure */ - body: string; + body?: string; } /* Cosmos DB SQL userDefinedFunction resource object */ @@ -948,7 +968,7 @@ export interface SqlUserDefinedFunctionResource { /* Name of the Cosmos DB SQL userDefinedFunction */ id: string; /* Body of the User Defined Function */ - body: string; + body?: string; } /* Cosmos DB SQL trigger resource object */ @@ -956,11 +976,11 @@ export interface SqlTriggerResource { /* Name of the Cosmos DB SQL trigger */ id: string; /* Body of the Trigger */ - body: string; + body?: string; /* Type of the Trigger */ - triggerType: string; + triggerType?: string; /* The operation the trigger is associated with */ - triggerOperation: string; + triggerOperation?: string; } /* Cosmos DB MongoDB database resource object */ @@ -974,13 +994,13 @@ export interface MongoDBCollectionResource { /* Name of the Cosmos DB MongoDB collection */ id: string; /* A key-value pair of shard keys to be applied for the request. */ - shardKey: ShardKeys; + shardKey?: ShardKeys; /* List of index keys */ - indexes: MongoIndex[]; + indexes?: MongoIndex[]; /* Analytical TTL. */ - analyticalStorageTtl: number; + analyticalStorageTtl?: number; } /* The shard key and partition kind pair, only support "Hash" partition kind */ @@ -989,16 +1009,16 @@ export type ShardKeys = { [key: string]: string }; /* Cosmos DB MongoDB collection index key */ export interface MongoIndex { /* Cosmos DB MongoDB collection index keys */ - key: MongoIndexKeys; + key?: MongoIndexKeys; /* Cosmos DB MongoDB collection index key options */ - options: MongoIndexOptions; + options?: MongoIndexOptions; } /* Cosmos DB MongoDB collection resource object */ export interface MongoIndexKeys { /* List of keys for each MongoDB collection in the Azure Cosmos DB service */ - keys: Key[]; + keys?: Key[]; } /* A Key. */ @@ -1007,9 +1027,9 @@ export type Key = string; /* Cosmos DB MongoDB collection index options */ export interface MongoIndexOptions { /* Expire after seconds */ - expireAfterSeconds: number; + expireAfterSeconds?: number; /* Is unique or not */ - unique: boolean; + unique?: boolean; } /* Cosmos DB table resource object */ @@ -1029,46 +1049,46 @@ export interface CassandraTableResource { /* Name of the Cosmos DB Cassandra table */ id: string; /* Time to live of the Cosmos DB Cassandra table */ - defaultTtl: number; + defaultTtl?: number; /* Schema of the Cosmos DB Cassandra table */ - schema: CassandraSchema; + schema?: CassandraSchema; /* Analytical TTL. */ - analyticalStorageTtl: number; + analyticalStorageTtl?: number; } /* Cosmos DB Cassandra table schema */ export interface CassandraSchema { /* List of Cassandra table columns. */ - columns: Column[]; + columns?: Column[]; /* List of partition key. */ - partitionKeys: CassandraPartitionKey[]; + partitionKeys?: CassandraPartitionKey[]; /* List of cluster key. */ - clusterKeys: ClusterKey[]; + clusterKeys?: ClusterKey[]; } /* Cosmos DB Cassandra table column */ export interface Column { /* Name of the Cosmos DB Cassandra table column */ - name: string; + name?: string; /* Type of the Cosmos DB Cassandra table column */ - type: string; + type?: string; } /* Cosmos DB Cassandra table partition key */ export interface CassandraPartitionKey { /* Name of the Cosmos DB Cassandra table partition key */ - name: string; + name?: string; } /* Cosmos DB Cassandra table cluster key */ export interface ClusterKey { /* Name of the Cosmos DB Cassandra table cluster key */ - name: string; + name?: string; /* Order of the Cosmos DB Cassandra table cluster key, only support "Asc" and "Desc" */ - orderBy: string; + orderBy?: string; } /* Cosmos DB Gremlin database resource object */ @@ -1082,38 +1102,38 @@ export interface GremlinGraphResource { /* Name of the Cosmos DB Gremlin graph */ id: string; /* The configuration of the indexing policy. By default, the indexing is automatic for all document paths within the graph */ - indexingPolicy: IndexingPolicy; + indexingPolicy?: IndexingPolicy; /* The configuration of the partition key to be used for partitioning data into multiple partitions */ - partitionKey: ContainerPartitionKey; + partitionKey?: ContainerPartitionKey; /* Default time to live */ - defaultTtl: number; + defaultTtl?: number; /* The unique key policy configuration for specifying uniqueness constraints on documents in the collection in the Azure Cosmos DB service. */ - uniqueKeyPolicy: UniqueKeyPolicy; + uniqueKeyPolicy?: UniqueKeyPolicy; /* The conflict resolution policy for the graph. */ - conflictResolutionPolicy: ConflictResolutionPolicy; + conflictResolutionPolicy?: ConflictResolutionPolicy; } /* CreateUpdateOptions are a list of key-value pairs that describe the resource. Supported keys are "If-Match", "If-None-Match", "Session-Token" and "Throughput" */ export interface CreateUpdateOptions { /* Request Units per second. For example, "throughput": 10000. */ - throughput: number; + throughput?: number; /* Specifies the Autoscale settings. */ - autoscaleSettings: AutoscaleSettings; + autoscaleSettings?: AutoscaleSettings; } /* undocumented */ export interface AutoscaleSettings { /* Represents maximum throughput, the resource can scale up to. */ - maxThroughput: number; + maxThroughput?: number; } /* Cosmos DB capability object */ export interface Capability { /* Name of the Cosmos DB capability. For example, "name": "EnableCassandra". Current values also include "EnableTable" and "EnableGremlin". */ - name: string; + name?: string; } /* Tags are a list of key-value pairs that describe the resource. These tags can be used in viewing and grouping this resource (across resource groups). A maximum of 15 tags can be provided for a resource. Each tag must have a key no greater than 128 characters and value no greater than 256 characters. For example, the default experience for a template type is set with "defaultExperience": "Cassandra". Current "defaultExperience" values also include "Table", "Graph", "DocumentDB", and "MongoDB". */ @@ -1128,231 +1148,231 @@ export type IPRules = IpAddressOrRange[]; /* IpAddressOrRange object */ export interface IpAddressOrRange { /* A single IPv4 address or a single IPv4 address range in CIDR format. Provided IPs must be well-formatted and cannot be contained in one of the following ranges: 10.0.0.0/8, 100.64.0.0/10, 172.16.0.0/12, 192.168.0.0/16, since these are not enforceable by the IP address filter. Example of valid inputs: “23.40.210.245” or “23.40.210.0/8”. */ - ipAddressOrRange: string; + ipAddressOrRange?: string; } /* Virtual Network ACL Rule object */ export interface VirtualNetworkRule { /* Resource ID of a subnet, for example: /subscriptions/{subscriptionId}/resourceGroups/{groupName}/providers/Microsoft.Network/virtualNetworks/{virtualNetworkName}/subnets/{subnetName}. */ - id: string; + id?: string; /* Create firewall rule before the virtual network has vnet service endpoint enabled. */ - ignoreMissingVNetServiceEndpoint: boolean; + ignoreMissingVNetServiceEndpoint?: boolean; } /* A private endpoint connection */ export type PrivateEndpointConnection = unknown & { /* Resource properties. */ - properties: PrivateEndpointConnectionProperties; + properties?: PrivateEndpointConnectionProperties; }; /* Properties of a private endpoint connection. */ export interface PrivateEndpointConnectionProperties { /* Private endpoint which the connection belongs to. */ - privateEndpoint: PrivateEndpointProperty; + privateEndpoint?: PrivateEndpointProperty; /* Connection State of the Private Endpoint Connection. */ - privateLinkServiceConnectionState: PrivateLinkServiceConnectionStateProperty; + privateLinkServiceConnectionState?: PrivateLinkServiceConnectionStateProperty; } /* Private endpoint which the connection belongs to. */ export interface PrivateEndpointProperty { /* Resource id of the private endpoint. */ - id: string; + id?: string; } /* Connection State of the Private Endpoint Connection. */ export interface PrivateLinkServiceConnectionStateProperty { /* The private link service connection status. */ - status: string; + status?: string; /* Any action that is required beyond basic workflow (approve/ reject/ disconnect) */ - readonly actionsRequired: string; + readonly actionsRequired?: string; } /* REST API operation */ export interface Operation { /* Operation name: {provider}/{resource}/{operation} */ - name: string; + name?: string; /* The object that represents the operation. */ - display: unknown; + display?: unknown; } /* Result of the request to list Resource Provider operations. It contains a list of operations and a URL link to get the next set of results. */ export interface OperationListResult { /* List of operations supported by the Resource Provider. */ - value: Operation[]; + value?: Operation[]; /* URL to get the next set of operation list results if there are any. */ - nextLink: string; + nextLink?: string; } /* The response to a list usage request. */ export interface UsagesResult { /* The list of usages for the database. A usage is a point in time metric */ - readonly value: Usage[]; + readonly value?: Usage[]; } /* The usage data for a usage request. */ export interface Usage { /* The unit of the metric. */ - unit: UnitType; + unit?: UnitType; /* The name information for the metric. */ - readonly name: MetricName; + readonly name?: MetricName; /* The quota period used to summarize the usage values. */ - readonly quotaPeriod: string; + readonly quotaPeriod?: string; /* Maximum value for this metric */ - readonly limit: number; + readonly limit?: number; /* Current value for this metric */ - readonly currentValue: number; + readonly currentValue?: number; } /* The response to a list partition level usage request. */ export interface PartitionUsagesResult { /* The list of partition-level usages for the database. A usage is a point in time metric */ - readonly value: PartitionUsage[]; + readonly value?: PartitionUsage[]; } /* The partition level usage data for a usage request. */ export type PartitionUsage = Usage & { /* The partition id (GUID identifier) of the usages. */ - readonly partitionId: string; + readonly partitionId?: string; /* The partition key range id (integer identifier) of the usages. */ - readonly partitionKeyRangeId: string; + readonly partitionKeyRangeId?: string; }; /* The response to a list metric definitions request. */ export interface MetricDefinitionsListResult { /* The list of metric definitions for the account. */ - readonly value: MetricDefinition[]; + readonly value?: MetricDefinition[]; } /* The definition of a metric. */ export interface MetricDefinition { /* The list of metric availabilities for the account. */ - readonly metricAvailabilities: MetricAvailability[]; + readonly metricAvailabilities?: MetricAvailability[]; /* The primary aggregation type of the metric. */ - readonly primaryAggregationType: string; + readonly primaryAggregationType?: string; /* The unit of the metric. */ - unit: UnitType; + unit?: UnitType; /* The resource uri of the database. */ - readonly resourceUri: string; + readonly resourceUri?: string; /* The name information for the metric. */ - readonly name: MetricName; + readonly name?: MetricName; } /* The availability of the metric. */ export interface MetricAvailability { /* The time grain to be used to summarize the metric values. */ - readonly timeGrain: string; + readonly timeGrain?: string; /* The retention for the metric values. */ - readonly retention: string; + readonly retention?: string; } /* The response to a list metrics request. */ export interface MetricListResult { /* The list of metrics for the account. */ - readonly value: Metric[]; + readonly value?: Metric[]; } /* Metric data */ export interface Metric { /* The start time for the metric (ISO-8601 format). */ - readonly startTime: string; + readonly startTime?: string; /* The end time for the metric (ISO-8601 format). */ - readonly endTime: string; + readonly endTime?: string; /* The time grain to be used to summarize the metric values. */ - readonly timeGrain: string; + readonly timeGrain?: string; /* The unit of the metric. */ - unit: UnitType; + unit?: UnitType; /* The name information for the metric. */ - readonly name: MetricName; + readonly name?: MetricName; /* The metric values for the specified time window and timestep. */ - readonly metricValues: MetricValue[]; + readonly metricValues?: MetricValue[]; } /* A metric name. */ export interface MetricName { /* The name of the metric. */ - readonly value: string; + readonly value?: string; /* The friendly name of the metric. */ - readonly localizedValue: string; + readonly localizedValue?: string; } /* Represents metrics values. */ export interface MetricValue { /* The number of values for the metric. */ - readonly _count: number; + readonly _count?: number; /* The average value of the metric. */ - readonly average: number; + readonly average?: number; /* The max value of the metric. */ - readonly maximum: number; + readonly maximum?: number; /* The min value of the metric. */ - readonly minimum: number; + readonly minimum?: number; /* The metric timestamp (ISO-8601 format). */ - readonly timestamp: string; + readonly timestamp?: string; /* The total value of the metric. */ - readonly total: number; + readonly total?: number; } /* The response to a list percentile metrics request. */ export interface PercentileMetricListResult { /* The list of percentile metrics for the account. */ - readonly value: PercentileMetric[]; + readonly value?: PercentileMetric[]; } /* Percentile Metric data */ export interface PercentileMetric { /* The start time for the metric (ISO-8601 format). */ - readonly startTime: string; + readonly startTime?: string; /* The end time for the metric (ISO-8601 format). */ - readonly endTime: string; + readonly endTime?: string; /* The time grain to be used to summarize the metric values. */ - readonly timeGrain: string; + readonly timeGrain?: string; /* The unit of the metric. */ - unit: UnitType; + unit?: UnitType; /* The name information for the metric. */ - readonly name: MetricName; + readonly name?: MetricName; /* The percentile metric values for the specified time window and timestep. */ - readonly metricValues: PercentileMetricValue[]; + readonly metricValues?: PercentileMetricValue[]; } /* Represents percentile metrics values. */ export type PercentileMetricValue = MetricValue & { /* The 10th percentile value for the metric. */ - readonly P10: number; + readonly P10?: number; /* The 25th percentile value for the metric. */ - readonly P25: number; + readonly P25?: number; /* The 50th percentile value for the metric. */ - readonly P50: number; + readonly P50?: number; /* The 75th percentile value for the metric. */ - readonly P75: number; + readonly P75?: number; /* The 90th percentile value for the metric. */ - readonly P90: number; + readonly P90?: number; /* The 95th percentile value for the metric. */ - readonly P95: number; + readonly P95?: number; /* The 99th percentile value for the metric. */ - readonly P99: number; + readonly P99?: number; }; /* The response to a list partition metrics request. */ export interface PartitionMetricListResult { /* The list of partition-level metrics for the account. */ - readonly value: PartitionMetric[]; + readonly value?: PartitionMetric[]; } /* The metric values for a single partition. */ export type PartitionMetric = Metric & { /* The partition id (GUID identifier) of the metric values. */ - readonly partitionId: string; + readonly partitionId?: string; /* The partition key range id (integer identifier) of the metric values. */ - readonly partitionKeyRangeId: string; + readonly partitionKeyRangeId?: string; }; /* The unit of the metric. */ @@ -1367,5 +1387,5 @@ export type PublicNetworkAccess = "Enabled" | "Disabled"; /* undocumented */ export interface ApiProperties { /* Describes the ServerVersion of an a MongoDB account. */ - serverVersion: string; + serverVersion?: string; } diff --git a/utils/armClientGenerator/generator.ts b/utils/armClientGenerator/generator.ts index b0b506f98..b4bd7023f 100644 --- a/utils/armClientGenerator/generator.ts +++ b/utils/armClientGenerator/generator.ts @@ -102,31 +102,31 @@ interface Property { }[]; } -const propertyToType = (property: Property, prop: string) => { +const propertyToType = (property: Property, prop: string, required: boolean) => { if (property) { if (property.allOf) { outputTypes.push(` /* ${property.description || "undocumented"} */ - ${property.readOnly ? "readonly " : ""}${prop}: ${property.allOf - .map((allof: { $ref: string }) => refToType(allof.$ref)) - .join(" & ")}`); + ${property.readOnly ? "readonly " : ""}${prop}${ + required ? "" : "?" + }: ${property.allOf.map((allof: { $ref: string }) => refToType(allof.$ref)).join(" & ")}`); } else if (property.$ref) { const type = refToType(property.$ref); outputTypes.push(` /* ${property.description || "undocumented"} */ - ${property.readOnly ? "readonly " : ""}${prop}: ${type} + ${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${type} `); } else if (property.type === "array") { const type = refToType(property.items.$ref); outputTypes.push(` /* ${property.description || "undocumented"} */ - ${property.readOnly ? "readonly " : ""}${prop}: ${type}[] + ${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${type}[] `); } else if (property.type === "object") { const type = refToType(property.$ref); outputTypes.push(` /* ${property.description || "undocumented"} */ - ${property.readOnly ? "readonly " : ""}${prop}: ${type} + ${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${type} `); } else { if (property.type === undefined) { @@ -135,7 +135,7 @@ const propertyToType = (property: Property, prop: string) => { } outputTypes.push(` /* ${property.description || "undocumented"} */ - ${property.readOnly ? "readonly " : ""}${prop}: ${ + ${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${ propertyMap[property.type] ? propertyMap[property.type] : property.type }`); } @@ -166,7 +166,7 @@ async function main() { } for (const prop in schema.definitions[definition].properties) { const property = schema.definitions[definition].properties[prop]; - propertyToType(property, prop); + propertyToType(property, prop, schema.definitions[definition].required?.includes(prop)); } outputTypes.push(`}`); outputTypes.push("\n\n"); @@ -245,7 +245,7 @@ async function main() { ) : Promise<${responseType(operation, "Types")}> { const path = \`${path.replace(/{/g, "${")}\` return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "${method.toLocaleUpperCase()}", apiVersion, ${ - bodyParameter ? "body: JSON.stringify(body)" : "" + bodyParameter ? "body" : "" } }) } `); From 22b2e1df4854cfbfdb70d231be8413399e4306fb Mon Sep 17 00:00:00 2001 From: Tanuj Mittal Date: Fri, 14 Aug 2020 17:45:13 -0700 Subject: [PATCH 02/15] Support empty offers for serverless accounts (#132) * Add support for no offers when serverless accounts * Add workaround for ignoring failed /offers when using connection string * Revert @azure/cosmos upgrade --- src/Common/DataAccessUtilityBase.ts | 11 ++++++ src/Explorer/Explorer.ts | 59 +++++++++++++---------------- src/Explorer/Tree/Collection.ts | 8 ++-- src/Explorer/Tree/Database.ts | 13 ++++--- 4 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/Common/DataAccessUtilityBase.ts b/src/Common/DataAccessUtilityBase.ts index 7b6f1633e..04bc90627 100644 --- a/src/Common/DataAccessUtilityBase.ts +++ b/src/Common/DataAccessUtilityBase.ts @@ -454,6 +454,10 @@ export function readCollectionQuotaInfo( } export function readOffers(options: any): Q.Promise { + if (options.isServerless) { + return Q([]); // Reading offers is not supported for serverless accounts + } + try { if (configContext.platform === Platform.Portal) { return sendCachedDataMessage(MessageTypes.AllOffers, [ @@ -469,6 +473,13 @@ export function readOffers(options: any): Q.Promise { .offers.readAll() .fetchAll() .then(response => response.resources) + .catch(error => { + // This should be removed when we can correctly identify if an account is serverless when connected using connection string too. + if (error.message.includes("Reading or replacing offers is not supported for serverless accounts")) { + return []; + } + throw error; + }) ); } diff --git a/src/Explorer/Explorer.ts b/src/Explorer/Explorer.ts index 377d31cb7..3d82f245a 100644 --- a/src/Explorer/Explorer.ts +++ b/src/Explorer/Explorer.ts @@ -1475,38 +1475,33 @@ export default class Explorer { ); }; - if (this.isServerlessEnabled()) { - // Serverless accounts don't support offers call - refreshDatabases(); - } else { - const offerPromise: Q.Promise = readOffers(); - this._setLoadingStatusText("Fetching offers..."); - offerPromise.then( - (offers: DataModels.Offer[]) => { - this._setLoadingStatusText("Successfully fetched offers."); - refreshDatabases(offers); - }, - error => { - this._setLoadingStatusText("Failed to fetch offers."); - this.isRefreshingExplorer(false); - deferred.reject(error); - TelemetryProcessor.traceFailure( - Action.LoadDatabases, - { - databaseAccountName: this.databaseAccount().name, - defaultExperience: this.defaultExperience(), - dataExplorerArea: Constants.Areas.ResourceTree, - error: JSON.stringify(error) - }, - startKey - ); - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Error, - `Error while refreshing databases: ${JSON.stringify(error)}` - ); - } - ); - } + const offerPromise: Q.Promise = readOffers({ isServerless: this.isServerlessEnabled() }); + this._setLoadingStatusText("Fetching offers..."); + offerPromise.then( + (offers: DataModels.Offer[]) => { + this._setLoadingStatusText("Successfully fetched offers."); + refreshDatabases(offers); + }, + error => { + this._setLoadingStatusText("Failed to fetch offers."); + this.isRefreshingExplorer(false); + deferred.reject(error); + TelemetryProcessor.traceFailure( + Action.LoadDatabases, + { + databaseAccountName: this.databaseAccount().name, + defaultExperience: this.defaultExperience(), + dataExplorerArea: Constants.Areas.ResourceTree, + error: JSON.stringify(error) + }, + startKey + ); + NotificationConsoleUtils.logConsoleMessage( + ConsoleDataType.Error, + `Error while refreshing databases: ${JSON.stringify(error)}` + ); + } + ); return deferred.promise.then( () => { diff --git a/src/Explorer/Tree/Collection.ts b/src/Explorer/Tree/Collection.ts index 121bc686f..f88df007b 100644 --- a/src/Explorer/Tree/Collection.ts +++ b/src/Explorer/Tree/Collection.ts @@ -648,7 +648,9 @@ export default class Collection implements ViewModels.Collection { }); // TODO: Use the collection entity cache to get quota info const quotaInfoPromise: Q.Promise = readCollectionQuotaInfo(this); - const offerInfoPromise: Q.Promise = readOffers(); + const offerInfoPromise: Q.Promise = readOffers({ + isServerless: this.container.isServerlessEnabled() + }); Q.all([quotaInfoPromise, offerInfoPromise]).then( () => { this.container.isRefreshingExplorer(false); @@ -657,9 +659,7 @@ export default class Collection implements ViewModels.Collection { const quotaInfo = _.omit(quotaInfoWithUniqueKeyPolicy, "uniqueKeyPolicy"); const collectionOffer = this._getOfferForCollection(offerInfoPromise.valueOf(), collectionDataModel); - const isDatabaseShared = this.getDatabase() && this.getDatabase().isDatabaseShared(); - const isServerless = this.container.isServerlessEnabled(); - if ((isDatabaseShared || isServerless) && !collectionOffer) { + if (!collectionOffer) { this.quotaInfo(quotaInfo); TelemetryProcessor.traceSuccess( Action.LoadOffers, diff --git a/src/Explorer/Tree/Database.ts b/src/Explorer/Tree/Database.ts index d92e0c9bc..cb007248e 100644 --- a/src/Explorer/Tree/Database.ts +++ b/src/Explorer/Tree/Database.ts @@ -123,10 +123,6 @@ export default class Database implements ViewModels.Database { public readSettings(): Q.Promise { const deferred: Q.Deferred = Q.defer(); - if (this.container.isServerlessEnabled()) { - deferred.resolve(); - } - this.container.isRefreshingExplorer(true); const databaseDataModel: DataModels.Database = { id: this.id(), @@ -138,7 +134,9 @@ export default class Database implements ViewModels.Database { defaultExperience: this.container.defaultExperience() }); - const offerInfoPromise: Q.Promise = readOffers(); + const offerInfoPromise: Q.Promise = readOffers({ + isServerless: this.container.isServerlessEnabled() + }); Q.all([offerInfoPromise]).then( () => { this.container.isRefreshingExplorer(false); @@ -147,6 +145,11 @@ export default class Database implements ViewModels.Database { offerInfoPromise.valueOf(), databaseDataModel ); + + if (!databaseOffer) { + return; + } + readOffer(databaseOffer).then((offerDetail: DataModels.OfferWithHeaders) => { const offerThroughputInfo: DataModels.OfferThroughputInfo = { minimumRUForCollection: From f44a3da5684501e3cf61fe697be81570d06c38b4 Mon Sep 17 00:00:00 2001 From: Laurent Nguyen Date: Mon, 17 Aug 2020 09:50:57 +0200 Subject: [PATCH 03/15] Allow boolean partition key in graph visualization (#150) * Allow boolean pk values to be displayed * Add unit tests --- .../Graph/GraphExplorerComponent/GraphExplorer.test.tsx | 5 +++++ src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx b/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx index 348c43576..7f98595f8 100644 --- a/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx +++ b/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx @@ -92,6 +92,11 @@ describe("getPkIdFromDocumentId", () => { expect(GraphExplorer.getPkIdFromDocumentId(doc, "mypk")).toEqual("[234, 'id']"); }); + it("should create pkid pair from partitioned graph (pk as boolean)", () => { + const doc = createFakeDoc({ id: "id", mypk: true }); + expect(GraphExplorer.getPkIdFromDocumentId(doc, "mypk")).toEqual("[true, 'id']"); + }); + it("should create pkid pair from partitioned graph (pk as valid array value)", () => { const doc = createFakeDoc({ id: "id", mypk: [{ id: "someid", _value: "pkvalue" }] }); expect(GraphExplorer.getPkIdFromDocumentId(doc, "mypk")).toEqual("['pkvalue', 'id']"); diff --git a/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx b/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx index de56d7015..aa939ef5e 100644 --- a/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx +++ b/src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx @@ -1371,7 +1371,7 @@ export class GraphExplorer extends React.Component 0) { // pk is [{ id: 'id', _value: 'value' }] pk = pk[0]["_value"]; From dde2ca75c4a81728f051d1922f0fd1edebc97c00 Mon Sep 17 00:00:00 2001 From: Tanuj Mittal Date: Mon, 17 Aug 2020 11:39:11 -0700 Subject: [PATCH 04/15] Show submit button by default in GenericRightPaneComponent & other fixes (#144) * Bug fixes * Fix build --- .../Panes/GenericRightPaneComponent.tsx | 4 +- .../Panes/PublishNotebookPaneAdapter.tsx | 2 +- .../Panes/PublishNotebookPaneComponent.tsx | 2 +- src/Explorer/Panes/UploadItemsPaneAdapter.tsx | 1 - src/Explorer/Tabs/NotebookV2Tab.ts | 63 +++++++++------- src/Explorer/Tree/ResourceTreeAdapter.tsx | 75 +++++++++++-------- 6 files changed, 81 insertions(+), 66 deletions(-) diff --git a/src/Explorer/Panes/GenericRightPaneComponent.tsx b/src/Explorer/Panes/GenericRightPaneComponent.tsx index 30f278349..b3db7c358 100644 --- a/src/Explorer/Panes/GenericRightPaneComponent.tsx +++ b/src/Explorer/Panes/GenericRightPaneComponent.tsx @@ -16,7 +16,7 @@ export interface GenericRightPaneProps { onSubmit: () => void; submitButtonText: string; title: string; - isSubmitButtonVisible?: boolean; + isSubmitButtonHidden?: boolean; } export interface GenericRightPaneState { @@ -108,7 +108,7 @@ export class GenericRightPaneComponent extends React.Component
this.close(), onSubmit: () => this.submit(), - isSubmitButtonVisible: this.isCodeOfConductAccepted + isSubmitButtonHidden: !this.isCodeOfConductAccepted }; const publishNotebookPaneProps: PublishNotebookPaneProps = { diff --git a/src/Explorer/Panes/PublishNotebookPaneComponent.tsx b/src/Explorer/Panes/PublishNotebookPaneComponent.tsx index 986b42e2e..1980e7a77 100644 --- a/src/Explorer/Panes/PublishNotebookPaneComponent.tsx +++ b/src/Explorer/Panes/PublishNotebookPaneComponent.tsx @@ -285,7 +285,7 @@ export class PublishNotebookPaneComponent extends React.Component this.close(), diff --git a/src/Explorer/Tabs/NotebookV2Tab.ts b/src/Explorer/Tabs/NotebookV2Tab.ts index 5f3de274b..e369bc7be 100644 --- a/src/Explorer/Tabs/NotebookV2Tab.ts +++ b/src/Explorer/Tabs/NotebookV2Tab.ts @@ -147,6 +147,30 @@ export default class NotebookTabV2 extends TabsBase { const cellCodeType = "code"; const cellMarkdownType = "markdown"; const cellRawType = "raw"; + + const saveButtonChildren = []; + if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) { + saveButtonChildren.push({ + iconName: "Copy", + onCommandClick: () => this.copyNotebook(), + commandButtonLabel: copyToLabel, + hasPopup: false, + disabled: false, + ariaLabel: copyToLabel + }); + } + + if (this.container.isGalleryPublishEnabled()) { + saveButtonChildren.push({ + iconName: "PublishContent", + onCommandClick: async () => await this.publishToGallery(), + commandButtonLabel: publishLabel, + hasPopup: false, + disabled: false, + ariaLabel: publishLabel + }); + } + let buttons: CommandButtonComponentProps[] = [ { iconSrc: SaveIcon, @@ -156,34 +180,17 @@ export default class NotebookTabV2 extends TabsBase { hasPopup: false, disabled: false, ariaLabel: saveLabel, - children: this.container.isGalleryPublishEnabled() - ? [ - { - iconName: "Save", - onCommandClick: () => this.notebookComponentAdapter.notebookSave(), - commandButtonLabel: saveLabel, - hasPopup: false, - disabled: false, - ariaLabel: saveLabel - }, - { - iconName: "Copy", - onCommandClick: () => this.copyNotebook(), - commandButtonLabel: copyToLabel, - hasPopup: false, - disabled: false, - ariaLabel: copyToLabel - }, - { - iconName: "PublishContent", - onCommandClick: async () => await this.publishToGallery(), - commandButtonLabel: publishLabel, - hasPopup: false, - disabled: false, - ariaLabel: publishLabel - } - ] - : undefined + children: saveButtonChildren.length && [ + { + iconName: "Save", + onCommandClick: () => this.notebookComponentAdapter.notebookSave(), + commandButtonLabel: saveLabel, + hasPopup: false, + disabled: false, + ariaLabel: saveLabel + }, + ...saveButtonChildren + ] }, { iconSrc: null, diff --git a/src/Explorer/Tree/ResourceTreeAdapter.tsx b/src/Explorer/Tree/ResourceTreeAdapter.tsx index 2564a4930..7f9d8d6d5 100644 --- a/src/Explorer/Tree/ResourceTreeAdapter.tsx +++ b/src/Explorer/Tree/ResourceTreeAdapter.tsx @@ -546,43 +546,52 @@ export class ResourceTreeAdapter implements ReactAdapter { (activeTab as any).notebookPath() === item.path ); }, - contextMenu: createFileContextMenu - ? [ - { - label: "Rename", - iconSrc: NotebookIcon, - onClick: () => this.container.renameNotebook(item) - }, - { - label: "Delete", - iconSrc: DeleteIcon, - onClick: () => { - this.container.showOkCancelModalDialog( - "Confirm delete", - `Are you sure you want to delete "${item.name}"`, - "Delete", - () => this.container.deleteNotebookFile(item).then(() => this.triggerRender()), - "Cancel", - undefined - ); - } - }, - { - label: "Copy to ...", - iconSrc: CopyIcon, - onClick: () => this.copyNotebook(item) - }, - { - label: "Download", - iconSrc: NotebookIcon, - onClick: () => this.container.downloadFile(item) - } - ] - : undefined, + contextMenu: createFileContextMenu && this.createFileContextMenu(item), data: item }; } + private createFileContextMenu(item: NotebookContentItem): TreeNodeMenuItem[] { + let items: TreeNodeMenuItem[] = [ + { + label: "Rename", + iconSrc: NotebookIcon, + onClick: () => this.container.renameNotebook(item) + }, + { + label: "Delete", + iconSrc: DeleteIcon, + onClick: () => { + this.container.showOkCancelModalDialog( + "Confirm delete", + `Are you sure you want to delete "${item.name}"`, + "Delete", + () => this.container.deleteNotebookFile(item).then(() => this.triggerRender()), + "Cancel", + undefined + ); + } + }, + { + label: "Copy to ...", + iconSrc: CopyIcon, + onClick: () => this.copyNotebook(item) + }, + { + label: "Download", + iconSrc: NotebookIcon, + onClick: () => this.container.downloadFile(item) + } + ]; + + // "Copy to ..." isn't needed if github locations are not available + if (!this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) { + items = items.filter(item => item.label !== "Copy to ..."); + } + + return items; + } + private copyNotebook = async (item: NotebookContentItem) => { const content = await this.container.readFile(item); if (content) { From 07b9c1d1b7cfe04421a2173ab04e9a1a31b29725 Mon Sep 17 00:00:00 2001 From: victor-meng <56978073+victor-meng@users.noreply.github.com> Date: Mon, 17 Aug 2020 11:42:02 -0700 Subject: [PATCH 05/15] Switch back to using SDK to read databases and collections for Mongo API (#153) --- src/Common/dataAccess/readCollections.ts | 6 +++++- src/Common/dataAccess/readDatabases.ts | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Common/dataAccess/readCollections.ts b/src/Common/dataAccess/readCollections.ts index 2bc51599f..79cecc077 100644 --- a/src/Common/dataAccess/readCollections.ts +++ b/src/Common/dataAccess/readCollections.ts @@ -16,7 +16,11 @@ export async function readCollections(databaseId: string): Promise { let databases: DataModels.Database[]; const clearMessage = logConsoleProgress(`Querying databases`); try { - if (window.authType === AuthType.AAD) { + if ( + window.authType === AuthType.AAD && + userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB && + userContext.defaultExperience !== DefaultAccountExperienceType.Table + ) { databases = await readDatabasesWithARM(); } else { const sdkResponse = await client() From 3279460cfd281df728cbba7c988cc9093fd723dc Mon Sep 17 00:00:00 2001 From: Tanuj Mittal Date: Mon, 17 Aug 2020 14:26:19 -0700 Subject: [PATCH 06/15] Update @azure/cosmos to 3.9.0 (#154) --- package-lock.json | 24 +++++++++++++++--------- package.json | 2 +- src/Common/DataAccessUtilityBase.ts | 6 ++++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index e81110728..f1f0283ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,13 +5,14 @@ "requires": true, "dependencies": { "@azure/cosmos": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.7.4.tgz", - "integrity": "sha512-IbSEadapQDajSCXj7gUc8OklkOd/oAY4w7XBLHouWc4iKQTtntb2DmGjhrbh2W5Ku+pmBSr1GTApCjQ55iIjlQ==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.9.0.tgz", + "integrity": "sha512-SA+QB54I8Dvg/ZolHpsEDLK/sbSB9sFmSU1ElnMTFw88TVik+LYHq4o/srU2TY6Gr1BketjPmgLVEqrmnRvjkw==", "requires": { "@types/debug": "^4.1.4", "debug": "^4.1.1", "fast-json-stable-stringify": "^2.0.0", + "jsbi": "^3.1.3", "node-abort-controller": "^1.0.4", "node-fetch": "^2.6.0", "os-name": "^3.1.0", @@ -22,14 +23,14 @@ }, "dependencies": { "tslib": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", - "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", + "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==" }, "uuid": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz", - "integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==" } } }, @@ -20204,6 +20205,11 @@ "esprima": "^4.0.0" } }, + "jsbi": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.3.tgz", + "integrity": "sha512-nBJqA0C6Qns+ZxurbEoIR56wyjiUszpNy70FHvxO5ervMoCbZVE3z3kxr5nKGhlxr/9MhKTSUBs7cAwwuf3g9w==" + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", diff --git a/package.json b/package.json index be5bc90dd..3a222621a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Cosmos Explorer", "main": "index.js", "dependencies": { - "@azure/cosmos": "3.7.4", + "@azure/cosmos": "3.9.0", "@azure/cosmos-language-service": "0.0.4", "@jupyterlab/services": "4.2.0", "@jupyterlab/terminal": "1.2.1", diff --git a/src/Common/DataAccessUtilityBase.ts b/src/Common/DataAccessUtilityBase.ts index 04bc90627..c498bf8ef 100644 --- a/src/Common/DataAccessUtilityBase.ts +++ b/src/Common/DataAccessUtilityBase.ts @@ -14,7 +14,8 @@ import { PartitionKeyDefinition, QueryIterator, Resource, - TriggerDefinition + TriggerDefinition, + OfferDefinition } from "@azure/cosmos"; import { ContainerRequest } from "@azure/cosmos/dist-esm/client/Container/ContainerRequest"; import { client } from "./CosmosClient"; @@ -244,7 +245,8 @@ export function updateOffer( return Q( client() .offer(offer.id) - .replace(newOffer, options) + // TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660) + .replace((newOffer as unknown) as OfferDefinition, options) .then(response => { return Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => response.resource); }) From 951289e190a1c2ab2909df780941e808f9b2de01 Mon Sep 17 00:00:00 2001 From: victor-meng <56978073+victor-meng@users.noreply.github.com> Date: Tue, 18 Aug 2020 11:04:37 -0700 Subject: [PATCH 07/15] Switch back to SDK for deleteDatabase and use RP for readCollections for table API (#155) --- src/Common/dataAccess/deleteDatabase.ts | 2 +- src/Common/dataAccess/readCollections.ts | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Common/dataAccess/deleteDatabase.ts b/src/Common/dataAccess/deleteDatabase.ts index a44a2d5bb..c92a9aa66 100644 --- a/src/Common/dataAccess/deleteDatabase.ts +++ b/src/Common/dataAccess/deleteDatabase.ts @@ -15,7 +15,7 @@ export async function deleteDatabase(databaseId: string): Promise { const clearMessage = logConsoleProgress(`Deleting database ${databaseId}`); try { - if (window.authType === AuthType.AAD) { + if (window.authType === AuthType.AAD && userContext.defaultExperience !== DefaultAccountExperienceType.Table) { await deleteDatabaseWithARM(databaseId); } else { await client() diff --git a/src/Common/dataAccess/readCollections.ts b/src/Common/dataAccess/readCollections.ts index 79cecc077..6ae92a105 100644 --- a/src/Common/dataAccess/readCollections.ts +++ b/src/Common/dataAccess/readCollections.ts @@ -16,11 +16,7 @@ export async function readCollections(databaseId: string): Promise Date: Wed, 19 Aug 2020 13:20:10 -0500 Subject: [PATCH 08/15] Update Config context ARM endpoint (#158) --- src/Explorer/Explorer.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Explorer/Explorer.ts b/src/Explorer/Explorer.ts index 3d82f245a..81a7ff2ac 100644 --- a/src/Explorer/Explorer.ts +++ b/src/Explorer/Explorer.ts @@ -37,7 +37,7 @@ import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer import { BrowseQueriesPane } from "./Panes/BrowseQueriesPane"; import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient"; import { CommandBarComponentAdapter } from "./Menus/CommandBar/CommandBarComponentAdapter"; -import { configContext } from "../ConfigContext"; +import { configContext, updateConfigContext } from "../ConfigContext"; import { ConsoleData, ConsoleDataType } from "./Menus/NotificationConsole/NotificationConsoleComponent"; import { decryptJWTToken, getAuthorizationHeader } from "../Utils/AuthorizationUtils"; import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility"; @@ -1949,12 +1949,17 @@ export default class Explorer { this._importExplorerConfigComplete = true; + updateConfigContext({ + ARM_ENDPOINT: this.armEndpoint() + }); + updateUserContext({ authorizationToken, masterKey, - databaseAccount + databaseAccount, + resourceGroup: inputs.resourceGroup, + subscriptionId: inputs.subscriptionId }); - updateUserContext({ resourceGroup: inputs.resourceGroup, subscriptionId: inputs.subscriptionId }); TelemetryProcessor.traceSuccess( Action.LoadDatabaseAccount, { From 5e6ac78b7ddf13a1fa7e618a1b4b47a83448b6b6 Mon Sep 17 00:00:00 2001 From: victor-meng <56978073+victor-meng@users.noreply.github.com> Date: Wed, 19 Aug 2020 15:54:23 -0700 Subject: [PATCH 09/15] Tables - ReadCollection: Switch back to using SDK (#157) --- src/Common/dataAccess/readCollections.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Common/dataAccess/readCollections.ts b/src/Common/dataAccess/readCollections.ts index 6ae92a105..79cecc077 100644 --- a/src/Common/dataAccess/readCollections.ts +++ b/src/Common/dataAccess/readCollections.ts @@ -16,7 +16,11 @@ export async function readCollections(databaseId: string): Promise Date: Wed, 19 Aug 2020 16:11:43 -0700 Subject: [PATCH 10/15] Fix settings tab issue for cassandra and add feature flag (#159) * Fix settings tab issue for cassandra and add feature flag * Adding the rest of the changes.... Co-authored-by: Steve Faulkner --- src/Common/Constants.ts | 1 + src/Common/dataAccess/deleteCollection.ts | 2 +- src/Common/dataAccess/deleteDatabase.ts | 6 +++++- src/Common/dataAccess/readCollections.ts | 1 + src/Common/dataAccess/readDatabases.ts | 4 +++- src/Explorer/Explorer.ts | 4 ++++ src/UserContext.ts | 1 + 7 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index 4d56c53d9..8458bc0c0 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -134,6 +134,7 @@ export class Features { public static readonly enableAutoPilotV2 = "enableautopilotv2"; public static readonly ttl90Days = "ttl90days"; public static readonly enableRightPanelV2 = "enablerightpanelv2"; + public static readonly enableSDKoperations = "enablesdkoperations"; } export class AfecFeatures { diff --git a/src/Common/dataAccess/deleteCollection.ts b/src/Common/dataAccess/deleteCollection.ts index 9be2fcce0..75d282fce 100644 --- a/src/Common/dataAccess/deleteCollection.ts +++ b/src/Common/dataAccess/deleteCollection.ts @@ -15,7 +15,7 @@ import { refreshCachedResources } from "../DataAccessUtilityBase"; export async function deleteCollection(databaseId: string, collectionId: string): Promise { const clearMessage = logConsoleProgress(`Deleting container ${collectionId}`); try { - if (window.authType === AuthType.AAD) { + if (window.authType === AuthType.AAD && !userContext.useSDKOperations) { await deleteCollectionWithARM(databaseId, collectionId); } else { await client() diff --git a/src/Common/dataAccess/deleteDatabase.ts b/src/Common/dataAccess/deleteDatabase.ts index c92a9aa66..4a2767d0f 100644 --- a/src/Common/dataAccess/deleteDatabase.ts +++ b/src/Common/dataAccess/deleteDatabase.ts @@ -15,7 +15,11 @@ export async function deleteDatabase(databaseId: string): Promise { const clearMessage = logConsoleProgress(`Deleting database ${databaseId}`); try { - if (window.authType === AuthType.AAD && userContext.defaultExperience !== DefaultAccountExperienceType.Table) { + if ( + window.authType === AuthType.AAD && + userContext.defaultExperience !== DefaultAccountExperienceType.Table && + !userContext.useSDKOperations + ) { await deleteDatabaseWithARM(databaseId); } else { await client() diff --git a/src/Common/dataAccess/readCollections.ts b/src/Common/dataAccess/readCollections.ts index 79cecc077..6ebca7b13 100644 --- a/src/Common/dataAccess/readCollections.ts +++ b/src/Common/dataAccess/readCollections.ts @@ -18,6 +18,7 @@ export async function readCollections(databaseId: string): Promise { try { if ( window.authType === AuthType.AAD && + !userContext.useSDKOperations && userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB && - userContext.defaultExperience !== DefaultAccountExperienceType.Table + userContext.defaultExperience !== DefaultAccountExperienceType.Table && + userContext.defaultExperience !== DefaultAccountExperienceType.Cassandra ) { databases = await readDatabasesWithARM(); } else { diff --git a/src/Explorer/Explorer.ts b/src/Explorer/Explorer.ts index 81a7ff2ac..3b7810e79 100644 --- a/src/Explorer/Explorer.ts +++ b/src/Explorer/Explorer.ts @@ -975,6 +975,10 @@ export default class Explorer { this.sparkClusterConnectionInfo.valueHasMutated(); } + if (this.isFeatureEnabled(Constants.Features.enableSDKoperations)) { + updateUserContext({ useSDKOperations: true }); + } + featureSubcription.dispose(); }); diff --git a/src/UserContext.ts b/src/UserContext.ts index e9b4f6e62..2cba6a417 100644 --- a/src/UserContext.ts +++ b/src/UserContext.ts @@ -11,6 +11,7 @@ interface UserContext { authorizationToken?: string; resourceToken?: string; defaultExperience?: DefaultAccountExperienceType; + useSDKOperations?: boolean; } const userContext: Readonly = {} as const; From 47a5c315b5405672d7baa4eab2c35f2dc9451569 Mon Sep 17 00:00:00 2001 From: victor-meng <56978073+victor-meng@users.noreply.github.com> Date: Fri, 21 Aug 2020 11:24:01 -0700 Subject: [PATCH 11/15] Move updateCollection to ARM (#152) --- src/Common/DataAccessUtilityBase.ts | 19 -- src/Common/DocumentClientUtilityBase.ts | 36 --- src/Common/dataAccess/updateCollection.ts | 225 ++++++++++++++ src/Contracts/DataModels.ts | 9 +- src/Explorer/Tabs/SettingsTab.test.ts | 8 +- src/Explorer/Tabs/SettingsTab.ts | 361 ++++++++++------------ 6 files changed, 407 insertions(+), 251 deletions(-) create mode 100644 src/Common/dataAccess/updateCollection.ts diff --git a/src/Common/DataAccessUtilityBase.ts b/src/Common/DataAccessUtilityBase.ts index c498bf8ef..30620b7ff 100644 --- a/src/Common/DataAccessUtilityBase.ts +++ b/src/Common/DataAccessUtilityBase.ts @@ -6,8 +6,6 @@ import * as ViewModels from "../Contracts/ViewModels"; import Q from "q"; import { ConflictDefinition, - ContainerDefinition, - ContainerResponse, DatabaseResponse, FeedOptions, ItemDefinition, @@ -203,23 +201,6 @@ export function getPartitionKeyHeader(partitionKeyDefinition: DataModels.Partiti return [partitionKeyValue]; } -export function updateCollection( - databaseId: string, - collectionId: string, - newCollection: DataModels.Collection, - options: any = {} -): Q.Promise { - return Q( - client() - .database(databaseId) - .container(collectionId) - .replace(newCollection as ContainerDefinition, options) - .then(async (response: ContainerResponse) => { - return refreshCachedResources().then(() => response.resource as DataModels.Collection); - }) - ); -} - export function updateDocument( collection: ViewModels.CollectionBase, documentId: DocumentId, diff --git a/src/Common/DocumentClientUtilityBase.ts b/src/Common/DocumentClientUtilityBase.ts index 172e4ae9c..dbc8adec5 100644 --- a/src/Common/DocumentClientUtilityBase.ts +++ b/src/Common/DocumentClientUtilityBase.ts @@ -266,42 +266,6 @@ export function readDocument(collection: ViewModels.CollectionBase, documentId: return deferred.promise; } -export function updateCollection( - databaseId: string, - collection: ViewModels.Collection, - newCollection: DataModels.Collection -): Q.Promise { - var deferred = Q.defer(); - const id = NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.InProgress, - `Updating container ${collection.id()}` - ); - DataAccessUtilityBase.updateCollection(databaseId, collection.id(), newCollection) - .then( - (replacedCollection: DataModels.Collection) => { - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Info, - `Successfully updated container ${collection.id()}` - ); - deferred.resolve(replacedCollection); - }, - (error: any) => { - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Error, - `Failed to update container ${collection.id()}: ${JSON.stringify(error)}` - ); - Logger.logError(JSON.stringify(error), "UpdateCollection", error.code); - sendNotificationForError(error); - deferred.reject(error); - } - ) - .finally(() => { - NotificationConsoleUtils.clearInProgressMessageWithId(id); - }); - - return deferred.promise; -} - export function updateDocument( collection: ViewModels.CollectionBase, documentId: DocumentId, diff --git a/src/Common/dataAccess/updateCollection.ts b/src/Common/dataAccess/updateCollection.ts new file mode 100644 index 000000000..794c10003 --- /dev/null +++ b/src/Common/dataAccess/updateCollection.ts @@ -0,0 +1,225 @@ +import { AuthType } from "../../AuthType"; +import { Collection } from "../../Contracts/DataModels"; +import { ContainerDefinition } from "@azure/cosmos"; +import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType"; +import { + ExtendedResourceProperties, + SqlContainerCreateUpdateParameters, + SqlContainerResource +} from "../../Utils/arm/generatedClients/2020-04-01/types"; +import { RequestOptions } from "@azure/cosmos/dist-esm"; +import { client } from "../CosmosClient"; +import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; +import { + createUpdateCassandraTable, + getCassandraTable +} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; +import { + createUpdateMongoDBCollection, + getMongoDBCollection +} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources"; +import { + createUpdateGremlinGraph, + getGremlinGraph +} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; +import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources"; +import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; +import { logError } from "../Logger"; +import { refreshCachedResources } from "../DataAccessUtilityBase"; +import { sendNotificationForError } from "./sendNotificationForError"; +import { userContext } from "../../UserContext"; + +export async function updateCollection( + databaseId: string, + collectionId: string, + newCollection: Collection, + options: RequestOptions = {} +): Promise { + let collection: Collection; + const clearMessage = logConsoleProgress(`Updating container ${collectionId}`); + + try { + if ( + window.authType === AuthType.AAD && + userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB && + userContext.defaultExperience !== DefaultAccountExperienceType.Table + ) { + collection = await updateCollectionWithARM(databaseId, collectionId, newCollection); + } else { + const sdkResponse = await client() + .database(databaseId) + .container(collectionId) + .replace(newCollection as ContainerDefinition, options); + collection = sdkResponse.resource as Collection; + } + } catch (error) { + logConsoleError(`Failed to update container ${collectionId}: ${JSON.stringify(error)}`); + logError(JSON.stringify(error), "UpdateCollection", error.code); + sendNotificationForError(error); + throw error; + } + logConsoleInfo(`Successfully updated container ${collectionId}`); + clearMessage(); + await refreshCachedResources(); + return collection; +} + +async function updateCollectionWithARM( + databaseId: string, + collectionId: string, + newCollection: Collection +): Promise { + const subscriptionId = userContext.subscriptionId; + const resourceGroup = userContext.resourceGroup; + const accountName = userContext.databaseAccount.name; + const defaultExperience = userContext.defaultExperience; + + switch (defaultExperience) { + case DefaultAccountExperienceType.DocumentDB: + return updateSqlContainer(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection); + case DefaultAccountExperienceType.MongoDB: + return updateMongoDBCollection( + databaseId, + collectionId, + subscriptionId, + resourceGroup, + accountName, + newCollection + ); + case DefaultAccountExperienceType.Cassandra: + return updateCassandraTable(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection); + case DefaultAccountExperienceType.Graph: + return updateGremlinGraph(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection); + case DefaultAccountExperienceType.Table: + return updateTable(collectionId, subscriptionId, resourceGroup, accountName, newCollection); + default: + throw new Error(`Unsupported default experience type: ${defaultExperience}`); + } +} + +async function updateSqlContainer( + databaseId: string, + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getSqlContainer(subscriptionId, resourceGroup, accountName, databaseId, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateSqlContainer( + subscriptionId, + resourceGroup, + accountName, + databaseId, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error(`Sql container to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`); +} + +async function updateMongoDBCollection( + databaseId: string, + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getMongoDBCollection(subscriptionId, resourceGroup, accountName, databaseId, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateMongoDBCollection( + subscriptionId, + resourceGroup, + accountName, + databaseId, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error( + `MongoDB collection to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}` + ); +} + +async function updateCassandraTable( + databaseId: string, + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getCassandraTable(subscriptionId, resourceGroup, accountName, databaseId, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateCassandraTable( + subscriptionId, + resourceGroup, + accountName, + databaseId, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error( + `Cassandra table to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}` + ); +} + +async function updateGremlinGraph( + databaseId: string, + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getGremlinGraph(subscriptionId, resourceGroup, accountName, databaseId, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateGremlinGraph( + subscriptionId, + resourceGroup, + accountName, + databaseId, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error(`Gremlin graph to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`); +} + +async function updateTable( + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getTable(subscriptionId, resourceGroup, accountName, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateTable( + subscriptionId, + resourceGroup, + accountName, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error(`Table to update does not exist. Table id: ${collectionId}`); +} diff --git a/src/Contracts/DataModels.ts b/src/Contracts/DataModels.ts index fa8a77a69..4abe8a0f4 100644 --- a/src/Contracts/DataModels.ts +++ b/src/Contracts/DataModels.ts @@ -153,7 +153,14 @@ export interface KeyResource { Token: string; } -export interface IndexingPolicy {} +export interface IndexingPolicy { + automatic: boolean; + indexingMode: string; + includedPaths: any; + excludedPaths: any; + compositeIndexes?: any; + spatialIndexes?: any; +} export interface PartitionKey { paths: string[]; diff --git a/src/Explorer/Tabs/SettingsTab.test.ts b/src/Explorer/Tabs/SettingsTab.test.ts index 67242446c..3e3e13b00 100644 --- a/src/Explorer/Tabs/SettingsTab.test.ts +++ b/src/Explorer/Tabs/SettingsTab.test.ts @@ -7,6 +7,7 @@ import Database from "../Tree/Database"; import Explorer from "../Explorer"; import SettingsTab from "../Tabs/SettingsTab"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; +import { IndexingPolicies } from "../../Shared/Constants"; describe("Settings tab", () => { const baseCollection: DataModels.Collection = { @@ -16,7 +17,7 @@ describe("Settings tab", () => { mode: DataModels.ConflictResolutionMode.LastWriterWins, conflictResolutionPath: "/_ts" }, - indexingPolicy: {}, + indexingPolicy: IndexingPolicies.SharedDatabaseDefault, _rid: "", _self: "", _etag: "", @@ -51,7 +52,7 @@ describe("Settings tab", () => { defaultTtl: 200, partitionKey: null, conflictResolutionPolicy: null, - indexingPolicy: {}, + indexingPolicy: IndexingPolicies.SharedDatabaseDefault, _rid: "", _self: "", _etag: "", @@ -345,7 +346,6 @@ describe("Settings tab", () => { const offer: DataModels.Offer = null; const defaultTtl = 200; - const indexingPolicy = {}; const database = new Database(explorer, baseDatabase, null); const conflictResolutionPolicy = { mode: DataModels.ConflictResolutionMode.LastWriterWins, @@ -367,7 +367,7 @@ describe("Settings tab", () => { } : null, conflictResolutionPolicy: conflictResolutionPolicy, - indexingPolicy: indexingPolicy, + indexingPolicy: IndexingPolicies.SharedDatabaseDefault, _rid: "", _self: "", _etag: "", diff --git a/src/Explorer/Tabs/SettingsTab.ts b/src/Explorer/Tabs/SettingsTab.ts index f3f24fd0a..844a26222 100644 --- a/src/Explorer/Tabs/SettingsTab.ts +++ b/src/Explorer/Tabs/SettingsTab.ts @@ -17,7 +17,8 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { PlatformType } from "../../PlatformType"; import { RequestOptions } from "@azure/cosmos/dist-esm"; import Explorer from "../Explorer"; -import { updateOffer, updateCollection } from "../../Common/DocumentClientUtilityBase"; +import { updateOffer } from "../../Common/DocumentClientUtilityBase"; +import { updateCollection } from "../../Common/dataAccess/updateCollection"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; import { userContext } from "../../UserContext"; import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit"; @@ -1009,8 +1010,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor ); } - public onSaveClick = (): Q.Promise => { - let promises: Q.Promise[] = []; + public onSaveClick = async (): Promise => { this.isExecutionError(false); this.isExecuting(true); @@ -1023,50 +1023,60 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor const newCollectionAttributes: any = {}; - if (this.shouldUpdateCollection()) { - let defaultTtl: number; - switch (this.timeToLive()) { - case "on": - defaultTtl = Number(this.timeToLiveSeconds()); - break; - case "on-nodefault": - defaultTtl = -1; - break; - case "off": - default: - defaultTtl = undefined; - break; - } + try { + if (this.shouldUpdateCollection()) { + let defaultTtl: number; + switch (this.timeToLive()) { + case "on": + defaultTtl = Number(this.timeToLiveSeconds()); + break; + case "on-nodefault": + defaultTtl = -1; + break; + case "off": + default: + defaultTtl = undefined; + break; + } - newCollectionAttributes.defaultTtl = defaultTtl; + newCollectionAttributes.defaultTtl = defaultTtl; - newCollectionAttributes.indexingPolicy = this.indexingPolicyContent(); + newCollectionAttributes.indexingPolicy = this.indexingPolicyContent(); - newCollectionAttributes.changeFeedPolicy = - this.changeFeedPolicyVisible() && this.changeFeedPolicyToggled() === ChangeFeedPolicyToggledState.On - ? ({ - retentionDuration: Constants.BackendDefaults.maxChangeFeedRetentionDuration - } as DataModels.ChangeFeedPolicy) + newCollectionAttributes.changeFeedPolicy = + this.changeFeedPolicyVisible() && this.changeFeedPolicyToggled() === ChangeFeedPolicyToggledState.On + ? ({ + retentionDuration: Constants.BackendDefaults.maxChangeFeedRetentionDuration + } as DataModels.ChangeFeedPolicy) + : undefined; + + newCollectionAttributes.analyticalStorageTtl = this.isAnalyticalStorageEnabled + ? this.analyticalStorageTtlSelection() === "on" + ? Number(this.analyticalStorageTtlSeconds()) + : Constants.AnalyticalStorageTtl.Infinite : undefined; - newCollectionAttributes.analyticalStorageTtl = this.isAnalyticalStorageEnabled - ? this.analyticalStorageTtlSelection() === "on" - ? Number(this.analyticalStorageTtlSeconds()) - : Constants.AnalyticalStorageTtl.Infinite - : undefined; + newCollectionAttributes.geospatialConfig = { + type: this.geospatialConfigType() + }; - newCollectionAttributes.geospatialConfig = { - type: this.geospatialConfigType() - }; + const conflictResolutionChanges: DataModels.ConflictResolutionPolicy = this.getUpdatedConflictResolutionPolicy(); + if (!!conflictResolutionChanges) { + newCollectionAttributes.conflictResolutionPolicy = conflictResolutionChanges; + } - const conflictResolutionChanges: DataModels.ConflictResolutionPolicy = this.getUpdatedConflictResolutionPolicy(); - if (!!conflictResolutionChanges) { - newCollectionAttributes.conflictResolutionPolicy = conflictResolutionChanges; - } + const newCollection: DataModels.Collection = _.extend( + {}, + this.collection.rawDataModel, + newCollectionAttributes + ); + const updatedCollection: DataModels.Collection = await updateCollection( + this.collection.databaseId, + this.collection.id(), + newCollection + ); - const newCollection: DataModels.Collection = _.extend({}, this.collection.rawDataModel, newCollectionAttributes); - const updateCollectionPromise = updateCollection(this.collection.databaseId, this.collection, newCollection).then( - (updatedCollection: DataModels.Collection) => { + if (updatedCollection) { this.collection.rawDataModel = updatedCollection; this.collection.defaultTtl(updatedCollection.defaultTtl); this.collection.analyticalStorageTtl(updatedCollection.analyticalStorageTtl); @@ -1076,164 +1086,133 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor this.collection.changeFeedPolicy(updatedCollection.changeFeedPolicy); this.collection.geospatialConfig(updatedCollection.geospatialConfig); } - ); - - promises.push(updateCollectionPromise); - } - - if ( - this.throughput.editableIsDirty() || - this.rupm.editableIsDirty() || - this._isAutoPilotDirty() || - this._hasProvisioningTypeChanged() - ) { - const newThroughput = this.throughput(); - const isRUPerMinuteThroughputEnabled: boolean = this.rupm() === Constants.RUPMStates.on; - let newOffer: DataModels.Offer = _.extend({}, this.collection.offer()); - const originalThroughputValue: number = this.throughput.getEditableOriginalValue(); - - if (newOffer.content) { - newOffer.content.offerThroughput = newThroughput; - newOffer.content.offerIsRUPerMinuteThroughputEnabled = isRUPerMinuteThroughputEnabled; - } else { - newOffer = _.extend({}, newOffer, { - content: { - offerThroughput: newThroughput, - offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled - } - }); - } - - const headerOptions: RequestOptions = { initialHeaders: {} }; - - if (this.isAutoPilotSelected()) { - if (!this.hasAutoPilotV2FeatureFlag()) { - newOffer.content.offerAutopilotSettings = { - maxThroughput: this.autoPilotThroughput() - }; - } else { - newOffer.content.offerAutopilotSettings = { - tier: this.selectedAutoPilotTier() - }; - } - - // user has changed from provisioned --> autoscale - if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) { - headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToAutopilot] = "true"; - delete newOffer.content.offerAutopilotSettings; - } else { - delete newOffer.content.offerThroughput; - } - } else { - this.isAutoPilotSelected(false); - this.userCanChangeProvisioningTypes(false || !this.hasAutoPilotV2FeatureFlag()); - - // user has changed from autoscale --> provisioned - if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) { - headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToManualThroughput] = "true"; - } else { - delete newOffer.content.offerAutopilotSettings; - } } if ( - this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && - newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && - this.container != null + this.throughput.editableIsDirty() || + this.rupm.editableIsDirty() || + this._isAutoPilotDirty() || + this._hasProvisioningTypeChanged() ) { - const requestPayload = { - subscriptionId: userContext.subscriptionId, - databaseAccountName: userContext.databaseAccount.name, - resourceGroup: userContext.resourceGroup, - databaseName: this.collection.databaseId, - collectionName: this.collection.id(), - throughput: newThroughput, - offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled - }; - const updateOfferBeyondLimitPromise = updateOfferThroughputBeyondLimit(requestPayload).then( - () => { - this.collection.offer().content.offerThroughput = originalThroughputValue; - this.throughput(originalThroughputValue); - this.notificationStatusInfo( - throughputApplyDelayedMessage( - this.isAutoPilotSelected(), - originalThroughputValue, - this._getThroughputUnit(), - this.collection.databaseId, - this.collection.id(), - newThroughput - ) - ); - this.throughput.valueHasMutated(); // force component re-render - }, - (error: any) => { - TelemetryProcessor.traceFailure( - Action.UpdateSettings, - { - databaseAccountName: this.container.databaseAccount().name, - databaseName: this.collection && this.collection.databaseId, - collectionName: this.collection && this.collection.id(), - defaultExperience: this.container.defaultExperience(), - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle(), - error: error - }, - startKey - ); - } - ); - promises.push(Q(updateOfferBeyondLimitPromise)); - } else { - const updateOfferPromise = updateOffer(this.collection.offer(), newOffer, headerOptions).then( - (updatedOffer: DataModels.Offer) => { - this.collection.offer(updatedOffer); - this.collection.offer.valueHasMutated(); - } - ); + const newThroughput = this.throughput(); + const isRUPerMinuteThroughputEnabled: boolean = this.rupm() === Constants.RUPMStates.on; + let newOffer: DataModels.Offer = _.extend({}, this.collection.offer()); + const originalThroughputValue: number = this.throughput.getEditableOriginalValue(); - promises.push(updateOfferPromise); - } - } - - if (promises.length === 0) { - this.isExecuting(false); - } - - return Q.all(promises) - .then( - () => { - this.container.isRefreshingExplorer(false); - this._setBaseline(); - this.collection.readSettings(); - this._wasAutopilotOriginallySet(this.isAutoPilotSelected()); - TelemetryProcessor.traceSuccess( - Action.UpdateSettings, - { - databaseAccountName: this.container.databaseAccount().name, - defaultExperience: this.container.defaultExperience(), - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle() - }, - startKey - ); - }, - (reason: any) => { - this.container.isRefreshingExplorer(false); - this.isExecutionError(true); - console.error(reason); - TelemetryProcessor.traceFailure( - Action.UpdateSettings, - { - databaseAccountName: this.container.databaseAccount().name, - defaultExperience: this.container.defaultExperience(), - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle() - }, - startKey - ); + if (newOffer.content) { + newOffer.content.offerThroughput = newThroughput; + newOffer.content.offerIsRUPerMinuteThroughputEnabled = isRUPerMinuteThroughputEnabled; + } else { + newOffer = _.extend({}, newOffer, { + content: { + offerThroughput: newThroughput, + offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled + } + }); } - ) - .finally(() => this.isExecuting(false)); + + const headerOptions: RequestOptions = { initialHeaders: {} }; + + if (this.isAutoPilotSelected()) { + if (!this.hasAutoPilotV2FeatureFlag()) { + newOffer.content.offerAutopilotSettings = { + maxThroughput: this.autoPilotThroughput() + }; + } else { + newOffer.content.offerAutopilotSettings = { + tier: this.selectedAutoPilotTier() + }; + } + + // user has changed from provisioned --> autoscale + if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) { + headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToAutopilot] = "true"; + delete newOffer.content.offerAutopilotSettings; + } else { + delete newOffer.content.offerThroughput; + } + } else { + this.isAutoPilotSelected(false); + this.userCanChangeProvisioningTypes(false || !this.hasAutoPilotV2FeatureFlag()); + + // user has changed from autoscale --> provisioned + if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) { + headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToManualThroughput] = "true"; + } else { + delete newOffer.content.offerAutopilotSettings; + } + } + + if ( + this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && + newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && + this.container != null + ) { + const requestPayload = { + subscriptionId: userContext.subscriptionId, + databaseAccountName: userContext.databaseAccount.name, + resourceGroup: userContext.resourceGroup, + databaseName: this.collection.databaseId, + collectionName: this.collection.id(), + throughput: newThroughput, + offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled + }; + + await updateOfferThroughputBeyondLimit(requestPayload); + this.collection.offer().content.offerThroughput = originalThroughputValue; + this.throughput(originalThroughputValue); + this.notificationStatusInfo( + throughputApplyDelayedMessage( + this.isAutoPilotSelected(), + originalThroughputValue, + this._getThroughputUnit(), + this.collection.databaseId, + this.collection.id(), + newThroughput + ) + ); + this.throughput.valueHasMutated(); // force component re-render + } else { + const updatedOffer: DataModels.Offer = await updateOffer(this.collection.offer(), newOffer, headerOptions); + this.collection.offer(updatedOffer); + this.collection.offer.valueHasMutated(); + } + } + + this.container.isRefreshingExplorer(false); + this._setBaseline(); + this.collection.readSettings(); + this._wasAutopilotOriginallySet(this.isAutoPilotSelected()); + TelemetryProcessor.traceSuccess( + Action.UpdateSettings, + { + databaseAccountName: this.container.databaseAccount().name, + defaultExperience: this.container.defaultExperience(), + dataExplorerArea: Constants.Areas.Tab, + tabTitle: this.tabTitle() + }, + startKey + ); + } catch (error) { + this.container.isRefreshingExplorer(false); + this.isExecutionError(true); + console.error(error); + TelemetryProcessor.traceFailure( + Action.UpdateSettings, + { + databaseAccountName: this.container.databaseAccount().name, + databaseName: this.collection && this.collection.databaseId, + collectionName: this.collection && this.collection.id(), + defaultExperience: this.container.defaultExperience(), + dataExplorerArea: Constants.Areas.Tab, + tabTitle: this.tabTitle(), + error: error + }, + startKey + ); + } + + this.isExecuting(false); }; public onRevertClick = (): Q.Promise => { From e837f574a8e346d94eb7cdead6726be881fd7537 Mon Sep 17 00:00:00 2001 From: Steve Faulkner Date: Fri, 21 Aug 2020 14:38:30 -0500 Subject: [PATCH 12/15] Fix Telemetry for String Case (#163) --- src/Shared/Telemetry/TelemetryProcessor.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Shared/Telemetry/TelemetryProcessor.ts b/src/Shared/Telemetry/TelemetryProcessor.ts index 8d8d16d95..bc6910a57 100644 --- a/src/Shared/Telemetry/TelemetryProcessor.ts +++ b/src/Shared/Telemetry/TelemetryProcessor.ts @@ -114,13 +114,17 @@ export default class TelemetryProcessor { return validTimestamp; } - private static getData(data?: any): any { + private static getData(data: any = {}): any { + if (typeof data === "string") { + data = { message: data }; + } return { // TODO: Need to `any` here since the window imports Explorer which can't be in strict mode yet authType: (window as any).authType, subscriptionId: userContext.subscriptionId, platform: configContext.platform, - ...(data ? data : []) + env: process.env.NODE_ENV, + ...data }; } } From 38732af907f44b63cd8305ab5cbe868ba21f97ca Mon Sep 17 00:00:00 2001 From: Steve Faulkner Date: Fri, 21 Aug 2020 19:51:36 -0500 Subject: [PATCH 13/15] Triple equal lint rule (#162) --- .eslintrc.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index bbc9fcc46..cb7e34f01 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -41,6 +41,7 @@ module.exports = { "@typescript-eslint/no-extraneous-class": "error", "no-null/no-null": "error", "@typescript-eslint/no-explicit-any": "error", - "prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }] + "prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }], + eqeqeq: "error" } }; From 3223ff76854407c77308fb5a8fcee86881c893f3 Mon Sep 17 00:00:00 2001 From: victor-meng <56978073+victor-meng@users.noreply.github.com> Date: Tue, 25 Aug 2020 15:45:37 -0700 Subject: [PATCH 14/15] Move createDatabase to RP (#166) --- src/Common/DataAccessUtilityBase.ts | 51 ----- src/Common/DocumentClientUtilityBase.ts | 33 ---- src/Common/dataAccess/createDatabase.ts | 250 ++++++++++++++++++++++++ src/Contracts/DataModels.ts | 5 +- src/Explorer/Panes/AddDatabasePane.ts | 156 ++------------- src/Utils/arm/request.ts | 16 +- 6 files changed, 269 insertions(+), 242 deletions(-) create mode 100644 src/Common/dataAccess/createDatabase.ts diff --git a/src/Common/DataAccessUtilityBase.ts b/src/Common/DataAccessUtilityBase.ts index 30620b7ff..b10b75629 100644 --- a/src/Common/DataAccessUtilityBase.ts +++ b/src/Common/DataAccessUtilityBase.ts @@ -6,7 +6,6 @@ import * as ViewModels from "../Contracts/ViewModels"; import Q from "q"; import { ConflictDefinition, - DatabaseResponse, FeedOptions, ItemDefinition, PartitionKeyDefinition, @@ -544,26 +543,6 @@ export function getOrCreateDatabaseAndCollection( ); } -export function createDatabase( - request: DataModels.CreateDatabaseRequest, - options: any -): Q.Promise { - var deferred = Q.defer(); - - _createDatabase(request, options).then( - (createdDatabase: DataModels.Database) => { - refreshCachedOffers().then(() => { - deferred.resolve(createdDatabase); - }); - }, - _createDatabaseError => { - deferred.reject(_createDatabaseError); - } - ); - - return deferred.promise; -} - export function refreshCachedOffers(): Q.Promise { if (configContext.platform === Platform.Portal) { return sendCachedDataMessage(MessageTypes.RefreshOffers, []); @@ -592,33 +571,3 @@ export function queryConflicts( .conflicts.query(query, options); return Q(documentsIterator); } - -function _createDatabase(request: DataModels.CreateDatabaseRequest, options: any = {}): Q.Promise { - const { databaseId, databaseLevelThroughput, offerThroughput, autoPilot, hasAutoPilotV2FeatureFlag } = request; - const createBody: DatabaseRequest = { id: databaseId }; - const databaseOptions: any = options && _.omit(options, "sharedOfferThroughput"); - // TODO: replace when SDK support autopilot - const initialHeaders = autoPilot - ? !hasAutoPilotV2FeatureFlag - ? { - [Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({ maxThroughput: autoPilot.maxThroughput }) - } - : { - [Constants.HttpHeaders.autoPilotTier]: autoPilot.autopilotTier - } - : undefined; - if (!!databaseLevelThroughput) { - if (autoPilot) { - databaseOptions.initialHeaders = initialHeaders; - } - createBody.throughput = offerThroughput; - } - - return Q( - client() - .databases.create(createBody, databaseOptions) - .then((response: DatabaseResponse) => { - return refreshCachedResources(databaseOptions).then(() => response.resource); - }) - ); -} diff --git a/src/Common/DocumentClientUtilityBase.ts b/src/Common/DocumentClientUtilityBase.ts index dbc8adec5..67e330ae2 100644 --- a/src/Common/DocumentClientUtilityBase.ts +++ b/src/Common/DocumentClientUtilityBase.ts @@ -890,36 +890,3 @@ export function getOrCreateDatabaseAndCollection( return deferred.promise; } - -export function createDatabase( - request: DataModels.CreateDatabaseRequest, - options: any = {} -): Q.Promise { - const deferred: Q.Deferred = Q.defer(); - const id = NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.InProgress, - `Creating a new database ${request.databaseId}` - ); - - DataAccessUtilityBase.createDatabase(request, options) - .then( - (database: DataModels.Database) => { - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Info, - `Successfully created database ${request.databaseId}` - ); - deferred.resolve(database); - }, - (error: any) => { - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Error, - `Error while creating database ${request.databaseId}:\n ${JSON.stringify(error)}` - ); - sendNotificationForError(error); - deferred.reject(error); - } - ) - .finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id)); - - return deferred.promise; -} diff --git a/src/Common/dataAccess/createDatabase.ts b/src/Common/dataAccess/createDatabase.ts new file mode 100644 index 000000000..57e25ca14 --- /dev/null +++ b/src/Common/dataAccess/createDatabase.ts @@ -0,0 +1,250 @@ +import * as DataModels from "../../Contracts/DataModels"; +import { AuthType } from "../../AuthType"; +import { DatabaseResponse } from "@azure/cosmos"; +import { DatabaseRequest } from "@azure/cosmos/dist-esm/client/Database/DatabaseRequest"; +import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType"; +import { RequestOptions } from "@azure/cosmos/dist-esm"; +import { + SqlDatabaseCreateUpdateParameters, + CreateUpdateOptions +} from "../../Utils/arm/generatedClients/2020-04-01/types"; +import { client } from "../CosmosClient"; +import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; +import { + createUpdateCassandraKeyspace, + getCassandraKeyspace +} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; +import { + createUpdateMongoDBDatabase, + getMongoDBDatabase +} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources"; +import { + createUpdateGremlinDatabase, + getGremlinDatabase +} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; +import { logConsoleProgress, logConsoleError, logConsoleInfo } from "../../Utils/NotificationConsoleUtils"; +import { logError } from "../Logger"; +import { refreshCachedOffers, refreshCachedResources } from "../DataAccessUtilityBase"; +import { sendNotificationForError } from "./sendNotificationForError"; +import { userContext } from "../../UserContext"; + +export async function createDatabase(params: DataModels.CreateDatabaseParams): Promise { + let database: DataModels.Database; + const clearMessage = logConsoleProgress(`Creating a new database ${params.databaseId}`); + try { + if ( + window.authType === AuthType.AAD && + !userContext.useSDKOperations && + userContext.defaultExperience !== DefaultAccountExperienceType.Table + ) { + database = await createDatabaseWithARM(params); + } else { + database = await createDatabaseWithSDK(params); + } + } catch (error) { + logConsoleError(`Error while creating database ${params.databaseId}:\n ${error.message}`); + logError(JSON.stringify(error), "CreateDatabase", error.code); + sendNotificationForError(error); + clearMessage(); + throw error; + } + logConsoleInfo(`Successfully created database ${params.databaseId}`); + await refreshCachedResources(); + await refreshCachedOffers(); + clearMessage(); + return database; +} + +async function createDatabaseWithARM(params: DataModels.CreateDatabaseParams): Promise { + const defaultExperience = userContext.defaultExperience; + switch (defaultExperience) { + case DefaultAccountExperienceType.DocumentDB: + return createSqlDatabase(params); + case DefaultAccountExperienceType.MongoDB: + return createMongoDatabase(params); + case DefaultAccountExperienceType.Cassandra: + return createCassandraKeyspace(params); + case DefaultAccountExperienceType.Graph: + return createGremlineDatabase(params); + default: + throw new Error(`Unsupported default experience type: ${defaultExperience}`); + } +} + +async function createSqlDatabase(params: DataModels.CreateDatabaseParams): Promise { + try { + const getResponse = await getSqlDatabase( + userContext.subscriptionId, + userContext.resourceGroup, + userContext.databaseAccount.name, + params.databaseId + ); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + throw new Error(`Create database failed: database with id ${params.databaseId} already exists`); + } + } catch (error) { + if (error.code !== "NotFound") { + throw error; + } + } + + const options: CreateUpdateOptions = constructRpOptions(params); + const rpPayload: SqlDatabaseCreateUpdateParameters = { + properties: { + resource: { + id: params.databaseId + }, + options + } + }; + const createResponse = await createUpdateSqlDatabase( + userContext.subscriptionId, + userContext.resourceGroup, + userContext.databaseAccount.name, + params.databaseId, + rpPayload + ); + return createResponse && (createResponse.properties.resource as DataModels.Database); +} + +async function createMongoDatabase(params: DataModels.CreateDatabaseParams): Promise { + try { + const getResponse = await getMongoDBDatabase( + userContext.subscriptionId, + userContext.resourceGroup, + userContext.databaseAccount.name, + params.databaseId + ); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + throw new Error(`Create database failed: database with id ${params.databaseId} already exists`); + } + } catch (error) { + if (error.code !== "NotFound") { + throw error; + } + } + + const options: CreateUpdateOptions = constructRpOptions(params); + const rpPayload: SqlDatabaseCreateUpdateParameters = { + properties: { + resource: { + id: params.databaseId + }, + options + } + }; + const createResponse = await createUpdateMongoDBDatabase( + userContext.subscriptionId, + userContext.resourceGroup, + userContext.databaseAccount.name, + params.databaseId, + rpPayload + ); + return createResponse && (createResponse.properties.resource as DataModels.Database); +} + +async function createCassandraKeyspace(params: DataModels.CreateDatabaseParams): Promise { + try { + const getResponse = await getCassandraKeyspace( + userContext.subscriptionId, + userContext.resourceGroup, + userContext.databaseAccount.name, + params.databaseId + ); + if (getResponse?.properties?.resource) { + throw new Error(`Create database failed: database with id ${params.databaseId} already exists`); + } + } catch (error) { + if (error.code !== "NotFound") { + throw error; + } + } + + const options: CreateUpdateOptions = constructRpOptions(params); + const rpPayload: SqlDatabaseCreateUpdateParameters = { + properties: { + resource: { + id: params.databaseId + }, + options + } + }; + const createResponse = await createUpdateCassandraKeyspace( + userContext.subscriptionId, + userContext.resourceGroup, + userContext.databaseAccount.name, + params.databaseId, + rpPayload + ); + return createResponse && (createResponse.properties.resource as DataModels.Database); +} + +async function createGremlineDatabase(params: DataModels.CreateDatabaseParams): Promise { + try { + const getResponse = await getGremlinDatabase( + userContext.subscriptionId, + userContext.resourceGroup, + userContext.databaseAccount.name, + params.databaseId + ); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + throw new Error(`Create database failed: database with id ${params.databaseId} already exists`); + } + } catch (error) { + if (error.code !== "NotFound") { + throw error; + } + } + + const options: CreateUpdateOptions = constructRpOptions(params); + const rpPayload: SqlDatabaseCreateUpdateParameters = { + properties: { + resource: { + id: params.databaseId + }, + options + } + }; + const createResponse = await createUpdateGremlinDatabase( + userContext.subscriptionId, + userContext.resourceGroup, + userContext.databaseAccount.name, + params.databaseId, + rpPayload + ); + return createResponse && (createResponse.properties.resource as DataModels.Database); +} + +async function createDatabaseWithSDK(params: DataModels.CreateDatabaseParams): Promise { + const createBody: DatabaseRequest = { id: params.databaseId }; + const databaseOptions: RequestOptions = {}; + // TODO: replace when SDK support autopilot + if (params.databaseLevelThroughput) { + if (params.autoPilotMaxThroughput) { + createBody.maxThroughput = params.autoPilotMaxThroughput; + } else { + createBody.throughput = params.offerThroughput; + } + } + + const response: DatabaseResponse = await client().databases.create(createBody, databaseOptions); + return response.resource; +} + +function constructRpOptions(params: DataModels.CreateDatabaseParams): CreateUpdateOptions { + if (!params.databaseLevelThroughput) { + return {}; + } + + if (params.autoPilotMaxThroughput) { + return { + autoscaleSettings: { + maxThroughput: params.autoPilotMaxThroughput + } + }; + } + + return { + throughput: params.offerThroughput + }; +} diff --git a/src/Contracts/DataModels.ts b/src/Contracts/DataModels.ts index 4abe8a0f4..77ac52cb7 100644 --- a/src/Contracts/DataModels.ts +++ b/src/Contracts/DataModels.ts @@ -327,12 +327,11 @@ export interface AutoPilotOfferSettings { targetMaxThroughput?: number; } -export interface CreateDatabaseRequest { +export interface CreateDatabaseParams { + autoPilotMaxThroughput?: number; databaseId: string; databaseLevelThroughput?: boolean; offerThroughput?: number; - autoPilot?: AutoPilotCreationSettings; - hasAutoPilotV2FeatureFlag?: boolean; } export interface SharedThroughputRange { diff --git a/src/Explorer/Panes/AddDatabasePane.ts b/src/Explorer/Panes/AddDatabasePane.ts index 8355d7dcf..e5e831180 100644 --- a/src/Explorer/Panes/AddDatabasePane.ts +++ b/src/Explorer/Panes/AddDatabasePane.ts @@ -14,8 +14,8 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan import { AddDbUtilities } from "../../Shared/AddDatabaseUtility"; import { CassandraAPIDataClient } from "../Tables/TableDataClient"; import { ContextualPaneBase } from "./ContextualPaneBase"; +import { createDatabase } from "../../Common/dataAccess/createDatabase"; import { PlatformType } from "../../PlatformType"; -import { refreshCachedOffers, refreshCachedResources, createDatabase } from "../../Common/DocumentClientUtilityBase"; import { userContext } from "../../UserContext"; export default class AddDatabasePane extends ContextualPaneBase { @@ -304,76 +304,23 @@ export default class AddDatabasePane extends ContextualPaneBase { this.formErrors(""); this.isExecuting(true); - const createDatabaseParameters: DataModels.RpParameters = { - db: addDatabasePaneStartMessage.database.id, - st: addDatabasePaneStartMessage.database.shared, - offerThroughput: addDatabasePaneStartMessage.offerThroughput, - sid: userContext.subscriptionId, - rg: userContext.resourceGroup, - dba: addDatabasePaneStartMessage.databaseAccountName + const createDatabaseParams: DataModels.CreateDatabaseParams = { + autoPilotMaxThroughput: this.maxAutoPilotThroughputSet(), + databaseId: addDatabasePaneStartMessage.database.id, + databaseLevelThroughput: addDatabasePaneStartMessage.database.shared, + offerThroughput: addDatabasePaneStartMessage.offerThroughput }; - const autopilotSettings = this._getAutopilotSettings(); - - if (this.container.isPreferredApiCassandra()) { - this._createKeyspace(createDatabaseParameters, autopilotSettings, startKey); - } else if (this.container.isPreferredApiMongoDB() && EnvironmentUtility.isAadUser()) { - this._createMongoDatabase(createDatabaseParameters, autopilotSettings, startKey); - } else if (this.container.isPreferredApiGraph() && EnvironmentUtility.isAadUser()) { - this._createGremlinDatabase(createDatabaseParameters, autopilotSettings, startKey); - } else if (this.container.isPreferredApiDocumentDB() && EnvironmentUtility.isAadUser()) { - this._createSqlDatabase(createDatabaseParameters, autopilotSettings, startKey); - } else { - this._createDatabase(offerThroughput, startKey); - } - } - - private _createSqlDatabase( - createDatabaseParameters: DataModels.RpParameters, - autoPilotSettings: DataModels.RpOptions, - startKey: number - ) { - AddDbUtilities.createSqlDatabase(this.container.armEndpoint(), createDatabaseParameters, autoPilotSettings).then( - () => { - Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => { - this._onCreateDatabaseSuccess(createDatabaseParameters.offerThroughput, startKey); - }); + createDatabase(createDatabaseParams).then( + (database: DataModels.Database) => { + this._onCreateDatabaseSuccess(offerThroughput, startKey); + }, + (reason: any) => { + this._onCreateDatabaseFailure(reason, offerThroughput, reason); } ); } - private _createMongoDatabase( - createDatabaseParameters: DataModels.RpParameters, - autoPilotSettings: DataModels.RpOptions, - startKey: number - ) { - AddDbUtilities.createMongoDatabaseWithARM( - this.container.armEndpoint(), - createDatabaseParameters, - autoPilotSettings - ).then(() => { - Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => { - this._onCreateDatabaseSuccess(createDatabaseParameters.offerThroughput, startKey); - }); - }); - } - - private _createGremlinDatabase( - createDatabaseParameters: DataModels.RpParameters, - autoPilotSettings: DataModels.RpOptions, - startKey: number - ) { - AddDbUtilities.createGremlinDatabase( - this.container.armEndpoint(), - createDatabaseParameters, - autoPilotSettings - ).then(() => { - Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => { - this._onCreateDatabaseSuccess(createDatabaseParameters.offerThroughput, startKey); - }); - }); - } - public resetData() { this.databaseId(""); this.databaseCreateNewShared(this.getSharedThroughputDefault()); @@ -396,71 +343,6 @@ export default class AddDatabasePane extends ContextualPaneBase { return true; } - private _createDatabase(offerThroughput: number, telemetryStartKey: number): void { - const autoPilot: DataModels.AutoPilotCreationSettings = this._isAutoPilotSelectedAndWhatTier(); - const createRequest: DataModels.CreateDatabaseRequest = { - databaseId: this.databaseId().trim(), - offerThroughput, - databaseLevelThroughput: this.databaseCreateNewShared(), - autoPilot, - hasAutoPilotV2FeatureFlag: this.hasAutoPilotV2FeatureFlag() - }; - createDatabase(createRequest).then( - (database: DataModels.Database) => { - this._onCreateDatabaseSuccess(offerThroughput, telemetryStartKey); - }, - (reason: any) => { - this._onCreateDatabaseFailure(reason, offerThroughput, reason); - } - ); - } - - private _createKeyspace( - createDatabaseParameters: DataModels.RpParameters, - autoPilotSettings: DataModels.RpOptions, - startKey: number - ): void { - if (EnvironmentUtility.isAadUser()) { - this._createKeyspaceUsingRP(createDatabaseParameters, autoPilotSettings, startKey); - } else { - this._createKeyspaceUsingProxy(createDatabaseParameters.offerThroughput, startKey); - } - } - - private _createKeyspaceUsingProxy(offerThroughput: number, telemetryStartKey: number): void { - const provisionThroughputQueryPart: string = this.databaseCreateNewShared() - ? `AND cosmosdb_provisioned_throughput=${offerThroughput}` - : ""; - const createKeyspaceQuery: string = `CREATE KEYSPACE ${this.databaseId().trim()} WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 3 } ${provisionThroughputQueryPart};`; - (this.container.tableDataClient as CassandraAPIDataClient) - .createKeyspace( - this.container.databaseAccount().properties.cassandraEndpoint, - this.container.databaseAccount().id, - this.container, - createKeyspaceQuery - ) - .then( - () => { - this._onCreateDatabaseSuccess(offerThroughput, telemetryStartKey); - }, - (reason: any) => { - this._onCreateDatabaseFailure(reason, offerThroughput, telemetryStartKey); - } - ); - } - - private _createKeyspaceUsingRP( - createKeyspaceParameters: DataModels.RpParameters, - autoPilotSettings: DataModels.RpOptions, - startKey: number - ): void { - AddDbUtilities.createCassandraKeyspace(createKeyspaceParameters, autoPilotSettings).then(() => { - Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => { - this._onCreateDatabaseSuccess(createKeyspaceParameters.offerThroughput, startKey); - }); - }); - } - private _onCreateDatabaseSuccess(offerThroughput: number, startKey: number): void { this.isExecuting(false); this.close(); @@ -581,20 +463,6 @@ export default class AddDatabasePane extends ContextualPaneBase { return undefined; } - private _getAutopilotSettings(): DataModels.RpOptions { - if ( - (!this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected() && this.maxAutoPilotThroughputSet()) || - (this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected() && this.selectedAutoPilotTier()) - ) { - return !this.hasAutoPilotV2FeatureFlag() - ? { - [Constants.HttpHeaders.autoPilotThroughput]: { maxThroughput: this.maxAutoPilotThroughputSet() * 1 } - } - : { [Constants.HttpHeaders.autoPilotTier]: this.selectedAutoPilotTier().toString() }; - } - return undefined; - } - private _updateThroughputLimitByDatabase() { const throughputDefaults = this.container.collectionCreationDefaults.throughput; this.throughput(throughputDefaults.shared); diff --git a/src/Utils/arm/request.ts b/src/Utils/arm/request.ts index 95c10b20d..857841463 100644 --- a/src/Utils/arm/request.ts +++ b/src/Utils/arm/request.ts @@ -6,15 +6,9 @@ Instead, generate ARM clients that consume this function with stricter typing. */ import promiseRetry, { AbortError } from "p-retry"; +import { ErrorResponse } from "./generatedClients/2020-04-01/types"; import { userContext } from "../../UserContext"; -interface ErrorResponse { - error: { - code: string; - message: string; - }; -} - interface ARMError extends Error { code: string; } @@ -40,8 +34,8 @@ export async function armRequest({ host, path, apiVersion, method, body: requ }); if (!response.ok) { const errorResponse = (await response.json()) as ErrorResponse; - const error = new Error(errorResponse.error?.message) as ARMError; - error.code = errorResponse.error.code; + const error = new Error(errorResponse.message) as ARMError; + error.code = errorResponse.code; throw error; } @@ -84,8 +78,8 @@ async function getOperationStatus(operationStatusUrl: string) { }); if (!response.ok) { const errorResponse = (await response.json()) as ErrorResponse; - const error = new Error(errorResponse.error?.message) as ARMError; - error.code = errorResponse.error.code; + const error = new Error(errorResponse.message) as ARMError; + error.code = errorResponse.code; throw new AbortError(error); } const body = (await response.json()) as OperationResponse; From 5c84b3a7d475f7c62043c7b3417f057a312fb08d Mon Sep 17 00:00:00 2001 From: Jordi Bunster Date: Tue, 25 Aug 2020 22:48:58 -0700 Subject: [PATCH 15/15] Allow 'platform' only to be overriden (#167) ConfigContext defines all kinds of URLs and what not, I'm not sure about the security implications of allowing all this stuff to be modifiable by just anyone. --- src/ConfigContext.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/ConfigContext.ts b/src/ConfigContext.ts index da4abb0aa..afa40f310 100644 --- a/src/ConfigContext.ts +++ b/src/ConfigContext.ts @@ -80,12 +80,20 @@ export async function initializeConfiguration(): Promise { console.error(error); } } - // Allow override of any config value with URL query parameters + // Allow override of platform value with URL query parameter const params = new URLSearchParams(window.location.search); - params.forEach((value, key) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (configContext as any)[key] = value; - }); + if (params.has("platform")) { + const platform = params.get("platform"); + switch (platform) { + default: + console.log("Invalid platform query parameter given, ignoring"); + break; + case Platform.Portal: + case Platform.Hosted: + case Platform.Emulator: + updateConfigContext({ platform }); + } + } } catch (error) { console.log("No configuration file found using defaults"); }