Initial Move from Azure DevOps to GitHub

This commit is contained in:
Steve Faulkner
2020-05-25 21:30:55 -05:00
commit 36581fb6d9
986 changed files with 195242 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,645 @@
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 { CosmosClient } from "../Common/CosmosClient";
import { HttpStatusCodes } from "../Common/Constants";
import { MessageHandler } from "../Common/MessageHandler";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
import { SubscriptionType } from "../Contracts/ViewModels";
export class CreateSqlCollectionUtilities {
public static createSqlCollection(
armEndpoint: string,
databaseId: string,
analyticalStorageTtl: number,
collectionId: string,
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,
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;
}
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) {
MessageHandler.sendMessage({ type: MessageTypes.ForbiddenError });
}
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,
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,
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: {}
}
};
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) {
MessageHandler.sendMessage({ type: MessageTypes.ForbiddenError });
}
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) {
MessageHandler.sendMessage({ type: MessageTypes.ForbiddenError });
return;
}
throw new Error(`Error creating table`);
}
}
public static getDefaultStorage(flight: string, subscriptionType: SubscriptionType): string {
const flightDefaults: string = Utilities._getDefaults(flight, subscriptionType).storage;
return flightDefaults;
}
public static getDefaultThroughput(flight: string, subscriptionType: SubscriptionType): ThroughputDefaults {
const flightDefaults: ThroughputDefaults = Utilities._getDefaults(flight, subscriptionType).throughput;
return flightDefaults;
}
public static getMaxRUForStorageOption(
subscriptionType = SharedConstants.CollectionCreation.DefaultSubscriptionType,
flight = SharedConstants.CollectionCreation.DefaultAddCollectionDefaultFlight,
storageOption: string
): number {
if (storageOption === SharedConstants.CollectionCreation.storage10Gb) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs10K;
}
const defaults = Utilities._getDefaults(flight, subscriptionType);
return defaults.throughput.unlimitedmax;
}
public static getMinRUForStorageOption(
subscriptionType = SharedConstants.CollectionCreation.DefaultSubscriptionType,
flight = SharedConstants.CollectionCreation.DefaultAddCollectionDefaultFlight,
storageOption: string
): number {
if (storageOption === SharedConstants.CollectionCreation.storage10Gb) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs400;
}
const collectionDefaults = Utilities._getCollectionDefaults(subscriptionType, flight);
return collectionDefaults.throughput.unlimitedmin;
}
public static _getDefaults(flight: string, subscriptionType: SubscriptionType): CollectionDefaults {
return Utilities._getCollectionDefaults(subscriptionType, flight);
}
private static _getCollectionDefaults(
subscriptionType = SharedConstants.CollectionCreation.DefaultSubscriptionType,
flight = SharedConstants.CollectionCreation.DefaultAddCollectionDefaultFlight
): CollectionDefaults {
if (!(flight in Utilities._defaultsMap)) {
flight = SharedConstants.CollectionCreation.DefaultAddCollectionDefaultFlight;
}
return Utilities._defaultsMap[flight][SubscriptionType[subscriptionType]];
}
private static _exceedsThreshold(container: ViewModels.Explorer): boolean {
const unlimitedThreshold: number = 5;
const databases = (container && container.databases && container.databases()) || [];
return _.any(
databases,
database =>
database && database.collections && database.collections() && database.collections().length > unlimitedThreshold
);
}
private static _defaultsMap: { [flight: string]: { [subscriptionType: string]: CollectionDefaults } } = {
"0": {
Benefits: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Free: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
EA: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Internal: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
PAYG: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
}
},
"1": {
Benefits: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Free: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
EA: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Internal: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
PAYG: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
}
},
"2": {
Benefits: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Free: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
EA: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Internal: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
PAYG: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
}
},
"3": {
Benefits: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Free: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
EA: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Internal: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
PAYG: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
}
},
"20190618.1": {
Benefits: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Free: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
EA: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: container =>
Utilities._exceedsThreshold(container)
? SharedConstants.CollectionCreation.DefaultCollectionRUs1000
: SharedConstants.CollectionCreation.DefaultCollectionRUs10K,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs20000
}
},
Internal: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
PAYG: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
}
},
"20190618.2": {
Benefits: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
Free: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
EA: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs1000,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs5000
}
},
Internal: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs100K,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
},
PAYG: {
storage: SharedConstants.CollectionCreation.storage100Gb,
throughput: {
fixed: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimited: () => SharedConstants.CollectionCreation.DefaultCollectionRUs400,
unlimitedmax: SharedConstants.CollectionCreation.DefaultCollectionRUs1Million,
unlimitedmin: SharedConstants.CollectionCreation.DefaultCollectionRUs400,
shared: SharedConstants.CollectionCreation.DefaultCollectionRUs400
}
}
}
};
private static _getAzureTableUri(params: DataModels.CreateDatabaseAndCollectionRequest): string {
return `subscriptions/${CosmosClient.subscriptionId()}/resourceGroups/${CosmosClient.resourceGroup()}/providers/Microsoft.DocumentDB/databaseAccounts/${
CosmosClient.databaseAccount().name
}/tables/${params.collectionId}`;
}
}
export interface CollectionDefaults {
storage: string;
throughput: ThroughputDefaults;
}
export interface ThroughputDefaults {
fixed: number;
unlimited: (explorer: ViewModels.Explorer) => number;
unlimitedmax: number;
unlimitedmin: number;
shared: number;
}

View File

@@ -0,0 +1,153 @@
import { AddDbUtilities } from "./AddDatabaseUtility";
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
jest.mock("../ResourceProvider/ResourceProviderClient.ts");
describe("Add Database Utitlity", () => {
const armEndpoint = "https://management.azure.com";
const properties = {
pk: "state",
coll: "abc-collection",
cd: true,
db: "a1-db",
offerThroughput: 50000,
st: true,
sid: "a1",
rg: "b1",
dba: "main"
};
describe("getRpClient", () => {
it("should return an instance of ResourceProviderClient", () => {
expect(AddDbUtilities.getRpClient()).not.toBeFalsy();
expect(AddDbUtilities.getRpClient()).toBeInstanceOf(ResourceProviderClient);
});
});
describe("getGremlinDatabaseUri", () => {
it("should return a uri in the correct format", () => {
expect(AddDbUtilities.getGremlinDatabaseUri(properties)).toEqual(
"subscriptions/a1/resourceGroups/b1/providers/Microsoft.DocumentDB/databaseAccounts/main/gremlinDatabases/a1-db"
);
});
});
describe("createGremlinDatabase", () => {
it("should utilize resource provider client", () => {
const resourceProviderClientSpy = spyOn<any>(AddDbUtilities, "getRpClient");
AddDbUtilities.createGremlinDatabase(armEndpoint, properties, undefined);
expect(resourceProviderClientSpy).toHaveBeenCalled();
});
it("should invoke getGremlinDatabaseUri", () => {
const getGremlinDatabaseUriSpy = spyOn<any>(AddDbUtilities, "getGremlinDatabaseUri");
AddDbUtilities.createGremlinDatabase(armEndpoint, properties, undefined);
expect(getGremlinDatabaseUriSpy).toHaveBeenCalled();
});
it("should invoke a put call via resource provider client to create a database and set throughput if shared throughtput is true", () => {
const resourceProviderClientPutAsyncSpy = jest.spyOn(ResourceProviderClient.prototype, "putAsync");
AddDbUtilities.createGremlinDatabase(armEndpoint, properties, undefined);
expect(
resourceProviderClientPutAsyncSpy
).toHaveBeenCalledWith(
"subscriptions/a1/resourceGroups/b1/providers/Microsoft.DocumentDB/databaseAccounts/main/gremlinDatabases/a1-db",
"2020-03-01",
{ properties: { options: { throughput: "50000" }, resource: { id: "a1-db" } } }
);
});
it("should invoke a put call via resource provider client to create a database and set autopilot if shared throughtput is true and autopilot settings are passed", () => {
const resourceProviderClientPutAsyncSpy = jest.spyOn(ResourceProviderClient.prototype, "putAsync");
AddDbUtilities.createGremlinDatabase(armEndpoint, properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
expect(
resourceProviderClientPutAsyncSpy
).toHaveBeenCalledWith(
"subscriptions/a1/resourceGroups/b1/providers/Microsoft.DocumentDB/databaseAccounts/main/gremlinDatabases/a1-db",
"2020-03-01",
{ properties: { options: { "x-ms-cosmos-offer-autopilot-tier": "1" }, resource: { id: "a1-db" } } }
);
});
it("should invoke a put call via resource provider client to create a database and not set throughput if shared throughtput is false", () => {
const properties = {
pk: "state",
coll: "abc-collection",
cd: true,
db: "a2-db",
st: false,
sid: "a2",
rg: "c1",
dba: "main"
};
const resourceProviderClientPutAsyncSpy = jest.spyOn(ResourceProviderClient.prototype, "putAsync");
AddDbUtilities.createGremlinDatabase(armEndpoint, properties, undefined);
expect(
resourceProviderClientPutAsyncSpy
).toHaveBeenCalledWith(
"subscriptions/a2/resourceGroups/c1/providers/Microsoft.DocumentDB/databaseAccounts/main/gremlinDatabases/a2-db",
"2020-03-01",
{ properties: { options: {}, resource: { id: "a2-db" } } }
);
});
});
describe("createSqlDatabase", () => {
it("should utilize resource provider client", () => {
const resourceProviderClientSpy = spyOn<any>(AddDbUtilities, "getRpClient");
AddDbUtilities.createSqlDatabase(armEndpoint, properties, undefined);
expect(resourceProviderClientSpy).toHaveBeenCalled();
});
it("should invoke getSqlDatabaseUri", () => {
const getSqlDatabaseUriSpy = spyOn<any>(AddDbUtilities, "getSqlDatabaseUri");
AddDbUtilities.createSqlDatabase(armEndpoint, properties, undefined);
expect(getSqlDatabaseUriSpy).toHaveBeenCalled();
});
it("should invoke a put call via resource provider client to create a database and set throughput if shared throughtput is true", () => {
const resourceProviderClientPutAsyncSpy = jest.spyOn(ResourceProviderClient.prototype, "putAsync");
AddDbUtilities.createSqlDatabase(armEndpoint, properties, undefined);
expect(
resourceProviderClientPutAsyncSpy
).toHaveBeenCalledWith(
"subscriptions/a1/resourceGroups/b1/providers/Microsoft.DocumentDB/databaseAccounts/main/sqlDatabases/a1-db",
"2020-03-01",
{ properties: { options: { throughput: "50000" }, resource: { id: "a1-db" } } }
);
});
it("should invoke a put call via resource provider client to create a database and set autopilot if shared throughtput is true and autopilot settings are passed", () => {
const resourceProviderClientPutAsyncSpy = jest.spyOn(ResourceProviderClient.prototype, "putAsync");
AddDbUtilities.createSqlDatabase(armEndpoint, properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
expect(
resourceProviderClientPutAsyncSpy
).toHaveBeenCalledWith(
"subscriptions/a1/resourceGroups/b1/providers/Microsoft.DocumentDB/databaseAccounts/main/sqlDatabases/a1-db",
"2020-03-01",
{ properties: { options: { "x-ms-cosmos-offer-autopilot-tier": "1" }, resource: { id: "a1-db" } } }
);
});
it("should invoke a put call via resource provider client to create a database and not set throughput if shared throughtput is false", () => {
const properties = {
pk: "state",
coll: "abc-collection",
cd: true,
db: "a2-db",
st: false,
sid: "a2",
rg: "c1",
dba: "main"
};
const resourceProviderClientPutAsyncSpy = jest.spyOn(ResourceProviderClient.prototype, "putAsync");
AddDbUtilities.createSqlDatabase(armEndpoint, properties, undefined);
expect(
resourceProviderClientPutAsyncSpy
).toHaveBeenCalledWith(
"subscriptions/a2/resourceGroups/c1/providers/Microsoft.DocumentDB/databaseAccounts/main/sqlDatabases/a2-db",
"2020-03-01",
{ properties: { options: {}, resource: { id: "a2-db" } } }
);
});
});
});

View File

@@ -0,0 +1,192 @@
import * as DataExplorerConstants from "../Common/Constants";
import * as DataModels from "../Contracts/DataModels";
import { config } from "../Config";
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import { CosmosClient } from "../Common/CosmosClient";
import { HttpStatusCodes } from "../Common/Constants";
import { MessageHandler } from "../Common/MessageHandler";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
export class AddDbUtilities {
// todo - remove any
public static async createMongoDatabaseWithARM(
armEndpoint: string,
params: DataModels.RpParameters,
rpOptions: DataModels.RpOptions
): Promise<any> {
const rpPayloadToCreateDatabase: DataModels.MongoCreationRequest = {
properties: {
resource: {
id: params.db
},
options: {}
}
};
if (params.st) {
if (rpOptions) {
rpPayloadToCreateDatabase.properties.options = rpOptions;
} else {
rpPayloadToCreateDatabase.properties.options["throughput"] =
params.offerThroughput && params.offerThroughput.toString();
}
}
try {
await AddDbUtilities.getRpClient<DataModels.CreateDatabaseWithRpResponse>(armEndpoint).putAsync(
AddDbUtilities._getMongoDatabaseUri(params),
DataExplorerConstants.ArmApiVersions.publicVersion,
rpPayloadToCreateDatabase
);
} catch (reason) {
AddDbUtilities._handleCreationError(reason, params);
}
}
// todo - remove any
public static async createCassandraKeyspace(
armEndpoint: string,
params: DataModels.RpParameters,
rpOptions: DataModels.RpOptions
): Promise<any> {
const rpPayloadToCreateKeyspace: DataModels.CreationRequest = {
properties: {
resource: {
id: params.db
},
options: {}
}
};
if (params.st) {
if (rpOptions) {
rpPayloadToCreateKeyspace.properties.options = rpOptions;
} else {
rpPayloadToCreateKeyspace.properties.options["throughput"] =
params.offerThroughput && params.offerThroughput.toString();
}
}
try {
await AddDbUtilities.getRpClient<DataModels.CreateDatabaseWithRpResponse>(armEndpoint).putAsync(
AddDbUtilities._getCassandraKeyspaceUri(params),
DataExplorerConstants.ArmApiVersions.publicVersion,
rpPayloadToCreateKeyspace
);
} catch (reason) {
AddDbUtilities._handleCreationError(reason, params, "keyspace");
}
}
public static async createSqlDatabase(
armEndpoint: string,
params: DataModels.RpParameters,
rpOptions: DataModels.RpOptions
): Promise<any> {
const rpPayloadToCreateSqlDatabase: DataModels.CreationRequest = {
properties: {
resource: {
id: params.db
},
options: {}
}
};
if (params.st) {
if (rpOptions) {
rpPayloadToCreateSqlDatabase.properties.options = rpOptions;
} else {
rpPayloadToCreateSqlDatabase.properties.options["throughput"] =
params.offerThroughput && params.offerThroughput.toString();
}
}
try {
await AddDbUtilities.getRpClient<DataModels.CreateDatabaseWithRpResponse>(armEndpoint).putAsync(
AddDbUtilities.getSqlDatabaseUri(params),
DataExplorerConstants.ArmApiVersions.publicVersion,
rpPayloadToCreateSqlDatabase
);
} catch (reason) {
AddDbUtilities._handleCreationError(reason, params, "database");
}
}
public static getRpClient<T>(armEndpoint?: string): ResourceProviderClient<T> {
return new ResourceProviderClient<T>(armEndpoint || config.ARM_ENDPOINT);
}
public static async createGremlinDatabase(
armEndpoint: string,
params: DataModels.RpParameters,
autoPilotSettings: DataModels.RpOptions
): Promise<DataModels.CreateDatabaseWithRpResponse> {
const rpPayloadToCreateDatabase: DataModels.CreationRequest = {
properties: {
resource: {
id: params.db
},
options: {}
}
};
const uri = AddDbUtilities.getGremlinDatabaseUri(params);
if (params.st) {
if (autoPilotSettings) {
rpPayloadToCreateDatabase.properties.options = autoPilotSettings;
} else {
rpPayloadToCreateDatabase.properties.options["throughput"] =
params.offerThroughput && params.offerThroughput.toString();
}
}
return new Promise<DataModels.CreateDatabaseWithRpResponse>((resolve, reject) => {
AddDbUtilities.getRpClient<DataModels.CreateDatabaseWithRpResponse>(armEndpoint)
.putAsync(uri, DataExplorerConstants.ArmApiVersions.publicVersion, rpPayloadToCreateDatabase)
.then(
() => {
resolve();
},
reason => {
AddDbUtilities._handleCreationError(reason, params);
reject();
}
);
});
}
private static _handleCreationError(reason: any, params: DataModels.RpParameters, dbType: string = "database") {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error creating ${dbType}: ${JSON.stringify(reason)}, Payload: ${params}`
);
if (reason.status === HttpStatusCodes.Forbidden) {
MessageHandler.sendMessage({ type: MessageTypes.ForbiddenError });
return;
}
throw new Error(`Error creating ${dbType}`);
}
private static _getMongoDatabaseUri(params: DataModels.RpParameters): string {
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${
CosmosClient.databaseAccount().name
}/mongodbDatabases/${params.db}`;
}
private static _getCassandraKeyspaceUri(params: DataModels.RpParameters): string {
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${
CosmosClient.databaseAccount().name
}/cassandraKeyspaces/${params.db}`;
}
public static getGremlinDatabaseUri(params: DataModels.RpParameters): string {
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${params.dba}/gremlinDatabases/${params.db}`;
}
public static getSqlDatabaseUri(params: DataModels.RpParameters): string {
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${params.dba}/sqlDatabases/${params.db}`;
}
}

43
src/Shared/Ajax.ts Normal file
View File

@@ -0,0 +1,43 @@
import Q from "q";
import $ from "jquery";
export default class Ajax {
public static head<T>(url: string): Q.Promise<any> {
return Ajax._ajax(url, "HEAD");
}
public static post<T>(url: string, data?: any): Q.Promise<any> {
return Ajax._ajax(url, "POST", data);
}
public static put<T>(url: string, data?: any): Q.Promise<any> {
return Ajax._ajax(url, "PUT", data);
}
public static get<T>(url: string, data?: any): Q.Promise<any> {
return Ajax._ajax(url, "GET", data);
}
public static Delete<T>(url: string, data?: any): Q.Promise<any> {
return Ajax._ajax(url, "DELETE", data);
}
static _ajax<T>(url: string, method: string, data?: any): Q.Promise<any> {
return Q($.ajax(url, Ajax._getNetAjaxSettings(url, method, data)));
}
static _getNetAjaxSettings<T>(url: string, method: string, data?: any): JQueryAjaxSettings<T> {
var newSettings: JQueryAjaxSettings<T> = {
url: url,
type: method,
cache: false,
contentType: "application/json",
traditional: true
};
if (!!data) {
newSettings.data = typeof data === "string" ? data : JSON.stringify(data || {});
}
return newSettings;
}
}

348
src/Shared/Constants.ts Normal file
View File

@@ -0,0 +1,348 @@
import { SubscriptionType } from "../Contracts/ViewModels";
export const hoursInAMonth = 730;
export class AutoscalePricing {
public static MonthlyPricing = {
default: {
singleMaster: {
Currency: "USD",
CurrencySign: "$",
Standard: {
StartingPrice: 24,
PricePerRU: 0.09,
PricePerGB: 0.25
}
},
multiMaster: {
Currency: "USD",
CurrencySign: "$",
Standard: {
StartingPrice: 24,
PricePerRU: 0.12,
PricePerGB: 0.25
}
}
},
mooncake: {
singleMaster: {
Currency: "RMB",
CurrencySign: "¥",
Standard: {
StartingPrice: 152,
PricePerRU: 0.57,
PricePerGB: 2.576
}
},
multiMaster: {
Currency: "RMB",
CurrencySign: "¥",
Standard: {
StartingPrice: 152,
PricePerRU: 0.76,
PricePerGB: 2.576
}
}
}
};
public static HourlyPricing = {
default: {
singleMaster: {
Currency: "USD",
CurrencySign: "$",
Standard: {
StartingPrice: 24 / hoursInAMonth,
PricePerRU: 0.00012,
PricePerGB: 0.25 / hoursInAMonth
}
},
multiMaster: {
Currency: "USD",
CurrencySign: "$",
Standard: {
StartingPrice: 24 / hoursInAMonth,
PricePerRU: 0.00016,
PricePerGB: 0.25 / hoursInAMonth
}
}
},
mooncake: {
singleMaster: {
Currency: "RMB",
CurrencySign: "¥",
Standard: {
StartingPrice: AutoscalePricing.MonthlyPricing.mooncake.singleMaster.Standard.StartingPrice / hoursInAMonth, // per hour
PricePerRU: 0.000765,
PricePerGB: AutoscalePricing.MonthlyPricing.mooncake.singleMaster.Standard.PricePerGB / hoursInAMonth
}
},
multiMaster: {
Currency: "RMB",
CurrencySign: "¥",
Standard: {
StartingPrice: AutoscalePricing.MonthlyPricing.mooncake.multiMaster.Standard.StartingPrice / hoursInAMonth, // per hour
PricePerRU: 0.00102,
PricePerGB: AutoscalePricing.MonthlyPricing.mooncake.multiMaster.Standard.PricePerGB / hoursInAMonth
}
}
}
};
}
export class OfferPricing {
public static MonthlyPricing = {
default: {
Currency: "USD",
CurrencySign: "$",
S1Price: 25,
S2Price: 50,
S3Price: 100,
Standard: {
StartingPrice: 24,
PricePerRU: 0.06,
PricePerGB: 0.25
}
},
mooncake: {
Currency: "RMB",
CurrencySign: "¥",
S1Price: 110.3,
S2Price: 220.6,
S3Price: 441.2,
Standard: {
StartingPrice: 152,
PricePerRU: 0.3794,
PricePerGB: 2.576
}
}
};
public static HourlyPricing = {
default: {
Currency: "USD",
CurrencySign: "$",
S1Price: 0.0336,
S2Price: 0.0672,
S3Price: 0.1344,
Standard: {
StartingPrice: 24 / hoursInAMonth, // per hour
PricePerRU: 0.00008,
PricePerRUPM: (10 * 2) / 1000 / hoursInAMonth, // preview price: $2 per 1000 RU/m per month -> 100 RU/s
PricePerGB: 0.25 / hoursInAMonth
}
},
mooncake: {
Currency: "RMB",
CurrencySign: "¥",
S1Price: 0.15,
S2Price: 0.3,
S3Price: 0.6,
Standard: {
StartingPrice: OfferPricing.MonthlyPricing.mooncake.Standard.StartingPrice / hoursInAMonth, // per hour
PricePerRU: 0.00051,
PricePerRUPM: (10 * 20) / 1000 / hoursInAMonth, // preview price: 20rmb per 1000 RU/m per month -> 100 RU/s
PricePerGB: OfferPricing.MonthlyPricing.mooncake.Standard.PricePerGB / hoursInAMonth
}
}
};
}
export class GeneralResources {
public static loadingText: string = "Loading...";
}
export class CollectionCreation {
// TODO generate these values based on Product\Services\Documents\ImageStore\GatewayApplication\Settings.xml
public static readonly MinRUPerPartitionBelow7Partitions: number = 400;
public static readonly MinRU7PartitionsTo25Partitions: number = 2500;
public static readonly MinRUPerPartitionAbove25Partitions: number = 100;
public static readonly MaxRUPerPartition: number = 10000;
public static readonly MaxRUPMPerPartition: number = 5000;
public static readonly MinPartitionedCollectionRUs: number = 2500;
public static readonly NumberOfPartitionsInFixedCollection: number = 1;
public static readonly NumberOfPartitionsInUnlimitedCollection: number = 10;
public static storage10Gb: string = "10";
public static storage100Gb: string = "100";
public static readonly DefaultCollectionRUs1000: number = 1000;
public static readonly DefaultCollectionRUs10K: number = 10000;
public static readonly DefaultCollectionRUs400: number = 400;
public static readonly DefaultCollectionRUs2000: number = 2000;
public static readonly DefaultCollectionRUs2500: number = 2500;
public static readonly DefaultCollectionRUs5000: number = 5000;
public static readonly DefaultCollectionRUs15000: number = 15000;
public static readonly DefaultCollectionRUs20000: number = 20000;
public static readonly DefaultCollectionRUs25000: number = 25000;
public static readonly DefaultCollectionRUs100K: number = 100000;
public static readonly DefaultCollectionRUs1Million: number = 1000000;
public static readonly DefaultAddCollectionDefaultFlight: string = "0";
public static readonly DefaultSubscriptionType: SubscriptionType = SubscriptionType.Free;
public static readonly TablesAPIDefaultDatabase: string = "TablesDB";
}
export class IndexingPolicies {
public static SharedDatabaseDefault = {
indexingMode: "consistent",
automatic: true,
includedPaths: <any>[],
excludedPaths: [
{
path: "/*"
}
]
};
public static AllPropertiesIndexed = {
indexingMode: "consistent",
automatic: true,
includedPaths: [
{
path: "/*",
indexes: [
{
kind: "Range",
dataType: "Number",
precision: -1
},
{
kind: "Range",
dataType: "String",
precision: -1
}
]
}
],
excludedPaths: <any>[]
};
// todo - remove mongo indexing policy ticket # 616274
public static Mongo = {
indexingMode: "consistent",
automatic: true,
includedPaths: [
{
path: "/*",
indexes: [
{
kind: "Range",
dataType: "Number",
precision: -1
},
{
kind: "Range",
dataType: "String",
precision: -1
},
{
kind: "Spatial",
dataType: "Point"
},
{
kind: "Spatial",
dataType: "LineString"
},
{
kind: "Spatial",
dataType: "Polygon"
}
]
}
],
excludedPaths: <any>[]
};
}
export class SubscriptionUtilMappings {
// TODO: Expose this through a web API from the portal
public static SubscriptionTypeMap: { [key: string]: SubscriptionType } = {
"AAD_2015-09-01": SubscriptionType.Free,
"AzureDynamics_2014-09-01": SubscriptionType.Free,
"AzureInOpen_2014-09-01": SubscriptionType.EA,
"AzurePass_2014-09-01": SubscriptionType.Free,
"BackupStorage_2014-09-01": SubscriptionType.PAYG,
"BizSpark_2014-09-01": SubscriptionType.Benefits,
"BizSparkPlus_2014-09-01": SubscriptionType.Benefits,
"CSP_2015-05-01": SubscriptionType.EA,
"Default_2014-09-01": SubscriptionType.PAYG,
"DevEssentials_2016-01-01": SubscriptionType.Benefits,
"DreamSpark_2015-02-01": SubscriptionType.Benefits,
"EnterpriseAgreement_2014-09-01": SubscriptionType.EA,
"FreeTrial_2014-09-01": SubscriptionType.Free,
"Internal_2014-09-01": SubscriptionType.Internal,
"LegacyMonetaryCommitment_2014-09-01": SubscriptionType.EA,
"LightweightTrial_2016-09-01": SubscriptionType.Free,
"MonetaryCommitment_2015-05-01": SubscriptionType.EA,
"MPN_2014-09-01": SubscriptionType.Benefits,
"MSDN_2014-09-01": SubscriptionType.Benefits,
"MSDNDevTest_2014-09-01": SubscriptionType.Benefits,
"PayAsYouGo_2014-09-01": SubscriptionType.PAYG,
"Sponsored_2016-01-01": SubscriptionType.Benefits
};
public static FreeTierSubscriptionIds: string[] = [
"b8f2ff04-0a81-4cf9-95ef-5828d16981d2",
"39b1fdff-e5b2-4f83-adb4-33cb3aabf5ea",
"41f6d14d-ece1-46e4-942c-02c00d67f7d6",
"11dc62e3-77dc-4ef5-a46b-480ec6caa8fe",
"199d0919-60bd-448e-b64d-8461a0fe9747",
"a57b6849-d443-44cf-a3b7-7dd07ead9401"
];
}
export class Offers {
public static offerTypeS1: string = "S1";
public static offerTypeS2: string = "S2";
public static offerTypeS3: string = "S3";
public static offerTypeStandard: string = "Standard";
}
export class OfferThoughput {
public static offerS1Throughput: number = 250;
public static offerS2Throughput: number = 1000;
public static offerS3Throughput: number = 2500;
}
export class OfferVersions {
public static offerV1: string = "V1";
public static offerV2: string = "V2";
}
export class InvalidOffers {
public static offerTypeInvalid: string = "Invalid";
public static offerTypeError: string = "Loading Error";
}
export class SpecTypes {
public static collection: string = "DocumentDbCollection";
}
export class CurrencyCodes {
public static usd: string = "USD";
public static rmb: string = "RMB";
}
export class ColorSchemes {
public static standard: string = "mediumBlue";
public static legacy: string = "yellowGreen";
}
export class FeatureIds {
public static storage: string = "storage";
public static sla: string = "sla";
public static partitioned: string = "partitioned";
public static singlePartitioned: string = "singlePartition";
public static legacySinglePartitioned: string = "legacySinglePartition";
}
export class FeatureIconNames {
public static storage: string = "SSD";
public static sla: string = "Monitoring";
public static productionReady: string = "ProductionReadyDb";
}
export class AutopilotDocumentation {
public static Url: string = "https://aka.ms/cosmos-autoscale-info";
}

View File

@@ -0,0 +1,133 @@
import * as Constants from "../Common/Constants";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import { DefaultExperienceUtility } from "./DefaultExperienceUtility";
describe("Default Experience Utility", () => {
describe("getDefaultExperienceFromApiKind()", () => {
function runScenario(apiKind: number, expectedExperience: string): void {
const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(apiKind);
expect(resolvedExperience).toEqual(expectedExperience);
}
describe("On SQL", () => {
it("should return SQL", () => runScenario(DataModels.ApiKind.SQL, Constants.DefaultAccountExperience.DocumentDB));
});
describe("On MongoDB", () => {
it("should return MongoDB", () =>
runScenario(DataModels.ApiKind.MongoDB, Constants.DefaultAccountExperience.MongoDB));
});
describe("On Table", () => {
it("should return Table", () => runScenario(DataModels.ApiKind.Table, Constants.DefaultAccountExperience.Table));
});
describe("On Cassandra", () => {
it("should return Cassandra", () =>
runScenario(DataModels.ApiKind.Cassandra, Constants.DefaultAccountExperience.Cassandra));
});
describe("On Graph", () => {
it("should return Graph", () => runScenario(DataModels.ApiKind.Graph, Constants.DefaultAccountExperience.Graph));
});
describe("On unknown", () => {
it("should return Default", () => runScenario(-1, Constants.DefaultAccountExperience.Default));
});
});
describe("getApiKindFromDefaultExperience()", () => {
function runScenario(defaultExperience: string, expectedApiKind: number): void {
const resolvedApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(defaultExperience);
expect(resolvedApiKind).toEqual(expectedApiKind);
}
describe("On SQL", () => {
it("should return SQL", () => runScenario(Constants.DefaultAccountExperience.DocumentDB, DataModels.ApiKind.SQL));
});
describe("On MongoDB", () => {
it("should return MongoDB", () =>
runScenario(Constants.DefaultAccountExperience.MongoDB, DataModels.ApiKind.MongoDB));
});
describe("On Table", () => {
it("should return Table", () => runScenario(Constants.DefaultAccountExperience.Table, DataModels.ApiKind.Table));
});
describe("On Cassandra", () => {
it("should return Cassandra", () =>
runScenario(Constants.DefaultAccountExperience.Cassandra, DataModels.ApiKind.Cassandra));
});
describe("On Graph", () => {
it("should return Graph", () => runScenario(Constants.DefaultAccountExperience.Graph, DataModels.ApiKind.Graph));
});
describe("On null", () => {
it("should return SQL", () => runScenario(null, DataModels.ApiKind.SQL));
});
});
describe("getDefaultExperienceFromDatabaseAccount()", () => {
function runScenario(databaseAccount: ViewModels.DatabaseAccount, expectedDefaultExperience: string): void {
const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromDatabaseAccount(databaseAccount);
expect(resolvedExperience).toEqual(expectedDefaultExperience);
}
const databaseAccountWithWrongTagsAndCapabilities: ViewModels.DatabaseAccount = {
id: "test",
kind: "GlobalDocumentDB",
name: "test",
location: "somewhere",
type: "DocumentDB",
tags: {
defaultExperience: "Gremlin (graph)"
},
properties: {
documentEndpoint: "",
cassandraEndpoint: "",
gremlinEndpoint: "",
tableEndpoint: "",
capabilities: [
{
name: Constants.CapabilityNames.EnableGremlin,
description: "something"
}
]
}
};
const databaseAccountWithApiKind: ViewModels.DatabaseAccount = {
id: "test",
kind: Constants.AccountKind.MongoDB,
name: "test",
location: "somewhere",
type: "DocumentDB",
tags: {},
properties: {
documentEndpoint: "",
cassandraEndpoint: "",
gremlinEndpoint: "",
tableEndpoint: "",
capabilities: [
{
name: Constants.CapabilityNames.EnableGremlin,
description: "something"
}
]
}
};
describe("Disregard tags", () => {
it("should return Graph", () =>
runScenario(databaseAccountWithWrongTagsAndCapabilities, Constants.DefaultAccountExperience.Graph));
});
describe("Respect Kind over capabilities", () => {
it("should return MongoDB", () =>
runScenario(databaseAccountWithApiKind, Constants.DefaultAccountExperience.MongoDB));
});
});
});

View File

@@ -0,0 +1,139 @@
import * as _ from "underscore";
import * as Constants from "../Common/Constants";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
export class DefaultExperienceUtility {
public static getDefaultExperienceFromDatabaseAccount(databaseAccount: ViewModels.DatabaseAccount): string {
if (!databaseAccount) {
return null;
}
const kind: string =
databaseAccount && databaseAccount.kind && databaseAccount.kind && databaseAccount.kind.toLowerCase();
const capabilities: ViewModels.Capability[] =
(databaseAccount.properties && databaseAccount.properties.capabilities) || [];
return DefaultExperienceUtility._getDefaultExperience(kind, capabilities);
}
public static getApiKindFromDefaultExperience(defaultExperience: string): DataModels.ApiKind {
if (!defaultExperience) {
return DataModels.ApiKind.SQL;
}
switch (defaultExperience) {
case Constants.DefaultAccountExperience.DocumentDB:
return DataModels.ApiKind.SQL;
case Constants.DefaultAccountExperience.MongoDB:
case Constants.DefaultAccountExperience.ApiForMongoDB:
return DataModels.ApiKind.MongoDB;
case Constants.DefaultAccountExperience.Table:
return DataModels.ApiKind.Table;
case Constants.DefaultAccountExperience.Cassandra:
return DataModels.ApiKind.Cassandra;
case Constants.DefaultAccountExperience.Graph:
return DataModels.ApiKind.Graph;
default:
return DataModels.ApiKind.SQL;
}
}
public static getDefaultExperienceFromApiKind(apiKind: DataModels.ApiKind): string {
if (apiKind == null) {
return Constants.DefaultAccountExperience.Default;
}
switch (apiKind) {
case DataModels.ApiKind.SQL:
return Constants.DefaultAccountExperience.DocumentDB;
case DataModels.ApiKind.MongoDB:
case DataModels.ApiKind.MongoDBCompute:
return Constants.DefaultAccountExperience.MongoDB;
case DataModels.ApiKind.Table:
return Constants.DefaultAccountExperience.Table;
case DataModels.ApiKind.Cassandra:
return Constants.DefaultAccountExperience.Cassandra;
case DataModels.ApiKind.Graph:
return Constants.DefaultAccountExperience.Graph;
default:
return Constants.DefaultAccountExperience.Default;
}
}
private static _getDefaultExperience(kind: string, capabilities: ViewModels.Capability[]): string {
const defaultDefaultExperience: string = Constants.DefaultAccountExperience.DocumentDB;
const defaultExperienceFromKind: string = DefaultExperienceUtility._getDefaultExperienceFromAccountKind(kind);
const defaultExperienceFromCapabilities: string = DefaultExperienceUtility._getDefaultExperienceFromAccountCapabilities(
capabilities
);
if (!!defaultExperienceFromKind) {
return defaultExperienceFromKind;
}
if (!!defaultExperienceFromCapabilities) {
return defaultExperienceFromCapabilities;
}
return defaultDefaultExperience;
}
private static _getDefaultExperienceFromAccountKind(kind: string): string {
if (!kind) {
return null;
}
if (kind.toLowerCase() === Constants.AccountKind.MongoDB.toLowerCase()) {
return Constants.DefaultAccountExperience.MongoDB;
}
if (kind.toLowerCase() === Constants.AccountKind.Parse.toLowerCase()) {
return Constants.DefaultAccountExperience.MongoDB;
}
return null;
}
private static _getDefaultExperienceFromAccountCapabilities(capabilities: ViewModels.Capability[]): string {
if (!capabilities) {
return null;
}
if (!Array.isArray(capabilities)) {
return null;
}
const enableTable: ViewModels.Capability = DefaultExperienceUtility._findCapability(
capabilities,
Constants.CapabilityNames.EnableTable
);
if (enableTable) {
return Constants.DefaultAccountExperience.Table;
}
const enableGremlin: ViewModels.Capability = DefaultExperienceUtility._findCapability(
capabilities,
Constants.CapabilityNames.EnableGremlin
);
if (enableGremlin) {
return Constants.DefaultAccountExperience.Graph;
}
const enableCassandra: ViewModels.Capability = DefaultExperienceUtility._findCapability(
capabilities,
Constants.CapabilityNames.EnableCassandra
);
if (enableCassandra) {
return Constants.DefaultAccountExperience.Cassandra;
}
return null;
}
private static _findCapability(capabilities: ViewModels.Capability[], capabilityName: string): ViewModels.Capability {
return _.find(capabilities, (capability: ViewModels.Capability) => {
return capability && capability.name && capability.name.toLowerCase() === capabilityName.toLowerCase();
});
}
}

View File

@@ -0,0 +1,22 @@
import * as Constants from "../Common/Constants";
import { LocalStorageUtility, StorageKey } from "./StorageUtility";
export class ExplorerSettings {
public static createDefaultSettings() {
LocalStorageUtility.setEntryNumber(StorageKey.ActualItemPerPage, Constants.Queries.itemsPerPage);
LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, Constants.Queries.itemsPerPage);
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "true");
LocalStorageUtility.setEntryNumber(
StorageKey.MaxDegreeOfParellism,
Constants.Queries.DefaultMaxDegreeOfParallelism
);
}
public static hasSettingsDefined(): boolean {
return (
LocalStorageUtility.hasItem(StorageKey.ActualItemPerPage) &&
LocalStorageUtility.hasItem(StorageKey.IsCrossPartitionQueryEnabled) &&
LocalStorageUtility.hasItem(StorageKey.MaxDegreeOfParellism)
);
}
}

View File

@@ -0,0 +1,47 @@
import * as Constants from "./Constants";
export function computeRUUsagePrice(serverId: string, rupmEnabled: boolean, requestUnits: number): string {
if (serverId === "mooncake") {
let ruCharge = requestUnits * Constants.OfferPricing.HourlyPricing.mooncake.Standard.PricePerRU,
rupmCharge = rupmEnabled ? requestUnits * Constants.OfferPricing.HourlyPricing.mooncake.Standard.PricePerRUPM : 0;
return (
calculateEstimateNumber(ruCharge + rupmCharge) + " " + Constants.OfferPricing.HourlyPricing.mooncake.Currency
);
}
let ruCharge = requestUnits * Constants.OfferPricing.HourlyPricing.default.Standard.PricePerRU,
rupmCharge = rupmEnabled ? requestUnits * Constants.OfferPricing.HourlyPricing.default.Standard.PricePerRUPM : 0;
return calculateEstimateNumber(ruCharge + rupmCharge) + " " + Constants.OfferPricing.HourlyPricing.default.Currency;
}
export function computeStorageUsagePrice(serverId: string, storageUsedRoundUpToGB: number): string {
if (serverId === "mooncake") {
let storageCharge = storageUsedRoundUpToGB * Constants.OfferPricing.HourlyPricing.mooncake.Standard.PricePerGB;
return calculateEstimateNumber(storageCharge) + " " + Constants.OfferPricing.HourlyPricing.mooncake.Currency;
}
let storageCharge = storageUsedRoundUpToGB * Constants.OfferPricing.HourlyPricing.default.Standard.PricePerGB;
return calculateEstimateNumber(storageCharge) + " " + Constants.OfferPricing.HourlyPricing.default.Currency;
}
export function computeDisplayUsageString(usageInKB: number): string {
let usageInMB = usageInKB / 1024,
usageInGB = usageInMB / 1024,
displayUsageString =
usageInGB > 0.1
? usageInGB.toFixed(2) + " GB"
: usageInMB > 0.1
? usageInMB.toFixed(2) + " MB"
: usageInKB.toFixed(2) + " KB";
return displayUsageString;
}
export function usageInGB(usageInKB: number): number {
let usageInMB = usageInKB / 1024,
usageInGB = usageInMB / 1024;
return Math.ceil(usageInGB);
}
function calculateEstimateNumber(n: number): string {
return n >= 1 ? n.toFixed(2) : n.toPrecision(2);
}

View File

@@ -0,0 +1,55 @@
import { StorageKey, LocalStorageUtility, SessionStorageUtility } from "./StorageUtility";
describe("Storage Utility", () => {
beforeAll(() => {
localStorage.clear();
});
afterEach(() => {
localStorage.clear();
});
it("should find a value that exist in local storage", () => {
localStorage.setItem(StorageKey[StorageKey.ActualItemPerPage], "123");
expect(LocalStorageUtility.hasItem(StorageKey.ActualItemPerPage)).toBe(true);
});
it("should not find a value that does not exist in local storage", () => {
expect(LocalStorageUtility.hasItem(StorageKey.ActualItemPerPage)).toBe(false);
});
it("should place the string key/value pair into local storage", () => {
LocalStorageUtility.setEntryString(StorageKey.ActualItemPerPage, "abc");
expect(localStorage.getItem(StorageKey[StorageKey.ActualItemPerPage])).toEqual("abc");
});
it("should place the number key/value pair into local storage as a string", () => {
LocalStorageUtility.setEntryNumber(StorageKey.ActualItemPerPage, 123);
expect(localStorage.getItem(StorageKey[StorageKey.ActualItemPerPage])).toEqual("123");
});
it("should retrieve the string value", () => {
localStorage.setItem(StorageKey[StorageKey.ActualItemPerPage], "123");
expect(LocalStorageUtility.getEntryString(StorageKey.ActualItemPerPage)).toEqual("123");
});
it("should retrieve the string value and convert to number", () => {
localStorage.setItem(StorageKey[StorageKey.ActualItemPerPage], "123");
const result = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage);
expect(Number(result)).toEqual(123);
});
it("should remove the entry from local storage if exists", () => {
localStorage.setItem(StorageKey[StorageKey.ActualItemPerPage], "123");
LocalStorageUtility.removeEntry(StorageKey.ActualItemPerPage);
expect(LocalStorageUtility.getEntryString(StorageKey.ActualItemPerPage)).toBeNull();
});
it("should remove the entry from session storage if exists", () => {
sessionStorage.setItem(StorageKey[StorageKey.ActualItemPerPage], "123");
SessionStorageUtility.removeEntry(StorageKey.ActualItemPerPage);
expect(LocalStorageUtility.getEntryString(StorageKey.ActualItemPerPage)).toBeNull();
});
});

View File

@@ -0,0 +1,76 @@
import { StringUtility } from "./StringUtility";
export class LocalStorageUtility {
public static hasItem(key: StorageKey): boolean {
return !!localStorage.getItem(StorageKey[key]);
}
public static getEntryString(key: StorageKey): string {
return localStorage.getItem(StorageKey[key]);
}
public static getEntryNumber(key: StorageKey): number {
return StringUtility.toNumber(localStorage.getItem(StorageKey[key]));
}
public static getEntryBoolean(key: StorageKey): boolean {
return StringUtility.toBoolean(localStorage.getItem(StorageKey[key]));
}
public static setEntryString(key: StorageKey, value: string): void {
localStorage.setItem(StorageKey[key], value);
}
public static removeEntry(key: StorageKey): void {
return localStorage.removeItem(StorageKey[key]);
}
public static setEntryNumber(key: StorageKey, value: number): void {
localStorage.setItem(StorageKey[key], value.toString());
}
public static setEntryBoolean(key: StorageKey, value: boolean): void {
localStorage.setItem(StorageKey[key], value.toString());
}
}
export class SessionStorageUtility {
public static hasItem(key: StorageKey): boolean {
return !!sessionStorage.getItem(StorageKey[key]);
}
public static getEntryString(key: StorageKey): string {
return sessionStorage.getItem(StorageKey[key]);
}
public static getEntryNumber(key: StorageKey): number {
return StringUtility.toNumber(localStorage.getItem(StorageKey[key]));
}
public static removeEntry(key: StorageKey): void {
return sessionStorage.removeItem(StorageKey[key]);
}
public static setEntryString(key: StorageKey, value: string): void {
sessionStorage.setItem(StorageKey[key], value);
}
public static setEntryNumber(key: StorageKey, value: number): void {
sessionStorage.setItem(StorageKey[key], value.toString());
}
}
export enum StorageKey {
ActualItemPerPage,
CustomItemPerPage,
DatabaseAccountId,
EncryptedKeyToken,
IsCrossPartitionQueryEnabled,
MaxDegreeOfParellism,
IsGraphAutoVizDisabled,
TenantId,
MostRecentActivity,
SetPartitionKeyUndefined,
NotebookMetadata,
NotebookName
}

View File

@@ -0,0 +1,15 @@
import { StringUtility } from "./StringUtility";
describe("String utility", () => {
it("Convert to integer from string", () => {
expect(StringUtility.toNumber("123")).toBe(123);
});
it("Convert to boolean from string (true)", () => {
expect(StringUtility.toBoolean("true")).toBe(true);
});
it("Convert to boolean from string (false)", () => {
expect(StringUtility.toBoolean("false")).toBe(false);
});
});

View File

@@ -0,0 +1,9 @@
export class StringUtility {
public static toNumber(num: string): number {
return Number(num);
}
public static toBoolean(valueStr: string): boolean {
return valueStr === "true";
}
}

View File

@@ -0,0 +1,155 @@
/**
* Defines constants related to logging telemetry. This file should be kept in sync with the one in the portal extension code as much as possible.
*
* TODO: Move this to ExplorerContracts (265329)
*/
export class General {
public static ExtensionName: string = "Microsoft_Azure_DocumentDB";
public static BladeNamePrefix: string = "Extension/Microsoft_Azure_DocumentDB/Blade/";
}
/**
* This is to be kept in sync with the one in portal. Please update the one in the portal if you add/remove any entry.
*/
export enum Action {
CollapseTreeNode,
CreateDatabaseAccount,
CreateAzureFunction,
CreateCollection,
CreateDocument,
CreateStoredProcedure,
CreateTrigger,
CreateUDF,
DeleteCollection,
DeleteDatabase,
DeleteDocument,
DownloadQuickstart,
ExpandTreeNode,
ExecuteQuery,
HasFeature,
GetVNETServices,
InitializeAccountLocationFromResourceGroup,
InitializeDataExplorer,
LoadDatabaseAccount,
LoadCollections,
LoadDatabases,
LoadMetrics,
LoadOffers,
LoadSingleCollectionWithOfferAndStatistics,
MongoShell,
OpenMetrics,
ContextualPane,
ScaleThroughput,
SelectItem,
SwitchQuickstartPlatform,
Tab,
UpdateDocument,
UpdateRegions,
UpdateSettings,
UpdateStoredProcedure,
UpdateTrigger,
UpdateUDF,
ViewWarning,
LoadBlade,
LoadResourceTree,
LoadMetricsTab,
AccountLevelThroughput,
CreateDatabase,
ResolveConflict,
DeleteConflict,
SaveQuery,
SetupSavedQueries,
LoadSavedQuery,
DeleteSavedQuery,
ConnectEncryptionToken,
SignInAad,
SignOutAad,
FetchTenants,
FetchSubscriptions,
FetchAccounts,
GetAccountKeys,
LoadingStatus,
AccountSwitch,
SubscriptionSwitch,
TenantSwitch,
DefaultTenantSwitch,
ResetNotebookWorkspace,
CreateNotebookWorkspace,
NotebookErrorNotification,
CreateSparkCluster,
UpdateSparkCluster,
DeleteSparkCluster,
LibraryManage,
ClusterLibraryManage,
ModifyOptionForThroughputWithSharedDatabase,
EnableAzureSynapseLink,
CreateNewNotebook,
OpenSampleNotebook,
ExecuteCell,
ExecuteAllCells,
NotebookEnabled,
NotebooksGitHubConnect,
NotebooksGitHubAuthorize,
NotebooksGitHubManualRepoAdd,
NotebooksGitHubManageRepo,
NotebooksGitHubCommit,
NotebooksGitHubDisconnect
}
export class ActionModifiers {
public static Start: string = "start";
public static Success: string = "success";
public static Failed: string = "failed";
public static Mark: string = "mark";
public static Open: string = "open";
public static IFrameReady: string = "iframeready";
public static Close: string = "close";
public static Submit: string = "submit";
public static IndexAll: string = "index all properties";
public static NoIndex: string = "no indexing";
public static Cancel: string = "cancel";
}
export class CosmosDBEndpointNames {
public static Gateway: string = "CosmosDBGateway";
public static ResourceProvider: string = "DocumentDBResourceProvider";
}
export enum SourceBlade {
AddCollection,
AzureFunction,
BrowseCollectionBlade,
CassandraAccountCreateBlade,
CollectionSetting,
DatabaseAccountCreateBlade,
DataExplorer,
DeleteCollection,
DeleteDatabase,
DocumentExplorer,
FirewallVNETBlade,
Metrics,
NonDocumentDBAccountCreateBlade,
OverviewBlade,
QueryExplorer,
Quickstart,
ReaderWarning,
ResourceMenu,
RpcProvider,
ScaleCollection,
ScriptExplorer,
Keys
}
export class BladeLoadRequirements {
public static collections: string = "Collections";
public static collectionsWithOffers: string = "CollectionsWithOffers";
public static databaseAccount: string = "DatabaseAccount";
public static keys: string = "Keys";
public static metrics: string = "Metrics";
public static notifications: string = "Notifications";
public static singleCollection: string = "SingleCollection";
public static keysBlade: string[] = [BladeLoadRequirements.databaseAccount, BladeLoadRequirements.keys];
public static metricsBlade: string[] = [BladeLoadRequirements.databaseAccount];
public static overview: string[] = [BladeLoadRequirements.databaseAccount, BladeLoadRequirements.notifications];
}

View File

@@ -0,0 +1,136 @@
import { Action, ActionModifiers } from "./TelemetryConstants";
import { MessageHandler } from "../../Common/MessageHandler";
import { MessageTypes } from "../../Contracts/ExplorerContracts";
/**
* Class that persists telemetry data to the portal tables.
*/
// TODO: Move to a separate Diagnostics folder
// TODO: Log telemetry for StorageExplorer case/other clients as well
export default class TelemetryProcessor {
public static trace(action: Action, actionModifier: string = ActionModifiers.Mark, data?: any): void {
MessageHandler.sendMessage({
type: MessageTypes.TelemetryInfo,
data: {
action: Action[action],
actionModifier: actionModifier,
data: JSON.stringify(data)
}
});
const appInsights: Microsoft.ApplicationInsights.IAppInsights = (<any>window).appInsights;
if (!appInsights) {
return;
}
appInsights.trackEvent(Action[action], data);
}
public static traceStart(action: Action, data?: any): number {
const timestamp: number = Date.now();
MessageHandler.sendMessage({
type: MessageTypes.TelemetryInfo,
data: {
action: Action[action],
actionModifier: ActionModifiers.Start,
timestamp: timestamp,
data: JSON.stringify(data)
}
});
const appInsights: Microsoft.ApplicationInsights.IAppInsights = (<any>window).appInsights;
if (appInsights) {
appInsights.startTrackEvent(Action[action]);
}
return timestamp;
}
public static traceSuccess(action: Action, data?: any, timestamp?: number): void {
MessageHandler.sendMessage({
type: MessageTypes.TelemetryInfo,
data: {
action: Action[action],
actionModifier: ActionModifiers.Success,
timestamp: timestamp || Date.now(),
data: JSON.stringify(data)
}
});
const appInsights: Microsoft.ApplicationInsights.IAppInsights = (<any>window).appInsights;
if (!appInsights) {
return;
}
appInsights.stopTrackEvent(Action[action], data);
}
public static traceFailure(action: Action, data?: any, timestamp?: number): void {
MessageHandler.sendMessage({
type: MessageTypes.TelemetryInfo,
data: {
action: Action[action],
actionModifier: ActionModifiers.Failed,
timestamp: timestamp || Date.now(),
data: JSON.stringify(data)
}
});
const appInsights: Microsoft.ApplicationInsights.IAppInsights = (<any>window).appInsights;
if (!appInsights) {
return;
}
appInsights.stopTrackEvent(Action[action], data);
}
public static traceCancel(action: Action, data?: any, timestamp?: number): void {
MessageHandler.sendMessage({
type: MessageTypes.TelemetryInfo,
data: {
action: Action[action],
actionModifier: ActionModifiers.Cancel,
timestamp: timestamp || Date.now(),
data: JSON.stringify(data)
}
});
const appInsights: Microsoft.ApplicationInsights.IAppInsights = (<any>window).appInsights;
if (!appInsights) {
return;
}
appInsights.stopTrackEvent(Action[action], data);
}
public static traceOpen(action: Action, data?: any, timestamp?: number): number {
MessageHandler.sendMessage({
type: MessageTypes.TelemetryInfo,
data: {
action: Action[action],
actionModifier: ActionModifiers.Open,
timestamp: timestamp || Date.now(),
data: JSON.stringify(data)
}
});
const appInsights: Microsoft.ApplicationInsights.IAppInsights = (<any>window).appInsights;
if (appInsights) {
appInsights.startTrackEvent(Action[action]);
}
return timestamp;
}
public static traceMark(action: Action, data?: any, timestamp?: number): number {
MessageHandler.sendMessage({
type: MessageTypes.TelemetryInfo,
data: {
action: Action[action],
actionModifier: ActionModifiers.Mark,
timestamp: timestamp || Date.now(),
data: JSON.stringify(data)
}
});
const appInsights: Microsoft.ApplicationInsights.IAppInsights = (<any>window).appInsights;
if (appInsights) {
appInsights.startTrackEvent(Action[action]);
}
return timestamp;
}
}

10
src/Shared/appInsights.ts Normal file
View File

@@ -0,0 +1,10 @@
import { ApplicationInsights } from "@microsoft/applicationinsights-web";
const appInsights = new ApplicationInsights({
config: {
instrumentationKey: "fa645d97-6237-4656-9559-0ee0cb55ee49"
}
});
appInsights.loadAppInsights();
appInsights.trackPageView(); // Manually call trackPageView to establish the current user/session/pageview
export { appInsights };