mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-12-01 09:57:00 +00:00
Move create collection to RP (#173)
This commit is contained in:
parent
c2cd383ece
commit
fae59d8754
@ -3,8 +3,8 @@
|
|||||||
"offerThroughput": 400,
|
"offerThroughput": 400,
|
||||||
"databaseLevelThroughput": false,
|
"databaseLevelThroughput": false,
|
||||||
"collectionId": "Persons",
|
"collectionId": "Persons",
|
||||||
"rupmEnabled": false,
|
"createNewDatabase": true,
|
||||||
"partitionKey": { "kind": "Hash", "paths": ["/firstname"] },
|
"partitionKey": { "kind": "Hash", "paths": ["/firstname"], "version": 1 },
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"firstname": "Eva",
|
"firstname": "Eva",
|
||||||
|
@ -8,15 +8,12 @@ import {
|
|||||||
ConflictDefinition,
|
ConflictDefinition,
|
||||||
FeedOptions,
|
FeedOptions,
|
||||||
ItemDefinition,
|
ItemDefinition,
|
||||||
PartitionKeyDefinition,
|
|
||||||
QueryIterator,
|
QueryIterator,
|
||||||
Resource,
|
Resource,
|
||||||
TriggerDefinition,
|
TriggerDefinition,
|
||||||
OfferDefinition
|
OfferDefinition
|
||||||
} from "@azure/cosmos";
|
} from "@azure/cosmos";
|
||||||
import { ContainerRequest } from "@azure/cosmos/dist-esm/client/Container/ContainerRequest";
|
|
||||||
import { client } from "./CosmosClient";
|
import { client } from "./CosmosClient";
|
||||||
import { DatabaseRequest } from "@azure/cosmos/dist-esm/client/Database/DatabaseRequest";
|
|
||||||
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||||
import { sendCachedDataMessage } from "./MessageHandler";
|
import { sendCachedDataMessage } from "./MessageHandler";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
@ -480,69 +477,6 @@ export function readOffer(requestedResource: DataModels.Offer, options: any): Q.
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOrCreateDatabaseAndCollection(
|
|
||||||
request: DataModels.CreateDatabaseAndCollectionRequest,
|
|
||||||
options: any
|
|
||||||
): Q.Promise<DataModels.Collection> {
|
|
||||||
const databaseOptions: any = options && _.omit(options, "sharedOfferThroughput");
|
|
||||||
const {
|
|
||||||
databaseId,
|
|
||||||
databaseLevelThroughput,
|
|
||||||
collectionId,
|
|
||||||
partitionKey,
|
|
||||||
indexingPolicy,
|
|
||||||
uniqueKeyPolicy,
|
|
||||||
offerThroughput,
|
|
||||||
analyticalStorageTtl,
|
|
||||||
hasAutoPilotV2FeatureFlag
|
|
||||||
} = request;
|
|
||||||
|
|
||||||
const createBody: DatabaseRequest = {
|
|
||||||
id: databaseId
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: replace when SDK support autopilot
|
|
||||||
const initialHeaders = request.autoPilot
|
|
||||||
? !hasAutoPilotV2FeatureFlag
|
|
||||||
? {
|
|
||||||
[Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({
|
|
||||||
maxThroughput: request.autoPilot.maxThroughput
|
|
||||||
})
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
[Constants.HttpHeaders.autoPilotTier]: request.autoPilot.autopilotTier
|
|
||||||
}
|
|
||||||
: undefined;
|
|
||||||
if (databaseLevelThroughput) {
|
|
||||||
if (request.autoPilot) {
|
|
||||||
databaseOptions.initialHeaders = initialHeaders;
|
|
||||||
}
|
|
||||||
createBody.throughput = offerThroughput;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.databases.createIfNotExists(createBody, databaseOptions)
|
|
||||||
.then(response => {
|
|
||||||
return response.database.containers.create(
|
|
||||||
{
|
|
||||||
id: collectionId,
|
|
||||||
partitionKey: (partitionKey || undefined) as PartitionKeyDefinition,
|
|
||||||
indexingPolicy: indexingPolicy ? indexingPolicy : undefined,
|
|
||||||
uniqueKeyPolicy: uniqueKeyPolicy ? uniqueKeyPolicy : undefined,
|
|
||||||
analyticalStorageTtl: analyticalStorageTtl,
|
|
||||||
throughput: databaseLevelThroughput || request.autoPilot ? undefined : offerThroughput
|
|
||||||
} as ContainerRequest, // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
|
|
||||||
{
|
|
||||||
initialHeaders: databaseLevelThroughput ? undefined : initialHeaders
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(containerResponse => containerResponse.resource as DataModels.Collection)
|
|
||||||
.finally(() => refreshCachedResources(options))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function refreshCachedOffers(): Q.Promise<void> {
|
export function refreshCachedOffers(): Q.Promise<void> {
|
||||||
if (configContext.platform === Platform.Portal) {
|
if (configContext.platform === Platform.Portal) {
|
||||||
return sendCachedDataMessage(MessageTypes.RefreshOffers, []);
|
return sendCachedDataMessage(MessageTypes.RefreshOffers, []);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import * as Constants from "./Constants";
|
import * as Constants from "./Constants";
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import * as ErrorParserUtility from "./ErrorParserUtility";
|
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
@ -856,37 +855,3 @@ export function readOffer(
|
|||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOrCreateDatabaseAndCollection(
|
|
||||||
request: DataModels.CreateDatabaseAndCollectionRequest,
|
|
||||||
options: any = {}
|
|
||||||
): Q.Promise<DataModels.Collection> {
|
|
||||||
const deferred: Q.Deferred<DataModels.Collection> = Q.defer<DataModels.Collection>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Creating a new container ${request.collectionId} for database ${request.databaseId}`
|
|
||||||
);
|
|
||||||
|
|
||||||
DataAccessUtilityBase.getOrCreateDatabaseAndCollection(request, options)
|
|
||||||
.then(
|
|
||||||
(collection: DataModels.Collection) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully created container ${request.collectionId}`
|
|
||||||
);
|
|
||||||
deferred.resolve(collection);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
const sanitizedError = ErrorParserUtility.replaceKnownError(JSON.stringify(error));
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while creating container ${request.collectionId}:\n ${sanitizedError}`
|
|
||||||
);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
|
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
|
||||||
import queryString from "querystring";
|
import queryString from "querystring";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import * as Constants from "../Common/Constants";
|
|
||||||
import * as DataExplorerConstants from "../Common/Constants";
|
import * as DataExplorerConstants from "../Common/Constants";
|
||||||
import { configContext } from "../ConfigContext";
|
import { configContext } from "../ConfigContext";
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
@ -285,43 +284,35 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createMongoCollectionWithProxy(
|
export function createMongoCollectionWithProxy(
|
||||||
databaseId: string,
|
params: DataModels.CreateCollectionParams
|
||||||
collectionId: string,
|
|
||||||
offerThroughput: number,
|
|
||||||
shardKey: string,
|
|
||||||
createDatabase: boolean,
|
|
||||||
sharedThroughput: boolean,
|
|
||||||
isSharded: boolean,
|
|
||||||
autopilotOptions?: DataModels.RpOptions
|
|
||||||
): Promise<DataModels.Collection> {
|
): Promise<DataModels.Collection> {
|
||||||
const databaseAccount = userContext.databaseAccount;
|
const databaseAccount = userContext.databaseAccount;
|
||||||
const params: DataModels.MongoParameters = {
|
const shardKey: string = params.partitionKey?.paths[0];
|
||||||
|
const mongoParams: DataModels.MongoParameters = {
|
||||||
resourceUrl: databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint,
|
resourceUrl: databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint,
|
||||||
db: databaseId,
|
db: params.databaseId,
|
||||||
coll: collectionId,
|
coll: params.collectionId,
|
||||||
pk: shardKey,
|
pk: shardKey,
|
||||||
offerThroughput,
|
offerThroughput: params.offerThroughput,
|
||||||
cd: createDatabase,
|
cd: params.createNewDatabase,
|
||||||
st: sharedThroughput,
|
st: params.databaseLevelThroughput,
|
||||||
is: isSharded,
|
is: !!shardKey,
|
||||||
rid: "",
|
rid: "",
|
||||||
rtype: "colls",
|
rtype: "colls",
|
||||||
sid: userContext.subscriptionId,
|
sid: userContext.subscriptionId,
|
||||||
rg: userContext.resourceGroup,
|
rg: userContext.resourceGroup,
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
isAutoPilot: false
|
isAutoPilot: !!params.autoPilotMaxThroughput,
|
||||||
|
autoPilotThroughput: params.autoPilotMaxThroughput?.toString()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (autopilotOptions) {
|
|
||||||
params.isAutoPilot = true;
|
|
||||||
params.autoPilotTier = autopilotOptions[Constants.HttpHeaders.autoPilotTier] as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
|
|
||||||
return window
|
return window
|
||||||
.fetch(
|
.fetch(
|
||||||
`${endpoint}/createCollection?${queryString.stringify((params as unknown) as queryString.ParsedUrlQueryInput)}`,
|
`${endpoint}/createCollection?${queryString.stringify(
|
||||||
|
(mongoParams as unknown) as queryString.ParsedUrlQueryInput
|
||||||
|
)}`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@ -335,7 +326,7 @@ export function createMongoCollectionWithProxy(
|
|||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
return errorHandling(response, "creating collection", params);
|
return errorHandling(response, "creating collection", mongoParams);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,13 +10,8 @@ import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
|||||||
import { QueryUtils } from "../Utils/QueryUtils";
|
import { QueryUtils } from "../Utils/QueryUtils";
|
||||||
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
import {
|
import { createDocument, deleteDocument, queryDocuments, queryDocumentsPage } from "./DocumentClientUtilityBase";
|
||||||
createDocument,
|
import { createCollection } from "./dataAccess/createCollection";
|
||||||
deleteDocument,
|
|
||||||
getOrCreateDatabaseAndCollection,
|
|
||||||
queryDocuments,
|
|
||||||
queryDocumentsPage
|
|
||||||
} from "./DocumentClientUtilityBase";
|
|
||||||
import * as ErrorParserUtility from "./ErrorParserUtility";
|
import * as ErrorParserUtility from "./ErrorParserUtility";
|
||||||
import * as Logger from "./Logger";
|
import * as Logger from "./Logger";
|
||||||
|
|
||||||
@ -41,12 +36,13 @@ export class QueriesClient {
|
|||||||
ConsoleDataType.InProgress,
|
ConsoleDataType.InProgress,
|
||||||
"Setting up account for saving queries"
|
"Setting up account for saving queries"
|
||||||
);
|
);
|
||||||
return getOrCreateDatabaseAndCollection({
|
return createCollection({
|
||||||
collectionId: SavedQueries.CollectionName,
|
collectionId: SavedQueries.CollectionName,
|
||||||
|
createNewDatabase: true,
|
||||||
databaseId: SavedQueries.DatabaseName,
|
databaseId: SavedQueries.DatabaseName,
|
||||||
partitionKey: QueriesClient.PartitionKey,
|
partitionKey: QueriesClient.PartitionKey,
|
||||||
offerThroughput: SavedQueries.OfferThroughput,
|
offerThroughput: SavedQueries.OfferThroughput,
|
||||||
databaseLevelThroughput: undefined
|
databaseLevelThroughput: false
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(collection: DataModels.Collection) => {
|
(collection: DataModels.Collection) => {
|
||||||
|
81
src/Common/dataAccess/createCollection.test.ts
Normal file
81
src/Common/dataAccess/createCollection.test.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
jest.mock("../../Utils/arm/request");
|
||||||
|
jest.mock("../CosmosClient");
|
||||||
|
jest.mock("../DataAccessUtilityBase");
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
import { CreateCollectionParams, DatabaseAccount } from "../../Contracts/DataModels";
|
||||||
|
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||||
|
import { armRequest } from "../../Utils/arm/request";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { createCollection, constructRpOptions } from "./createCollection";
|
||||||
|
import { updateUserContext } from "../../UserContext";
|
||||||
|
|
||||||
|
describe("createCollection", () => {
|
||||||
|
const createCollectionParams: CreateCollectionParams = {
|
||||||
|
createNewDatabase: false,
|
||||||
|
collectionId: "testContainer",
|
||||||
|
databaseId: "testDatabase",
|
||||||
|
databaseLevelThroughput: true,
|
||||||
|
offerThroughput: 400
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
updateUserContext({
|
||||||
|
databaseAccount: {
|
||||||
|
name: "test"
|
||||||
|
} as DatabaseAccount,
|
||||||
|
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call ARM if logged in with AAD", async () => {
|
||||||
|
window.authType = AuthType.AAD;
|
||||||
|
await createCollection(createCollectionParams);
|
||||||
|
expect(armRequest).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call SDK if not logged in with non-AAD method", async () => {
|
||||||
|
window.authType = AuthType.MasterKey;
|
||||||
|
(client as jest.Mock).mockReturnValue({
|
||||||
|
databases: {
|
||||||
|
createIfNotExists: () => {
|
||||||
|
return {
|
||||||
|
database: {
|
||||||
|
containers: {
|
||||||
|
create: () => ({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await createCollection(createCollectionParams);
|
||||||
|
expect(client).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("constructRpOptions should return the correct options", () => {
|
||||||
|
expect(constructRpOptions(createCollectionParams)).toEqual({});
|
||||||
|
|
||||||
|
const manualThroughputParams: CreateCollectionParams = {
|
||||||
|
createNewDatabase: false,
|
||||||
|
collectionId: "testContainer",
|
||||||
|
databaseId: "testDatabase",
|
||||||
|
databaseLevelThroughput: false,
|
||||||
|
offerThroughput: 400
|
||||||
|
};
|
||||||
|
expect(constructRpOptions(manualThroughputParams)).toEqual({ throughput: 400 });
|
||||||
|
|
||||||
|
const autoPilotThroughputParams: CreateCollectionParams = {
|
||||||
|
createNewDatabase: false,
|
||||||
|
collectionId: "testContainer",
|
||||||
|
databaseId: "testDatabase",
|
||||||
|
databaseLevelThroughput: false,
|
||||||
|
offerThroughput: 400,
|
||||||
|
autoPilotMaxThroughput: 4000
|
||||||
|
};
|
||||||
|
expect(constructRpOptions(autoPilotThroughputParams)).toEqual({
|
||||||
|
autoscaleSettings: {
|
||||||
|
maxThroughput: 4000
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
371
src/Common/dataAccess/createCollection.ts
Normal file
371
src/Common/dataAccess/createCollection.ts
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
|
import * as ErrorParserUtility from "../ErrorParserUtility";
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
import { ContainerResponse, DatabaseResponse } from "@azure/cosmos";
|
||||||
|
import { ContainerRequest } from "@azure/cosmos/dist-esm/client/Container/ContainerRequest";
|
||||||
|
import { DatabaseRequest } from "@azure/cosmos/dist-esm/client/Database/DatabaseRequest";
|
||||||
|
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||||
|
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||||
|
import * as ARMTypes from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { createMongoCollectionWithProxy } from "../MongoProxyClient";
|
||||||
|
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 { logConsoleProgress, logConsoleError, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { refreshCachedResources } from "../DataAccessUtilityBase";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
|
import { createDatabase } from "./createDatabase";
|
||||||
|
|
||||||
|
export const createCollection = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||||
|
let collection: DataModels.Collection;
|
||||||
|
const clearMessage = logConsoleProgress(
|
||||||
|
`Creating a new container ${params.collectionId} for database ${params.databaseId}`
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||||
|
if (params.createNewDatabase) {
|
||||||
|
const createDatabaseParams: DataModels.CreateDatabaseParams = {
|
||||||
|
autoPilotMaxThroughput: params.autoPilotMaxThroughput,
|
||||||
|
databaseId: params.databaseId,
|
||||||
|
databaseLevelThroughput: params.databaseLevelThroughput,
|
||||||
|
offerThroughput: params.offerThroughput
|
||||||
|
};
|
||||||
|
await createDatabase(createDatabaseParams);
|
||||||
|
}
|
||||||
|
collection = await createCollectionWithARM(params);
|
||||||
|
} else if (userContext.defaultExperience === DefaultAccountExperienceType.MongoDB) {
|
||||||
|
collection = await createMongoCollectionWithProxy(params);
|
||||||
|
} else {
|
||||||
|
collection = await createCollectionWithSDK(params);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const sanitizedError = ErrorParserUtility.replaceKnownError(JSON.stringify(error));
|
||||||
|
logConsoleError(`Error while creating container ${params.collectionId}:\n ${sanitizedError}`);
|
||||||
|
logError(JSON.stringify(error), "CreateCollection", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
clearMessage();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
logConsoleInfo(`Successfully created container ${params.collectionId}`);
|
||||||
|
await refreshCachedResources();
|
||||||
|
clearMessage();
|
||||||
|
return collection;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCollectionWithARM = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||||
|
const defaultExperience = userContext.defaultExperience;
|
||||||
|
switch (defaultExperience) {
|
||||||
|
case DefaultAccountExperienceType.DocumentDB:
|
||||||
|
return createSqlContainer(params);
|
||||||
|
case DefaultAccountExperienceType.MongoDB:
|
||||||
|
return createMongoCollection(params);
|
||||||
|
case DefaultAccountExperienceType.Cassandra:
|
||||||
|
return createCassandraTable(params);
|
||||||
|
case DefaultAccountExperienceType.Graph:
|
||||||
|
return createGraph(params);
|
||||||
|
case DefaultAccountExperienceType.Table:
|
||||||
|
return createTable(params);
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSqlContainer = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||||
|
try {
|
||||||
|
const getResponse = await getSqlContainer(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.databaseId,
|
||||||
|
params.collectionId
|
||||||
|
);
|
||||||
|
if (getResponse?.properties?.resource) {
|
||||||
|
throw new Error(`Create container failed: container with id ${params.collectionId} already exists`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== "NotFound") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||||
|
const resource: ARMTypes.SqlContainerResource = {
|
||||||
|
id: params.collectionId
|
||||||
|
};
|
||||||
|
if (params.analyticalStorageTtl) {
|
||||||
|
resource.analyticalStorageTtl = params.analyticalStorageTtl;
|
||||||
|
}
|
||||||
|
if (params.indexingPolicy) {
|
||||||
|
resource.indexingPolicy = params.indexingPolicy;
|
||||||
|
}
|
||||||
|
if (params.partitionKey) {
|
||||||
|
resource.partitionKey = params.partitionKey;
|
||||||
|
}
|
||||||
|
if (params.uniqueKeyPolicy) {
|
||||||
|
resource.uniqueKeyPolicy = params.uniqueKeyPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rpPayload: ARMTypes.SqlDatabaseCreateUpdateParameters = {
|
||||||
|
properties: {
|
||||||
|
resource,
|
||||||
|
options
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createResponse = await createUpdateSqlContainer(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.databaseId,
|
||||||
|
params.collectionId,
|
||||||
|
rpPayload
|
||||||
|
);
|
||||||
|
return createResponse && (createResponse.properties.resource as DataModels.Collection);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createMongoCollection = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||||
|
try {
|
||||||
|
const getResponse = await getMongoDBCollection(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.databaseId,
|
||||||
|
params.collectionId
|
||||||
|
);
|
||||||
|
if (getResponse?.properties?.resource) {
|
||||||
|
throw new Error(`Create collection failed: collection with id ${params.collectionId} already exists`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== "NotFound") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||||
|
const resource: ARMTypes.MongoDBCollectionResource = {
|
||||||
|
id: params.collectionId
|
||||||
|
};
|
||||||
|
if (params.analyticalStorageTtl) {
|
||||||
|
resource.analyticalStorageTtl = params.analyticalStorageTtl;
|
||||||
|
}
|
||||||
|
if (params.partitionKey) {
|
||||||
|
const partitionKeyPath: string = params.partitionKey.paths[0];
|
||||||
|
resource.shardKey = { [partitionKeyPath]: "Hash" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const rpPayload: ARMTypes.MongoDBCollectionCreateUpdateParameters = {
|
||||||
|
properties: {
|
||||||
|
resource,
|
||||||
|
options
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createResponse = await createUpdateMongoDBCollection(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.databaseId,
|
||||||
|
params.collectionId,
|
||||||
|
rpPayload
|
||||||
|
);
|
||||||
|
return createResponse && (createResponse.properties.resource as DataModels.Collection);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCassandraTable = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||||
|
try {
|
||||||
|
const getResponse = await getCassandraTable(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.databaseId,
|
||||||
|
params.collectionId
|
||||||
|
);
|
||||||
|
if (getResponse?.properties?.resource) {
|
||||||
|
throw new Error(`Create table failed: table with id ${params.collectionId} already exists`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== "NotFound") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||||
|
const resource: ARMTypes.CassandraTableResource = {
|
||||||
|
id: params.collectionId
|
||||||
|
};
|
||||||
|
if (params.analyticalStorageTtl) {
|
||||||
|
resource.analyticalStorageTtl = params.analyticalStorageTtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rpPayload: ARMTypes.CassandraTableCreateUpdateParameters = {
|
||||||
|
properties: {
|
||||||
|
resource,
|
||||||
|
options
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createResponse = await createUpdateCassandraTable(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.databaseId,
|
||||||
|
params.collectionId,
|
||||||
|
rpPayload
|
||||||
|
);
|
||||||
|
return createResponse && (createResponse.properties.resource as DataModels.Collection);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createGraph = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||||
|
try {
|
||||||
|
const getResponse = await getGremlinGraph(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.databaseId,
|
||||||
|
params.collectionId
|
||||||
|
);
|
||||||
|
if (getResponse?.properties?.resource) {
|
||||||
|
throw new Error(`Create graph failed: graph with id ${params.collectionId} already exists`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== "NotFound") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||||
|
const resource: ARMTypes.GremlinGraphResource = {
|
||||||
|
id: params.collectionId
|
||||||
|
};
|
||||||
|
|
||||||
|
if (params.indexingPolicy) {
|
||||||
|
resource.indexingPolicy = params.indexingPolicy;
|
||||||
|
}
|
||||||
|
if (params.partitionKey) {
|
||||||
|
resource.partitionKey = params.partitionKey;
|
||||||
|
}
|
||||||
|
if (params.uniqueKeyPolicy) {
|
||||||
|
resource.uniqueKeyPolicy = params.uniqueKeyPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rpPayload: ARMTypes.GremlinGraphCreateUpdateParameters = {
|
||||||
|
properties: {
|
||||||
|
resource,
|
||||||
|
options
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createResponse = await createUpdateGremlinGraph(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.databaseId,
|
||||||
|
params.collectionId,
|
||||||
|
rpPayload
|
||||||
|
);
|
||||||
|
return createResponse && (createResponse.properties.resource as DataModels.Collection);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createTable = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||||
|
try {
|
||||||
|
const getResponse = await getTable(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.collectionId
|
||||||
|
);
|
||||||
|
if (getResponse?.properties?.resource) {
|
||||||
|
throw new Error(`Create table failed: table with id ${params.collectionId} already exists`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== "NotFound") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||||
|
const resource: ARMTypes.TableResource = {
|
||||||
|
id: params.collectionId
|
||||||
|
};
|
||||||
|
|
||||||
|
const rpPayload: ARMTypes.TableCreateUpdateParameters = {
|
||||||
|
properties: {
|
||||||
|
resource,
|
||||||
|
options
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createResponse = await createUpdateTable(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.collectionId,
|
||||||
|
rpPayload
|
||||||
|
);
|
||||||
|
return createResponse && (createResponse.properties.resource as DataModels.Collection);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const constructRpOptions = (params: DataModels.CreateDatabaseParams): ARMTypes.CreateUpdateOptions => {
|
||||||
|
if (params.databaseLevelThroughput) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.autoPilotMaxThroughput) {
|
||||||
|
return {
|
||||||
|
autoscaleSettings: {
|
||||||
|
maxThroughput: params.autoPilotMaxThroughput
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
throughput: params.offerThroughput
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCollectionWithSDK = async (params: DataModels.CreateCollectionParams): Promise<DataModels.Collection> => {
|
||||||
|
const createCollectionBody: ContainerRequest = {
|
||||||
|
id: params.collectionId,
|
||||||
|
partitionKey: params.partitionKey || undefined,
|
||||||
|
indexingPolicy: params.indexingPolicy || undefined,
|
||||||
|
uniqueKeyPolicy: params.uniqueKeyPolicy || undefined,
|
||||||
|
analyticalStorageTtl: params.analyticalStorageTtl
|
||||||
|
} as ContainerRequest; // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
|
||||||
|
const collectionOptions: RequestOptions = {};
|
||||||
|
const createDatabaseBody: DatabaseRequest = { id: params.databaseId };
|
||||||
|
|
||||||
|
if (params.databaseLevelThroughput) {
|
||||||
|
if (params.autoPilotMaxThroughput) {
|
||||||
|
createDatabaseBody.maxThroughput = params.autoPilotMaxThroughput;
|
||||||
|
} else {
|
||||||
|
createDatabaseBody.throughput = params.offerThroughput;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (params.autoPilotMaxThroughput) {
|
||||||
|
createCollectionBody.maxThroughput = params.autoPilotMaxThroughput;
|
||||||
|
} else {
|
||||||
|
createCollectionBody.throughput = params.offerThroughput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const databaseResponse: DatabaseResponse = await client().databases.createIfNotExists(createDatabaseBody);
|
||||||
|
const collectionResponse: ContainerResponse = await databaseResponse?.database.containers.create(
|
||||||
|
createCollectionBody,
|
||||||
|
collectionOptions
|
||||||
|
);
|
||||||
|
return collectionResponse?.resource as DataModels.Collection;
|
||||||
|
};
|
@ -3,8 +3,10 @@ import { AuthType } from "../../AuthType";
|
|||||||
import { DatabaseResponse } from "@azure/cosmos";
|
import { DatabaseResponse } from "@azure/cosmos";
|
||||||
import { DatabaseRequest } from "@azure/cosmos/dist-esm/client/Database/DatabaseRequest";
|
import { DatabaseRequest } from "@azure/cosmos/dist-esm/client/Database/DatabaseRequest";
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
|
||||||
import {
|
import {
|
||||||
|
CassandraKeyspaceCreateUpdateParameters,
|
||||||
|
GremlinDatabaseCreateUpdateParameters,
|
||||||
|
MongoDBDatabaseCreateUpdateParameters,
|
||||||
SqlDatabaseCreateUpdateParameters,
|
SqlDatabaseCreateUpdateParameters,
|
||||||
CreateUpdateOptions
|
CreateUpdateOptions
|
||||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||||
@ -79,7 +81,7 @@ async function createSqlDatabase(params: DataModels.CreateDatabaseParams): Promi
|
|||||||
userContext.databaseAccount.name,
|
userContext.databaseAccount.name,
|
||||||
params.databaseId
|
params.databaseId
|
||||||
);
|
);
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
if (getResponse?.properties?.resource) {
|
||||||
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -115,7 +117,7 @@ async function createMongoDatabase(params: DataModels.CreateDatabaseParams): Pro
|
|||||||
userContext.databaseAccount.name,
|
userContext.databaseAccount.name,
|
||||||
params.databaseId
|
params.databaseId
|
||||||
);
|
);
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
if (getResponse?.properties?.resource) {
|
||||||
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -125,7 +127,7 @@ async function createMongoDatabase(params: DataModels.CreateDatabaseParams): Pro
|
|||||||
}
|
}
|
||||||
|
|
||||||
const options: CreateUpdateOptions = constructRpOptions(params);
|
const options: CreateUpdateOptions = constructRpOptions(params);
|
||||||
const rpPayload: SqlDatabaseCreateUpdateParameters = {
|
const rpPayload: MongoDBDatabaseCreateUpdateParameters = {
|
||||||
properties: {
|
properties: {
|
||||||
resource: {
|
resource: {
|
||||||
id: params.databaseId
|
id: params.databaseId
|
||||||
@ -161,7 +163,7 @@ async function createCassandraKeyspace(params: DataModels.CreateDatabaseParams):
|
|||||||
}
|
}
|
||||||
|
|
||||||
const options: CreateUpdateOptions = constructRpOptions(params);
|
const options: CreateUpdateOptions = constructRpOptions(params);
|
||||||
const rpPayload: SqlDatabaseCreateUpdateParameters = {
|
const rpPayload: CassandraKeyspaceCreateUpdateParameters = {
|
||||||
properties: {
|
properties: {
|
||||||
resource: {
|
resource: {
|
||||||
id: params.databaseId
|
id: params.databaseId
|
||||||
@ -187,7 +189,7 @@ async function createGremlineDatabase(params: DataModels.CreateDatabaseParams):
|
|||||||
userContext.databaseAccount.name,
|
userContext.databaseAccount.name,
|
||||||
params.databaseId
|
params.databaseId
|
||||||
);
|
);
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
if (getResponse?.properties?.resource) {
|
||||||
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -197,7 +199,7 @@ async function createGremlineDatabase(params: DataModels.CreateDatabaseParams):
|
|||||||
}
|
}
|
||||||
|
|
||||||
const options: CreateUpdateOptions = constructRpOptions(params);
|
const options: CreateUpdateOptions = constructRpOptions(params);
|
||||||
const rpPayload: SqlDatabaseCreateUpdateParameters = {
|
const rpPayload: GremlinDatabaseCreateUpdateParameters = {
|
||||||
properties: {
|
properties: {
|
||||||
resource: {
|
resource: {
|
||||||
id: params.databaseId
|
id: params.databaseId
|
||||||
@ -217,8 +219,7 @@ async function createGremlineDatabase(params: DataModels.CreateDatabaseParams):
|
|||||||
|
|
||||||
async function createDatabaseWithSDK(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
async function createDatabaseWithSDK(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
||||||
const createBody: DatabaseRequest = { id: params.databaseId };
|
const createBody: DatabaseRequest = { id: params.databaseId };
|
||||||
const databaseOptions: RequestOptions = {};
|
|
||||||
// TODO: replace when SDK support autopilot
|
|
||||||
if (params.databaseLevelThroughput) {
|
if (params.databaseLevelThroughput) {
|
||||||
if (params.autoPilotMaxThroughput) {
|
if (params.autoPilotMaxThroughput) {
|
||||||
createBody.maxThroughput = params.autoPilotMaxThroughput;
|
createBody.maxThroughput = params.autoPilotMaxThroughput;
|
||||||
@ -227,7 +228,7 @@ async function createDatabaseWithSDK(params: DataModels.CreateDatabaseParams): P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response: DatabaseResponse = await client().databases.create(createBody, databaseOptions);
|
const response: DatabaseResponse = await client().databases.create(createBody);
|
||||||
return response.resource;
|
return response.resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,6 +334,19 @@ export interface CreateDatabaseParams {
|
|||||||
offerThroughput?: number;
|
offerThroughput?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CreateCollectionParams {
|
||||||
|
createNewDatabase: boolean;
|
||||||
|
collectionId: string;
|
||||||
|
databaseId: string;
|
||||||
|
databaseLevelThroughput: boolean;
|
||||||
|
offerThroughput: number;
|
||||||
|
analyticalStorageTtl?: number;
|
||||||
|
autoPilotMaxThroughput?: number;
|
||||||
|
indexingPolicy?: IndexingPolicy;
|
||||||
|
partitionKey?: PartitionKey;
|
||||||
|
uniqueKeyPolicy?: UniqueKeyPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SharedThroughputRange {
|
export interface SharedThroughputRange {
|
||||||
minimumRU: number;
|
minimumRU: number;
|
||||||
maximumRU: number;
|
maximumRU: number;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
jest.mock("../../Common/DocumentClientUtilityBase");
|
jest.mock("../../Common/DocumentClientUtilityBase");
|
||||||
|
jest.mock("../../Common/dataAccess/createCollection");
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
@ -33,8 +34,8 @@ describe("ContainerSampleGenerator", () => {
|
|||||||
databaseId: sampleDatabaseId,
|
databaseId: sampleDatabaseId,
|
||||||
offerThroughput: 400,
|
offerThroughput: 400,
|
||||||
databaseLevelThroughput: false,
|
databaseLevelThroughput: false,
|
||||||
|
createNewDatabase: true,
|
||||||
collectionId: sampleCollectionId,
|
collectionId: sampleCollectionId,
|
||||||
rupmEnabled: false,
|
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
firstname: "Eva",
|
firstname: "Eva",
|
||||||
@ -99,8 +100,8 @@ describe("ContainerSampleGenerator", () => {
|
|||||||
databaseId: sampleDatabaseId,
|
databaseId: sampleDatabaseId,
|
||||||
offerThroughput: 400,
|
offerThroughput: 400,
|
||||||
databaseLevelThroughput: false,
|
databaseLevelThroughput: false,
|
||||||
|
createNewDatabase: true,
|
||||||
collectionId: sampleCollectionId,
|
collectionId: sampleCollectionId,
|
||||||
rupmEnabled: false,
|
|
||||||
data: [
|
data: [
|
||||||
"g.addV('person').property(id, '1').property('_partitionKey','pk').property('name', 'Eva').property('age', 44)"
|
"g.addV('person').property(id, '1').property('_partitionKey','pk').property('name', 'Eva').property('age', 44)"
|
||||||
]
|
]
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import * as Constants from "../../Common/Constants";
|
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import GraphTab from ".././Tabs/GraphTab";
|
import GraphTab from ".././Tabs/GraphTab";
|
||||||
@ -6,10 +5,11 @@ import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsol
|
|||||||
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
|
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { createDocument, getOrCreateDatabaseAndCollection } from "../../Common/DocumentClientUtilityBase";
|
import { createDocument } from "../../Common/DocumentClientUtilityBase";
|
||||||
|
import { createCollection } from "../../Common/dataAccess/createCollection";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
|
|
||||||
interface SampleDataFile extends DataModels.CreateDatabaseAndCollectionRequest {
|
interface SampleDataFile extends DataModels.CreateCollectionParams {
|
||||||
data: any[];
|
data: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,18 +54,11 @@ export class ContainerSampleGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async createContainerAsync(): Promise<ViewModels.Collection> {
|
private async createContainerAsync(): Promise<ViewModels.Collection> {
|
||||||
const createRequest: DataModels.CreateDatabaseAndCollectionRequest = {
|
const createRequest: DataModels.CreateCollectionParams = {
|
||||||
...this.sampleDataFile
|
...this.sampleDataFile
|
||||||
};
|
};
|
||||||
|
|
||||||
const options: any = {};
|
await createCollection(createRequest);
|
||||||
if (this.container.isPreferredApiMongoDB()) {
|
|
||||||
options.initialHeaders = options.initialHeaders || {};
|
|
||||||
options.initialHeaders[Constants.HttpHeaders.supportSpatialLegacyCoordinates] = true;
|
|
||||||
options.initialHeaders[Constants.HttpHeaders.usePolygonsSmallerThanAHemisphere] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
await getOrCreateDatabaseAndCollection(createRequest, options);
|
|
||||||
await this.container.refreshAllDatabases();
|
await this.container.refreshAllDatabases();
|
||||||
const database = this.container.findDatabaseWithId(this.sampleDataFile.databaseId);
|
const database = this.container.findDatabaseWithId(this.sampleDataFile.databaseId);
|
||||||
if (!database) {
|
if (!database) {
|
||||||
|
@ -9,18 +9,15 @@ import * as PricingUtils from "../../Utils/PricingUtils";
|
|||||||
import * as SharedConstants from "../../Shared/Constants";
|
import * as SharedConstants from "../../Shared/Constants";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import editable from "../../Common/EditableUtility";
|
import editable from "../../Common/EditableUtility";
|
||||||
import EnvironmentUtility from "../../Common/EnvironmentUtility";
|
|
||||||
import Q from "q";
|
|
||||||
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import { configContext, Platform } from "../../ConfigContext";
|
import { configContext, Platform } from "../../ConfigContext";
|
||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
import { createMongoCollectionWithARM, createMongoCollectionWithProxy } from "../../Common/MongoProxyClient";
|
|
||||||
import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent";
|
import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent";
|
||||||
import { HashMap } from "../../Common/HashMap";
|
import { HashMap } from "../../Common/HashMap";
|
||||||
import { PlatformType } from "../../PlatformType";
|
import { PlatformType } from "../../PlatformType";
|
||||||
import { refreshCachedResources, getOrCreateDatabaseAndCollection } from "../../Common/DocumentClientUtilityBase";
|
import { refreshCachedResources } from "../../Common/DocumentClientUtilityBase";
|
||||||
import { userContext } from "../../UserContext";
|
import { createCollection } from "../../Common/dataAccess/createCollection";
|
||||||
|
|
||||||
export interface AddCollectionPaneOptions extends ViewModels.PaneOptions {
|
export interface AddCollectionPaneOptions extends ViewModels.PaneOptions {
|
||||||
isPreferredApiTable: ko.Computed<boolean>;
|
isPreferredApiTable: ko.Computed<boolean>;
|
||||||
@ -811,7 +808,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
let databaseId: string = this.databaseCreateNew() ? this.databaseId().trim() : this.databaseId();
|
let databaseId: string = this.databaseCreateNew() ? this.databaseId().trim() : this.databaseId();
|
||||||
let collectionId: string = this.collectionId().trim();
|
let collectionId: string = this.collectionId().trim();
|
||||||
let rupm: boolean = this.rupm() === Constants.RUPMStates.on;
|
|
||||||
|
|
||||||
let indexingPolicy: DataModels.IndexingPolicy;
|
let indexingPolicy: DataModels.IndexingPolicy;
|
||||||
// todo - remove mongo indexing policy ticket # 616274
|
// todo - remove mongo indexing policy ticket # 616274
|
||||||
@ -828,130 +824,28 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.formErrors("");
|
this.formErrors("");
|
||||||
|
|
||||||
this.isExecuting(true);
|
this.isExecuting(true);
|
||||||
|
|
||||||
const createRequest: DataModels.CreateDatabaseAndCollectionRequest = {
|
const databaseLevelThroughput: boolean = this.databaseCreateNew()
|
||||||
|
? this.databaseCreateNewShared()
|
||||||
|
: this.databaseHasSharedOffer() && !this.collectionWithThroughputInShared();
|
||||||
|
const autoPilotMaxThroughput: number = databaseLevelThroughput
|
||||||
|
? this.isSharedAutoPilotSelected() && this.sharedAutoPilotThroughput()
|
||||||
|
: this.isAutoPilotSelected() && this.autoPilotThroughput();
|
||||||
|
const createCollectionParams: DataModels.CreateCollectionParams = {
|
||||||
|
createNewDatabase: this.databaseCreateNew(),
|
||||||
collectionId,
|
collectionId,
|
||||||
databaseId,
|
databaseId,
|
||||||
|
databaseLevelThroughput,
|
||||||
offerThroughput,
|
offerThroughput,
|
||||||
databaseLevelThroughput: this.databaseHasSharedOffer() && !this.collectionWithThroughputInShared(),
|
|
||||||
rupmEnabled: rupm,
|
|
||||||
partitionKey,
|
|
||||||
indexingPolicy,
|
|
||||||
uniqueKeyPolicy,
|
|
||||||
autoPilot,
|
|
||||||
analyticalStorageTtl: this._getAnalyticalStorageTtl(),
|
analyticalStorageTtl: this._getAnalyticalStorageTtl(),
|
||||||
hasAutoPilotV2FeatureFlag: this.hasAutoPilotV2FeatureFlag()
|
autoPilotMaxThroughput,
|
||||||
|
indexingPolicy,
|
||||||
|
partitionKey,
|
||||||
|
uniqueKeyPolicy
|
||||||
};
|
};
|
||||||
|
|
||||||
const options: any = {};
|
createCollection(createCollectionParams).then(
|
||||||
if (this.container.isPreferredApiMongoDB()) {
|
|
||||||
options.initialHeaders = options.initialHeaders || {};
|
|
||||||
options.initialHeaders[Constants.HttpHeaders.supportSpatialLegacyCoordinates] = true;
|
|
||||||
options.initialHeaders[Constants.HttpHeaders.usePolygonsSmallerThanAHemisphere] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const databaseCreateNew = this.databaseCreateNew();
|
|
||||||
const useDatabaseSharedOffer = this.shouldUseDatabaseThroughput();
|
|
||||||
const isSharded: boolean = !!partitionKeyPath;
|
|
||||||
const autopilotSettings: DataModels.RpOptions = this._getAutopilotSettings();
|
|
||||||
|
|
||||||
let createCollectionFunc: () => Q.Promise<DataModels.Collection | DataModels.CreateCollectionWithRpResponse>;
|
|
||||||
|
|
||||||
if (this.container.isPreferredApiMongoDB()) {
|
|
||||||
const isFixedCollectionWithSharedThroughputBeingCreated =
|
|
||||||
this.container.isFixedCollectionWithSharedThroughputSupported() &&
|
|
||||||
!this.isUnlimitedStorageSelected() &&
|
|
||||||
this.databaseHasSharedOffer();
|
|
||||||
const isAadUser = EnvironmentUtility.isAadUser();
|
|
||||||
|
|
||||||
// note: v3 autopilot not supported yet for Mongo fixed collections (only tier supported)
|
|
||||||
if (!isAadUser || isFixedCollectionWithSharedThroughputBeingCreated) {
|
|
||||||
createCollectionFunc = () =>
|
|
||||||
Q(
|
|
||||||
createMongoCollectionWithProxy(
|
|
||||||
databaseId,
|
|
||||||
collectionId,
|
|
||||||
offerThroughput,
|
|
||||||
partitionKeyPath,
|
|
||||||
databaseCreateNew,
|
|
||||||
useDatabaseSharedOffer,
|
|
||||||
isSharded,
|
|
||||||
autopilotSettings
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
createCollectionFunc = () =>
|
|
||||||
Q(
|
|
||||||
createMongoCollectionWithARM(
|
|
||||||
this.container.armEndpoint(),
|
|
||||||
databaseId,
|
|
||||||
this._getAnalyticalStorageTtl(),
|
|
||||||
collectionId,
|
|
||||||
offerThroughput,
|
|
||||||
partitionKeyPath,
|
|
||||||
databaseCreateNew,
|
|
||||||
useDatabaseSharedOffer,
|
|
||||||
isSharded,
|
|
||||||
autopilotSettings
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (this.container.isPreferredApiTable() && EnvironmentUtility.isAadUser()) {
|
|
||||||
createCollectionFunc = () =>
|
|
||||||
Q(
|
|
||||||
AddCollectionUtility.Utilities.createAzureTableWithARM(
|
|
||||||
this.container.armEndpoint(),
|
|
||||||
createRequest,
|
|
||||||
autopilotSettings
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else if (this.container.isPreferredApiGraph() && EnvironmentUtility.isAadUser()) {
|
|
||||||
createCollectionFunc = () =>
|
|
||||||
Q(
|
|
||||||
AddCollectionUtility.CreateCollectionUtilities.createGremlinGraph(
|
|
||||||
this.container.armEndpoint(),
|
|
||||||
databaseId,
|
|
||||||
collectionId,
|
|
||||||
indexingPolicy,
|
|
||||||
offerThroughput,
|
|
||||||
partitionKeyPath,
|
|
||||||
partitionKey.version,
|
|
||||||
databaseCreateNew,
|
|
||||||
useDatabaseSharedOffer,
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
autopilotSettings
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else if (this.container.isPreferredApiDocumentDB() && EnvironmentUtility.isAadUser()) {
|
|
||||||
createCollectionFunc = () =>
|
|
||||||
Q(
|
|
||||||
AddCollectionUtility.CreateSqlCollectionUtilities.createSqlCollection(
|
|
||||||
this.container.armEndpoint(),
|
|
||||||
databaseId,
|
|
||||||
this._getAnalyticalStorageTtl(),
|
|
||||||
collectionId,
|
|
||||||
indexingPolicy,
|
|
||||||
offerThroughput,
|
|
||||||
partitionKeyPath,
|
|
||||||
partitionKey.version,
|
|
||||||
databaseCreateNew,
|
|
||||||
useDatabaseSharedOffer,
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
uniqueKeyPolicy,
|
|
||||||
autopilotSettings
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
createCollectionFunc = () => getOrCreateDatabaseAndCollection(createRequest, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
createCollectionFunc().then(
|
|
||||||
() => {
|
() => {
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
this.close();
|
this.close();
|
||||||
@ -1234,35 +1128,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
private _getAutopilotSettings(): DataModels.RpOptions {
|
|
||||||
if (
|
|
||||||
(!this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
this.databaseCreateNewShared() &&
|
|
||||||
this.isSharedAutoPilotSelected() &&
|
|
||||||
this.sharedAutoPilotThroughput()) ||
|
|
||||||
(this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
this.databaseCreateNewShared() &&
|
|
||||||
this.isSharedAutoPilotSelected() &&
|
|
||||||
this.selectedSharedAutoPilotTier())
|
|
||||||
) {
|
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
|
||||||
? {
|
|
||||||
[Constants.HttpHeaders.autoPilotThroughput]: { maxThroughput: this.sharedAutoPilotThroughput() * 1 }
|
|
||||||
}
|
|
||||||
: { [Constants.HttpHeaders.autoPilotTier]: this.selectedSharedAutoPilotTier().toString() };
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
(!this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected() && this.autoPilotThroughput()) ||
|
|
||||||
(this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected() && this.selectedAutoPilotTier())
|
|
||||||
) {
|
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
|
||||||
? {
|
|
||||||
[Constants.HttpHeaders.autoPilotThroughput]: { maxThroughput: this.autoPilotThroughput() * 1 }
|
|
||||||
}
|
|
||||||
: { [Constants.HttpHeaders.autoPilotTier]: this.selectedAutoPilotTier().toString() };
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _calculateNumberOfPartitions(): number {
|
private _calculateNumberOfPartitions(): number {
|
||||||
// Note: this will not validate properly on accounts that have been set up for custom partitioning,
|
// Note: this will not validate properly on accounts that have been set up for custom partitioning,
|
||||||
|
@ -852,6 +852,10 @@ export interface SqlContainerResource {
|
|||||||
|
|
||||||
/* The conflict resolution policy for the container. */
|
/* The conflict resolution policy for the container. */
|
||||||
conflictResolutionPolicy?: ConflictResolutionPolicy;
|
conflictResolutionPolicy?: ConflictResolutionPolicy;
|
||||||
|
|
||||||
|
//TODO: this property is manually added. It should be auto-generated instead. Need to be fixed in the API spec.
|
||||||
|
/* Analytical storage time to live */
|
||||||
|
analyticalStorageTtl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cosmos DB indexing policy */
|
/* Cosmos DB indexing policy */
|
||||||
|
Loading…
Reference in New Issue
Block a user