2020-05-25 21:30:55 -05:00
|
|
|
import * as _ from "underscore";
|
|
|
|
import * as DataExplorerConstants from "../Common/Constants";
|
|
|
|
import * as DataModels from "../Contracts/DataModels";
|
|
|
|
import * as SharedConstants from "./Constants";
|
|
|
|
import * as ViewModels from "../Contracts/ViewModels";
|
|
|
|
import { AddDbUtilities } from "../Shared/AddDatabaseUtility";
|
|
|
|
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
|
|
|
import { HttpStatusCodes } from "../Common/Constants";
|
2020-07-24 16:45:48 -05:00
|
|
|
import { sendMessage } from "../Common/MessageHandler";
|
2020-05-25 21:30:55 -05:00
|
|
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
2020-08-03 17:11:07 -05:00
|
|
|
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
2020-05-25 21:30:55 -05:00
|
|
|
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
|
2020-07-20 12:59:40 -05:00
|
|
|
import Explorer from "../Explorer/Explorer";
|
2020-08-06 14:03:46 -05:00
|
|
|
import { userContext } from "../UserContext";
|
2020-05-25 21:30:55 -05:00
|
|
|
|
|
|
|
export class CreateSqlCollectionUtilities {
|
|
|
|
public static createSqlCollection(
|
|
|
|
armEndpoint: string,
|
|
|
|
databaseId: string,
|
|
|
|
analyticalStorageTtl: number,
|
|
|
|
collectionId: string,
|
2020-07-13 10:54:59 -07:00
|
|
|
indexingPolicy: DataModels.IndexingPolicy,
|
2020-05-25 21:30:55 -05:00
|
|
|
offerThroughput: number,
|
|
|
|
partitionKey: string,
|
|
|
|
partitionKeyVersion: number,
|
|
|
|
createDatabase: boolean,
|
|
|
|
useDatabaseSharedOffer: boolean,
|
|
|
|
sid: string,
|
|
|
|
rg: string,
|
|
|
|
dba: string,
|
|
|
|
uniqueKeyPolicy: DataModels.UniqueKeyPolicy,
|
|
|
|
additionalOptions: DataModels.RpOptions
|
|
|
|
): Promise<DataModels.CreateCollectionWithRpResponse> {
|
|
|
|
const params: DataModels.SqlCollectionParameters = {
|
|
|
|
uniqueKeyPolicy,
|
|
|
|
db: databaseId,
|
|
|
|
coll: collectionId,
|
|
|
|
pk: partitionKey,
|
|
|
|
offerThroughput,
|
|
|
|
cd: createDatabase,
|
|
|
|
st: useDatabaseSharedOffer,
|
|
|
|
sid,
|
|
|
|
rg,
|
|
|
|
dba,
|
|
|
|
analyticalStorageTtl,
|
2020-07-13 10:54:59 -07:00
|
|
|
indexingPolicy,
|
2020-05-25 21:30:55 -05:00
|
|
|
partitionKeyVersion
|
|
|
|
};
|
|
|
|
|
|
|
|
if (params.cd) {
|
|
|
|
return AddDbUtilities.createSqlDatabase(armEndpoint, params, additionalOptions).then(() => {
|
|
|
|
return CreateSqlCollectionUtilities.createSqlCollectionWithARM(armEndpoint, params, additionalOptions);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return CreateSqlCollectionUtilities.createSqlCollectionWithARM(armEndpoint, params, additionalOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static async createSqlCollectionWithARM(
|
|
|
|
armEndpoint: string,
|
|
|
|
params: DataModels.SqlCollectionParameters,
|
|
|
|
rpOptions: DataModels.RpOptions
|
|
|
|
): Promise<DataModels.CreateCollectionWithRpResponse> {
|
|
|
|
const rpPayloadToCreateCollection: DataModels.SqlCollectionCreationRequest = {
|
|
|
|
properties: {
|
|
|
|
resource: {
|
|
|
|
id: params.coll,
|
|
|
|
partitionKey: {
|
|
|
|
paths: [params.pk],
|
|
|
|
kind: "Hash",
|
|
|
|
version: params.partitionKeyVersion
|
|
|
|
}
|
|
|
|
},
|
|
|
|
options: {}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (params.analyticalStorageTtl) {
|
|
|
|
rpPayloadToCreateCollection.properties.resource.analyticalStorageTtl = params.analyticalStorageTtl;
|
|
|
|
}
|
|
|
|
|
2020-07-13 10:54:59 -07:00
|
|
|
if (params.indexingPolicy) {
|
|
|
|
rpPayloadToCreateCollection.properties.resource.indexingPolicy = params.indexingPolicy;
|
|
|
|
}
|
|
|
|
|
2020-05-25 21:30:55 -05:00
|
|
|
if (!params.st) {
|
|
|
|
if (rpOptions) {
|
|
|
|
rpPayloadToCreateCollection.properties.options = rpOptions;
|
|
|
|
} else {
|
|
|
|
rpPayloadToCreateCollection.properties.options["throughput"] =
|
|
|
|
params.offerThroughput && params.offerThroughput.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params.uniqueKeyPolicy) {
|
|
|
|
rpPayloadToCreateCollection.properties.resource.uniqueKeyPolicy = params.uniqueKeyPolicy;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
let response = await new ResourceProviderClient<DataModels.CreateCollectionWithRpResponse>(armEndpoint).putAsync(
|
|
|
|
CreateSqlCollectionUtilities.getSqlCollectionUri(params),
|
|
|
|
DataExplorerConstants.ArmApiVersions.publicVersion,
|
|
|
|
rpPayloadToCreateCollection
|
|
|
|
);
|
|
|
|
return response;
|
|
|
|
} catch (response) {
|
|
|
|
NotificationConsoleUtils.logConsoleMessage(
|
|
|
|
ConsoleDataType.Error,
|
|
|
|
`Error creating collection: ${JSON.stringify(response)}`
|
|
|
|
);
|
|
|
|
if (response.status === HttpStatusCodes.Forbidden) {
|
2020-07-24 16:45:48 -05:00
|
|
|
sendMessage({ type: MessageTypes.ForbiddenError });
|
2020-05-25 21:30:55 -05:00
|
|
|
}
|
|
|
|
throw new Error(`Error creating collection`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static getSqlCollectionUri(params: DataModels.SqlCollectionParameters): string {
|
|
|
|
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${params.dba}/sqlDatabases/${params.db}/containers/${params.coll}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class CreateCollectionUtilities {
|
|
|
|
public static createGremlinGraph(
|
|
|
|
armEndpoint: string,
|
|
|
|
databaseId: string,
|
|
|
|
collectionId: string,
|
2020-07-13 10:54:59 -07:00
|
|
|
indexingPolicy: DataModels.IndexingPolicy,
|
2020-05-25 21:30:55 -05:00
|
|
|
offerThroughput: number,
|
|
|
|
partitionKey: string,
|
|
|
|
partitionKeyVersion: number,
|
|
|
|
createDatabase: boolean,
|
|
|
|
useDatabaseSharedOffer: boolean,
|
|
|
|
sid: string,
|
|
|
|
rg: string,
|
|
|
|
dba: string,
|
|
|
|
additionalOptions: DataModels.RpOptions
|
|
|
|
): Promise<DataModels.CreateCollectionWithRpResponse> {
|
|
|
|
const params: DataModels.GraphParameters = {
|
|
|
|
db: databaseId,
|
|
|
|
coll: collectionId,
|
|
|
|
pk: partitionKey,
|
|
|
|
offerThroughput,
|
|
|
|
cd: createDatabase,
|
|
|
|
st: useDatabaseSharedOffer,
|
|
|
|
sid,
|
|
|
|
rg,
|
|
|
|
dba,
|
2020-07-13 10:54:59 -07:00
|
|
|
indexingPolicy,
|
2020-05-25 21:30:55 -05:00
|
|
|
partitionKeyVersion
|
|
|
|
};
|
|
|
|
|
|
|
|
if (params.cd) {
|
|
|
|
return AddDbUtilities.createGremlinDatabase(armEndpoint, params, additionalOptions).then(() => {
|
|
|
|
return CreateCollectionUtilities.createGremlinGraphWithARM(armEndpoint, params, additionalOptions);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return CreateCollectionUtilities.createGremlinGraphWithARM(armEndpoint, params, additionalOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static async createGremlinGraphWithARM(
|
|
|
|
armEndpoint: string,
|
|
|
|
params: DataModels.GraphParameters,
|
|
|
|
rpOptions: DataModels.RpOptions
|
|
|
|
): Promise<DataModels.CreateCollectionWithRpResponse> {
|
|
|
|
const rpPayloadToCreateCollection: DataModels.GraphCreationRequest = {
|
|
|
|
properties: {
|
|
|
|
resource: {
|
|
|
|
id: params.coll,
|
|
|
|
partitionKey: {
|
|
|
|
paths: [params.pk],
|
|
|
|
kind: "Hash",
|
|
|
|
version: params.partitionKeyVersion
|
|
|
|
}
|
|
|
|
},
|
|
|
|
options: {}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-07-13 10:54:59 -07:00
|
|
|
if (params.indexingPolicy) {
|
|
|
|
rpPayloadToCreateCollection.properties.resource.indexingPolicy = params.indexingPolicy;
|
|
|
|
}
|
|
|
|
|
2020-05-25 21:30:55 -05:00
|
|
|
if (!params.st) {
|
|
|
|
if (rpOptions) {
|
|
|
|
rpPayloadToCreateCollection.properties.options = rpOptions;
|
|
|
|
} else {
|
|
|
|
rpPayloadToCreateCollection.properties.options["throughput"] =
|
|
|
|
params.offerThroughput && params.offerThroughput.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
let response = await new ResourceProviderClient<DataModels.CreateCollectionWithRpResponse>(armEndpoint).putAsync(
|
|
|
|
CreateCollectionUtilities.getGremlinGraphUri(params),
|
|
|
|
DataExplorerConstants.ArmApiVersions.publicVersion,
|
|
|
|
rpPayloadToCreateCollection
|
|
|
|
);
|
|
|
|
return response;
|
|
|
|
} catch (response) {
|
|
|
|
NotificationConsoleUtils.logConsoleMessage(
|
|
|
|
ConsoleDataType.Error,
|
|
|
|
`Error creating graph: ${JSON.stringify(response)}`
|
|
|
|
);
|
|
|
|
if (response.status === HttpStatusCodes.Forbidden) {
|
2020-07-24 16:45:48 -05:00
|
|
|
sendMessage({ type: MessageTypes.ForbiddenError });
|
2020-05-25 21:30:55 -05:00
|
|
|
}
|
|
|
|
throw new Error(`Error creating graph`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static getGremlinGraphUri(params: DataModels.GraphParameters): string {
|
|
|
|
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${params.dba}/gremlinDatabases/${params.db}/graphs/${params.coll}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export class Utilities {
|
|
|
|
public static async createAzureTableWithARM(
|
|
|
|
armEndpoint: string,
|
|
|
|
params: DataModels.CreateDatabaseAndCollectionRequest,
|
|
|
|
rpOptions: DataModels.RpOptions
|
|
|
|
): Promise<any> {
|
|
|
|
const rpPayloadToCreateDatabase: DataModels.CreationRequest = {
|
|
|
|
properties: {
|
|
|
|
resource: {
|
|
|
|
id: params.collectionId
|
|
|
|
},
|
|
|
|
options: {}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!params.databaseLevelThroughput) {
|
|
|
|
if (rpOptions) {
|
|
|
|
rpPayloadToCreateDatabase.properties.options = rpOptions;
|
|
|
|
} else {
|
|
|
|
rpPayloadToCreateDatabase.properties.options["throughput"] =
|
|
|
|
params.offerThroughput && params.offerThroughput.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
await new ResourceProviderClient(armEndpoint).putAsync(
|
|
|
|
Utilities._getAzureTableUri(params),
|
|
|
|
DataExplorerConstants.ArmApiVersions.publicVersion,
|
|
|
|
rpPayloadToCreateDatabase
|
|
|
|
);
|
|
|
|
} catch (reason) {
|
|
|
|
NotificationConsoleUtils.logConsoleMessage(
|
|
|
|
ConsoleDataType.Error,
|
|
|
|
`Error creating table: ${JSON.stringify(reason)}, Payload: ${params}`
|
|
|
|
);
|
|
|
|
if (reason.status === HttpStatusCodes.Forbidden) {
|
2020-07-24 16:45:48 -05:00
|
|
|
sendMessage({ type: MessageTypes.ForbiddenError });
|
2020-05-25 21:30:55 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
throw new Error(`Error creating table`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static getMaxRUForStorageOption(
|
2020-05-26 13:53:41 -05:00
|
|
|
defaults: ViewModels.CollectionCreationDefaults,
|
2020-05-25 21:30:55 -05:00
|
|
|
storageOption: string
|
|
|
|
): number {
|
|
|
|
if (storageOption === SharedConstants.CollectionCreation.storage10Gb) {
|
|
|
|
return SharedConstants.CollectionCreation.DefaultCollectionRUs10K;
|
|
|
|
}
|
|
|
|
|
|
|
|
return defaults.throughput.unlimitedmax;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static getMinRUForStorageOption(
|
2020-05-26 13:53:41 -05:00
|
|
|
defaults: ViewModels.CollectionCreationDefaults,
|
2020-05-25 21:30:55 -05:00
|
|
|
storageOption: string
|
|
|
|
): number {
|
|
|
|
if (storageOption === SharedConstants.CollectionCreation.storage10Gb) {
|
|
|
|
return SharedConstants.CollectionCreation.DefaultCollectionRUs400;
|
|
|
|
}
|
|
|
|
|
2020-05-26 13:53:41 -05:00
|
|
|
return defaults.throughput.unlimitedmin;
|
2020-05-25 21:30:55 -05:00
|
|
|
}
|
|
|
|
|
2020-07-20 12:59:40 -05:00
|
|
|
public static getMaxThroughput(defaults: ViewModels.CollectionCreationDefaults, container: Explorer): number {
|
2020-05-26 13:53:41 -05:00
|
|
|
const throughput = defaults.throughput.unlimited;
|
|
|
|
if (typeof throughput === "number") {
|
|
|
|
return throughput;
|
|
|
|
} else {
|
|
|
|
return this._exceedsThreshold(throughput.collectionThreshold, container)
|
|
|
|
? throughput.greatThanThreshold
|
|
|
|
: throughput.lessThanOrEqualToThreshold;
|
2020-05-25 21:30:55 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-20 12:59:40 -05:00
|
|
|
private static _exceedsThreshold(unlimitedThreshold: number, container: Explorer): boolean {
|
2020-05-25 21:30:55 -05:00
|
|
|
const databases = (container && container.databases && container.databases()) || [];
|
|
|
|
return _.any(
|
|
|
|
databases,
|
|
|
|
database =>
|
|
|
|
database && database.collections && database.collections() && database.collections().length > unlimitedThreshold
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static _getAzureTableUri(params: DataModels.CreateDatabaseAndCollectionRequest): string {
|
2020-08-06 14:03:46 -05:00
|
|
|
return `subscriptions/${userContext.subscriptionId}/resourceGroups/${userContext.resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${userContext.databaseAccount.name}/tables/${params.collectionId}`;
|
2020-05-25 21:30:55 -05:00
|
|
|
}
|
|
|
|
}
|