mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-23 19:01:28 +00:00
Compare commits
30 Commits
users/srna
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cc291a04f | ||
|
|
92c4440d38 | ||
|
|
1ccffab911 | ||
|
|
dc56f7e154 | ||
|
|
e62184a1f2 | ||
|
|
26c832437b | ||
|
|
832f8d560d | ||
|
|
d85c96d408 | ||
|
|
bad6a60d07 | ||
|
|
b690fe18e6 | ||
|
|
1bbe08378c | ||
|
|
9b021b29b9 | ||
|
|
562ac38ff1 | ||
|
|
949f9203b8 | ||
|
|
de7761ba4b | ||
|
|
40f4efab7c | ||
|
|
34c41e1557 | ||
|
|
03b19fc875 | ||
|
|
d6a4924710 | ||
|
|
5ccf26e403 | ||
|
|
ef7da10b6e | ||
|
|
dfd18152ca | ||
|
|
e22675bc40 | ||
|
|
c4257bf4a9 | ||
|
|
728eeefa17 | ||
|
|
83b13de685 | ||
|
|
c401f88aae | ||
|
|
af820c0fbf | ||
|
|
a2845a0102 | ||
|
|
ed9b443bf6 |
@@ -1522,6 +1522,10 @@ p {
|
|||||||
.tooltipVisible();
|
.tooltipVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.infoTooltip a {
|
||||||
|
color: @AccentHigh;
|
||||||
|
}
|
||||||
|
|
||||||
.nowrap {
|
.nowrap {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
@@ -1739,7 +1743,7 @@ input::-webkit-calendar-picker-indicator {
|
|||||||
padding-right: 34px;
|
padding-right: 34px;
|
||||||
color: @BaseDark;
|
color: @BaseDark;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: auto;
|
||||||
margin: (2 * @MediumSpace) 0px;
|
margin: (2 * @MediumSpace) 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2972,6 +2976,10 @@ settings-pane {
|
|||||||
.enableAnalyticalStorageRadio:nth-child(n+2) {
|
.enableAnalyticalStorageRadio:nth-child(n+2) {
|
||||||
margin-left: @LargeSpace;
|
margin-left: @LargeSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.enableAnalyticalStorageRadioLabel {
|
||||||
|
padding: 0px
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.addCollectionLabel {
|
.addCollectionLabel {
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
import * as _ from "underscore";
|
|
||||||
import * as Constants from "./Constants";
|
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
|
||||||
import * as HeadersUtility from "./HeadersUtility";
|
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
|
||||||
import Q from "q";
|
|
||||||
import {
|
import {
|
||||||
ConflictDefinition,
|
ConflictDefinition,
|
||||||
FeedOptions,
|
FeedOptions,
|
||||||
ItemDefinition,
|
ItemDefinition,
|
||||||
|
OfferDefinition,
|
||||||
QueryIterator,
|
QueryIterator,
|
||||||
Resource,
|
Resource
|
||||||
TriggerDefinition,
|
|
||||||
OfferDefinition
|
|
||||||
} from "@azure/cosmos";
|
} from "@azure/cosmos";
|
||||||
import { client } from "./CosmosClient";
|
|
||||||
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
|
||||||
import { sendCachedDataMessage } from "./MessageHandler";
|
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
|
||||||
import { OfferUtils } from "../Utils/OfferUtils";
|
|
||||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||||
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
import Q from "q";
|
||||||
import { Platform, configContext } from "../ConfigContext";
|
import { configContext, Platform } from "../ConfigContext";
|
||||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import ConflictId from "../Explorer/Tree/ConflictId";
|
import ConflictId from "../Explorer/Tree/ConflictId";
|
||||||
|
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||||
|
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
||||||
|
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||||
|
import { OfferUtils } from "../Utils/OfferUtils";
|
||||||
|
import * as Constants from "./Constants";
|
||||||
|
import { client } from "./CosmosClient";
|
||||||
|
import * as HeadersUtility from "./HeadersUtility";
|
||||||
|
import { sendCachedDataMessage } from "./MessageHandler";
|
||||||
|
|
||||||
export function getCommonQueryOptions(options: FeedOptions): any {
|
export function getCommonQueryOptions(options: FeedOptions): any {
|
||||||
const storedItemPerPageSetting: number = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage);
|
const storedItemPerPageSetting: number = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage);
|
||||||
@@ -55,85 +53,55 @@ export function queryDocuments(
|
|||||||
return Q(documentsIterator);
|
return Q(documentsIterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readStoredProcedures(
|
export function getPartitionKeyHeaderForConflict(conflictId: ConflictId): Object {
|
||||||
collection: ViewModels.Collection,
|
const partitionKeyDefinition: DataModels.PartitionKey = conflictId.partitionKey;
|
||||||
options?: any
|
const partitionKeyValue: any = conflictId.partitionKeyValue;
|
||||||
): Q.Promise<DataModels.StoredProcedure[]> {
|
|
||||||
|
return getPartitionKeyHeader(partitionKeyDefinition, partitionKeyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPartitionKeyHeader(partitionKeyDefinition: DataModels.PartitionKey, partitionKeyValue: any): Object {
|
||||||
|
if (!partitionKeyDefinition) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partitionKeyValue === undefined) {
|
||||||
|
return [{}];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [partitionKeyValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateOffer(
|
||||||
|
offer: DataModels.Offer,
|
||||||
|
newOffer: DataModels.Offer,
|
||||||
|
options?: RequestOptions
|
||||||
|
): Q.Promise<DataModels.Offer> {
|
||||||
return Q(
|
return Q(
|
||||||
client()
|
client()
|
||||||
.database(collection.databaseId)
|
.offer(offer.id)
|
||||||
.container(collection.id())
|
// TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660)
|
||||||
.scripts.storedProcedures.readAll(options)
|
.replace((newOffer as unknown) as OfferDefinition, options)
|
||||||
.fetchAll()
|
.then(response => {
|
||||||
.then(response => response.resources as DataModels.StoredProcedure[])
|
return Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => response.resource);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readStoredProcedure(
|
export function updateDocument(
|
||||||
collection: ViewModels.Collection,
|
collection: ViewModels.CollectionBase,
|
||||||
requestedResource: DataModels.Resource,
|
documentId: DocumentId,
|
||||||
options?: any
|
newDocument: any
|
||||||
): Q.Promise<DataModels.StoredProcedure> {
|
): Q.Promise<any> {
|
||||||
return Q(
|
const partitionKey = documentId.partitionKeyValue;
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.storedProcedure(requestedResource.id)
|
|
||||||
.read(options)
|
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export function readUserDefinedFunctions(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
options: any
|
|
||||||
): Q.Promise<DataModels.UserDefinedFunction[]> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.userDefinedFunctions.readAll(options)
|
|
||||||
.fetchAll()
|
|
||||||
.then(response => response.resources as DataModels.UserDefinedFunction[])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export function readUserDefinedFunction(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
requestedResource: DataModels.Resource,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.UserDefinedFunction> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.userDefinedFunction(requestedResource.id)
|
|
||||||
.read(options)
|
|
||||||
.then(response => response.resource as DataModels.UserDefinedFunction)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readTriggers(collection: ViewModels.Collection, options: any): Q.Promise<DataModels.Trigger[]> {
|
|
||||||
return Q(
|
return Q(
|
||||||
client()
|
client()
|
||||||
.database(collection.databaseId)
|
.database(collection.databaseId)
|
||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.triggers.readAll(options)
|
.item(documentId.id(), partitionKey)
|
||||||
.fetchAll()
|
.replace(newDocument)
|
||||||
.then(response => response.resources as DataModels.Trigger[])
|
.then(response => response.resource)
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readTrigger(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
requestedResource: DataModels.Resource,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.Trigger> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.trigger(requestedResource.id)
|
|
||||||
.read(options)
|
|
||||||
.then(response => response.resource as DataModels.Trigger)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +133,16 @@ export function executeStoredProcedure(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
|
||||||
|
return Q(
|
||||||
|
client()
|
||||||
|
.database(collection.databaseId)
|
||||||
|
.container(collection.id())
|
||||||
|
.items.create(newDocument)
|
||||||
|
.then(response => response.resource)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
|
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
|
||||||
const partitionKey = documentId.partitionKeyValue;
|
const partitionKey = documentId.partitionKeyValue;
|
||||||
|
|
||||||
@@ -178,155 +156,6 @@ export function readDocument(collection: ViewModels.CollectionBase, documentId:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPartitionKeyHeaderForConflict(conflictId: ConflictId): Object {
|
|
||||||
const partitionKeyDefinition: DataModels.PartitionKey = conflictId.partitionKey;
|
|
||||||
const partitionKeyValue: any = conflictId.partitionKeyValue;
|
|
||||||
|
|
||||||
return getPartitionKeyHeader(partitionKeyDefinition, partitionKeyValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPartitionKeyHeader(partitionKeyDefinition: DataModels.PartitionKey, partitionKeyValue: any): Object {
|
|
||||||
if (!partitionKeyDefinition) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (partitionKeyValue === undefined) {
|
|
||||||
return [{}];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [partitionKeyValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateDocument(
|
|
||||||
collection: ViewModels.CollectionBase,
|
|
||||||
documentId: DocumentId,
|
|
||||||
newDocument: any
|
|
||||||
): Q.Promise<any> {
|
|
||||||
const partitionKey = documentId.partitionKeyValue;
|
|
||||||
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.item(documentId.id(), partitionKey)
|
|
||||||
.replace(newDocument)
|
|
||||||
.then(response => response.resource)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateOffer(
|
|
||||||
offer: DataModels.Offer,
|
|
||||||
newOffer: DataModels.Offer,
|
|
||||||
options?: RequestOptions
|
|
||||||
): Q.Promise<DataModels.Offer> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.offer(offer.id)
|
|
||||||
// TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660)
|
|
||||||
.replace((newOffer as unknown) as OfferDefinition, options)
|
|
||||||
.then(response => {
|
|
||||||
return Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => response.resource);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateStoredProcedure(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
storedProcedure: DataModels.StoredProcedure,
|
|
||||||
options: any
|
|
||||||
): Q.Promise<DataModels.StoredProcedure> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.storedProcedure(storedProcedure.id)
|
|
||||||
.replace(storedProcedure, options)
|
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateUserDefinedFunction(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
userDefinedFunction: DataModels.UserDefinedFunction,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.UserDefinedFunction> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.userDefinedFunction(userDefinedFunction.id)
|
|
||||||
.replace(userDefinedFunction, options)
|
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateTrigger(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
trigger: DataModels.Trigger,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.Trigger> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.trigger(trigger.id)
|
|
||||||
.replace(trigger as TriggerDefinition, options)
|
|
||||||
.then(response => response.resource as DataModels.Trigger)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.items.create(newDocument)
|
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createStoredProcedure(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
newStoredProcedure: DataModels.StoredProcedure,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.StoredProcedure> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.storedProcedures.create(newStoredProcedure, options)
|
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createUserDefinedFunction(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
newUserDefinedFunction: DataModels.UserDefinedFunction,
|
|
||||||
options: any
|
|
||||||
): Q.Promise<DataModels.UserDefinedFunction> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.userDefinedFunctions.create(newUserDefinedFunction, options)
|
|
||||||
.then(response => response.resource as DataModels.UserDefinedFunction)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createTrigger(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
newTrigger: DataModels.Trigger,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.Trigger> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.triggers.create(newTrigger as TriggerDefinition, options)
|
|
||||||
.then(response => response.resource as DataModels.Trigger)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
|
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
|
||||||
const partitionKey = documentId.partitionKeyValue;
|
const partitionKey = documentId.partitionKeyValue;
|
||||||
|
|
||||||
@@ -355,48 +184,6 @@ export function deleteConflict(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteStoredProcedure(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
storedProcedure: DataModels.StoredProcedure,
|
|
||||||
options: any
|
|
||||||
): Q.Promise<any> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.storedProcedure(storedProcedure.id)
|
|
||||||
.delete()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteUserDefinedFunction(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
userDefinedFunction: DataModels.UserDefinedFunction,
|
|
||||||
options: any
|
|
||||||
): Q.Promise<any> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.userDefinedFunction(userDefinedFunction.id)
|
|
||||||
.delete()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteTrigger(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
trigger: DataModels.Trigger,
|
|
||||||
options: any
|
|
||||||
): Q.Promise<any> {
|
|
||||||
return Q(
|
|
||||||
client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.scripts.trigger(trigger.id)
|
|
||||||
.delete()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readCollectionQuotaInfo(
|
export function readCollectionQuotaInfo(
|
||||||
collection: ViewModels.Collection,
|
collection: ViewModels.Collection,
|
||||||
options: any
|
options: any
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import * as Constants from "./Constants";
|
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
|
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||||
|
import Q from "q";
|
||||||
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 Q from "q";
|
|
||||||
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
|
||||||
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import * as DataAccessUtilityBase from "./DataAccessUtilityBase";
|
|
||||||
import * as Logger from "./Logger";
|
|
||||||
import { MinimalQueryIterator, nextPage } from "./IteratorUtilities";
|
|
||||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
|
||||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
|
||||||
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
|
||||||
import ConflictId from "../Explorer/Tree/ConflictId";
|
import ConflictId from "../Explorer/Tree/ConflictId";
|
||||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||||
|
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
||||||
|
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
|
||||||
|
import * as Constants from "./Constants";
|
||||||
import { sendNotificationForError } from "./dataAccess/sendNotificationForError";
|
import { sendNotificationForError } from "./dataAccess/sendNotificationForError";
|
||||||
|
import * as DataAccessUtilityBase from "./DataAccessUtilityBase";
|
||||||
|
import { MinimalQueryIterator, nextPage } from "./IteratorUtilities";
|
||||||
|
import * as Logger from "./Logger";
|
||||||
|
|
||||||
// TODO: Log all promise resolutions and errors with verbosity levels
|
// TODO: Log all promise resolutions and errors with verbosity levels
|
||||||
export function queryDocuments(
|
export function queryDocuments(
|
||||||
@@ -42,121 +41,6 @@ export function getEntityName() {
|
|||||||
return "item";
|
return "item";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readStoredProcedures(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
options: any = {}
|
|
||||||
): Q.Promise<DataModels.StoredProcedure[]> {
|
|
||||||
var deferred = Q.defer<DataModels.StoredProcedure[]>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Querying stored procedures for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.readStoredProcedures(collection, options)
|
|
||||||
.then(
|
|
||||||
(storedProcedures: DataModels.StoredProcedure[]) => {
|
|
||||||
deferred.resolve(storedProcedures);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to query stored procedures for container ${collection.id()}: ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "ReadStoredProcedures", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readStoredProcedure(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
requestedResource: DataModels.Resource,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.StoredProcedure> {
|
|
||||||
return DataAccessUtilityBase.readStoredProcedure(collection, requestedResource, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readUserDefinedFunctions(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
options: any = {}
|
|
||||||
): Q.Promise<DataModels.UserDefinedFunction[]> {
|
|
||||||
var deferred = Q.defer<DataModels.UserDefinedFunction[]>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Querying user defined functions for collection ${collection.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.readUserDefinedFunctions(collection, options)
|
|
||||||
.then(
|
|
||||||
(userDefinedFunctions: DataModels.UserDefinedFunction[]) => {
|
|
||||||
deferred.resolve(userDefinedFunctions);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to query user defined functions for container ${collection.id()}: ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "ReadUDFs", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readUserDefinedFunction(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
requestedResource: DataModels.Resource,
|
|
||||||
options: any
|
|
||||||
): Q.Promise<DataModels.UserDefinedFunction> {
|
|
||||||
return DataAccessUtilityBase.readUserDefinedFunction(collection, requestedResource, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readTriggers(collection: ViewModels.Collection, options: any): Q.Promise<DataModels.Trigger[]> {
|
|
||||||
var deferred = Q.defer<DataModels.Trigger[]>();
|
|
||||||
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Querying triggers for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.readTriggers(collection, options)
|
|
||||||
.then(
|
|
||||||
(triggers: DataModels.Trigger[]) => {
|
|
||||||
deferred.resolve(triggers);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to query triggers for container ${collection.id()}: ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "ReadTriggers", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readTrigger(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
requestedResource: DataModels.Resource,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.Trigger> {
|
|
||||||
return DataAccessUtilityBase.readTrigger(collection, requestedResource, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function executeStoredProcedure(
|
export function executeStoredProcedure(
|
||||||
collection: ViewModels.Collection,
|
collection: ViewModels.Collection,
|
||||||
storedProcedure: StoredProcedure,
|
storedProcedure: StoredProcedure,
|
||||||
@@ -165,22 +49,17 @@ export function executeStoredProcedure(
|
|||||||
): Q.Promise<any> {
|
): Q.Promise<any> {
|
||||||
var deferred = Q.defer<any>();
|
var deferred = Q.defer<any>();
|
||||||
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const clearMessage = logConsoleProgress(`Executing stored procedure ${storedProcedure.id()}`);
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Executing stored procedure ${storedProcedure.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.executeStoredProcedure(collection, storedProcedure, partitionKeyValue, params)
|
DataAccessUtilityBase.executeStoredProcedure(collection, storedProcedure, partitionKeyValue, params)
|
||||||
.then(
|
.then(
|
||||||
(response: any) => {
|
(response: any) => {
|
||||||
deferred.resolve(response);
|
deferred.resolve(response);
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleInfo(
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
|
`Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}: ${JSON.stringify(
|
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}: ${JSON.stringify(
|
||||||
error
|
error
|
||||||
)}`
|
)}`
|
||||||
@@ -191,7 +70,7 @@ export function executeStoredProcedure(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -205,32 +84,23 @@ export function queryDocumentsPage(
|
|||||||
): Q.Promise<ViewModels.QueryResults> {
|
): Q.Promise<ViewModels.QueryResults> {
|
||||||
var deferred = Q.defer<ViewModels.QueryResults>();
|
var deferred = Q.defer<ViewModels.QueryResults>();
|
||||||
const entityName = getEntityName();
|
const entityName = getEntityName();
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Querying ${entityName} for container ${resourceName}`
|
|
||||||
);
|
|
||||||
Q(nextPage(documentsIterator, firstItemIndex))
|
Q(nextPage(documentsIterator, firstItemIndex))
|
||||||
.then(
|
.then(
|
||||||
(result: ViewModels.QueryResults) => {
|
(result: ViewModels.QueryResults) => {
|
||||||
const itemCount = (result.documents && result.documents.length) || 0;
|
const itemCount = (result.documents && result.documents.length) || 0;
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`
|
|
||||||
);
|
|
||||||
deferred.resolve(result);
|
deferred.resolve(result);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(`Failed to query ${entityName} for container ${resourceName}: ${JSON.stringify(error)}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to query ${entityName} for container ${resourceName}: ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "QueryDocumentsPage", error.code);
|
Logger.logError(JSON.stringify(error), "QueryDocumentsPage", error.code);
|
||||||
sendNotificationForError(error);
|
sendNotificationForError(error);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -239,27 +109,21 @@ export function queryDocumentsPage(
|
|||||||
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
|
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
|
||||||
var deferred = Q.defer<any>();
|
var deferred = Q.defer<any>();
|
||||||
const entityName = getEntityName();
|
const entityName = getEntityName();
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const clearMessage = logConsoleProgress(`Reading ${entityName} ${documentId.id()}`);
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Reading ${entityName} ${documentId.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.readDocument(collection, documentId)
|
DataAccessUtilityBase.readDocument(collection, documentId)
|
||||||
.then(
|
.then(
|
||||||
(document: any) => {
|
(document: any) => {
|
||||||
deferred.resolve(document);
|
deferred.resolve(document);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(`Failed to read ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to read ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "ReadDocument", error.code);
|
Logger.logError(JSON.stringify(error), "ReadDocument", error.code);
|
||||||
sendNotificationForError(error);
|
sendNotificationForError(error);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -272,31 +136,22 @@ export function updateDocument(
|
|||||||
): Q.Promise<any> {
|
): Q.Promise<any> {
|
||||||
var deferred = Q.defer<any>();
|
var deferred = Q.defer<any>();
|
||||||
const entityName = getEntityName();
|
const entityName = getEntityName();
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const clearMessage = logConsoleProgress(`Updating ${entityName} ${documentId.id()}`);
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Updating ${entityName} ${documentId.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.updateDocument(collection, documentId, newDocument)
|
DataAccessUtilityBase.updateDocument(collection, documentId, newDocument)
|
||||||
.then(
|
.then(
|
||||||
(updatedDocument: any) => {
|
(updatedDocument: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully updated ${entityName} ${documentId.id()}`
|
|
||||||
);
|
|
||||||
deferred.resolve(updatedDocument);
|
deferred.resolve(updatedDocument);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(`Failed to update ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to update ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "UpdateDocument", error.code);
|
Logger.logError(JSON.stringify(error), "UpdateDocument", error.code);
|
||||||
sendNotificationForError(error);
|
sendNotificationForError(error);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -308,24 +163,15 @@ export function updateOffer(
|
|||||||
options: RequestOptions
|
options: RequestOptions
|
||||||
): Q.Promise<DataModels.Offer> {
|
): Q.Promise<DataModels.Offer> {
|
||||||
var deferred = Q.defer<any>();
|
var deferred = Q.defer<any>();
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const clearMessage = logConsoleProgress(`Updating offer for resource ${offer.resource}`);
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Updating offer for resource ${offer.resource}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.updateOffer(offer, newOffer, options)
|
DataAccessUtilityBase.updateOffer(offer, newOffer, options)
|
||||||
.then(
|
.then(
|
||||||
(replacedOffer: DataModels.Offer) => {
|
(replacedOffer: DataModels.Offer) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleInfo(`Successfully updated offer for resource ${offer.resource}`);
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully updated offer for resource ${offer.resource}`
|
|
||||||
);
|
|
||||||
deferred.resolve(replacedOffer);
|
deferred.resolve(replacedOffer);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(`Error updating offer for resource ${offer.resource}: ${JSON.stringify(error)}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error updating offer for resource ${offer.resource}: ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(
|
Logger.logError(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
oldOffer: offer,
|
oldOffer: offer,
|
||||||
@@ -340,108 +186,7 @@ export function updateOffer(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateStoredProcedure(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
storedProcedure: DataModels.StoredProcedure,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.StoredProcedure> {
|
|
||||||
var deferred = Q.defer<any>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Updating stored procedure ${storedProcedure.id}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.updateStoredProcedure(collection, storedProcedure, options)
|
|
||||||
.then(
|
|
||||||
(updatedStoredProcedure: DataModels.StoredProcedure) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully updated stored procedure ${storedProcedure.id}`
|
|
||||||
);
|
|
||||||
deferred.resolve(updatedStoredProcedure);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while updating stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "UpdateStoredProcedure", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateUserDefinedFunction(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
userDefinedFunction: DataModels.UserDefinedFunction,
|
|
||||||
options: any = {}
|
|
||||||
): Q.Promise<DataModels.UserDefinedFunction> {
|
|
||||||
var deferred = Q.defer<any>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Updating user defined function ${userDefinedFunction.id}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.updateUserDefinedFunction(collection, userDefinedFunction, options)
|
|
||||||
.then(
|
|
||||||
(updatedUserDefinedFunction: DataModels.UserDefinedFunction) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully updated user defined function ${userDefinedFunction.id}`
|
|
||||||
);
|
|
||||||
deferred.resolve(updatedUserDefinedFunction);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while updating user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "UpdateUDF", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateTrigger(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
trigger: DataModels.Trigger
|
|
||||||
): Q.Promise<DataModels.Trigger> {
|
|
||||||
var deferred = Q.defer<any>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, `Updating trigger ${trigger.id}`);
|
|
||||||
DataAccessUtilityBase.updateTrigger(collection, trigger)
|
|
||||||
.then(
|
|
||||||
(updatedTrigger: DataModels.Trigger) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, `Updated trigger ${trigger.id}`);
|
|
||||||
deferred.resolve(updatedTrigger);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while updating trigger ${trigger.id}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "UpdateTrigger", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -450,22 +195,15 @@ export function updateTrigger(
|
|||||||
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
|
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
|
||||||
var deferred = Q.defer<any>();
|
var deferred = Q.defer<any>();
|
||||||
const entityName = getEntityName();
|
const entityName = getEntityName();
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const clearMessage = logConsoleProgress(`Creating new ${entityName} for container ${collection.id()}`);
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Creating new ${entityName} for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.createDocument(collection, newDocument)
|
DataAccessUtilityBase.createDocument(collection, newDocument)
|
||||||
.then(
|
.then(
|
||||||
(savedDocument: any) => {
|
(savedDocument: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleInfo(`Successfully created new ${entityName} for container ${collection.id()}`);
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully created new ${entityName} for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
deferred.resolve(savedDocument);
|
deferred.resolve(savedDocument);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while creating new ${entityName} for container ${collection.id()}:\n ${JSON.stringify(error)}`
|
`Error while creating new ${entityName} for container ${collection.id()}:\n ${JSON.stringify(error)}`
|
||||||
);
|
);
|
||||||
Logger.logError(JSON.stringify(error), "CreateDocument", error.code);
|
Logger.logError(JSON.stringify(error), "CreateDocument", error.code);
|
||||||
@@ -474,115 +212,7 @@ export function createDocument(collection: ViewModels.CollectionBase, newDocumen
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createStoredProcedure(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
newStoredProcedure: DataModels.StoredProcedure,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.StoredProcedure> {
|
|
||||||
var deferred = Q.defer<any>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Creating stored procedure for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.createStoredProcedure(collection, newStoredProcedure, options)
|
|
||||||
.then(
|
|
||||||
(createdStoredProcedure: DataModels.StoredProcedure) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully created stored procedure for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
deferred.resolve(createdStoredProcedure);
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while creating stored procedure for container ${collection.id()}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "CreateStoredProcedure", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createUserDefinedFunction(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
newUserDefinedFunction: DataModels.UserDefinedFunction,
|
|
||||||
options?: any
|
|
||||||
): Q.Promise<DataModels.UserDefinedFunction> {
|
|
||||||
var deferred = Q.defer<any>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Creating user defined function for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.createUserDefinedFunction(collection, newUserDefinedFunction, options)
|
|
||||||
.then(
|
|
||||||
(createdUserDefinedFunction: DataModels.UserDefinedFunction) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully created user defined function for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
deferred.resolve(createdUserDefinedFunction);
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while creating user defined function for container ${collection.id()}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "CreateUDF", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createTrigger(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
newTrigger: DataModels.Trigger,
|
|
||||||
options: any = {}
|
|
||||||
): Q.Promise<DataModels.Trigger> {
|
|
||||||
var deferred = Q.defer<any>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Creating trigger for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.createTrigger(collection, newTrigger, options)
|
|
||||||
.then(
|
|
||||||
(createdTrigger: DataModels.Trigger) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully created trigger for container ${collection.id()}`
|
|
||||||
);
|
|
||||||
deferred.resolve(createdTrigger);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while creating trigger for container ${collection.id()}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "CreateTrigger", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -591,31 +221,22 @@ export function createTrigger(
|
|||||||
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
|
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
|
||||||
var deferred = Q.defer<any>();
|
var deferred = Q.defer<any>();
|
||||||
const entityName = getEntityName();
|
const entityName = getEntityName();
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`);
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Deleting ${entityName} ${documentId.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.deleteDocument(collection, documentId)
|
DataAccessUtilityBase.deleteDocument(collection, documentId)
|
||||||
.then(
|
.then(
|
||||||
(response: any) => {
|
(response: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully deleted ${entityName} ${documentId.id()}`
|
|
||||||
);
|
|
||||||
deferred.resolve(response);
|
deferred.resolve(response);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(`Error while deleting ${entityName} ${documentId.id()}:\n ${JSON.stringify(error)}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while deleting ${entityName} ${documentId.id()}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "DeleteDocument", error.code);
|
Logger.logError(JSON.stringify(error), "DeleteDocument", error.code);
|
||||||
sendNotificationForError(error);
|
sendNotificationForError(error);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -628,134 +249,22 @@ export function deleteConflict(
|
|||||||
): Q.Promise<any> {
|
): Q.Promise<any> {
|
||||||
var deferred = Q.defer<any>();
|
var deferred = Q.defer<any>();
|
||||||
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const clearMessage = logConsoleProgress(`Deleting conflict ${conflictId.id()}`);
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Deleting conflict ${conflictId.id()}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.deleteConflict(collection, conflictId, options)
|
DataAccessUtilityBase.deleteConflict(collection, conflictId, options)
|
||||||
.then(
|
.then(
|
||||||
(response: any) => {
|
(response: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleInfo(`Successfully deleted conflict ${conflictId.id()}`);
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully deleted conflict ${conflictId.id()}`
|
|
||||||
);
|
|
||||||
deferred.resolve(response);
|
deferred.resolve(response);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(`Error while deleting conflict ${conflictId.id()}:\n ${JSON.stringify(error)}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while deleting conflict ${conflictId.id()}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "DeleteConflict", error.code);
|
Logger.logError(JSON.stringify(error), "DeleteConflict", error.code);
|
||||||
sendNotificationForError(error);
|
sendNotificationForError(error);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteStoredProcedure(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
storedProcedure: DataModels.StoredProcedure,
|
|
||||||
options: any = {}
|
|
||||||
): Q.Promise<DataModels.StoredProcedure> {
|
|
||||||
var deferred = Q.defer<any>();
|
|
||||||
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Deleting stored procedure ${storedProcedure.id}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.deleteStoredProcedure(collection, storedProcedure, options)
|
|
||||||
.then(
|
|
||||||
(response: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully deleted stored procedure ${storedProcedure.id}`
|
|
||||||
);
|
|
||||||
deferred.resolve(response);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while deleting stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "DeleteStoredProcedure", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteUserDefinedFunction(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
userDefinedFunction: DataModels.UserDefinedFunction,
|
|
||||||
options: any = {}
|
|
||||||
): Q.Promise<DataModels.UserDefinedFunction> {
|
|
||||||
var deferred = Q.defer<any>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Deleting user defined function ${userDefinedFunction.id}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.deleteUserDefinedFunction(collection, userDefinedFunction, options)
|
|
||||||
.then(
|
|
||||||
(response: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Info,
|
|
||||||
`Successfully deleted user defined function ${userDefinedFunction.id}`
|
|
||||||
);
|
|
||||||
deferred.resolve(response);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while deleting user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "DeleteUDF", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteTrigger(
|
|
||||||
collection: ViewModels.Collection,
|
|
||||||
trigger: DataModels.Trigger,
|
|
||||||
options: any = {}
|
|
||||||
): Q.Promise<DataModels.Trigger> {
|
|
||||||
var deferred = Q.defer<any>();
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, `Deleting trigger ${trigger.id}`);
|
|
||||||
DataAccessUtilityBase.deleteTrigger(collection, trigger, options)
|
|
||||||
.then(
|
|
||||||
(response: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, `Successfully deleted trigger ${trigger.id}`);
|
|
||||||
deferred.resolve(response);
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while deleting trigger ${trigger.id}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "DeleteTrigger", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -775,27 +284,21 @@ export function readCollectionQuotaInfo(
|
|||||||
): Q.Promise<DataModels.CollectionQuotaInfo> {
|
): Q.Promise<DataModels.CollectionQuotaInfo> {
|
||||||
var deferred = Q.defer<DataModels.CollectionQuotaInfo>();
|
var deferred = Q.defer<DataModels.CollectionQuotaInfo>();
|
||||||
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const clearMessage = logConsoleProgress(`Querying quota info for container ${collection.id}`);
|
||||||
ConsoleDataType.InProgress,
|
|
||||||
`Querying quota info for container ${collection.id}`
|
|
||||||
);
|
|
||||||
DataAccessUtilityBase.readCollectionQuotaInfo(collection, options)
|
DataAccessUtilityBase.readCollectionQuotaInfo(collection, options)
|
||||||
.then(
|
.then(
|
||||||
(quota: DataModels.CollectionQuotaInfo) => {
|
(quota: DataModels.CollectionQuotaInfo) => {
|
||||||
deferred.resolve(quota);
|
deferred.resolve(quota);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(`Error while querying quota info for container ${collection.id}:\n ${JSON.stringify(error)}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while querying quota info for container ${collection.id}:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "ReadCollectionQuotaInfo", error.code);
|
Logger.logError(JSON.stringify(error), "ReadCollectionQuotaInfo", error.code);
|
||||||
sendNotificationForError(error);
|
sendNotificationForError(error);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -804,24 +307,21 @@ export function readCollectionQuotaInfo(
|
|||||||
export function readOffers(options: any = {}): Q.Promise<DataModels.Offer[]> {
|
export function readOffers(options: any = {}): Q.Promise<DataModels.Offer[]> {
|
||||||
var deferred = Q.defer<DataModels.Offer[]>();
|
var deferred = Q.defer<DataModels.Offer[]>();
|
||||||
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Querying offers");
|
const clearMessage = logConsoleProgress("Querying offers");
|
||||||
DataAccessUtilityBase.readOffers(options)
|
DataAccessUtilityBase.readOffers(options)
|
||||||
.then(
|
.then(
|
||||||
(offers: DataModels.Offer[]) => {
|
(offers: DataModels.Offer[]) => {
|
||||||
deferred.resolve(offers);
|
deferred.resolve(offers);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(`Error while querying offers:\n ${JSON.stringify(error)}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while querying offers:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "ReadOffers", error.code);
|
Logger.logError(JSON.stringify(error), "ReadOffers", error.code);
|
||||||
sendNotificationForError(error);
|
sendNotificationForError(error);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
@@ -833,24 +333,21 @@ export function readOffer(
|
|||||||
): Q.Promise<DataModels.OfferWithHeaders> {
|
): Q.Promise<DataModels.OfferWithHeaders> {
|
||||||
var deferred = Q.defer<DataModels.OfferWithHeaders>();
|
var deferred = Q.defer<DataModels.OfferWithHeaders>();
|
||||||
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Querying offer");
|
const clearMessage = logConsoleProgress("Querying offer");
|
||||||
DataAccessUtilityBase.readOffer(requestedResource, options)
|
DataAccessUtilityBase.readOffer(requestedResource, options)
|
||||||
.then(
|
.then(
|
||||||
(offer: DataModels.OfferWithHeaders) => {
|
(offer: DataModels.OfferWithHeaders) => {
|
||||||
deferred.resolve(offer);
|
deferred.resolve(offer);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
logConsoleError(`Error while querying offer:\n ${JSON.stringify(error)}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while querying offer:\n ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(error), "ReadOffer", error.code);
|
Logger.logError(JSON.stringify(error), "ReadOffer", error.code);
|
||||||
sendNotificationForError(error);
|
sendNotificationForError(error);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
clearMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import Q from "q";
|
import Q from "q";
|
||||||
import * as MessageHandler from "./MessageHandler";
|
import * as MessageHandler from "./MessageHandler";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
|
||||||
|
|
||||||
describe("Message Handler", () => {
|
describe("Message Handler", () => {
|
||||||
it("should handle cached message", async () => {
|
it("should handle cached message", async () => {
|
||||||
@@ -26,4 +25,34 @@ describe("Message Handler", () => {
|
|||||||
MessageHandler.runGarbageCollector();
|
MessageHandler.runGarbageCollector();
|
||||||
expect(MessageHandler.RequestMap["123"]).toBeUndefined();
|
expect(MessageHandler.RequestMap["123"]).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getDataExplorerWindow", () => {
|
||||||
|
it("should return current window if current window has dataExplorerPlatform property", () => {
|
||||||
|
const currentWindow: Window = { dataExplorerPlatform: 0 } as any;
|
||||||
|
|
||||||
|
expect(MessageHandler.getDataExplorerWindow(currentWindow)).toEqual(currentWindow);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return current window's parent if current window's parent has dataExplorerPlatform property", () => {
|
||||||
|
const parentWindow: Window = { dataExplorerPlatform: 0 } as any;
|
||||||
|
const currentWindow: Window = { parent: parentWindow } as any;
|
||||||
|
|
||||||
|
expect(MessageHandler.getDataExplorerWindow(currentWindow)).toEqual(parentWindow);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return undefined if none of the windows in the hierarchy have dataExplorerPlatform property and window's parent is reference to itself", () => {
|
||||||
|
const parentWindow: Window = {} as any;
|
||||||
|
(parentWindow as any).parent = parentWindow; // If a window does not have a parent, its parent property is a reference to itself.
|
||||||
|
const currentWindow: Window = { parent: parentWindow } as any;
|
||||||
|
|
||||||
|
expect(MessageHandler.getDataExplorerWindow(currentWindow)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return undefined if none of the windows in the hierarchy have dataExplorerPlatform property and window's parent is not defined", () => {
|
||||||
|
const parentWindow: Window = {} as any;
|
||||||
|
const currentWindow: Window = { parent: parentWindow } as any;
|
||||||
|
|
||||||
|
expect(MessageHandler.getDataExplorerWindow(currentWindow)).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,15 +48,37 @@ export function sendCachedDataMessage<TResponseDataModel>(
|
|||||||
|
|
||||||
export function sendMessage(data: any): void {
|
export function sendMessage(data: any): void {
|
||||||
if (canSendMessage()) {
|
if (canSendMessage()) {
|
||||||
window.parent.postMessage(
|
const dataExplorerWindow = getDataExplorerWindow(window);
|
||||||
|
if (dataExplorerWindow) {
|
||||||
|
dataExplorerWindow.parent.postMessage(
|
||||||
{
|
{
|
||||||
signature: "pcIframe",
|
signature: "pcIframe",
|
||||||
data: data
|
data: data
|
||||||
},
|
},
|
||||||
window.document.referrer
|
dataExplorerWindow.document.referrer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only exported for unit tests
|
||||||
|
export const getDataExplorerWindow = (currentWindow: Window): Window | undefined => {
|
||||||
|
// Start with the current window and traverse up the parent hierarchy to find a window
|
||||||
|
// with `dataExplorerPlatform` property
|
||||||
|
let dataExplorerWindow: Window | undefined = currentWindow;
|
||||||
|
// TODO: Need to `any` here since the window imports Explorer which can't be in strict mode yet
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
while (dataExplorerWindow && (dataExplorerWindow as any).dataExplorerPlatform == undefined) {
|
||||||
|
// If a window does not have a parent, its parent property is a reference to itself.
|
||||||
|
if (dataExplorerWindow.parent == dataExplorerWindow) {
|
||||||
|
dataExplorerWindow = undefined;
|
||||||
|
} else {
|
||||||
|
dataExplorerWindow = dataExplorerWindow.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataExplorerWindow;
|
||||||
|
};
|
||||||
|
|
||||||
export function canSendMessage(): boolean {
|
export function canSendMessage(): boolean {
|
||||||
return window.parent !== window;
|
return window.parent !== window;
|
||||||
|
|||||||
28
src/Common/dataAccess/createStoredProcedure.ts
Normal file
28
src/Common/dataAccess/createStoredProcedure.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function createStoredProcedure(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string,
|
||||||
|
storedProcedure: StoredProcedureDefinition
|
||||||
|
): Promise<StoredProcedureDefinition & Resource> {
|
||||||
|
let createdStoredProcedure: StoredProcedureDefinition & Resource;
|
||||||
|
const clearMessage = logConsoleProgress(`Creating stored procedure ${storedProcedure.id}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.storedProcedures.create(storedProcedure);
|
||||||
|
createdStoredProcedure = response.resource;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while creating stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "CreateStoredProcedure", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
return createdStoredProcedure;
|
||||||
|
}
|
||||||
28
src/Common/dataAccess/createTrigger.ts
Normal file
28
src/Common/dataAccess/createTrigger.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Resource, TriggerDefinition } from "@azure/cosmos";
|
||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function createTrigger(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string,
|
||||||
|
trigger: TriggerDefinition
|
||||||
|
): Promise<TriggerDefinition & Resource> {
|
||||||
|
let createdTrigger: TriggerDefinition & Resource;
|
||||||
|
const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.triggers.create(trigger);
|
||||||
|
createdTrigger = response.resource;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while creating trigger ${trigger.id}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "CreateTrigger", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
return createdTrigger;
|
||||||
|
}
|
||||||
28
src/Common/dataAccess/createUserDefinedFunction.ts
Normal file
28
src/Common/dataAccess/createUserDefinedFunction.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function createUserDefinedFunction(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string,
|
||||||
|
userDefinedFunction: UserDefinedFunctionDefinition
|
||||||
|
): Promise<UserDefinedFunctionDefinition & Resource> {
|
||||||
|
let createdUserDefinedFunction: UserDefinedFunctionDefinition & Resource;
|
||||||
|
const clearMessage = logConsoleProgress(`Creating user defined function ${userDefinedFunction.id}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.userDefinedFunctions.create(userDefinedFunction);
|
||||||
|
createdUserDefinedFunction = response.resource;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while creating user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "CreateUserupdateUserDefinedFunction", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
return createdUserDefinedFunction;
|
||||||
|
}
|
||||||
26
src/Common/dataAccess/deleteStoredProcedure.ts
Normal file
26
src/Common/dataAccess/deleteStoredProcedure.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function deleteStoredProcedure(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string,
|
||||||
|
storedProcedureId: string
|
||||||
|
): Promise<void> {
|
||||||
|
const clearMessage = logConsoleProgress(`Deleting stored procedure ${storedProcedureId}`);
|
||||||
|
try {
|
||||||
|
await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.storedProcedure(storedProcedureId)
|
||||||
|
.delete();
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while deleting stored procedure ${storedProcedureId}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "DeleteStoredProcedure", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
22
src/Common/dataAccess/deleteTrigger.ts
Normal file
22
src/Common/dataAccess/deleteTrigger.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function deleteTrigger(databaseId: string, collectionId: string, triggerId: string): Promise<void> {
|
||||||
|
const clearMessage = logConsoleProgress(`Deleting trigger ${triggerId}`);
|
||||||
|
try {
|
||||||
|
await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.trigger(triggerId)
|
||||||
|
.delete();
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while deleting trigger ${triggerId}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "DeleteTrigger", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
22
src/Common/dataAccess/deleteUserDefinedFunction.ts
Normal file
22
src/Common/dataAccess/deleteUserDefinedFunction.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function deleteUserDefinedFunction(databaseId: string, collectionId: string, id: string): Promise<void> {
|
||||||
|
const clearMessage = logConsoleProgress(`Deleting user defined function ${id}`);
|
||||||
|
try {
|
||||||
|
await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.userDefinedFunction(id)
|
||||||
|
.delete();
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while deleting user defined function ${id}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "DeleteUserDefinedFunction", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
83
src/Common/dataAccess/readDatabaseOffer.ts
Normal file
83
src/Common/dataAccess/readDatabaseOffer.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||||
|
import { HttpHeaders } from "../Constants";
|
||||||
|
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { getSqlDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||||
|
import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||||
|
import { getCassandraKeyspaceThroughput } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||||
|
import { getGremlinDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||||
|
import { readOffers } from "./readOffers";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
|
|
||||||
|
export const readDatabaseOffer = async (
|
||||||
|
params: DataModels.ReadDatabaseOfferParams
|
||||||
|
): Promise<DataModels.OfferWithHeaders> => {
|
||||||
|
let offerId = params.offerId;
|
||||||
|
if (!offerId) {
|
||||||
|
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||||
|
try {
|
||||||
|
offerId = await getDatabaseOfferIdWithARM(params.databaseId);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code !== "NotFound") {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offerId = await getDatabaseOfferIdWithSDK(params.databaseResourceId, params.isServerless);
|
||||||
|
if (!offerId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: RequestOptions = {
|
||||||
|
initialHeaders: {
|
||||||
|
[HttpHeaders.populateCollectionThroughputInfo]: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await client()
|
||||||
|
.offer(offerId)
|
||||||
|
.read(options);
|
||||||
|
return (
|
||||||
|
response && {
|
||||||
|
...response.resource,
|
||||||
|
headers: response.headers
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDatabaseOfferIdWithARM = async (databaseId: string): Promise<string> => {
|
||||||
|
let rpResponse;
|
||||||
|
const subscriptionId = userContext.subscriptionId;
|
||||||
|
const resourceGroup = userContext.resourceGroup;
|
||||||
|
const accountName = userContext.databaseAccount.name;
|
||||||
|
const defaultExperience = userContext.defaultExperience;
|
||||||
|
switch (defaultExperience) {
|
||||||
|
case DefaultAccountExperienceType.DocumentDB:
|
||||||
|
rpResponse = await getSqlDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
|
||||||
|
break;
|
||||||
|
case DefaultAccountExperienceType.MongoDB:
|
||||||
|
rpResponse = await getMongoDBDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
|
||||||
|
break;
|
||||||
|
case DefaultAccountExperienceType.Cassandra:
|
||||||
|
rpResponse = await getCassandraKeyspaceThroughput(subscriptionId, resourceGroup, accountName, databaseId);
|
||||||
|
break;
|
||||||
|
case DefaultAccountExperienceType.Graph:
|
||||||
|
rpResponse = await getGremlinDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rpResponse?.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDatabaseOfferIdWithSDK = async (databaseResourceId: string, isServerless: boolean): Promise<string> => {
|
||||||
|
const offers = await readOffers(isServerless);
|
||||||
|
const offer = offers.find(offer => offer.resource === databaseResourceId);
|
||||||
|
return offer?.id;
|
||||||
|
};
|
||||||
36
src/Common/dataAccess/readOffers.ts
Normal file
36
src/Common/dataAccess/readOffers.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { Offer } from "../../Contracts/DataModels";
|
||||||
|
import { ClientDefaults } from "../Constants";
|
||||||
|
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
||||||
|
import { Platform, configContext } from "../../ConfigContext";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { sendCachedDataMessage } from "../MessageHandler";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
|
|
||||||
|
export const readOffers = async (isServerless?: boolean): Promise<Offer[]> => {
|
||||||
|
if (isServerless) {
|
||||||
|
return []; // Reading offers is not supported for serverless accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (configContext.platform === Platform.Portal) {
|
||||||
|
return sendCachedDataMessage<Offer[]>(MessageTypes.AllOffers, [
|
||||||
|
userContext.databaseAccount.id,
|
||||||
|
ClientDefaults.portalCacheTimeoutMs
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// If error getting cached Offers, continue on and read via SDK
|
||||||
|
}
|
||||||
|
|
||||||
|
return client()
|
||||||
|
.offers.readAll()
|
||||||
|
.fetchAll()
|
||||||
|
.then(response => response.resources)
|
||||||
|
.catch(error => {
|
||||||
|
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too.
|
||||||
|
if (error.message.includes("Reading or replacing offers is not supported for serverless accounts")) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
};
|
||||||
27
src/Common/dataAccess/readStoredProcedures.ts
Normal file
27
src/Common/dataAccess/readStoredProcedures.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function readStoredProcedures(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string
|
||||||
|
): Promise<(StoredProcedureDefinition & Resource)[]> {
|
||||||
|
let sprocs: (StoredProcedureDefinition & Resource)[];
|
||||||
|
const clearMessage = logConsoleProgress(`Querying stored procedures for container ${collectionId}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.storedProcedures.readAll()
|
||||||
|
.fetchAll();
|
||||||
|
sprocs = response.resources;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Failed to query stored procedures for container ${collectionId}: ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "ReadStoredProcedures", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
clearMessage();
|
||||||
|
return sprocs;
|
||||||
|
}
|
||||||
27
src/Common/dataAccess/readTriggers.ts
Normal file
27
src/Common/dataAccess/readTriggers.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Resource, TriggerDefinition } from "@azure/cosmos";
|
||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function readTriggers(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string
|
||||||
|
): Promise<(TriggerDefinition & Resource)[]> {
|
||||||
|
let triggers: (TriggerDefinition & Resource)[];
|
||||||
|
const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.triggers.readAll()
|
||||||
|
.fetchAll();
|
||||||
|
triggers = response.resources;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Failed to query triggers for container ${collectionId}: ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "ReadTriggers", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
clearMessage();
|
||||||
|
return triggers;
|
||||||
|
}
|
||||||
27
src/Common/dataAccess/readUserDefinedFunctions.ts
Normal file
27
src/Common/dataAccess/readUserDefinedFunctions.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function readUserDefinedFunctions(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string
|
||||||
|
): Promise<(UserDefinedFunctionDefinition & Resource)[]> {
|
||||||
|
let udfs: (UserDefinedFunctionDefinition & Resource)[];
|
||||||
|
const clearMessage = logConsoleProgress(`Querying user defined functions for container ${collectionId}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.userDefinedFunctions.readAll()
|
||||||
|
.fetchAll();
|
||||||
|
udfs = response.resources;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Failed to query user defined functions for container ${collectionId}: ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "ReadUserDefinedFunctions", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
clearMessage();
|
||||||
|
return udfs;
|
||||||
|
}
|
||||||
29
src/Common/dataAccess/updateStoredProcedure.ts
Normal file
29
src/Common/dataAccess/updateStoredProcedure.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function updateStoredProcedure(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string,
|
||||||
|
storedProcedure: StoredProcedureDefinition
|
||||||
|
): Promise<StoredProcedureDefinition & Resource> {
|
||||||
|
let updatedStoredProcedure: StoredProcedureDefinition & Resource;
|
||||||
|
const clearMessage = logConsoleProgress(`Updating stored procedure ${storedProcedure.id}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.storedProcedure(storedProcedure.id)
|
||||||
|
.replace(storedProcedure);
|
||||||
|
updatedStoredProcedure = response.resource;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while updating stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "UpdateStoredProcedure", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
return updatedStoredProcedure;
|
||||||
|
}
|
||||||
29
src/Common/dataAccess/updateTrigger.ts
Normal file
29
src/Common/dataAccess/updateTrigger.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { TriggerDefinition } from "@azure/cosmos";
|
||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function updateTrigger(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string,
|
||||||
|
trigger: TriggerDefinition
|
||||||
|
): Promise<TriggerDefinition> {
|
||||||
|
let updatedTrigger: TriggerDefinition;
|
||||||
|
const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.trigger(trigger.id)
|
||||||
|
.replace(trigger);
|
||||||
|
updatedTrigger = response.resource;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while updating trigger ${trigger.id}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "UpdateTrigger", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
return updatedTrigger;
|
||||||
|
}
|
||||||
29
src/Common/dataAccess/updateUserDefinedFunction.ts
Normal file
29
src/Common/dataAccess/updateUserDefinedFunction.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||||
|
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function updateUserDefinedFunction(
|
||||||
|
databaseId: string,
|
||||||
|
collectionId: string,
|
||||||
|
userDefinedFunction: UserDefinedFunctionDefinition
|
||||||
|
): Promise<UserDefinedFunctionDefinition & Resource> {
|
||||||
|
let updatedUserDefinedFunction: UserDefinedFunctionDefinition & Resource;
|
||||||
|
const clearMessage = logConsoleProgress(`Updating user defined function ${userDefinedFunction.id}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.scripts.userDefinedFunction(userDefinedFunction.id)
|
||||||
|
.replace(userDefinedFunction);
|
||||||
|
updatedUserDefinedFunction = response.resource;
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while updating user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "UpdateUserupdateUserDefinedFunction", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
|
return updatedUserDefinedFunction;
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ export enum Platform {
|
|||||||
|
|
||||||
interface ConfigContext {
|
interface ConfigContext {
|
||||||
platform: Platform;
|
platform: Platform;
|
||||||
allowedParentFrameOrigins: RegExp;
|
allowedParentFrameOrigins: string[];
|
||||||
gitSha?: string;
|
gitSha?: string;
|
||||||
proxyPath?: string;
|
proxyPath?: string;
|
||||||
AAD_ENDPOINT: string;
|
AAD_ENDPOINT: string;
|
||||||
@@ -30,7 +30,12 @@ interface ConfigContext {
|
|||||||
// Default configuration
|
// Default configuration
|
||||||
let configContext: Readonly<ConfigContext> = {
|
let configContext: Readonly<ConfigContext> = {
|
||||||
platform: Platform.Portal,
|
platform: Platform.Portal,
|
||||||
allowedParentFrameOrigins: /^https:\/\/portal\.azure\.com$|^https:\/\/portal\.azure\.us$|^https:\/\/portal\.azure\.cn$|^https:\/\/portal\.microsoftazure\.de$|^https:\/\/.+\.portal\.azure\.com$|^https:\/\/.+\.portal\.azure\.us$|^https:\/\/.+\.portal\.azure\.cn$|^https:\/\/.+\.portal\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.com$|^https:\/\/main\.documentdb\.ext\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.cn$|^https:\/\/main\.documentdb\.ext\.azure\.us$/,
|
allowedParentFrameOrigins: [
|
||||||
|
`^https:\\/\\/cosmos.azure.(com|cn|us)$`,
|
||||||
|
`^https:\\/\\/[\\.\\w]+.portal.azure.(com|cn|us)$`,
|
||||||
|
`^https:\\/\\/[\\.\\w]+.ext.azure.(com|cn|us)$`,
|
||||||
|
`^https:\\/\\/[\\.\\w]+microsoftazure.de$`
|
||||||
|
],
|
||||||
// Webpack injects this at build time
|
// Webpack injects this at build time
|
||||||
gitSha: process.env.GIT_SHA,
|
gitSha: process.env.GIT_SHA,
|
||||||
hostedExplorerURL: "https://cosmos.azure.com/",
|
hostedExplorerURL: "https://cosmos.azure.com/",
|
||||||
@@ -73,8 +78,13 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
|
|||||||
const response = await fetch("./config.json");
|
const response = await fetch("./config.json");
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
try {
|
try {
|
||||||
const externalConfig = await response.json();
|
const { allowedParentFrameOrigins, ...externalConfig } = await response.json();
|
||||||
Object.assign(configContext, externalConfig);
|
Object.assign(configContext, externalConfig);
|
||||||
|
if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) {
|
||||||
|
updateConfigContext({
|
||||||
|
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins]
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Unable to parse json in config file");
|
console.error("Unable to parse json in config file");
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -88,10 +88,6 @@ export interface Resource {
|
|||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResourceRequest {
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Collection extends Resource {
|
export interface Collection extends Resource {
|
||||||
defaultTtl?: number;
|
defaultTtl?: number;
|
||||||
indexingPolicy?: IndexingPolicy;
|
indexingPolicy?: IndexingPolicy;
|
||||||
@@ -104,39 +100,12 @@ export interface Collection extends Resource {
|
|||||||
geospatialConfig?: GeospatialConfig;
|
geospatialConfig?: GeospatialConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateCollectionWithRpResponse extends Resource {
|
|
||||||
properties: Collection;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CollectionRequest extends ResourceRequest {
|
|
||||||
defaultTtl?: number;
|
|
||||||
indexingPolicy?: IndexingPolicy;
|
|
||||||
partitionKey?: PartitionKey;
|
|
||||||
uniqueKeyPolicy?: UniqueKeyPolicy;
|
|
||||||
conflictResolutionPolicy?: ConflictResolutionPolicy;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Database extends Resource {
|
export interface Database extends Resource {
|
||||||
collections?: Collection[];
|
collections?: Collection[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DocumentId extends Resource {}
|
export interface DocumentId extends Resource {}
|
||||||
|
|
||||||
export interface Script extends Resource {
|
|
||||||
body: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StoredProcedure extends Script {}
|
|
||||||
|
|
||||||
export interface UserDefinedFunction extends Script {}
|
|
||||||
|
|
||||||
export interface Trigger extends Script {
|
|
||||||
triggerType: string;
|
|
||||||
triggerOperation: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConflictId extends Resource {
|
export interface ConflictId extends Resource {
|
||||||
resourceId?: string;
|
resourceId?: string;
|
||||||
resourceType?: string;
|
resourceType?: string;
|
||||||
@@ -259,28 +228,6 @@ export interface ErrorDataModel {
|
|||||||
code?: string;
|
code?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a property bag for telemetry e.g. see ITelemetryError.
|
|
||||||
*/
|
|
||||||
export interface ITelemetryProperties {
|
|
||||||
[propertyName: string]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a property bag for telemetry e.g. see ITelemetryError.
|
|
||||||
*/
|
|
||||||
export interface ITelemetryEvent {
|
|
||||||
name: string;
|
|
||||||
properties?: ITelemetryProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines an error to be logged as telemetry data.
|
|
||||||
*/
|
|
||||||
export interface ITelemetryError extends ITelemetryEvent {
|
|
||||||
error: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateDatabaseAndCollectionRequest {
|
export interface CreateDatabaseAndCollectionRequest {
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
@@ -307,11 +254,6 @@ export enum AutopilotTier {
|
|||||||
Tier4 = 4
|
Tier4 = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RpOptions {
|
|
||||||
// tier is sent as string, autoscale as object (AutoPilotCreationSettings)
|
|
||||||
[key: string]: string | AutoPilotCreationSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Query {
|
export interface Query {
|
||||||
id: string;
|
id: string;
|
||||||
resourceId: string;
|
resourceId: string;
|
||||||
@@ -347,10 +289,11 @@ export interface CreateCollectionParams {
|
|||||||
uniqueKeyPolicy?: UniqueKeyPolicy;
|
uniqueKeyPolicy?: UniqueKeyPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SharedThroughputRange {
|
export interface ReadDatabaseOfferParams {
|
||||||
minimumRU: number;
|
databaseId: string;
|
||||||
maximumRU: number;
|
databaseResourceId?: string;
|
||||||
defaultRU: number;
|
isServerless?: boolean;
|
||||||
|
offerId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Notification {
|
export interface Notification {
|
||||||
@@ -495,25 +438,6 @@ export interface NotebookConfigurationEndpointInfo {
|
|||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SparkCluster {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
properties: {
|
|
||||||
kind: string;
|
|
||||||
driverSize: string;
|
|
||||||
workerSize: string;
|
|
||||||
workerInstanceCount: number;
|
|
||||||
creationTime: string;
|
|
||||||
status: string;
|
|
||||||
libraries?: SparkClusterLibrary[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SparkClusterFeedResponse {
|
|
||||||
value: SparkCluster[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SparkClusterConnectionInfo {
|
export interface SparkClusterConnectionInfo {
|
||||||
userName: string;
|
userName: string;
|
||||||
password: string;
|
password: string;
|
||||||
@@ -555,79 +479,10 @@ export interface MongoParameters extends RpParameters {
|
|||||||
analyticalStorageTtl?: number;
|
analyticalStorageTtl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GraphParameters extends RpParameters {
|
|
||||||
pk: string;
|
|
||||||
coll: string;
|
|
||||||
cd: Boolean;
|
|
||||||
indexingPolicy?: IndexingPolicy;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreationRequest {
|
|
||||||
properties: {
|
|
||||||
resource: {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
options: RpOptions;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SqlCollectionParameters extends RpParameters {
|
|
||||||
uniqueKeyPolicy?: UniqueKeyPolicy;
|
|
||||||
pk: string;
|
|
||||||
coll: string;
|
|
||||||
cd: Boolean;
|
|
||||||
analyticalStorageTtl?: number;
|
|
||||||
indexingPolicy?: IndexingPolicy;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MongoCreationRequest extends CreationRequest {
|
|
||||||
properties: {
|
|
||||||
resource: {
|
|
||||||
id: string;
|
|
||||||
analyticalStorageTtl?: number;
|
|
||||||
shardKey?: {};
|
|
||||||
};
|
|
||||||
options: RpOptions;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GraphCreationRequest extends CreationRequest {
|
|
||||||
properties: {
|
|
||||||
resource: {
|
|
||||||
id: string;
|
|
||||||
partitionKey: {};
|
|
||||||
indexingPolicy?: IndexingPolicy;
|
|
||||||
};
|
|
||||||
options: RpOptions;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateDatabaseWithRpResponse {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
properties: {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SparkClusterLibrary {
|
export interface SparkClusterLibrary {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SqlCollectionCreationRequest extends CreationRequest {
|
|
||||||
properties: {
|
|
||||||
resource: {
|
|
||||||
uniqueKeyPolicy?: UniqueKeyPolicy;
|
|
||||||
id: string;
|
|
||||||
partitionKey: {};
|
|
||||||
analyticalStorageTtl?: number;
|
|
||||||
indexingPolicy?: IndexingPolicy;
|
|
||||||
};
|
|
||||||
options: RpOptions;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Library extends SparkClusterLibrary {
|
export interface Library extends SparkClusterLibrary {
|
||||||
properties: {
|
properties: {
|
||||||
kind: "Jar";
|
kind: "Jar";
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
import * as DataModels from "./DataModels";
|
import {
|
||||||
|
QueryMetrics,
|
||||||
|
Resource,
|
||||||
|
StoredProcedureDefinition,
|
||||||
|
TriggerDefinition,
|
||||||
|
UserDefinedFunctionDefinition
|
||||||
|
} from "@azure/cosmos";
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient";
|
|
||||||
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import { QueryMetrics } from "@azure/cosmos";
|
|
||||||
import { UploadDetails } from "../workers/upload/definitions";
|
|
||||||
import Explorer from "../Explorer/Explorer";
|
import Explorer from "../Explorer/Explorer";
|
||||||
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
|
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
|
import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient";
|
||||||
|
import ConflictId from "../Explorer/Tree/ConflictId";
|
||||||
|
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||||
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
||||||
import Trigger from "../Explorer/Tree/Trigger";
|
import Trigger from "../Explorer/Tree/Trigger";
|
||||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
|
||||||
import ConflictId from "../Explorer/Tree/ConflictId";
|
import { UploadDetails } from "../workers/upload/definitions";
|
||||||
|
import * as DataModels from "./DataModels";
|
||||||
|
|
||||||
export interface TokenProvider {
|
export interface TokenProvider {
|
||||||
getAuthHeader(): Promise<Headers>;
|
getAuthHeader(): Promise<Headers>;
|
||||||
@@ -75,15 +81,15 @@ export interface Database extends TreeNode {
|
|||||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||||
|
|
||||||
selectDatabase(): void;
|
selectDatabase(): void;
|
||||||
expandDatabase(): void;
|
expandDatabase(): Promise<void>;
|
||||||
collapseDatabase(): void;
|
collapseDatabase(): void;
|
||||||
|
|
||||||
loadCollections(): Q.Promise<void>;
|
loadCollections(): Promise<void>;
|
||||||
findCollectionWithId(collectionRid: string): Collection;
|
findCollectionWithId(collectionRid: string): Collection;
|
||||||
openAddCollection(database: Database, event: MouseEvent): void;
|
openAddCollection(database: Database, event: MouseEvent): void;
|
||||||
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
|
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
|
||||||
readSettings(): void;
|
|
||||||
onSettingsClick: () => void;
|
onSettingsClick: () => void;
|
||||||
|
loadOffer(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollectionBase extends TreeNode {
|
export interface CollectionBase extends TreeNode {
|
||||||
@@ -153,13 +159,13 @@ export interface Collection extends CollectionBase {
|
|||||||
collapseUserDefinedFunctions(): void;
|
collapseUserDefinedFunctions(): void;
|
||||||
collapseTriggers(): void;
|
collapseTriggers(): void;
|
||||||
|
|
||||||
loadUserDefinedFunctions(): Q.Promise<any>;
|
loadUserDefinedFunctions(): Promise<any>;
|
||||||
loadStoredProcedures(): Q.Promise<any>;
|
loadStoredProcedures(): Promise<any>;
|
||||||
loadTriggers(): Q.Promise<any>;
|
loadTriggers(): Promise<any>;
|
||||||
|
|
||||||
createStoredProcedureNode(data: DataModels.StoredProcedure): StoredProcedure;
|
createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure;
|
||||||
createUserDefinedFunctionNode(data: DataModels.UserDefinedFunction): UserDefinedFunction;
|
createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction;
|
||||||
createTriggerNode(data: DataModels.Trigger): Trigger;
|
createTriggerNode(data: TriggerDefinition & Resource): Trigger;
|
||||||
findStoredProcedureWithId(sprocRid: string): StoredProcedure;
|
findStoredProcedureWithId(sprocRid: string): StoredProcedure;
|
||||||
findTriggerWithId(triggerRid: string): Trigger;
|
findTriggerWithId(triggerRid: string): Trigger;
|
||||||
findUserDefinedFunctionWithId(udfRid: string): UserDefinedFunction;
|
findUserDefinedFunctionWithId(udfRid: string): UserDefinedFunction;
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const deleteDatabaseMenuItem = {
|
const deleteDatabaseMenuItem = {
|
||||||
iconSrc: DeleteDatabaseIcon,
|
iconSrc: DeleteDatabaseIcon,
|
||||||
onClick: () => container.deleteDatabaseConfirmationPane.open(),
|
onClick: () => container.deleteDatabaseConfirmationPane.open(),
|
||||||
label: container.deleteDatabaseText()
|
label: container.deleteDatabaseText(),
|
||||||
|
styleClass: "deleteDatabaseMenuItem"
|
||||||
};
|
};
|
||||||
return [newCollectionMenuItem, deleteDatabaseMenuItem];
|
return [newCollectionMenuItem, deleteDatabaseMenuItem];
|
||||||
}
|
}
|
||||||
@@ -112,7 +113,8 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
|
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
|
||||||
},
|
},
|
||||||
label: container.deleteCollectionText()
|
label: container.deleteCollectionText(),
|
||||||
|
styleClass: "deleteCollectionMenuItem"
|
||||||
});
|
});
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
|
|||||||
public onRemoveItemKeyPress = (data: any, event: KeyboardEvent, source: any): boolean => {
|
public onRemoveItemKeyPress = (data: any, event: KeyboardEvent, source: any): boolean => {
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
||||||
this.removeItem(data, event);
|
this.removeItem(data, event);
|
||||||
|
(document.querySelector(".dynamicListItem:last-of-type input") as HTMLElement).focus();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -94,7 +95,7 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
|
|||||||
|
|
||||||
public addItem(): void {
|
public addItem(): void {
|
||||||
this.listItems.push({ value: ko.observable("") });
|
this.listItems.push({ value: ko.observable("") });
|
||||||
document.getElementById("uniqueKeyItems").focus();
|
(document.querySelector(".dynamicListItem:last-of-type input") as HTMLElement).focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onAddItemKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
public onAddItemKeyPress = (source: any, event: KeyboardEvent): boolean => {
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ export class GitHubReposComponent extends React.Component<GitHubReposComponentPr
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={"firstdivbg headerline"}>{header}</div>
|
<div className={"firstdivbg headerline"} role="heading" aria-level={2}>
|
||||||
|
{header}
|
||||||
|
</div>
|
||||||
<div className={"paneMainContent"}>{content}</div>
|
<div className={"paneMainContent"}>{content}</div>
|
||||||
{!this.props.showAuthorizeAccess && (
|
{!this.props.showAuthorizeAccess && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export interface NotebookViewerComponentProps {
|
|||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
backNavigationText: string;
|
backNavigationText: string;
|
||||||
hideInputs?: boolean;
|
hideInputs?: boolean;
|
||||||
|
hidePrompts?: boolean;
|
||||||
onBackClick: () => void;
|
onBackClick: () => void;
|
||||||
onTagClick: (tag: string) => void;
|
onTagClick: (tag: string) => void;
|
||||||
}
|
}
|
||||||
@@ -148,7 +149,8 @@ export class NotebookViewerComponent extends React.Component<
|
|||||||
{this.state.showProgressBar && <ProgressIndicator />}
|
{this.state.showProgressBar && <ProgressIndicator />}
|
||||||
|
|
||||||
{this.notebookComponentBootstrapper.renderComponent(NotebookReadOnlyRenderer, {
|
{this.notebookComponentBootstrapper.renderComponent(NotebookReadOnlyRenderer, {
|
||||||
hideInputs: this.props.hideInputs
|
hideInputs: this.props.hideInputs,
|
||||||
|
hidePrompts: this.props.hidePrompts
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{this.state.dialogProps && <DialogComponent {...this.state.dialogProps} />}
|
{this.state.dialogProps && <DialogComponent {...this.state.dialogProps} />}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
step: step,
|
step: step,
|
||||||
'class':'migration collid select-font-size',
|
'class':'migration collid select-font-size',
|
||||||
min: minAutoPilotThroughput,
|
min: minAutoPilotThroughput,
|
||||||
'aria-label': ariaLabel,
|
'aria-label': 'Max request units per second',
|
||||||
type: isAutoscaleThroughputInputFieldRequired() ? 'number' : 'hidden',
|
type: isAutoscaleThroughputInputFieldRequired() ? 'number' : 'hidden',
|
||||||
css: {
|
css: {
|
||||||
dirty: maxAutoPilotThroughputSet.editableIsDirty
|
dirty: maxAutoPilotThroughputSet.editableIsDirty
|
||||||
|
|||||||
@@ -159,4 +159,20 @@ describe("TreeNodeComponent", () => {
|
|||||||
const wrapper = shallow(<TreeNodeComponent {...props} />);
|
const wrapper = shallow(<TreeNodeComponent {...props} />);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders loading icon", () => {
|
||||||
|
const node: TreeNode = {
|
||||||
|
label: "label",
|
||||||
|
children: [],
|
||||||
|
isExpanded: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
node,
|
||||||
|
generation: 2,
|
||||||
|
paddingLeft: 9
|
||||||
|
};
|
||||||
|
const wrapper = shallow(<TreeNodeComponent {...props} />);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ import {
|
|||||||
|
|
||||||
import TriangleDownIcon from "../../../../images/Triangle-down.svg";
|
import TriangleDownIcon from "../../../../images/Triangle-down.svg";
|
||||||
import TriangleRightIcon from "../../../../images/Triangle-right.svg";
|
import TriangleRightIcon from "../../../../images/Triangle-right.svg";
|
||||||
|
import LoadingIndicator_3Squares from "../../../../images/LoadingIndicator_3Squares.gif";
|
||||||
|
|
||||||
export interface TreeNodeMenuItem {
|
export interface TreeNodeMenuItem {
|
||||||
label: string;
|
label: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
iconSrc?: string;
|
iconSrc?: string;
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
|
styleClass?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeNode {
|
export interface TreeNode {
|
||||||
@@ -37,6 +39,7 @@ export interface TreeNode {
|
|||||||
data?: any; // Piece of data corresponding to this node
|
data?: any; // Piece of data corresponding to this node
|
||||||
timestamp?: number;
|
timestamp?: number;
|
||||||
isLeavesParentsSeparate?: boolean; // Display parents together first, then leaves
|
isLeavesParentsSeparate?: boolean; // Display parents together first, then leaves
|
||||||
|
isLoading?: boolean;
|
||||||
isSelected?: () => boolean;
|
isSelected?: () => boolean;
|
||||||
onClick?: (isExpanded: boolean) => void; // Only if a leaf, other click will expand/collapse
|
onClick?: (isExpanded: boolean) => void; // Only if a leaf, other click will expand/collapse
|
||||||
onExpanded?: () => void;
|
onExpanded?: () => void;
|
||||||
@@ -183,6 +186,9 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
|||||||
)}
|
)}
|
||||||
{node.contextMenu && this.renderContextMenuButton(node)}
|
{node.contextMenu && this.renderContextMenuButton(node)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="loadingIconContainer">
|
||||||
|
<img className="loadingIcon" src={LoadingIndicator_3Squares} hidden={!this.props.node.isLoading} />
|
||||||
|
</div>
|
||||||
{node.children && (
|
{node.children && (
|
||||||
<AnimateHeight duration={TreeNodeComponent.transitionDurationMS} height={this.state.isExpanded ? "auto" : 0}>
|
<AnimateHeight duration={TreeNodeComponent.transitionDurationMS} height={this.state.isExpanded ? "auto" : 0}>
|
||||||
<div className="nodeChildren" data-test={node.label}>
|
<div className="nodeChildren" data-test={node.label}>
|
||||||
@@ -256,13 +262,20 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
|||||||
onContextMenu={e => e.target.dispatchEvent(TreeNodeComponent.createClickEvent())}
|
onContextMenu={e => e.target.dispatchEvent(TreeNodeComponent.createClickEvent())}
|
||||||
>
|
>
|
||||||
{props.item.onRenderIcon()}
|
{props.item.onRenderIcon()}
|
||||||
<span className="treeComponentMenuItemLabel">{props.item.text}</span>
|
<span
|
||||||
|
className={
|
||||||
|
"treeComponentMenuItemLabel" + (props.item.className ? ` ${props.item.className}Label` : "")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{props.item.text}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
items: node.contextMenu.map((menuItem: TreeNodeMenuItem) => ({
|
items: node.contextMenu.map((menuItem: TreeNodeMenuItem) => ({
|
||||||
key: menuItem.label,
|
key: menuItem.label,
|
||||||
text: menuItem.label,
|
text: menuItem.label,
|
||||||
disabled: menuItem.isDisabled,
|
disabled: menuItem.isDisabled,
|
||||||
|
className: menuItem.styleClass,
|
||||||
onClick: menuItem.onClick,
|
onClick: menuItem.onClick,
|
||||||
onRenderIcon: (props: any) => <img src={menuItem.iconSrc} alt="" />
|
onRenderIcon: (props: any) => <img src={menuItem.iconSrc} alt="" />
|
||||||
}))
|
}))
|
||||||
@@ -282,7 +295,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
|||||||
<img
|
<img
|
||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
src={this.state.isExpanded ? TriangleDownIcon : TriangleRightIcon}
|
src={this.state.isExpanded ? TriangleDownIcon : TriangleRightIcon}
|
||||||
alt={this.state.isExpanded ? "Branch is expanded" : "Branch is collapsed"}
|
alt={this.state.isExpanded ? `${node.label} branch is expanded` : `${node.label} branch is collapsed`}
|
||||||
onKeyPress={(event: React.KeyboardEvent<HTMLDivElement>) => this.onCollapseExpandIconKeyPress(event, node)}
|
onKeyPress={(event: React.KeyboardEvent<HTMLDivElement>) => this.onCollapseExpandIconKeyPress(event, node)}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ exports[`TreeNodeComponent does not render children by default 1`] = `
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Branch is collapsed"
|
alt="label branch is collapsed"
|
||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
onKeyPress={[Function]}
|
onKeyPress={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
@@ -63,6 +63,15 @@ exports[`TreeNodeComponent does not render children by default 1`] = `
|
|||||||
label
|
label
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="loadingIconContainer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="loadingIcon"
|
||||||
|
hidden={true}
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
@@ -140,7 +149,7 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Branch is expanded"
|
alt="label branch is expanded"
|
||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
onKeyPress={[Function]}
|
onKeyPress={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
@@ -179,6 +188,7 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
|
|||||||
"isBeakVisible": false,
|
"isBeakVisible": false,
|
||||||
"items": Array [
|
"items": Array [
|
||||||
Object {
|
Object {
|
||||||
|
"className": undefined,
|
||||||
"disabled": true,
|
"disabled": true,
|
||||||
"key": "menuLabel",
|
"key": "menuLabel",
|
||||||
"onClick": undefined,
|
"onClick": undefined,
|
||||||
@@ -201,6 +211,15 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="loadingIconContainer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="loadingIcon"
|
||||||
|
hidden={true}
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
@@ -261,6 +280,77 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`TreeNodeComponent renders loading icon 1`] = `
|
||||||
|
<div
|
||||||
|
className=" main2 nodeItem "
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyPress={[Function]}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="treeNodeHeader "
|
||||||
|
data-test="label"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"paddingLeft": 9,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="label branch is expanded"
|
||||||
|
className="expandCollapseIcon"
|
||||||
|
onKeyPress={[Function]}
|
||||||
|
role="button"
|
||||||
|
src=""
|
||||||
|
tabIndex={0}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="nodeLabel"
|
||||||
|
title="label"
|
||||||
|
>
|
||||||
|
label
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="loadingIconContainer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="loadingIcon"
|
||||||
|
hidden={true}
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<AnimateHeight
|
||||||
|
animateOpacity={false}
|
||||||
|
animationStateClasses={
|
||||||
|
Object {
|
||||||
|
"animating": "rah-animating",
|
||||||
|
"animatingDown": "rah-animating--down",
|
||||||
|
"animatingToHeightAuto": "rah-animating--to-height-auto",
|
||||||
|
"animatingToHeightSpecific": "rah-animating--to-height-specific",
|
||||||
|
"animatingToHeightZero": "rah-animating--to-height-zero",
|
||||||
|
"animatingUp": "rah-animating--up",
|
||||||
|
"static": "rah-static",
|
||||||
|
"staticHeightAuto": "rah-static--height-auto",
|
||||||
|
"staticHeightSpecific": "rah-static--height-specific",
|
||||||
|
"staticHeightZero": "rah-static--height-zero",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applyInlineTransitions={true}
|
||||||
|
delay={0}
|
||||||
|
duration={200}
|
||||||
|
easing="ease"
|
||||||
|
height="auto"
|
||||||
|
style={Object {}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="nodeChildren"
|
||||||
|
data-test="label"
|
||||||
|
/>
|
||||||
|
</AnimateHeight>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents separated 1`] = `
|
exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents separated 1`] = `
|
||||||
<div
|
<div
|
||||||
className="nodeClassname main12 nodeItem "
|
className="nodeClassname main12 nodeItem "
|
||||||
@@ -278,7 +368,7 @@ exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Branch is expanded"
|
alt="label branch is expanded"
|
||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
onKeyPress={[Function]}
|
onKeyPress={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
@@ -331,6 +421,15 @@ exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="loadingIconContainer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="loadingIcon"
|
||||||
|
hidden={true}
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
@@ -436,7 +535,7 @@ exports[`TreeNodeComponent renders unsorted children by default 1`] = `
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Branch is expanded"
|
alt="label branch is expanded"
|
||||||
className="expandCollapseIcon"
|
className="expandCollapseIcon"
|
||||||
onKeyPress={[Function]}
|
onKeyPress={[Function]}
|
||||||
role="button"
|
role="button"
|
||||||
@@ -450,6 +549,15 @@ exports[`TreeNodeComponent renders unsorted children by default 1`] = `
|
|||||||
label
|
label
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="loadingIconContainer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="loadingIcon"
|
||||||
|
hidden={true}
|
||||||
|
src=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
animateOpacity={false}
|
animateOpacity={false}
|
||||||
animationStateClasses={
|
animationStateClasses={
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.showingMenu {
|
&.showingMenu {
|
||||||
background-color: #EEE;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.treeMenuEllipsis {
|
.treeMenuEllipsis {
|
||||||
@@ -78,3 +78,12 @@
|
|||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loadingIconContainer {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.loadingIcon {
|
||||||
|
height: 6px;
|
||||||
|
margin-left: 38px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import CassandraAddCollectionPane from "./Panes/CassandraAddCollectionPane";
|
|||||||
import Database from "./Tree/Database";
|
import Database from "./Tree/Database";
|
||||||
import DeleteCollectionConfirmationPane from "./Panes/DeleteCollectionConfirmationPane";
|
import DeleteCollectionConfirmationPane from "./Panes/DeleteCollectionConfirmationPane";
|
||||||
import DeleteDatabaseConfirmationPane from "./Panes/DeleteDatabaseConfirmationPane";
|
import DeleteDatabaseConfirmationPane from "./Panes/DeleteDatabaseConfirmationPane";
|
||||||
import { readOffers, refreshCachedResources } from "../Common/DocumentClientUtilityBase";
|
import { refreshCachedResources } from "../Common/DocumentClientUtilityBase";
|
||||||
import { readCollection } from "../Common/dataAccess/readCollection";
|
import { readCollection } from "../Common/dataAccess/readCollection";
|
||||||
import { readDatabases } from "../Common/dataAccess/readDatabases";
|
import { readDatabases } from "../Common/dataAccess/readDatabases";
|
||||||
import EditTableEntityPane from "./Panes/Tables/EditTableEntityPane";
|
import EditTableEntityPane from "./Panes/Tables/EditTableEntityPane";
|
||||||
@@ -87,6 +87,7 @@ import { ContextualPaneBase } from "./Panes/ContextualPaneBase";
|
|||||||
import TabsBase from "./Tabs/TabsBase";
|
import TabsBase from "./Tabs/TabsBase";
|
||||||
import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent";
|
||||||
import { updateUserContext, userContext } from "../UserContext";
|
import { updateUserContext, userContext } from "../UserContext";
|
||||||
|
import { stringToBlob } from "../Utils/BlobUtils";
|
||||||
|
|
||||||
BindingHandlersRegisterer.registerBindingHandlers();
|
BindingHandlersRegisterer.registerBindingHandlers();
|
||||||
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
||||||
@@ -1423,8 +1424,6 @@ export default class Explorer {
|
|||||||
|
|
||||||
// TODO: Refactor
|
// TODO: Refactor
|
||||||
const deferred: Q.Deferred<any> = Q.defer();
|
const deferred: Q.Deferred<any> = Q.defer();
|
||||||
|
|
||||||
const refreshDatabases = (offers?: DataModels.Offer[]) => {
|
|
||||||
this._setLoadingStatusText("Fetching databases...");
|
this._setLoadingStatusText("Fetching databases...");
|
||||||
readDatabases().then(
|
readDatabases().then(
|
||||||
(databases: DataModels.Database[]) => {
|
(databases: DataModels.Database[]) => {
|
||||||
@@ -1439,7 +1438,7 @@ export default class Explorer {
|
|||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
const currentlySelectedNode: ViewModels.TreeNode = this.selectedNode();
|
const currentlySelectedNode: ViewModels.TreeNode = this.selectedNode();
|
||||||
const deltaDatabases = this.getDeltaDatabases(databases, offers);
|
const deltaDatabases = this.getDeltaDatabases(databases);
|
||||||
this.addDatabasesToList(deltaDatabases.toAdd);
|
this.addDatabasesToList(deltaDatabases.toAdd);
|
||||||
this.deleteDatabasesFromList(deltaDatabases.toDelete);
|
this.deleteDatabasesFromList(deltaDatabases.toDelete);
|
||||||
this.selectedNode(currentlySelectedNode);
|
this.selectedNode(currentlySelectedNode);
|
||||||
@@ -1477,35 +1476,6 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const offerPromise: Q.Promise<DataModels.Offer[]> = readOffers({ isServerless: this.isServerlessEnabled() });
|
|
||||||
this._setLoadingStatusText("Fetching offers...");
|
|
||||||
offerPromise.then(
|
|
||||||
(offers: DataModels.Offer[]) => {
|
|
||||||
this._setLoadingStatusText("Successfully fetched offers.");
|
|
||||||
refreshDatabases(offers);
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
this._setLoadingStatusText("Failed to fetch offers.");
|
|
||||||
this.isRefreshingExplorer(false);
|
|
||||||
deferred.reject(error);
|
|
||||||
TelemetryProcessor.traceFailure(
|
|
||||||
Action.LoadDatabases,
|
|
||||||
{
|
|
||||||
databaseAccountName: this.databaseAccount().name,
|
|
||||||
defaultExperience: this.defaultExperience(),
|
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
|
||||||
error: JSON.stringify(error)
|
|
||||||
},
|
|
||||||
startKey
|
|
||||||
);
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while refreshing databases: ${JSON.stringify(error)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return deferred.promise.then(
|
return deferred.promise.then(
|
||||||
() => {
|
() => {
|
||||||
@@ -2102,16 +2072,13 @@ export default class Explorer {
|
|||||||
defaultExperience: this.defaultExperience && this.defaultExperience(),
|
defaultExperience: this.defaultExperience && this.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree
|
dataExplorerArea: Constants.Areas.ResourceTree
|
||||||
});
|
});
|
||||||
databasesToLoad.forEach((database: ViewModels.Database) => {
|
databasesToLoad.forEach(async (database: ViewModels.Database) => {
|
||||||
loadCollectionPromises.push(
|
await database.loadCollections();
|
||||||
database.loadCollections().finally(() => {
|
|
||||||
const isNewDatabase: boolean = _.some(newDatabases, (db: ViewModels.Database) => db.rid === database.rid);
|
const isNewDatabase: boolean = _.some(newDatabases, (db: ViewModels.Database) => db.rid === database.rid);
|
||||||
if (isNewDatabase) {
|
if (isNewDatabase) {
|
||||||
database.expandDatabase();
|
database.expandDatabase();
|
||||||
}
|
}
|
||||||
this.tabsManager.refreshActiveTab(tab => tab.collection && tab.collection.getDatabase().rid === database.rid);
|
this.tabsManager.refreshActiveTab(tab => tab.collection && tab.collection.getDatabase().rid === database.rid);
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Q.all(loadCollectionPromises).done(
|
Q.all(loadCollectionPromises).done(
|
||||||
@@ -2256,8 +2223,7 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getDeltaDatabases(
|
private getDeltaDatabases(
|
||||||
updatedDatabaseList: DataModels.Database[],
|
updatedDatabaseList: DataModels.Database[]
|
||||||
updatedOffersList: DataModels.Offer[]
|
|
||||||
): { toAdd: ViewModels.Database[]; toDelete: ViewModels.Database[] } {
|
): { toAdd: ViewModels.Database[]; toDelete: ViewModels.Database[] } {
|
||||||
const newDatabases: DataModels.Database[] = _.filter(updatedDatabaseList, (database: DataModels.Database) => {
|
const newDatabases: DataModels.Database[] = _.filter(updatedDatabaseList, (database: DataModels.Database) => {
|
||||||
const databaseExists = _.some(
|
const databaseExists = _.some(
|
||||||
@@ -2266,10 +2232,9 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
return !databaseExists;
|
return !databaseExists;
|
||||||
});
|
});
|
||||||
const databasesToAdd: ViewModels.Database[] = _.map(newDatabases, (newDatabase: DataModels.Database) => {
|
const databasesToAdd: ViewModels.Database[] = newDatabases.map(
|
||||||
const databaseOffer: DataModels.Offer = this.getOfferForResource(updatedOffersList, newDatabase._self);
|
(newDatabase: DataModels.Database) => new Database(this, newDatabase)
|
||||||
return new Database(this, newDatabase, databaseOffer);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
let databasesToDelete: ViewModels.Database[] = [];
|
let databasesToDelete: ViewModels.Database[] = [];
|
||||||
ko.utils.arrayForEach(this.databases(), (database: ViewModels.Database) => {
|
ko.utils.arrayForEach(this.databases(), (database: ViewModels.Database) => {
|
||||||
@@ -2319,10 +2284,6 @@ export default class Explorer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOfferForResource(offers: DataModels.Offer[], resourceId: string): DataModels.Offer {
|
|
||||||
return _.find(offers, (offer: DataModels.Offer) => offer.resource === resourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public uploadFile(name: string, content: string, parent: NotebookContentItem): Promise<NotebookContentItem> {
|
public uploadFile(name: string, content: string, parent: NotebookContentItem): Promise<NotebookContentItem> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to upload notebook, but notebook is not enabled";
|
const error = "Attempt to upload notebook, but notebook is not enabled";
|
||||||
@@ -2621,9 +2582,11 @@ export default class Explorer {
|
|||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Downloading ${notebookFile.path}`);
|
||||||
|
|
||||||
return this.notebookManager?.notebookContentClient.readFileContent(notebookFile.path).then(
|
return this.notebookManager?.notebookContentClient.readFileContent(notebookFile.path).then(
|
||||||
(content: string) => {
|
(content: string) => {
|
||||||
const blob = new Blob([content], { type: "octet/stream" });
|
const blob = stringToBlob(content, "text/plain");
|
||||||
if (navigator.msSaveBlob) {
|
if (navigator.msSaveBlob) {
|
||||||
// for IE and Edge
|
// for IE and Edge
|
||||||
navigator.msSaveBlob(blob, notebookFile.name);
|
navigator.msSaveBlob(blob, notebookFile.name);
|
||||||
@@ -2640,12 +2603,16 @@ export default class Explorer {
|
|||||||
downloadLink.click();
|
downloadLink.click();
|
||||||
downloadLink.remove();
|
downloadLink.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
`Could not download notebook ${JSON.stringify(error)}`
|
`Could not download notebook ${JSON.stringify(error)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
clearMessage();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3153,4 +3120,15 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async loadSelectedDatabaseOffer(): Promise<void> {
|
||||||
|
const database = this.findSelectedDatabase();
|
||||||
|
await database?.loadOffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async loadDatabaseOffers(): Promise<void> {
|
||||||
|
this.databases()?.forEach(async (database: ViewModels.Database) => {
|
||||||
|
await database.loadOffer();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -391,31 +391,6 @@ export class CommandBarComponentButtonFactory {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createScaleAndSettingsButton(container: Explorer): CommandButtonComponentProps {
|
|
||||||
let isShared = false;
|
|
||||||
if (container.isDatabaseNodeSelected()) {
|
|
||||||
isShared = container.findSelectedDatabase().isDatabaseShared();
|
|
||||||
} else if (container.isNodeKindSelected("Collection")) {
|
|
||||||
const database: ViewModels.Database = container.findSelectedCollection().getDatabase();
|
|
||||||
isShared = database && database.isDatabaseShared();
|
|
||||||
}
|
|
||||||
|
|
||||||
const label = isShared ? "Settings" : "Scale & Settings";
|
|
||||||
|
|
||||||
return {
|
|
||||||
iconSrc: ScaleIcon,
|
|
||||||
iconAlt: label,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && (<any>selectedCollection).onSettingsClick();
|
|
||||||
},
|
|
||||||
commandButtonLabel: label,
|
|
||||||
ariaLabel: label,
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: container.isDatabaseNodeOrNoneSelected()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static createNewNotebookButton(container: Explorer): CommandButtonComponentProps {
|
private static createNewNotebookButton(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "New Notebook";
|
const label = "New Notebook";
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -130,11 +130,14 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
<span className="headerStatusEllipsis">{this.state.headerStatus}</span>
|
<span className="headerStatusEllipsis">{this.state.headerStatus}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="expandCollapseButton" role="button" tabIndex={0}>
|
<div
|
||||||
<img
|
className="expandCollapseButton"
|
||||||
src={this.state.isExpanded ? ChevronDownIcon : ChevronUpIcon}
|
role="button"
|
||||||
alt={this.state.isExpanded ? "collapse console" : "expand console"}
|
tabIndex={0}
|
||||||
/>
|
aria-label={this.state.isExpanded ? "collapse console" : "expand console"}
|
||||||
|
aria-expanded={this.state.isExpanded}
|
||||||
|
>
|
||||||
|
<img src={this.state.isExpanded ? ChevronDownIcon : ChevronUpIcon} alt="" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AnimateHeight
|
<AnimateHeight
|
||||||
|
|||||||
@@ -68,12 +68,14 @@ exports[`NotificationConsoleComponent renders the console (expanded) 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
aria-expanded={true}
|
||||||
|
aria-label="collapse console"
|
||||||
className="expandCollapseButton"
|
className="expandCollapseButton"
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="collapse console"
|
alt=""
|
||||||
src=""
|
src=""
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -194,17 +194,24 @@ export class NotebookContentClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public readFileContent(filePath: string): Promise<string> {
|
public async readFileContent(filePath: string): Promise<string> {
|
||||||
return this.contentProvider
|
const xhr = await this.contentProvider.get(this.getServerConfig(), filePath, { content: 1 }).toPromise();
|
||||||
.get(this.getServerConfig(), filePath, { type: "notebook", format: "text", content: 1 })
|
|
||||||
.toPromise()
|
|
||||||
.then(xhr => {
|
|
||||||
const content = (xhr.response as any).content;
|
const content = (xhr.response as any).content;
|
||||||
if (!content) {
|
if (!content) {
|
||||||
throw new Error("No content read");
|
throw new Error("No content read");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const format = (xhr.response as any).format;
|
||||||
|
switch (format) {
|
||||||
|
case "text":
|
||||||
|
return content;
|
||||||
|
case "base64":
|
||||||
|
return atob(content);
|
||||||
|
case "json":
|
||||||
return stringifyNotebook(content);
|
return stringifyNotebook(content);
|
||||||
});
|
default:
|
||||||
|
throw new Error(`Unsupported content format ${format}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private deleteNotebookFile(path: string): Promise<string> {
|
private deleteNotebookFile(path: string): Promise<string> {
|
||||||
|
|||||||
@@ -113,11 +113,14 @@ export default class NotebookManager {
|
|||||||
this.params.resourceTree.initializeGitHubRepos(pinnedRepos);
|
this.params.resourceTree.initializeGitHubRepos(pinnedRepos);
|
||||||
this.params.resourceTree.triggerRender();
|
this.params.resourceTree.triggerRender();
|
||||||
});
|
});
|
||||||
this.junoClient.getPinnedRepos(this.gitHubOAuthService.getTokenObservable()()?.scope);
|
this.refreshPinnedRepos();
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshPinnedRepos(): void {
|
public refreshPinnedRepos(): void {
|
||||||
this.junoClient.getPinnedRepos(this.gitHubOAuthService.getTokenObservable()()?.scope);
|
const token = this.gitHubOAuthService.getTokenObservable()();
|
||||||
|
if (token) {
|
||||||
|
this.junoClient.getPinnedRepos(token.scope);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openPublishNotebookPane(
|
public async openPublishNotebookPane(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import "./base.css";
|
|||||||
import "./default.css";
|
import "./default.css";
|
||||||
|
|
||||||
import { CodeCell, RawCell, Cells, MarkdownCell } from "@nteract/stateful-components";
|
import { CodeCell, RawCell, Cells, MarkdownCell } from "@nteract/stateful-components";
|
||||||
|
import Prompt, { PassedPromptProps } from "@nteract/stateful-components/lib/inputs/prompt";
|
||||||
import { AzureTheme } from "./AzureTheme";
|
import { AzureTheme } from "./AzureTheme";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -15,6 +16,7 @@ import "./NotebookReadOnlyRenderer.less";
|
|||||||
export interface NotebookRendererProps {
|
export interface NotebookRendererProps {
|
||||||
contentRef: any;
|
contentRef: any;
|
||||||
hideInputs?: boolean;
|
hideInputs?: boolean;
|
||||||
|
hidePrompts?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PassedEditorProps {
|
interface PassedEditorProps {
|
||||||
@@ -38,6 +40,29 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
|||||||
loadTransform(this.props as any);
|
loadTransform(this.props as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderPrompt(id: string, contentRef: string): JSX.Element {
|
||||||
|
if (this.props.hidePrompts) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Prompt id={id} contentRef={contentRef}>
|
||||||
|
{(props: PassedPromptProps) => {
|
||||||
|
if (props.status === "busy") {
|
||||||
|
return <React.Fragment>{"[*]"}</React.Fragment>;
|
||||||
|
}
|
||||||
|
if (props.status === "queued") {
|
||||||
|
return <React.Fragment>{"[…]"}</React.Fragment>;
|
||||||
|
}
|
||||||
|
if (typeof props.executionCount === "number") {
|
||||||
|
return <React.Fragment>{`[${props.executionCount}]`}</React.Fragment>;
|
||||||
|
}
|
||||||
|
return <React.Fragment>{"[ ]"}</React.Fragment>;
|
||||||
|
}}
|
||||||
|
</Prompt>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="NotebookReadOnlyRender">
|
<div className="NotebookReadOnlyRender">
|
||||||
@@ -46,6 +71,7 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
|||||||
code: ({ id, contentRef }: { id: any; contentRef: ContentRef }) => (
|
code: ({ id, contentRef }: { id: any; contentRef: ContentRef }) => (
|
||||||
<CodeCell id={id} contentRef={contentRef}>
|
<CodeCell id={id} contentRef={contentRef}>
|
||||||
{{
|
{{
|
||||||
|
prompt: (props: { id: string; contentRef: string }) => this.renderPrompt(props.id, props.contentRef),
|
||||||
editor: {
|
editor: {
|
||||||
codemirror: (props: PassedEditorProps) =>
|
codemirror: (props: PassedEditorProps) =>
|
||||||
this.props.hideInputs ? <></> : <CodeMirrorEditor {...props} readOnly={"nocursor"} />
|
this.props.hideInputs ? <></> : <CodeMirrorEditor {...props} readOnly={"nocursor"} />
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<script type="text/html" id="add-collection-inputs">
|
<script type="text/html" id="add-collection-inputs">
|
||||||
<!-- Add collection header - Start -->
|
<!-- Add collection header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span id="containerTitle" data-bind="text: title"></span>
|
<span id="containerTitle" role="heading" aria-level="2" data-bind="text: title" ></span>
|
||||||
<div class="closeImg" id="closeBtnAddCollection" role="button" aria-label="Add collection close pane"
|
<div class="closeImg" id="closeBtnAddCollection" role="button" aria-label="Add collection close pane"
|
||||||
data-bind="click: cancel, event: { keypress: onCloseKeyPress }" tabindex="0">
|
data-bind="click: cancel, event: { keypress: onCloseKeyPress }" tabindex="0">
|
||||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||||
@@ -115,10 +115,10 @@
|
|||||||
|
|
||||||
<!-- Database provisioned throughput - Start -->
|
<!-- Database provisioned throughput - Start -->
|
||||||
<!-- ko if: canConfigureThroughput -->
|
<!-- ko if: canConfigureThroughput -->
|
||||||
<div class="databaseProvision" aria-label="New database provision support"
|
<div class="databaseProvision" aria-label="Provision database throughput"
|
||||||
data-bind="visible: databaseCreateNew">
|
data-bind="visible: databaseCreateNew">
|
||||||
<input tabindex="0" type="checkbox" data-test="addCollectionPane-databaseSharedThroughput"
|
<input tabindex="0" type="checkbox" data-test="addCollectionPane-databaseSharedThroughput"
|
||||||
id="addCollection-databaseSharedThroughput" title="Provision shared throughput"
|
id="addCollection-databaseSharedThroughput" title="Provision database throughput"
|
||||||
data-bind="checked: databaseCreateNewShared" />
|
data-bind="checked: databaseCreateNewShared" />
|
||||||
<span class="databaseProvisionText" for="databaseSharedThroughput">Provision database throughput</span>
|
<span class="databaseProvisionText" for="databaseSharedThroughput">Provision database throughput</span>
|
||||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
<span class="infoTooltip" role="tooltip" tabindex="0">
|
||||||
@@ -517,13 +517,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<span class="mandatoryStar">*</span>
|
<span class="mandatoryStar">*</span>
|
||||||
<span class="addCollectionLabel">Analytical store</span>
|
<span class="addCollectionLabel">Analytical store</span>
|
||||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
<span class="infoTooltip" role="tooltip" tabindex="0" data-bind="event: { focus: function(data, event) { transferFocus('tooltip1', 'link1') } }">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information">
|
<img class="infoImg" src="/info-bubble.svg" alt="More information">
|
||||||
<span class="tooltiptext infoTooltipWidth">
|
<span id="tooltip1" class="tooltiptext infoTooltipWidth" data-bind="event: { mouseout: onMouseOut }">
|
||||||
Enable analytical store capability to perform near real-time analytics on your operational
|
Enable analytical store capability to perform near real-time analytics on your operational
|
||||||
data, without impacting the performance of transactional workloads.
|
data, without impacting the performance of transactional workloads.
|
||||||
Learn more <a class="errorLink" href="https://aka.ms/analytical-store-overview"
|
Learn more <a id="link1" class="errorLink" href="https://aka.ms/analytical-store-overview"
|
||||||
target="_blank">here</a>
|
target="_blank" data-bind="event: { focusout: onFocusOut, keydown: onKeyDown.bind($data, 'largePartitionKey') }">here</a>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -537,9 +537,11 @@
|
|||||||
attr: {
|
attr: {
|
||||||
'aria-checked': isAnalyticalStorageOn() ? 'true' : 'false'
|
'aria-checked': isAnalyticalStorageOn() ? 'true' : 'false'
|
||||||
}" />
|
}" />
|
||||||
<span for="enableAnalyticalStorageRadioOn" data-bind="disable: showEnableSynapseLink">
|
<label for="enableAnalyticalStorageRadioOn" class="enableAnalyticalStorageRadioLabel">
|
||||||
|
<span data-bind="disable: showEnableSynapseLink">
|
||||||
On
|
On
|
||||||
</span>
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="enableAnalyticalStorageRadio" id="enableAnalyticalStorageRadioOff"
|
<input class="enableAnalyticalStorageRadio" id="enableAnalyticalStorageRadioOff"
|
||||||
name="analyticalStore" type="radio" role="radio" tabindex="0" data-bind="
|
name="analyticalStore" type="radio" role="radio" tabindex="0" data-bind="
|
||||||
@@ -549,9 +551,11 @@
|
|||||||
attr: {
|
attr: {
|
||||||
'aria-checked': isAnalyticalStorageOn() ? 'false' : 'true'
|
'aria-checked': isAnalyticalStorageOn() ? 'false' : 'true'
|
||||||
}" />
|
}" />
|
||||||
<span for="enableAnalyticalStorageRadioOff" data-bind="disable: showEnableSynapseLink">
|
<label for="enableAnalyticalStorageRadioOff" class="enableAnalyticalStorageRadioLabel">
|
||||||
|
<span data-bind="disable: showEnableSynapseLink">
|
||||||
Off
|
Off
|
||||||
</span>
|
</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="paragraph italic" data-bind="visible: ttl90DaysEnabled() && isAnalyticalStorageOn()">
|
<div class="paragraph italic" data-bind="visible: ttl90DaysEnabled() && isAnalyticalStorageOn()">
|
||||||
|
|||||||
@@ -681,7 +681,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
public open(databaseId?: string) {
|
public async open(databaseId?: string) {
|
||||||
super.open();
|
super.open();
|
||||||
// TODO: Figure out if a database level partition split is about to happen once shared throughput read is available
|
// TODO: Figure out if a database level partition split is about to happen once shared throughput read is available
|
||||||
this.formWarnings("");
|
this.formWarnings("");
|
||||||
@@ -715,18 +715,40 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
dataExplorerArea: Constants.Areas.ContextualPane
|
dataExplorerArea: Constants.Areas.ContextualPane
|
||||||
};
|
};
|
||||||
|
|
||||||
|
await this.container.loadDatabaseOffers();
|
||||||
this._onDatabasesChange(this.container.databases());
|
this._onDatabasesChange(this.container.databases());
|
||||||
this._setFocus();
|
this._setFocus();
|
||||||
|
|
||||||
TelemetryProcessor.trace(Action.CreateCollection, ActionModifiers.Open, addCollectionPaneOpenMessage);
|
TelemetryProcessor.trace(Action.CreateCollection, ActionModifiers.Open, addCollectionPaneOpenMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private transferFocus(elementIdToKeepVisible: string, elementIdToFocus: string): void {
|
||||||
|
document.getElementById(elementIdToKeepVisible).style.visibility = "visible";
|
||||||
|
document.getElementById(elementIdToFocus).focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private onFocusOut(_: any, event: any): void {
|
||||||
|
event.target.parentElement.style.visibility = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private onMouseOut(_: any, event: any): void {
|
||||||
|
event.target.style.visibility = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private onKeyDown(previousActiveElementId: string, _: any, event: KeyboardEvent): boolean {
|
||||||
|
if (event.shiftKey && event.keyCode == Constants.KeyCodes.Tab) {
|
||||||
|
document.getElementById(previousActiveElementId).focus();
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// Execute default action
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _onDatabasesChange(newDatabaseIds: ViewModels.Database[]) {
|
private _onDatabasesChange(newDatabaseIds: ViewModels.Database[]) {
|
||||||
const cachedDatabaseIdsList = _.map(newDatabaseIds, (database: ViewModels.Database) => {
|
const cachedDatabaseIdsList = _.map(newDatabaseIds, (database: ViewModels.Database) => {
|
||||||
if (database && database.offer && database.offer()) {
|
if (database && database.offer && database.offer()) {
|
||||||
this._databaseOffers.set(database.id(), database.offer());
|
this._databaseOffers.set(database.id(), database.offer());
|
||||||
} else if (database && database.isDatabaseShared && database.isDatabaseShared()) {
|
|
||||||
database.readSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return database.id();
|
return database.id();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<script type="text/html" id="add-database-inputs">
|
<script type="text/html" id="add-database-inputs">
|
||||||
<!-- Add database header - Start -->
|
<!-- Add database header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span id="databaseTitle" data-bind="text: title"></span>
|
<span id="databaseTitle" role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div class="closeImg" role="button" aria-label="Close pane"
|
<div class="closeImg" role="button" aria-label="Close pane"
|
||||||
data-bind="click: cancel, event: { keypress: onCloseKeyPress }" tabindex="0">
|
data-bind="click: cancel, event: { keypress: onCloseKeyPress }" tabindex="0">
|
||||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||||
@@ -73,8 +73,8 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<input id="database-id" type="text" aria-required="true" autocomplete="off" pattern="[^/?#\\]*[^/?# \\]"
|
<input id="database-id" type="text" aria-required="true" autocomplete="off" pattern="[^/?#\\]*[^/?# \\]"
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?'" placeholder="Type a new database id"
|
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
||||||
size="40" class="collid" data-bind="textInput: databaseId, hasFocus: firstFieldHasFocus"
|
size="40" class="collid" data-bind="textInput: databaseId, hasFocus: firstFieldHasFocus, attr: { placeholder: databaseIdPlaceHolder }"
|
||||||
aria-label="Database id" autofocus>
|
aria-label="Database id" autofocus>
|
||||||
|
|
||||||
<!-- Database provisioned throughput - Start -->
|
<!-- Database provisioned throughput - Start -->
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { PlatformType } from "../../PlatformType";
|
|||||||
export default class AddDatabasePane extends ContextualPaneBase {
|
export default class AddDatabasePane extends ContextualPaneBase {
|
||||||
public defaultExperience: ko.Computed<string>;
|
public defaultExperience: ko.Computed<string>;
|
||||||
public databaseIdLabel: ko.Computed<string>;
|
public databaseIdLabel: ko.Computed<string>;
|
||||||
|
public databaseIdPlaceHolder: ko.Computed<string>;
|
||||||
public databaseId: ko.Observable<string>;
|
public databaseId: ko.Observable<string>;
|
||||||
public databaseIdTooltipText: ko.Computed<string>;
|
public databaseIdTooltipText: ko.Computed<string>;
|
||||||
public databaseLevelThroughputTooltipText: ko.Computed<string>;
|
public databaseLevelThroughputTooltipText: ko.Computed<string>;
|
||||||
@@ -70,6 +71,11 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
this.databaseIdLabel = ko.computed<string>(() =>
|
this.databaseIdLabel = ko.computed<string>(() =>
|
||||||
this.container.isPreferredApiCassandra() ? "Keyspace id" : "Database id"
|
this.container.isPreferredApiCassandra() ? "Keyspace id" : "Database id"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.databaseIdPlaceHolder = ko.computed<string>(() =>
|
||||||
|
this.container.isPreferredApiCassandra() ? "Type a new keyspace id" : "Type a new database id"
|
||||||
|
);
|
||||||
|
|
||||||
this.databaseIdTooltipText = ko.computed<string>(() => {
|
this.databaseIdTooltipText = ko.computed<string>(() => {
|
||||||
const isCassandraAccount: boolean = this.container.isPreferredApiCassandra();
|
const isCassandraAccount: boolean = this.container.isPreferredApiCassandra();
|
||||||
return `A ${isCassandraAccount ? "keyspace" : "database"} is a logical container of one or more ${
|
return `A ${isCassandraAccount ? "keyspace" : "database"} is a logical container of one or more ${
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="paneContentContainer">
|
<div class="paneContentContainer">
|
||||||
<!-- Save Query header - Start -->
|
<!-- Save Query header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
>
|
>
|
||||||
<!-- Add Cassandra collection header - Start -->
|
<!-- Add Cassandra collection header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -268,8 +268,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
const cachedKeyspaceIdsList = _.map(newKeyspaceIds, (keyspace: ViewModels.Database) => {
|
const cachedKeyspaceIdsList = _.map(newKeyspaceIds, (keyspace: ViewModels.Database) => {
|
||||||
if (keyspace && keyspace.offer && !!keyspace.offer()) {
|
if (keyspace && keyspace.offer && !!keyspace.offer()) {
|
||||||
this.keyspaceOffers.set(keyspace.id(), keyspace.offer());
|
this.keyspaceOffers.set(keyspace.id(), keyspace.offer());
|
||||||
} else if (keyspace && keyspace.isDatabaseShared && keyspace.isDatabaseShared()) {
|
|
||||||
keyspace.readSettings();
|
|
||||||
}
|
}
|
||||||
return keyspace.id();
|
return keyspace.id();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Explorer from "../Explorer";
|
|||||||
|
|
||||||
// TODO: Use specific actions for logging telemetry data
|
// TODO: Use specific actions for logging telemetry data
|
||||||
export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
|
export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
|
||||||
|
private initalFocusedElement: HTMLElement | undefined;
|
||||||
public id: string;
|
public id: string;
|
||||||
public container: Explorer;
|
public container: Explorer;
|
||||||
public firstFieldHasFocus: ko.Observable<boolean>;
|
public firstFieldHasFocus: ko.Observable<boolean>;
|
||||||
@@ -49,9 +50,11 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
|
|||||||
this.visible(false);
|
this.visible(false);
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
this.resetData();
|
this.resetData();
|
||||||
|
this.resetFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public open() {
|
public open() {
|
||||||
|
this.initalFocusedElement = document.activeElement as HTMLElement;
|
||||||
this.visible(true);
|
this.visible(true);
|
||||||
this.firstFieldHasFocus(true);
|
this.firstFieldHasFocus(true);
|
||||||
this.resizePane();
|
this.resizePane();
|
||||||
@@ -123,4 +126,11 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
|
|||||||
|
|
||||||
$(paneElement).height(newPaneElementHeight);
|
$(paneElement).height(newPaneElementHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resetFocus(): void {
|
||||||
|
if (this.initalFocusedElement) {
|
||||||
|
this.initalFocusedElement.focus();
|
||||||
|
this.initalFocusedElement = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
>
|
>
|
||||||
<!-- Delete Collection Confirmation header - Start -->
|
<!-- Delete Collection Confirmation header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
@@ -67,8 +67,7 @@
|
|||||||
name="collectionIdConfirmation"
|
name="collectionIdConfirmation"
|
||||||
required
|
required
|
||||||
class="collid"
|
class="collid"
|
||||||
data-bind="value: collectionIdConfirmation, hasFocus: firstFieldHasFocus"
|
data-bind="value: collectionIdConfirmation, hasFocus: firstFieldHasFocus, attr: { 'aria-label': collectionIdConfirmationText }"
|
||||||
aria-label="Confirm by typing the collection id"
|
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
>
|
>
|
||||||
<!-- Delete Database Confirmation header - Start -->
|
<!-- Delete Database Confirmation header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import DeleteFeedback from "../../Common/DeleteFeedback";
|
|||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
|
import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
|
||||||
|
import { ARMError } from "../../Utils/arm/request";
|
||||||
|
|
||||||
export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
|
export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
|
||||||
public databaseIdConfirmationText: ko.Observable<string>;
|
public databaseIdConfirmationText: ko.Observable<string>;
|
||||||
@@ -105,11 +106,12 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
|
|||||||
this.databaseDeleteFeedback("");
|
this.databaseDeleteFeedback("");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(reason: any) => {
|
(reason: unknown) => {
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
const message = ErrorParserUtility.parse(reason);
|
|
||||||
this.formErrors(message[0].message);
|
const message = reason instanceof ARMError ? reason.message : ErrorParserUtility.parse(reason)[0].message;
|
||||||
this.formErrorsDetails(message[0].message);
|
this.formErrors(message);
|
||||||
|
this.formErrorsDetails(message);
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.DeleteDatabase,
|
Action.DeleteDatabase,
|
||||||
{
|
{
|
||||||
@@ -130,7 +132,8 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
|
|||||||
super.resetData();
|
super.resetData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public open() {
|
public async open() {
|
||||||
|
await this.container.loadSelectedDatabaseOffer();
|
||||||
this.recordDeleteFeedback(this.shouldRecordFeedback());
|
this.recordDeleteFeedback(this.shouldRecordFeedback());
|
||||||
super.open();
|
super.open();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: execute">
|
<form class="paneContentContainer" data-bind="submit: execute">
|
||||||
<!-- Input params header - Start -->
|
<!-- Input params header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -70,7 +70,9 @@ export class GenericRightPaneComponent extends React.Component<GenericRightPaneP
|
|||||||
private renderPanelHeader = (): JSX.Element => {
|
private renderPanelHeader = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<div className="firstdivbg headerline">
|
<div className="firstdivbg headerline">
|
||||||
<span id="databaseTitle">{this.props.title}</span>
|
<span id="databaseTitle" role="heading" aria-level={2}>
|
||||||
|
{this.props.title}
|
||||||
|
</span>
|
||||||
<IconButton
|
<IconButton
|
||||||
ariaLabel="Close pane"
|
ariaLabel="Close pane"
|
||||||
title="Close pane"
|
title="Close pane"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
<form class="paneContentContainer" data-bind="submit: submit">
|
||||||
<!-- New Vertex header - Start -->
|
<!-- New Vertex header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span>New Vertex</span>
|
<span role="heading" aria-level="2">New Vertex</span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
<form class="paneContentContainer" data-bind="submit: submit">
|
||||||
<!-- Graph Styling header - Start -->
|
<!-- Graph Styling header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span>Graph Styling</span>
|
<span role="heading" aria-level="2">Graph Styling</span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
<form class="paneContentContainer" data-bind="submit: submit">
|
||||||
<!-- Load Query header - Start -->
|
<!-- Load Query header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
<form class="paneContentContainer" data-bind="submit: submit">
|
||||||
<!-- Renew ad-hoc access header - Start -->
|
<!-- Renew ad-hoc access header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
<form class="paneContentContainer" data-bind="submit: submit">
|
||||||
<!-- Save Query header - Start -->
|
<!-- Save Query header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel">
|
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel">
|
||||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
<form class="paneContentContainer" data-bind="submit: submit">
|
||||||
<!-- Settings Confirmation header - Start -->
|
<!-- Settings Confirmation header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="paneContentContainer">
|
<div class="paneContentContainer">
|
||||||
<!-- Setup notebooks header - Start -->
|
<!-- Setup notebooks header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
<form class="paneContentContainer" data-bind="submit: submit">
|
||||||
<!-- String Input header - Start -->
|
<!-- String Input header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel"></div>
|
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel"></div>
|
||||||
</div>
|
</div>
|
||||||
<!-- String Input header - End -->
|
<!-- String Input header - End -->
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<div class="contextual-pane-in">
|
<div class="contextual-pane-in">
|
||||||
<!-- Switch Directory header - Start -->
|
<!-- Switch Directory header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: close">
|
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: close">
|
||||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export default class AddTableEntityPane extends TableEntityPane {
|
|||||||
this.updateIsActionEnabled();
|
this.updateIsActionEnabled();
|
||||||
super.open();
|
super.open();
|
||||||
}
|
}
|
||||||
const focusElement = document.getElementById("addTableEntityValue");
|
const focusElement = document.getElementById("closeAddEntityPane");
|
||||||
focusElement && focusElement.focus();
|
focusElement && focusElement.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
|
id="closeAddEntityPane"
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
aria-label="Close pane"
|
aria-label="Close pane"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
>
|
>
|
||||||
<!-- Edit table entity header - Start -->
|
<!-- Edit table entity header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
<form class="paneContentContainer" data-bind="submit: submit">
|
||||||
<!-- Upload File header - Start -->
|
<!-- Upload File header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel">
|
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel">
|
||||||
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<form class="paneContentContainer" data-bind="submit: submit">
|
<form class="paneContentContainer" data-bind="submit: submit">
|
||||||
<!-- Upload items header - Start -->
|
<!-- Upload items header - Start -->
|
||||||
<div class="firstdivbg headerline">
|
<div class="firstdivbg headerline">
|
||||||
<span data-bind="text: title"></span>
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
<div
|
<div
|
||||||
class="closeImg"
|
class="closeImg"
|
||||||
role="button"
|
role="button"
|
||||||
|
|||||||
@@ -100,7 +100,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="loadMore">
|
<div class="loadMore">
|
||||||
<a role="link" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0"
|
<a role="button" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0"
|
||||||
>Load more</a
|
>Load more</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -598,7 +598,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
|
|||||||
() => {
|
() => {
|
||||||
this.container.isRefreshingExplorer(false);
|
this.container.isRefreshingExplorer(false);
|
||||||
this._setBaseline();
|
this._setBaseline();
|
||||||
this.database.readSettings();
|
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
Action.UpdateSettings,
|
Action.UpdateSettings,
|
||||||
{
|
{
|
||||||
@@ -643,8 +642,9 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
|
|||||||
};
|
};
|
||||||
|
|
||||||
public onActivate(): Q.Promise<any> {
|
public onActivate(): Q.Promise<any> {
|
||||||
return super.onActivate().then(() => {
|
return super.onActivate().then(async () => {
|
||||||
this.database.selectedSubnodeKind(ViewModels.CollectionTabKind.DatabaseSettings);
|
this.database.selectedSubnodeKind(ViewModels.CollectionTabKind.DatabaseSettings);
|
||||||
|
await this.database.loadOffer();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="loadMore">
|
<div class="loadMore">
|
||||||
<a role="link" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0"
|
<a role="button" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0"
|
||||||
>Load more</a
|
>Load more</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -390,7 +390,7 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="loadMore">
|
<div class="loadMore">
|
||||||
<a role="link" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0"
|
<a role="button" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0"
|
||||||
>Load more</a
|
>Load more</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -111,17 +111,18 @@
|
|||||||
data-bind="visible: isMetricsToggled() && allResultsMetadata().length > 0 && errors().length === 0"
|
data-bind="visible: isMetricsToggled() && allResultsMetadata().length > 0 && errors().length === 0"
|
||||||
>
|
>
|
||||||
<table class="queryMetricsSummary">
|
<table class="queryMetricsSummary">
|
||||||
|
<caption>
|
||||||
|
Query Statistics
|
||||||
|
</caption>
|
||||||
<thead class="queryMetricsSummaryHead">
|
<thead class="queryMetricsSummaryHead">
|
||||||
<tr class="queryMetricsSummaryHeader queryMetricsSummaryTuple">
|
<tr class="queryMetricsSummaryHeader queryMetricsSummaryTuple">
|
||||||
<th title="METRIC">METRIC</th>
|
<th title="METRIC" scope="col">METRIC</th>
|
||||||
<th></th>
|
<th title="VALUE" scope="col">VALUE</th>
|
||||||
<th title="VALUE">VALUE</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="queryMetricsSummaryBody" data-bind="with: aggregatedQueryMetrics">
|
<tbody class="queryMetricsSummaryBody" data-bind="with: aggregatedQueryMetrics">
|
||||||
<tr class="queryMetricsSummaryTuple">
|
<tr class="queryMetricsSummaryTuple">
|
||||||
<td title="Request Charge">Request Charge</td>
|
<td title="Request Charge">Request Charge</td>
|
||||||
<td></td>
|
|
||||||
<td>
|
<td>
|
||||||
<span
|
<span
|
||||||
data-bind="text: $parent.requestChargeDisplayText, attr: { title: $parent.requestChargeDisplayText }"
|
data-bind="text: $parent.requestChargeDisplayText, attr: { title: $parent.requestChargeDisplayText }"
|
||||||
@@ -130,7 +131,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple">
|
<tr class="queryMetricsSummaryTuple">
|
||||||
<td title="Showing Results">Showing Results</td>
|
<td title="Showing Results">Showing Results</td>
|
||||||
<td></td>
|
|
||||||
<td>
|
<td>
|
||||||
<span
|
<span
|
||||||
data-bind="text: $parent.showingDocumentsDisplayText, attr: { title: $parent.showingDocumentsDisplayText }"
|
data-bind="text: $parent.showingDocumentsDisplayText, attr: { title: $parent.showingDocumentsDisplayText }"
|
||||||
@@ -138,8 +138,8 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="Retrieved document count">Retrieved document count</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="Retrieved document count">Retrieved document count</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Total number of retrieved documents</span>
|
<span class="queryMetricTooltipText">Total number of retrieved documents</span>
|
||||||
@@ -148,8 +148,8 @@
|
|||||||
<td><span data-bind="text: retrievedDocumentCount, attr: { title: retrievedDocumentCount }"></span></td>
|
<td><span data-bind="text: retrievedDocumentCount, attr: { title: retrievedDocumentCount }"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="Retrieved document size">Retrieved document size</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="Retrieved document size">Retrieved document size</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Total size of retrieved documents in bytes</span>
|
<span class="queryMetricTooltipText">Total size of retrieved documents in bytes</span>
|
||||||
@@ -161,8 +161,8 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="Output document count">Output document count</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="Output document count">Output document count</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Number of output documents</span>
|
<span class="queryMetricTooltipText">Number of output documents</span>
|
||||||
@@ -171,8 +171,8 @@
|
|||||||
<td><span data-bind="text: outputDocumentCount, attr: { title: outputDocumentCount }"></span></td>
|
<td><span data-bind="text: outputDocumentCount, attr: { title: outputDocumentCount }"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="Output document size">Output document size</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="Output document size">Output document size</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Total size of output documents in bytes</span>
|
<span class="queryMetricTooltipText">Total size of output documents in bytes</span>
|
||||||
@@ -184,8 +184,8 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="Index hit document count">Index hit document count</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="Index hit document count">Index hit document count</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Total number of documents matched by the filter</span>
|
<span class="queryMetricTooltipText">Total number of documents matched by the filter</span>
|
||||||
@@ -194,8 +194,8 @@
|
|||||||
<td><span data-bind="text: indexHitDocumentCount, attr: { title: indexHitDocumentCount }"></span></td>
|
<td><span data-bind="text: indexHitDocumentCount, attr: { title: indexHitDocumentCount }"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="Index lookup time">Index lookup time</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="Index lookup time">Index lookup time</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Time spent in physical index layer</span>
|
<span class="queryMetricTooltipText">Time spent in physical index layer</span>
|
||||||
@@ -206,8 +206,8 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="Document load time">Document load time</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="Document load time">Document load time</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Time spent in loading documents</span>
|
<span class="queryMetricTooltipText">Time spent in loading documents</span>
|
||||||
@@ -218,8 +218,8 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="Query engine execution time">Query engine execution time</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="Query engine execution time">Query engine execution time</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText queryEngineExeTimeInfo"
|
<span class="queryMetricTooltipText queryEngineExeTimeInfo"
|
||||||
@@ -236,8 +236,8 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="System function execution time">System function execution time</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="System function execution time">System function execution time</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Total time spent executing system (built-in) functions</span>
|
<span class="queryMetricTooltipText">Total time spent executing system (built-in) functions</span>
|
||||||
@@ -251,8 +251,8 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="User defined function execution time">User defined function execution time</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="User defined function execution time">User defined function execution time</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Total time spent executing user-defined functions</span>
|
<span class="queryMetricTooltipText">Total time spent executing user-defined functions</span>
|
||||||
@@ -266,8 +266,8 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
|
||||||
<td><span title="Document write time">Document write time</span></td>
|
|
||||||
<td>
|
<td>
|
||||||
|
<span title="Document write time">Document write time</span>
|
||||||
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
<img class="infoImg" src="/info-bubble.svg" alt="More information" />
|
||||||
<span class="queryMetricTooltipText">Time spent to write query result set to response buffer</span>
|
<span class="queryMetricTooltipText">Time spent to write query result set to response buffer</span>
|
||||||
@@ -279,7 +279,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.roundTrips() != null">
|
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.roundTrips() != null">
|
||||||
<td title="Round Trips">Round Trips</td>
|
<td title="Round Trips">Round Trips</td>
|
||||||
<td></td>
|
|
||||||
<td><span data-bind="text: $parent.roundTrips, attr: { title: $parent.roundTrips }"></span></td>
|
<td><span data-bind="text: $parent.roundTrips, attr: { title: $parent.roundTrips }"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- TODO: Report activity id for mongo queries -->
|
<!-- TODO: Report activity id for mongo queries -->
|
||||||
|
|||||||
@@ -195,6 +195,10 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
caption {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
.queryMetricsSummaryHead {
|
.queryMetricsSummaryHead {
|
||||||
.flex-display();
|
.flex-display();
|
||||||
}
|
}
|
||||||
@@ -221,11 +225,7 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex: 0 1 auto;
|
flex: 0 0 50%;
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(2) {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:nth-child(3) {
|
&:nth-child(3) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<div class="error-bar">
|
<div class="error-bar">
|
||||||
<div class="error-message" aria-label="Error Message" data-bind="visible: hasQueryError">
|
<div class="error-message" aria-label="Error Message" data-bind="visible: hasQueryError">
|
||||||
<span><img class="entity-error-Img" src="/error_red.svg"/></span>
|
<span><img class="entity-error-Img" src="/error_red.svg"/></span>
|
||||||
<span class="error-text" data-bind="text: queryErrorMessage"></span>
|
<span class="error-text" role="alert" data-bind="text: queryErrorMessage"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Tables Query Tab Errors - End-->
|
<!-- Tables Query Tab Errors - End-->
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import * as Constants from "../../Common/Constants";
|
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
|
import * as monaco from "monaco-editor";
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
|
import DiscardIcon from "../../../images/discard.svg";
|
||||||
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
|
import * as Constants from "../../Common/Constants";
|
||||||
|
import editable from "../../Common/EditableUtility";
|
||||||
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 TabsBase from "./TabsBase";
|
|
||||||
import editable from "../../Common/EditableUtility";
|
|
||||||
import * as monaco from "monaco-editor";
|
|
||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
|
||||||
import DiscardIcon from "../../../images/discard.svg";
|
|
||||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import TabsBase from "./TabsBase";
|
||||||
|
|
||||||
export default abstract class ScriptTabBase extends TabsBase implements ViewModels.WaitsForTemplate {
|
export default abstract class ScriptTabBase extends TabsBase implements ViewModels.WaitsForTemplate {
|
||||||
public ariaLabel: ko.Observable<string>;
|
public ariaLabel: ko.Observable<string>;
|
||||||
@@ -30,7 +29,8 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
|
|||||||
public formIsValid: ko.Computed<boolean>;
|
public formIsValid: ko.Computed<boolean>;
|
||||||
public formIsDirty: ko.Computed<boolean>;
|
public formIsDirty: ko.Computed<boolean>;
|
||||||
public isNew: ko.Observable<boolean>;
|
public isNew: ko.Observable<boolean>;
|
||||||
public resource: ko.Observable<DataModels.Script>;
|
// TODO: Remove any. The SDK types for all the script.body are slightly incorrect which makes this REALLY hard to type correct.
|
||||||
|
public resource: ko.Observable<any>;
|
||||||
public isTemplateReady: ko.Observable<boolean>;
|
public isTemplateReady: ko.Observable<boolean>;
|
||||||
protected _partitionKey: DataModels.PartitionKey;
|
protected _partitionKey: DataModels.PartitionKey;
|
||||||
|
|
||||||
@@ -194,8 +194,8 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract onSaveClick: () => Q.Promise<any>;
|
public abstract onSaveClick: () => Promise<any>;
|
||||||
public abstract onUpdateClick: () => Q.Promise<any>;
|
public abstract onUpdateClick: () => Promise<any>;
|
||||||
|
|
||||||
public onDiscard = (): Q.Promise<any> => {
|
public onDiscard = (): Q.Promise<any> => {
|
||||||
this.setBaselines();
|
this.setBaselines();
|
||||||
@@ -206,14 +206,14 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
|
|||||||
return Q();
|
return Q();
|
||||||
};
|
};
|
||||||
|
|
||||||
public onSaveOrUpdateClick(): Q.Promise<any> {
|
public onSaveOrUpdateClick(): Promise<any> {
|
||||||
if (this.saveButton.visible()) {
|
if (this.saveButton.visible()) {
|
||||||
return this.onSaveClick();
|
return this.onSaveClick();
|
||||||
} else if (this.updateButton.visible()) {
|
} else if (this.updateButton.visible()) {
|
||||||
return this.onUpdateClick();
|
return this.onUpdateClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Q();
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getTabsButtons(): CommandButtonComponentProps[] {
|
protected getTabsButtons(): CommandButtonComponentProps[] {
|
||||||
|
|||||||
@@ -346,7 +346,6 @@ describe("Settings tab", () => {
|
|||||||
|
|
||||||
const offer: DataModels.Offer = null;
|
const offer: DataModels.Offer = null;
|
||||||
const defaultTtl = 200;
|
const defaultTtl = 200;
|
||||||
const database = new Database(explorer, baseDatabase, null);
|
|
||||||
const conflictResolutionPolicy = {
|
const conflictResolutionPolicy = {
|
||||||
mode: DataModels.ConflictResolutionMode.LastWriterWins,
|
mode: DataModels.ConflictResolutionMode.LastWriterWins,
|
||||||
conflictResolutionPath: "/_ts"
|
conflictResolutionPath: "/_ts"
|
||||||
@@ -507,7 +506,6 @@ describe("Settings tab", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const database = new Database(explorer, baseDatabase, null);
|
|
||||||
const container: DataModels.Collection = {
|
const container: DataModels.Collection = {
|
||||||
_rid: "_rid",
|
_rid: "_rid",
|
||||||
_self: "",
|
_self: "",
|
||||||
|
|||||||
@@ -1270,8 +1270,10 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onActivate(): Q.Promise<any> {
|
public onActivate(): Q.Promise<any> {
|
||||||
return super.onActivate().then(() => {
|
return super.onActivate().then(async () => {
|
||||||
this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
|
this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
|
||||||
|
const database: ViewModels.Database = this.collection.getDatabase();
|
||||||
|
await database.loadOffer();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
|
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as _ from "underscore";
|
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
|
import * as _ from "underscore";
|
||||||
|
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import { createStoredProcedure } from "../../Common/dataAccess/createStoredProcedure";
|
||||||
|
import { updateStoredProcedure } from "../../Common/dataAccess/updateStoredProcedure";
|
||||||
|
import editable from "../../Common/EditableUtility";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import editable from "../../Common/EditableUtility";
|
|
||||||
import ScriptTabBase from "./ScriptTabBase";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
|
||||||
import StoredProcedure from "../Tree/StoredProcedure";
|
|
||||||
import { createStoredProcedure, updateStoredProcedure } from "../../Common/DocumentClientUtilityBase";
|
|
||||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import StoredProcedure from "../Tree/StoredProcedure";
|
||||||
|
import ScriptTabBase from "./ScriptTabBase";
|
||||||
|
|
||||||
enum ToggleState {
|
enum ToggleState {
|
||||||
Result = "result",
|
Result = "result",
|
||||||
@@ -24,7 +25,6 @@ export default class StoredProcedureTab extends ScriptTabBase {
|
|||||||
public executeResultsEditorId: string;
|
public executeResultsEditorId: string;
|
||||||
public executeLogsEditorId: string;
|
public executeLogsEditorId: string;
|
||||||
public toggleState: ko.Observable<ToggleState>;
|
public toggleState: ko.Observable<ToggleState>;
|
||||||
|
|
||||||
public originalSprocBody: ViewModels.Editable<string>;
|
public originalSprocBody: ViewModels.Editable<string>;
|
||||||
public resultsData: ko.Observable<string>;
|
public resultsData: ko.Observable<string>;
|
||||||
public logsData: ko.Observable<string>;
|
public logsData: ko.Observable<string>;
|
||||||
@@ -54,13 +54,11 @@ export default class StoredProcedureTab extends ScriptTabBase {
|
|||||||
this.buildCommandBarOptions();
|
this.buildCommandBarOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSaveClick = (): Q.Promise<DataModels.StoredProcedure> => {
|
public onSaveClick = (): Promise<StoredProcedureDefinition & Resource> => {
|
||||||
const resource: DataModels.StoredProcedure = <DataModels.StoredProcedure>{
|
return this._createStoredProcedure({
|
||||||
id: this.id(),
|
id: this.id(),
|
||||||
body: this.editorContent()
|
body: this.editorContent()
|
||||||
};
|
});
|
||||||
|
|
||||||
return this._createStoredProcedure(resource);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public onDiscard = (): Q.Promise<any> => {
|
public onDiscard = (): Q.Promise<any> => {
|
||||||
@@ -72,8 +70,8 @@ export default class StoredProcedureTab extends ScriptTabBase {
|
|||||||
return Q();
|
return Q();
|
||||||
};
|
};
|
||||||
|
|
||||||
public onUpdateClick = (): Q.Promise<any> => {
|
public onUpdateClick = (): Promise<any> => {
|
||||||
const data: DataModels.StoredProcedure = this._getResource();
|
const data = this._getResource();
|
||||||
|
|
||||||
this.isExecutionError(false);
|
this.isExecutionError(false);
|
||||||
this.isExecuting(true);
|
this.isExecuting(true);
|
||||||
@@ -83,18 +81,18 @@ export default class StoredProcedureTab extends ScriptTabBase {
|
|||||||
dataExplorerArea: Constants.Areas.Tab,
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
tabTitle: this.tabTitle()
|
tabTitle: this.tabTitle()
|
||||||
});
|
});
|
||||||
return updateStoredProcedure(this.collection, data)
|
return updateStoredProcedure(this.collection.databaseId, this.collection.id(), data)
|
||||||
.then(
|
.then(
|
||||||
(updatedResource: DataModels.StoredProcedure) => {
|
updatedResource => {
|
||||||
this.resource(updatedResource);
|
this.resource(updatedResource);
|
||||||
this.tabTitle(updatedResource.id);
|
this.tabTitle(updatedResource.id);
|
||||||
this.node.id(updatedResource.id);
|
this.node.id(updatedResource.id);
|
||||||
this.node.body(updatedResource.body);
|
this.node.body(updatedResource.body as string);
|
||||||
this.setBaselines();
|
this.setBaselines();
|
||||||
|
|
||||||
const editorModel = this.editor() && this.editor().getModel();
|
const editorModel = this.editor() && this.editor().getModel();
|
||||||
editorModel && editorModel.setValue(updatedResource.body);
|
editorModel && editorModel.setValue(updatedResource.body as string);
|
||||||
this.editorContent.setBaseline(updatedResource.body);
|
this.editorContent.setBaseline(updatedResource.body as string);
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
Action.UpdateStoredProcedure,
|
Action.UpdateStoredProcedure,
|
||||||
{
|
{
|
||||||
@@ -220,18 +218,14 @@ export default class StoredProcedureTab extends ScriptTabBase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getResource(): DataModels.StoredProcedure {
|
private _getResource() {
|
||||||
const resource: DataModels.StoredProcedure = <DataModels.StoredProcedure>{
|
return {
|
||||||
_rid: this.resource()._rid,
|
|
||||||
_self: this.resource()._self,
|
|
||||||
id: this.id(),
|
id: this.id(),
|
||||||
body: this.editorContent()
|
body: this.editorContent()
|
||||||
};
|
};
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createStoredProcedure(resource: DataModels.StoredProcedure): Q.Promise<DataModels.StoredProcedure> {
|
private _createStoredProcedure(resource: StoredProcedureDefinition): Promise<StoredProcedureDefinition & Resource> {
|
||||||
this.isExecutionError(false);
|
this.isExecutionError(false);
|
||||||
this.isExecuting(true);
|
this.isExecuting(true);
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.CreateStoredProcedure, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.CreateStoredProcedure, {
|
||||||
@@ -241,7 +235,7 @@ export default class StoredProcedureTab extends ScriptTabBase {
|
|||||||
tabTitle: this.tabTitle()
|
tabTitle: this.tabTitle()
|
||||||
});
|
});
|
||||||
|
|
||||||
return createStoredProcedure(this.collection, resource)
|
return createStoredProcedure(this.collection.databaseId, this.collection.id(), resource)
|
||||||
.then(
|
.then(
|
||||||
createdResource => {
|
createdResource => {
|
||||||
this.tabTitle(createdResource.id);
|
this.tabTitle(createdResource.id);
|
||||||
@@ -256,8 +250,8 @@ export default class StoredProcedureTab extends ScriptTabBase {
|
|||||||
this.setBaselines();
|
this.setBaselines();
|
||||||
|
|
||||||
const editorModel = this.editor() && this.editor().getModel();
|
const editorModel = this.editor() && this.editor().getModel();
|
||||||
editorModel && editorModel.setValue(createdResource.body);
|
editorModel && editorModel.setValue(createdResource.body as string);
|
||||||
this.editorContent.setBaseline(createdResource.body);
|
this.editorContent.setBaseline(createdResource.body as string);
|
||||||
this.node = this.collection.createStoredProcedureNode(createdResource);
|
this.node = this.collection.createStoredProcedureNode(createdResource);
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
Action.CreateStoredProcedure,
|
Action.CreateStoredProcedure,
|
||||||
@@ -284,7 +278,7 @@ export default class StoredProcedureTab extends ScriptTabBase {
|
|||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
return Q.reject(createError);
|
return Promise.reject(createError);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => this.isExecuting(false));
|
.finally(() => this.isExecuting(false));
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import Q from "q";
|
import { Resource, TriggerDefinition, TriggerOperation, TriggerType } from "@azure/cosmos";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import { createTrigger } from "../../Common/dataAccess/createTrigger";
|
||||||
|
import { updateTrigger } from "../../Common/dataAccess/updateTrigger";
|
||||||
|
import editable from "../../Common/EditableUtility";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import ScriptTabBase from "./ScriptTabBase";
|
|
||||||
import editable from "../../Common/EditableUtility";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import Trigger from "../Tree/Trigger";
|
import Trigger from "../Tree/Trigger";
|
||||||
import { createTrigger, updateTrigger } from "../../Common/DocumentClientUtilityBase";
|
import ScriptTabBase from "./ScriptTabBase";
|
||||||
|
|
||||||
export default class TriggerTab extends ScriptTabBase {
|
export default class TriggerTab extends ScriptTabBase {
|
||||||
public collection: ViewModels.Collection;
|
public collection: ViewModels.Collection;
|
||||||
@@ -27,13 +27,17 @@ export default class TriggerTab extends ScriptTabBase {
|
|||||||
super.buildCommandBarOptions();
|
super.buildCommandBarOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSaveClick = (): Q.Promise<DataModels.Trigger> => {
|
public onSaveClick = (): Promise<TriggerDefinition & Resource> => {
|
||||||
const data: DataModels.Trigger = this._getResource();
|
return this._createTrigger({
|
||||||
return this._createTrigger(data);
|
id: this.id(),
|
||||||
|
body: this.editorContent(),
|
||||||
|
triggerOperation: this.triggerOperation() as TriggerOperation,
|
||||||
|
triggerType: this.triggerType() as TriggerType
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
public onUpdateClick = (): Q.Promise<any> => {
|
public onUpdateClick = (): Promise<any> => {
|
||||||
const data: DataModels.Trigger = this._getResource();
|
const data = this._getResource();
|
||||||
this.isExecutionError(false);
|
this.isExecutionError(false);
|
||||||
this.isExecuting(true);
|
this.isExecuting(true);
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.UpdateTrigger, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.UpdateTrigger, {
|
||||||
@@ -42,14 +46,19 @@ export default class TriggerTab extends ScriptTabBase {
|
|||||||
tabTitle: this.tabTitle()
|
tabTitle: this.tabTitle()
|
||||||
});
|
});
|
||||||
|
|
||||||
return updateTrigger(this.collection, data)
|
return updateTrigger(this.collection.databaseId, this.collection.id(), {
|
||||||
|
id: this.id(),
|
||||||
|
body: this.editorContent(),
|
||||||
|
triggerOperation: this.triggerOperation() as TriggerOperation,
|
||||||
|
triggerType: this.triggerType() as TriggerType
|
||||||
|
})
|
||||||
.then(
|
.then(
|
||||||
(createdResource: DataModels.Trigger) => {
|
createdResource => {
|
||||||
this.resource(createdResource);
|
this.resource(createdResource);
|
||||||
this.tabTitle(createdResource.id);
|
this.tabTitle(createdResource.id);
|
||||||
|
|
||||||
this.node.id(createdResource.id);
|
this.node.id(createdResource.id);
|
||||||
this.node.body(createdResource.body);
|
this.node.body(createdResource.body as string);
|
||||||
this.node.triggerType(createdResource.triggerOperation);
|
this.node.triggerType(createdResource.triggerOperation);
|
||||||
this.node.triggerOperation(createdResource.triggerOperation);
|
this.node.triggerOperation(createdResource.triggerOperation);
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
@@ -66,8 +75,8 @@ export default class TriggerTab extends ScriptTabBase {
|
|||||||
this.setBaselines();
|
this.setBaselines();
|
||||||
|
|
||||||
const editorModel = this.editor().getModel();
|
const editorModel = this.editor().getModel();
|
||||||
editorModel.setValue(createdResource.body);
|
editorModel.setValue(createdResource.body as string);
|
||||||
this.editorContent.setBaseline(createdResource.body);
|
this.editorContent.setBaseline(createdResource.body as string);
|
||||||
},
|
},
|
||||||
(createError: any) => {
|
(createError: any) => {
|
||||||
this.isExecutionError(true);
|
this.isExecutionError(true);
|
||||||
@@ -89,7 +98,7 @@ export default class TriggerTab extends ScriptTabBase {
|
|||||||
public setBaselines() {
|
public setBaselines() {
|
||||||
super.setBaselines();
|
super.setBaselines();
|
||||||
|
|
||||||
const resource = <DataModels.Trigger>this.resource();
|
const resource = this.resource();
|
||||||
this.triggerOperation.setBaseline(resource.triggerOperation);
|
this.triggerOperation.setBaseline(resource.triggerOperation);
|
||||||
this.triggerType.setBaseline(resource.triggerType);
|
this.triggerType.setBaseline(resource.triggerType);
|
||||||
}
|
}
|
||||||
@@ -109,7 +118,7 @@ export default class TriggerTab extends ScriptTabBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createTrigger(resource: DataModels.Trigger): Q.Promise<DataModels.Trigger> {
|
private _createTrigger(resource: TriggerDefinition): Promise<TriggerDefinition & Resource> {
|
||||||
this.isExecutionError(false);
|
this.isExecutionError(false);
|
||||||
this.isExecuting(true);
|
this.isExecuting(true);
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.CreateTrigger, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.CreateTrigger, {
|
||||||
@@ -119,9 +128,9 @@ export default class TriggerTab extends ScriptTabBase {
|
|||||||
tabTitle: this.tabTitle()
|
tabTitle: this.tabTitle()
|
||||||
});
|
});
|
||||||
|
|
||||||
return createTrigger(this.collection, resource)
|
return createTrigger(this.collection.databaseId, this.collection.id(), resource)
|
||||||
.then(
|
.then(
|
||||||
(createdResource: DataModels.Trigger) => {
|
createdResource => {
|
||||||
this.tabTitle(createdResource.id);
|
this.tabTitle(createdResource.id);
|
||||||
this.isNew(false);
|
this.isNew(false);
|
||||||
this.resource(createdResource);
|
this.resource(createdResource);
|
||||||
@@ -134,8 +143,8 @@ export default class TriggerTab extends ScriptTabBase {
|
|||||||
this.setBaselines();
|
this.setBaselines();
|
||||||
|
|
||||||
const editorModel = this.editor().getModel();
|
const editorModel = this.editor().getModel();
|
||||||
editorModel.setValue(createdResource.body);
|
editorModel.setValue(createdResource.body as string);
|
||||||
this.editorContent.setBaseline(createdResource.body);
|
this.editorContent.setBaseline(createdResource.body as string);
|
||||||
|
|
||||||
this.node = this.collection.createTriggerNode(createdResource);
|
this.node = this.collection.createTriggerNode(createdResource);
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
@@ -163,22 +172,18 @@ export default class TriggerTab extends ScriptTabBase {
|
|||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
return Q.reject(createError);
|
return Promise.reject(createError);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => this.isExecuting(false));
|
.finally(() => this.isExecuting(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getResource(): DataModels.Trigger {
|
private _getResource() {
|
||||||
const resource: DataModels.Trigger = <DataModels.Trigger>{
|
return {
|
||||||
_rid: this.resource()._rid,
|
|
||||||
_self: this.resource()._self,
|
|
||||||
id: this.id(),
|
id: this.id(),
|
||||||
body: this.editorContent(),
|
body: this.editorContent(),
|
||||||
triggerOperation: this.triggerOperation(),
|
triggerOperation: this.triggerOperation(),
|
||||||
triggerType: this.triggerType()
|
triggerType: this.triggerType()
|
||||||
};
|
};
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import Q from "q";
|
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import { createUserDefinedFunction } from "../../Common/dataAccess/createUserDefinedFunction";
|
||||||
|
import { updateUserDefinedFunction } from "../../Common/dataAccess/updateUserDefinedFunction";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import ScriptTabBase from "./ScriptTabBase";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import UserDefinedFunction from "../Tree/UserDefinedFunction";
|
import UserDefinedFunction from "../Tree/UserDefinedFunction";
|
||||||
import { createUserDefinedFunction, updateUserDefinedFunction } from "../../Common/DocumentClientUtilityBase";
|
import ScriptTabBase from "./ScriptTabBase";
|
||||||
|
|
||||||
export default class UserDefinedFunctionTab extends ScriptTabBase {
|
export default class UserDefinedFunctionTab extends ScriptTabBase {
|
||||||
public collection: ViewModels.Collection;
|
public collection: ViewModels.Collection;
|
||||||
@@ -19,13 +19,13 @@ export default class UserDefinedFunctionTab extends ScriptTabBase {
|
|||||||
super.buildCommandBarOptions();
|
super.buildCommandBarOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSaveClick = (): Q.Promise<DataModels.UserDefinedFunction> => {
|
public onSaveClick = (): Promise<UserDefinedFunctionDefinition & Resource> => {
|
||||||
const data: DataModels.UserDefinedFunction = this._getResource();
|
const data = this._getResource();
|
||||||
return this._createUserDefinedFunction(data);
|
return this._createUserDefinedFunction(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
public onUpdateClick = (): Q.Promise<any> => {
|
public onUpdateClick = (): Promise<any> => {
|
||||||
const data: DataModels.UserDefinedFunction = this._getResource();
|
const data = this._getResource();
|
||||||
this.isExecutionError(false);
|
this.isExecutionError(false);
|
||||||
this.isExecuting(true);
|
this.isExecuting(true);
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.UpdateUDF, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.UpdateUDF, {
|
||||||
@@ -35,14 +35,14 @@ export default class UserDefinedFunctionTab extends ScriptTabBase {
|
|||||||
tabTitle: this.tabTitle()
|
tabTitle: this.tabTitle()
|
||||||
});
|
});
|
||||||
|
|
||||||
return updateUserDefinedFunction(this.collection, data)
|
return updateUserDefinedFunction(this.collection.databaseId, this.collection.id(), data)
|
||||||
.then(
|
.then(
|
||||||
(createdResource: DataModels.UserDefinedFunction) => {
|
createdResource => {
|
||||||
this.resource(createdResource);
|
this.resource(createdResource);
|
||||||
this.tabTitle(createdResource.id);
|
this.tabTitle(createdResource.id);
|
||||||
|
|
||||||
this.node.id(createdResource.id);
|
this.node.id(createdResource.id);
|
||||||
this.node.body(createdResource.body);
|
this.node.body(createdResource.body as string);
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
Action.UpdateUDF,
|
Action.UpdateUDF,
|
||||||
{
|
{
|
||||||
@@ -57,8 +57,8 @@ export default class UserDefinedFunctionTab extends ScriptTabBase {
|
|||||||
this.setBaselines();
|
this.setBaselines();
|
||||||
|
|
||||||
const editorModel = this.editor().getModel();
|
const editorModel = this.editor().getModel();
|
||||||
editorModel.setValue(createdResource.body);
|
editorModel.setValue(createdResource.body as string);
|
||||||
this.editorContent.setBaseline(createdResource.body);
|
this.editorContent.setBaseline(createdResource.body as string);
|
||||||
},
|
},
|
||||||
(createError: any) => {
|
(createError: any) => {
|
||||||
this.isExecutionError(true);
|
this.isExecutionError(true);
|
||||||
@@ -93,8 +93,8 @@ export default class UserDefinedFunctionTab extends ScriptTabBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _createUserDefinedFunction(
|
private _createUserDefinedFunction(
|
||||||
resource: DataModels.UserDefinedFunction
|
resource: UserDefinedFunctionDefinition
|
||||||
): Q.Promise<DataModels.UserDefinedFunction> {
|
): Promise<UserDefinedFunctionDefinition & Resource> {
|
||||||
this.isExecutionError(false);
|
this.isExecutionError(false);
|
||||||
this.isExecuting(true);
|
this.isExecuting(true);
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.CreateUDF, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.CreateUDF, {
|
||||||
@@ -104,9 +104,9 @@ export default class UserDefinedFunctionTab extends ScriptTabBase {
|
|||||||
tabTitle: this.tabTitle()
|
tabTitle: this.tabTitle()
|
||||||
});
|
});
|
||||||
|
|
||||||
return createUserDefinedFunction(this.collection, resource)
|
return createUserDefinedFunction(this.collection.databaseId, this.collection.id(), resource)
|
||||||
.then(
|
.then(
|
||||||
(createdResource: DataModels.UserDefinedFunction) => {
|
createdResource => {
|
||||||
this.tabTitle(createdResource.id);
|
this.tabTitle(createdResource.id);
|
||||||
this.isNew(false);
|
this.isNew(false);
|
||||||
this.resource(createdResource);
|
this.resource(createdResource);
|
||||||
@@ -118,8 +118,8 @@ export default class UserDefinedFunctionTab extends ScriptTabBase {
|
|||||||
this.setBaselines();
|
this.setBaselines();
|
||||||
|
|
||||||
const editorModel = this.editor().getModel();
|
const editorModel = this.editor().getModel();
|
||||||
editorModel.setValue(createdResource.body);
|
editorModel.setValue(createdResource.body as string);
|
||||||
this.editorContent.setBaseline(createdResource.body);
|
this.editorContent.setBaseline(createdResource.body as string);
|
||||||
|
|
||||||
this.node = this.collection.createUserDefinedFunctionNode(createdResource);
|
this.node = this.collection.createUserDefinedFunctionNode(createdResource);
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
@@ -147,14 +147,14 @@ export default class UserDefinedFunctionTab extends ScriptTabBase {
|
|||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
return Q.reject(createError);
|
return Promise.reject(createError);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => this.isExecuting(false));
|
.finally(() => this.isExecuting(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getResource() {
|
private _getResource() {
|
||||||
const resource: DataModels.UserDefinedFunction = <DataModels.UserDefinedFunction>{
|
const resource = {
|
||||||
_rid: this.resource()._rid,
|
_rid: this.resource()._rid,
|
||||||
_self: this.resource()._self,
|
_self: this.resource()._self,
|
||||||
id: this.id(),
|
id: this.id(),
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
|
import { Resource, StoredProcedureDefinition, TriggerDefinition, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import * as _ from "underscore";
|
import * as _ from "underscore";
|
||||||
import UploadWorker from "worker-loader!../../workers/upload";
|
import UploadWorker from "worker-loader!../../workers/upload";
|
||||||
import { AuthType } from "../../AuthType";
|
import { AuthType } from "../../AuthType";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
|
import { readStoredProcedures } from "../../Common/dataAccess/readStoredProcedures";
|
||||||
|
import { readTriggers } from "../../Common/dataAccess/readTriggers";
|
||||||
|
import { readUserDefinedFunctions } from "../../Common/dataAccess/readUserDefinedFunctions";
|
||||||
|
import { createDocument, readCollectionQuotaInfo, readOffer, readOffers } from "../../Common/DocumentClientUtilityBase";
|
||||||
import * as Logger from "../../Common/Logger";
|
import * as Logger from "../../Common/Logger";
|
||||||
|
import { configContext } from "../../ConfigContext";
|
||||||
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 { PlatformType } from "../../PlatformType";
|
import { PlatformType } from "../../PlatformType";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import { OfferUtils } from "../../Utils/OfferUtils";
|
import { OfferUtils } from "../../Utils/OfferUtils";
|
||||||
import { StartUploadMessageParams, UploadDetails, UploadDetailsRecord } from "../../workers/upload/definitions";
|
import { StartUploadMessageParams, UploadDetails, UploadDetailsRecord } from "../../workers/upload/definitions";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { CassandraAPIDataClient, CassandraTableKey, CassandraTableKeys } from "../Tables/TableDataClient";
|
import { CassandraAPIDataClient, CassandraTableKey, CassandraTableKeys } from "../Tables/TableDataClient";
|
||||||
import ConflictId from "./ConflictId";
|
|
||||||
|
|
||||||
import DocumentId from "./DocumentId";
|
|
||||||
import ConflictsTab from "../Tabs/ConflictsTab";
|
import ConflictsTab from "../Tabs/ConflictsTab";
|
||||||
import DocumentsTab from "../Tabs/DocumentsTab";
|
import DocumentsTab from "../Tabs/DocumentsTab";
|
||||||
import GraphTab from "../Tabs/GraphTab";
|
import GraphTab from "../Tabs/GraphTab";
|
||||||
@@ -27,21 +32,11 @@ import MongoShellTab from "../Tabs/MongoShellTab";
|
|||||||
import QueryTab from "../Tabs/QueryTab";
|
import QueryTab from "../Tabs/QueryTab";
|
||||||
import QueryTablesTab from "../Tabs/QueryTablesTab";
|
import QueryTablesTab from "../Tabs/QueryTablesTab";
|
||||||
import SettingsTab from "../Tabs/SettingsTab";
|
import SettingsTab from "../Tabs/SettingsTab";
|
||||||
|
import ConflictId from "./ConflictId";
|
||||||
|
import DocumentId from "./DocumentId";
|
||||||
import StoredProcedure from "./StoredProcedure";
|
import StoredProcedure from "./StoredProcedure";
|
||||||
import Trigger from "./Trigger";
|
import Trigger from "./Trigger";
|
||||||
import UserDefinedFunction from "./UserDefinedFunction";
|
import UserDefinedFunction from "./UserDefinedFunction";
|
||||||
import { configContext } from "../../ConfigContext";
|
|
||||||
import Explorer from "../Explorer";
|
|
||||||
import {
|
|
||||||
createDocument,
|
|
||||||
readTriggers,
|
|
||||||
readUserDefinedFunctions,
|
|
||||||
readStoredProcedures,
|
|
||||||
readCollectionQuotaInfo,
|
|
||||||
readOffer,
|
|
||||||
readOffers
|
|
||||||
} from "../../Common/DocumentClientUtilityBase";
|
|
||||||
import { userContext } from "../../UserContext";
|
|
||||||
|
|
||||||
export default class Collection implements ViewModels.Collection {
|
export default class Collection implements ViewModels.Collection {
|
||||||
public nodeKind: string;
|
public nodeKind: string;
|
||||||
@@ -854,21 +849,21 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
Trigger.create(source, event);
|
Trigger.create(source, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createStoredProcedureNode(data: DataModels.StoredProcedure): StoredProcedure {
|
public createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure {
|
||||||
const node = new StoredProcedure(this.container, this, data);
|
const node = new StoredProcedure(this.container, this, data);
|
||||||
this.container.selectedNode(node);
|
this.container.selectedNode(node);
|
||||||
this.children.push(node);
|
this.children.push(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createUserDefinedFunctionNode(data: DataModels.UserDefinedFunction): UserDefinedFunction {
|
public createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction {
|
||||||
const node = new UserDefinedFunction(this.container, this, data);
|
const node = new UserDefinedFunction(this.container, this, data);
|
||||||
this.container.selectedNode(node);
|
this.container.selectedNode(node);
|
||||||
this.children.push(node);
|
this.children.push(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createTriggerNode(data: DataModels.Trigger): Trigger {
|
public createTriggerNode(data: TriggerDefinition & Resource): Trigger {
|
||||||
const node = new Trigger(this.container, this, data);
|
const node = new Trigger(this.container, this, data);
|
||||||
this.container.selectedNode(node);
|
this.container.selectedNode(node);
|
||||||
this.children.push(node);
|
this.children.push(node);
|
||||||
@@ -1062,8 +1057,8 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadStoredProcedures(): Q.Promise<any> {
|
public loadStoredProcedures(): Promise<any> {
|
||||||
return readStoredProcedures(this).then((storedProcedures: DataModels.StoredProcedure[]) => {
|
return readStoredProcedures(this.databaseId, this.id()).then(storedProcedures => {
|
||||||
const storedProceduresNodes: ViewModels.TreeNode[] = storedProcedures.map(
|
const storedProceduresNodes: ViewModels.TreeNode[] = storedProcedures.map(
|
||||||
storedProcedure => new StoredProcedure(this.container, this, storedProcedure)
|
storedProcedure => new StoredProcedure(this.container, this, storedProcedure)
|
||||||
);
|
);
|
||||||
@@ -1073,8 +1068,8 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadUserDefinedFunctions(): Q.Promise<any> {
|
public loadUserDefinedFunctions(): Promise<any> {
|
||||||
return readUserDefinedFunctions(this).then((userDefinedFunctions: DataModels.UserDefinedFunction[]) => {
|
return readUserDefinedFunctions(this.databaseId, this.id()).then(userDefinedFunctions => {
|
||||||
const userDefinedFunctionsNodes: ViewModels.TreeNode[] = userDefinedFunctions.map(
|
const userDefinedFunctionsNodes: ViewModels.TreeNode[] = userDefinedFunctions.map(
|
||||||
udf => new UserDefinedFunction(this.container, this, udf)
|
udf => new UserDefinedFunction(this.container, this, udf)
|
||||||
);
|
);
|
||||||
@@ -1084,8 +1079,8 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadTriggers(): Q.Promise<any> {
|
public loadTriggers(): Promise<any> {
|
||||||
return readTriggers(this, null /*options*/).then((triggers: DataModels.Trigger[]) => {
|
return readTriggers(this.databaseId, this.id()).then(triggers => {
|
||||||
const triggerNodes: ViewModels.TreeNode[] = triggers.map(trigger => new Trigger(this.container, this, trigger));
|
const triggerNodes: ViewModels.TreeNode[] = triggers.map(trigger => new Trigger(this.container, this, trigger));
|
||||||
const otherNodes = this.children().filter(node => node.nodeKind !== "Trigger");
|
const otherNodes = this.children().filter(node => node.nodeKind !== "Trigger");
|
||||||
const allNodes = otherNodes.concat(triggerNodes);
|
const allNodes = otherNodes.concat(triggerNodes);
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"
|
|||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import * as Logger from "../../Common/Logger";
|
import * as Logger from "../../Common/Logger";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { readOffers, readOffer } from "../../Common/DocumentClientUtilityBase";
|
|
||||||
import { readCollections } from "../../Common/dataAccess/readCollections";
|
import { readCollections } from "../../Common/dataAccess/readCollections";
|
||||||
|
import { readDatabaseOffer } from "../../Common/dataAccess/readDatabaseOffer";
|
||||||
|
|
||||||
export default class Database implements ViewModels.Database {
|
export default class Database implements ViewModels.Database {
|
||||||
public nodeKind: string;
|
public nodeKind: string;
|
||||||
@@ -27,13 +27,13 @@ export default class Database implements ViewModels.Database {
|
|||||||
public isDatabaseShared: ko.Computed<boolean>;
|
public isDatabaseShared: ko.Computed<boolean>;
|
||||||
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
|
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
|
||||||
|
|
||||||
constructor(container: Explorer, data: any, offer: DataModels.Offer) {
|
constructor(container: Explorer, data: any) {
|
||||||
this.nodeKind = "Database";
|
this.nodeKind = "Database";
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.self = data._self;
|
this.self = data._self;
|
||||||
this.rid = data._rid;
|
this.rid = data._rid;
|
||||||
this.id = ko.observable(data.id);
|
this.id = ko.observable(data.id);
|
||||||
this.offer = ko.observable(offer);
|
this.offer = ko.observable();
|
||||||
this.collections = ko.observableArray<Collection>();
|
this.collections = ko.observableArray<Collection>();
|
||||||
this.isDatabaseExpanded = ko.observable<boolean>(false);
|
this.isDatabaseExpanded = ko.observable<boolean>(false);
|
||||||
this.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
|
this.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
|
||||||
@@ -66,7 +66,7 @@ export default class Database implements ViewModels.Database {
|
|||||||
dataExplorerArea: Constants.Areas.Tab,
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
tabTitle: "Scale"
|
tabTitle: "Scale"
|
||||||
});
|
});
|
||||||
Q.all([pendingNotificationsPromise, this.readSettings()]).then(
|
pendingNotificationsPromise.then(
|
||||||
(data: any) => {
|
(data: any) => {
|
||||||
const pendingNotification: DataModels.Notification = data && data[0];
|
const pendingNotification: DataModels.Notification = data && data[0];
|
||||||
settingsTab = new DatabaseSettingsTab({
|
settingsTab = new DatabaseSettingsTab({
|
||||||
@@ -121,80 +121,6 @@ export default class Database implements ViewModels.Database {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public readSettings(): Q.Promise<void> {
|
|
||||||
const deferred: Q.Deferred<void> = Q.defer<void>();
|
|
||||||
this.container.isRefreshingExplorer(true);
|
|
||||||
const databaseDataModel: DataModels.Database = <DataModels.Database>{
|
|
||||||
id: this.id(),
|
|
||||||
_rid: this.rid,
|
|
||||||
_self: this.self
|
|
||||||
};
|
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.LoadOffers, {
|
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
|
||||||
defaultExperience: this.container.defaultExperience()
|
|
||||||
});
|
|
||||||
|
|
||||||
const offerInfoPromise: Q.Promise<DataModels.Offer[]> = readOffers({
|
|
||||||
isServerless: this.container.isServerlessEnabled()
|
|
||||||
});
|
|
||||||
Q.all([offerInfoPromise]).then(
|
|
||||||
() => {
|
|
||||||
this.container.isRefreshingExplorer(false);
|
|
||||||
|
|
||||||
const databaseOffer: DataModels.Offer = this._getOfferForDatabase(
|
|
||||||
offerInfoPromise.valueOf(),
|
|
||||||
databaseDataModel
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!databaseOffer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
readOffer(databaseOffer).then((offerDetail: DataModels.OfferWithHeaders) => {
|
|
||||||
const offerThroughputInfo: DataModels.OfferThroughputInfo = {
|
|
||||||
minimumRUForCollection:
|
|
||||||
offerDetail.content &&
|
|
||||||
offerDetail.content.collectionThroughputInfo &&
|
|
||||||
offerDetail.content.collectionThroughputInfo.minimumRUForCollection,
|
|
||||||
numPhysicalPartitions:
|
|
||||||
offerDetail.content &&
|
|
||||||
offerDetail.content.collectionThroughputInfo &&
|
|
||||||
offerDetail.content.collectionThroughputInfo.numPhysicalPartitions
|
|
||||||
};
|
|
||||||
|
|
||||||
databaseOffer.content.collectionThroughputInfo = offerThroughputInfo;
|
|
||||||
(databaseOffer as DataModels.OfferWithHeaders).headers = offerDetail.headers;
|
|
||||||
this.offer(databaseOffer);
|
|
||||||
this.offer.valueHasMutated();
|
|
||||||
|
|
||||||
TelemetryProcessor.traceSuccess(
|
|
||||||
Action.LoadOffers,
|
|
||||||
{
|
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
|
||||||
defaultExperience: this.container.defaultExperience()
|
|
||||||
},
|
|
||||||
startKey
|
|
||||||
);
|
|
||||||
deferred.resolve();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
this.container.isRefreshingExplorer(false);
|
|
||||||
deferred.reject(error);
|
|
||||||
TelemetryProcessor.traceFailure(
|
|
||||||
Action.LoadOffers,
|
|
||||||
{
|
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
|
||||||
defaultExperience: this.container.defaultExperience()
|
|
||||||
},
|
|
||||||
startKey
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isDatabaseNodeSelected(): boolean {
|
public isDatabaseNodeSelected(): boolean {
|
||||||
return (
|
return (
|
||||||
!this.isDatabaseExpanded() &&
|
!this.isDatabaseExpanded() &&
|
||||||
@@ -219,23 +145,13 @@ export default class Database implements ViewModels.Database {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public expandCollapseDatabase() {
|
public async expandDatabase() {
|
||||||
this.selectDatabase();
|
|
||||||
if (this.isDatabaseExpanded()) {
|
|
||||||
this.collapseDatabase();
|
|
||||||
} else {
|
|
||||||
this.expandDatabase();
|
|
||||||
}
|
|
||||||
this.container.onUpdateTabsButtons([]);
|
|
||||||
this.container.tabsManager.refreshActiveTab(tab => tab.collection && tab.collection.getDatabase().rid === this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public expandDatabase() {
|
|
||||||
if (this.isDatabaseExpanded()) {
|
if (this.isDatabaseExpanded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadCollections();
|
await this.loadOffer();
|
||||||
|
await this.loadCollections();
|
||||||
this.isDatabaseExpanded(true);
|
this.isDatabaseExpanded(true);
|
||||||
TelemetryProcessor.trace(Action.ExpandTreeNode, ActionModifiers.Mark, {
|
TelemetryProcessor.trace(Action.ExpandTreeNode, ActionModifiers.Mark, {
|
||||||
description: "Database node",
|
description: "Database node",
|
||||||
@@ -259,14 +175,10 @@ export default class Database implements ViewModels.Database {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadCollections(): Q.Promise<void> {
|
public async loadCollections(): Promise<void> {
|
||||||
let collectionVMs: Collection[] = [];
|
const collectionVMs: Collection[] = [];
|
||||||
let deferred: Q.Deferred<void> = Q.defer<void>();
|
const collections: DataModels.Collection[] = await readCollections(this.id());
|
||||||
|
const deltaCollections = this.getDeltaCollections(collections);
|
||||||
readCollections(this.id()).then(
|
|
||||||
(collections: DataModels.Collection[]) => {
|
|
||||||
let collectionsToAddVMPromises: Q.Promise<any>[] = [];
|
|
||||||
let deltaCollections = this.getDeltaCollections(collections);
|
|
||||||
|
|
||||||
deltaCollections.toAdd.forEach((collection: DataModels.Collection) => {
|
deltaCollections.toAdd.forEach((collection: DataModels.Collection) => {
|
||||||
const collectionVM: Collection = new Collection(this.container, this.id(), collection, null, null);
|
const collectionVM: Collection = new Collection(this.container, this.id(), collection, null, null);
|
||||||
@@ -276,15 +188,6 @@ export default class Database implements ViewModels.Database {
|
|||||||
//merge collections
|
//merge collections
|
||||||
this.addCollectionsToList(collectionVMs);
|
this.addCollectionsToList(collectionVMs);
|
||||||
this.deleteCollectionsFromList(deltaCollections.toDelete);
|
this.deleteCollectionsFromList(deltaCollections.toDelete);
|
||||||
|
|
||||||
deferred.resolve();
|
|
||||||
},
|
|
||||||
(error: any) => {
|
|
||||||
deferred.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public openAddCollection(database: Database, event: MouseEvent) {
|
public openAddCollection(database: Database, event: MouseEvent) {
|
||||||
@@ -296,6 +199,17 @@ export default class Database implements ViewModels.Database {
|
|||||||
return _.find(this.collections(), (collection: ViewModels.Collection) => collection.id() === collectionId);
|
return _.find(this.collections(), (collection: ViewModels.Collection) => collection.id() === collectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async loadOffer(): Promise<void> {
|
||||||
|
if (!this.offer()) {
|
||||||
|
const params: DataModels.ReadDatabaseOfferParams = {
|
||||||
|
databaseId: this.id(),
|
||||||
|
databaseResourceId: this.self,
|
||||||
|
isServerless: this.container.isServerlessEnabled()
|
||||||
|
};
|
||||||
|
this.offer(await readDatabaseOffer(params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _getPendingThroughputSplitNotification(): Q.Promise<DataModels.Notification> {
|
private _getPendingThroughputSplitNotification(): Q.Promise<DataModels.Notification> {
|
||||||
if (!this.container) {
|
if (!this.container) {
|
||||||
return Q.resolve(undefined);
|
return Q.resolve(undefined);
|
||||||
@@ -387,8 +301,4 @@ export default class Database implements ViewModels.Database {
|
|||||||
|
|
||||||
this.collections(collectionsToKeep);
|
this.collections(collectionsToKeep);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getOfferForDatabase(offers: DataModels.Offer[], database: DataModels.Database): DataModels.Offer {
|
|
||||||
return _.find(offers, (offer: DataModels.Offer) => offer.resource === database._self);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,14 +170,17 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
children: [],
|
children: [],
|
||||||
isSelected: () => this.isDataNodeSelected(database.rid, "Database", undefined),
|
isSelected: () => this.isDataNodeSelected(database.rid, "Database", undefined),
|
||||||
contextMenu: ResourceTreeContextMenuButtonFactory.createDatabaseContextMenu(this.container, database),
|
contextMenu: ResourceTreeContextMenuButtonFactory.createDatabaseContextMenu(this.container, database),
|
||||||
onClick: isExpanded => {
|
onClick: async isExpanded => {
|
||||||
// Rewritten version of expandCollapseDatabase():
|
// Rewritten version of expandCollapseDatabase():
|
||||||
if (!isExpanded) {
|
if (isExpanded) {
|
||||||
database.expandDatabase();
|
|
||||||
database.loadCollections();
|
|
||||||
} else {
|
|
||||||
database.collapseDatabase();
|
database.collapseDatabase();
|
||||||
|
} else {
|
||||||
|
if (databaseNode.children?.length === 0) {
|
||||||
|
databaseNode.isLoading = true;
|
||||||
}
|
}
|
||||||
|
await database.expandDatabase();
|
||||||
|
}
|
||||||
|
databaseNode.isLoading = false;
|
||||||
database.selectDatabase();
|
database.selectDatabase();
|
||||||
this.container.onUpdateTabsButtons([]);
|
this.container.onUpdateTabsButtons([]);
|
||||||
this.container.tabsManager.refreshActiveTab(
|
this.container.tabsManager.refreshActiveTab(
|
||||||
@@ -203,6 +206,12 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
databaseNode.children.push(this.buildCollectionNode(database, collection))
|
databaseNode.children.push(this.buildCollectionNode(database, collection))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
database.collections.subscribe((collections: ViewModels.Collection[]) => {
|
||||||
|
collections.forEach((collection: ViewModels.Collection) =>
|
||||||
|
databaseNode.children.push(this.buildCollectionNode(database, collection))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return databaseNode;
|
return databaseNode;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import { deleteStoredProcedure } from "../../Common/dataAccess/deleteStoredProcedure";
|
||||||
|
import { executeStoredProcedure } from "../../Common/DocumentClientUtilityBase";
|
||||||
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
|
||||||
import StoredProcedureTab from "../Tabs/StoredProcedureTab";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { deleteStoredProcedure, executeStoredProcedure } from "../../Common/DocumentClientUtilityBase";
|
import StoredProcedureTab from "../Tabs/StoredProcedureTab";
|
||||||
import TabsBase from "../Tabs/TabsBase";
|
import TabsBase from "../Tabs/TabsBase";
|
||||||
|
|
||||||
const sampleStoredProcedureBody: string = `// SAMPLE STORED PROCEDURE
|
const sampleStoredProcedureBody: string = `// SAMPLE STORED PROCEDURE
|
||||||
@@ -47,20 +47,20 @@ export default class StoredProcedure {
|
|||||||
public body: ko.Observable<string>;
|
public body: ko.Observable<string>;
|
||||||
public isExecuteEnabled: boolean;
|
public isExecuteEnabled: boolean;
|
||||||
|
|
||||||
constructor(container: Explorer, collection: ViewModels.Collection, data: DataModels.StoredProcedure) {
|
constructor(container: Explorer, collection: ViewModels.Collection, data: StoredProcedureDefinition & Resource) {
|
||||||
this.nodeKind = "StoredProcedure";
|
this.nodeKind = "StoredProcedure";
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.collection = collection;
|
this.collection = collection;
|
||||||
this.self = data._self;
|
this.self = data._self;
|
||||||
this.rid = data._rid;
|
this.rid = data._rid;
|
||||||
this.id = ko.observable(data.id);
|
this.id = ko.observable(data.id);
|
||||||
this.body = ko.observable(data.body);
|
this.body = ko.observable(data.body as string);
|
||||||
this.isExecuteEnabled = this.container.isFeatureEnabled(Constants.Features.executeSproc);
|
this.isExecuteEnabled = this.container.isFeatureEnabled(Constants.Features.executeSproc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
||||||
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.StoredProcedures).length + 1;
|
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.StoredProcedures).length + 1;
|
||||||
const storedProcedure = <DataModels.StoredProcedure>{
|
const storedProcedure = <StoredProcedureDefinition>{
|
||||||
id: "",
|
id: "",
|
||||||
body: sampleStoredProcedureBody
|
body: sampleStoredProcedureBody
|
||||||
};
|
};
|
||||||
@@ -104,7 +104,7 @@ export default class StoredProcedure {
|
|||||||
if (storedProcedureTab) {
|
if (storedProcedureTab) {
|
||||||
this.container.tabsManager.activateTab(storedProcedureTab);
|
this.container.tabsManager.activateTab(storedProcedureTab);
|
||||||
} else {
|
} else {
|
||||||
const storedProcedureData = <DataModels.StoredProcedure>{
|
const storedProcedureData = <StoredProcedureDefinition>{
|
||||||
_rid: this.rid,
|
_rid: this.rid,
|
||||||
_self: this.self,
|
_self: this.self,
|
||||||
id: this.id(),
|
id: this.id(),
|
||||||
@@ -137,14 +137,7 @@ export default class StoredProcedure {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const storedProcedureData = <DataModels.StoredProcedure>{
|
deleteStoredProcedure(this.collection.databaseId, this.collection.id(), this.id()).then(
|
||||||
_rid: this.rid,
|
|
||||||
_self: this.self,
|
|
||||||
id: this.id(),
|
|
||||||
body: this.body()
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteStoredProcedure(this.collection, storedProcedureData).then(
|
|
||||||
() => {
|
() => {
|
||||||
this.container.tabsManager.removeTabByComparator((tab: TabsBase) => tab.node && tab.node.rid === this.rid);
|
this.container.tabsManager.removeTabByComparator((tab: TabsBase) => tab.node && tab.node.rid === this.rid);
|
||||||
this.collection.children.remove(this);
|
this.collection.children.remove(this);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
|
import { StoredProcedureDefinition } from "@azure/cosmos";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import { deleteTrigger } from "../../Common/dataAccess/deleteTrigger";
|
||||||
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import TriggerTab from "../Tabs/TriggerTab";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { deleteTrigger } from "../../Common/DocumentClientUtilityBase";
|
import TriggerTab from "../Tabs/TriggerTab";
|
||||||
|
|
||||||
export default class Trigger {
|
export default class Trigger {
|
||||||
public nodeKind: string;
|
public nodeKind: string;
|
||||||
@@ -43,7 +43,7 @@ export default class Trigger {
|
|||||||
|
|
||||||
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
||||||
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Triggers).length + 1;
|
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Triggers).length + 1;
|
||||||
const trigger = <DataModels.Trigger>{
|
const trigger = <StoredProcedureDefinition>{
|
||||||
id: "",
|
id: "",
|
||||||
body: "function trigger(){}",
|
body: "function trigger(){}",
|
||||||
triggerOperation: "All",
|
triggerOperation: "All",
|
||||||
@@ -79,7 +79,7 @@ export default class Trigger {
|
|||||||
if (triggerTab) {
|
if (triggerTab) {
|
||||||
this.container.tabsManager.activateTab(triggerTab);
|
this.container.tabsManager.activateTab(triggerTab);
|
||||||
} else {
|
} else {
|
||||||
const triggerData = <DataModels.Trigger>{
|
const triggerData = <StoredProcedureDefinition>{
|
||||||
_rid: this.rid,
|
_rid: this.rid,
|
||||||
_self: this.self,
|
_self: this.self,
|
||||||
id: this.id(),
|
id: this.id(),
|
||||||
@@ -114,16 +114,7 @@ export default class Trigger {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const triggerData = <DataModels.Trigger>{
|
deleteTrigger(this.collection.databaseId, this.collection.id(), this.id()).then(
|
||||||
_rid: this.rid,
|
|
||||||
_self: this.self,
|
|
||||||
id: this.id(),
|
|
||||||
body: this.body(),
|
|
||||||
triggerOperation: this.triggerOperation(),
|
|
||||||
triggerType: this.triggerType()
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteTrigger(this.collection, triggerData).then(
|
|
||||||
() => {
|
() => {
|
||||||
this.container.tabsManager.removeTabByComparator(tab => tab.node && tab.node.rid === this.rid);
|
this.container.tabsManager.removeTabByComparator(tab => tab.node && tab.node.rid === this.rid);
|
||||||
this.collection.children.remove(this);
|
this.collection.children.remove(this);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
|
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import { deleteUserDefinedFunction } from "../../Common/dataAccess/deleteUserDefinedFunction";
|
||||||
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import UserDefinedFunctionTab from "../Tabs/UserDefinedFunctionTab";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { deleteUserDefinedFunction } from "../../Common/DocumentClientUtilityBase";
|
import UserDefinedFunctionTab from "../Tabs/UserDefinedFunctionTab";
|
||||||
|
|
||||||
export default class UserDefinedFunction {
|
export default class UserDefinedFunction {
|
||||||
public nodeKind: string;
|
public nodeKind: string;
|
||||||
@@ -17,7 +17,7 @@ export default class UserDefinedFunction {
|
|||||||
public id: ko.Observable<string>;
|
public id: ko.Observable<string>;
|
||||||
public body: ko.Observable<string>;
|
public body: ko.Observable<string>;
|
||||||
|
|
||||||
constructor(container: Explorer, collection: ViewModels.Collection, data: DataModels.UserDefinedFunction) {
|
constructor(container: Explorer, collection: ViewModels.Collection, data: UserDefinedFunctionDefinition & Resource) {
|
||||||
this.nodeKind = "UserDefinedFunction";
|
this.nodeKind = "UserDefinedFunction";
|
||||||
this.container = container;
|
this.container = container;
|
||||||
|
|
||||||
@@ -25,12 +25,12 @@ export default class UserDefinedFunction {
|
|||||||
this.self = data._self;
|
this.self = data._self;
|
||||||
this.rid = data._rid;
|
this.rid = data._rid;
|
||||||
this.id = ko.observable(data.id);
|
this.id = ko.observable(data.id);
|
||||||
this.body = ko.observable(data.body);
|
this.body = ko.observable(data.body as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
public static create(source: ViewModels.Collection, event: MouseEvent) {
|
||||||
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.UserDefinedFunctions).length + 1;
|
const id = source.container.tabsManager.getTabs(ViewModels.CollectionTabKind.UserDefinedFunctions).length + 1;
|
||||||
const userDefinedFunction = <DataModels.UserDefinedFunction>{
|
const userDefinedFunction = {
|
||||||
id: "",
|
id: "",
|
||||||
body: "function userDefinedFunction(){}"
|
body: "function userDefinedFunction(){}"
|
||||||
};
|
};
|
||||||
@@ -64,7 +64,7 @@ export default class UserDefinedFunction {
|
|||||||
if (userDefinedFunctionTab) {
|
if (userDefinedFunctionTab) {
|
||||||
this.container.tabsManager.activateTab(userDefinedFunctionTab);
|
this.container.tabsManager.activateTab(userDefinedFunctionTab);
|
||||||
} else {
|
} else {
|
||||||
const userDefinedFunctionData = <DataModels.UserDefinedFunction>{
|
const userDefinedFunctionData = {
|
||||||
_rid: this.rid,
|
_rid: this.rid,
|
||||||
_self: this.self,
|
_self: this.self,
|
||||||
id: this.id(),
|
id: this.id(),
|
||||||
@@ -107,13 +107,7 @@ export default class UserDefinedFunction {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userDefinedFunctionData = <DataModels.UserDefinedFunction>{
|
deleteUserDefinedFunction(this.collection.databaseId, this.collection.id(), this.id()).then(
|
||||||
_rid: this.rid,
|
|
||||||
_self: this.self,
|
|
||||||
id: this.id(),
|
|
||||||
body: this.body()
|
|
||||||
};
|
|
||||||
deleteUserDefinedFunction(this.collection, userDefinedFunctionData).then(
|
|
||||||
() => {
|
() => {
|
||||||
this.container.tabsManager.removeTabByComparator(tab => tab.node && tab.node.rid === this.rid);
|
this.container.tabsManager.removeTabByComparator(tab => tab.node && tab.node.rid === this.rid);
|
||||||
this.collection.children.remove(this);
|
this.collection.children.remove(this);
|
||||||
|
|||||||
@@ -35,13 +35,19 @@ const onInit = async () => {
|
|||||||
const galleryItemJunoResponse = await junoClient.getNotebookInfo(galleryItemId);
|
const galleryItemJunoResponse = await junoClient.getNotebookInfo(galleryItemId);
|
||||||
galleryItem = galleryItemJunoResponse.data;
|
galleryItem = galleryItemJunoResponse.data;
|
||||||
}
|
}
|
||||||
render(notebookUrl, backNavigationText, hideInputs, galleryItem, onBackClick);
|
|
||||||
|
// The main purpose of hiding the prompt is to hide everything when hiding inputs.
|
||||||
|
// It is generally not very useful to just hide the prompt.
|
||||||
|
const hidePrompts = hideInputs;
|
||||||
|
|
||||||
|
render(notebookUrl, backNavigationText, hideInputs, hidePrompts, galleryItem, onBackClick);
|
||||||
};
|
};
|
||||||
|
|
||||||
const render = (
|
const render = (
|
||||||
notebookUrl: string,
|
notebookUrl: string,
|
||||||
backNavigationText: string,
|
backNavigationText: string,
|
||||||
hideInputs: boolean,
|
hideInputs?: boolean,
|
||||||
|
hidePrompts?: boolean,
|
||||||
galleryItem?: IGalleryItem,
|
galleryItem?: IGalleryItem,
|
||||||
onBackClick?: () => void
|
onBackClick?: () => void
|
||||||
) => {
|
) => {
|
||||||
@@ -51,6 +57,7 @@ const render = (
|
|||||||
galleryItem,
|
galleryItem,
|
||||||
backNavigationText,
|
backNavigationText,
|
||||||
hideInputs,
|
hideInputs,
|
||||||
|
hidePrompts,
|
||||||
onBackClick: onBackClick,
|
onBackClick: onBackClick,
|
||||||
onTagClick: undefined
|
onTagClick: undefined
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ export enum Action {
|
|||||||
NotebooksGitHubManualRepoAdd,
|
NotebooksGitHubManualRepoAdd,
|
||||||
NotebooksGitHubManageRepo,
|
NotebooksGitHubManageRepo,
|
||||||
NotebooksGitHubCommit,
|
NotebooksGitHubCommit,
|
||||||
NotebooksGitHubDisconnect
|
NotebooksGitHubDisconnect,
|
||||||
|
OpenTerminal
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ActionModifiers = {
|
export const ActionModifiers = {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function trace(action: Action, actionModifier: string = ActionModifiers.M
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
appInsights.trackEvent({ name: Action[action] }, getData(data));
|
appInsights.trackEvent({ name: Action[action] }, getData(actionModifier, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceStart(action: Action, data?: unknown): number {
|
export function traceStart(action: Action, data?: unknown): number {
|
||||||
@@ -49,7 +49,7 @@ export function traceSuccess(action: Action, data?: unknown, timestamp?: number)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
appInsights.stopTrackEvent(Action[action], getData(data));
|
appInsights.stopTrackEvent(Action[action], getData(ActionModifiers.Success, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceFailure(action: Action, data?: unknown, timestamp?: number): void {
|
export function traceFailure(action: Action, data?: unknown, timestamp?: number): void {
|
||||||
@@ -63,7 +63,7 @@ export function traceFailure(action: Action, data?: unknown, timestamp?: number)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
appInsights.stopTrackEvent(Action[action], getData(data));
|
appInsights.stopTrackEvent(Action[action], getData(ActionModifiers.Failed, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceCancel(action: Action, data?: unknown, timestamp?: number): void {
|
export function traceCancel(action: Action, data?: unknown, timestamp?: number): void {
|
||||||
@@ -77,7 +77,7 @@ export function traceCancel(action: Action, data?: unknown, timestamp?: number):
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
appInsights.stopTrackEvent(Action[action], getData(data));
|
appInsights.stopTrackEvent(Action[action], getData(ActionModifiers.Cancel, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceOpen(action: Action, data?: unknown, timestamp?: number): number {
|
export function traceOpen(action: Action, data?: unknown, timestamp?: number): number {
|
||||||
@@ -112,7 +112,7 @@ export function traceMark(action: Action, data?: unknown, timestamp?: number): n
|
|||||||
return validTimestamp;
|
return validTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getData(data: unknown = {}): { [key: string]: string } | undefined {
|
function getData(actionModifier: string, data: unknown = {}): { [key: string]: string } | undefined {
|
||||||
if (typeof data === "string") {
|
if (typeof data === "string") {
|
||||||
data = { message: data };
|
data = { message: data };
|
||||||
}
|
}
|
||||||
@@ -124,6 +124,7 @@ function getData(data: unknown = {}): { [key: string]: string } | undefined {
|
|||||||
subscriptionId: userContext.subscriptionId as string,
|
subscriptionId: userContext.subscriptionId as string,
|
||||||
platform: configContext.platform,
|
platform: configContext.platform,
|
||||||
env: process.env.NODE_ENV as string,
|
env: process.env.NODE_ENV as string,
|
||||||
|
actionModifier,
|
||||||
...data
|
...data
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { ApplicationInsights } from "@microsoft/applicationinsights-web";
|
|||||||
|
|
||||||
const appInsights = new ApplicationInsights({
|
const appInsights = new ApplicationInsights({
|
||||||
config: {
|
config: {
|
||||||
instrumentationKey: "fa645d97-6237-4656-9559-0ee0cb55ee49",
|
instrumentationKey: "fa645d97-6237-4656-9559-0ee0cb55ee49"
|
||||||
disableFetchTracking: false
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
appInsights.loadAppInsights();
|
appInsights.loadAppInsights();
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import "@jupyterlab/terminal/style/index.css";
|
|||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { ServerConnection } from "@jupyterlab/services";
|
import { ServerConnection } from "@jupyterlab/services";
|
||||||
import { JupyterLabAppFactory } from "./JupyterLabAppFactory";
|
import { JupyterLabAppFactory } from "./JupyterLabAppFactory";
|
||||||
|
import { Action } from "../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
|
||||||
const getUrlVars = (): { [key: string]: string } => {
|
const getUrlVars = (): { [key: string]: string } => {
|
||||||
const vars: { [key: string]: string } = {};
|
const vars: { [key: string]: string } = {};
|
||||||
@@ -14,10 +16,7 @@ const getUrlVars = (): { [key: string]: string } => {
|
|||||||
return vars;
|
return vars;
|
||||||
};
|
};
|
||||||
|
|
||||||
const main = (): void => {
|
const createServerSettings = (urlVars: { [key: string]: string }): ServerConnection.ISettings => {
|
||||||
const urlVars = getUrlVars();
|
|
||||||
console.log("URL parameters", urlVars);
|
|
||||||
|
|
||||||
let body: BodyInit;
|
let body: BodyInit;
|
||||||
if (urlVars.hasOwnProperty("terminalEndpoint")) {
|
if (urlVars.hasOwnProperty("terminalEndpoint")) {
|
||||||
body = JSON.stringify({
|
body = JSON.stringify({
|
||||||
@@ -39,14 +38,29 @@ const main = (): void => {
|
|||||||
fetch: window.parent.fetch
|
fetch: window.parent.fetch
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const serverSettings = ServerConnection.makeSettings(options);
|
|
||||||
|
|
||||||
|
return ServerConnection.makeSettings(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const main = async (): Promise<void> => {
|
||||||
|
const urlVars = getUrlVars();
|
||||||
|
const serverSettings = createServerSettings(urlVars);
|
||||||
|
|
||||||
|
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, {
|
||||||
|
baseUrl: serverSettings.baseUrl
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
if (urlVars.hasOwnProperty("terminal")) {
|
if (urlVars.hasOwnProperty("terminal")) {
|
||||||
JupyterLabAppFactory.createTerminalApp(serverSettings);
|
await JupyterLabAppFactory.createTerminalApp(serverSettings);
|
||||||
return;
|
} else {
|
||||||
|
throw new Error("Only terminal is supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Only terminal is supported");
|
TelemetryProcessor.traceSuccess(Action.OpenTerminal, startTime);
|
||||||
|
} catch (error) {
|
||||||
|
TelemetryProcessor.traceFailure(Action.OpenTerminal, startTime);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("load", main);
|
window.addEventListener("load", main);
|
||||||
|
|||||||
17
src/Utils/BlobUtils.ts
Normal file
17
src/Utils/BlobUtils.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export const stringToBlob = (data: string, contentType: string, sliceSize = 512): Blob => {
|
||||||
|
const byteArrays = [];
|
||||||
|
|
||||||
|
for (let offset = 0; offset < data.length; offset += sliceSize) {
|
||||||
|
const slice = data.slice(offset, offset + sliceSize);
|
||||||
|
|
||||||
|
const byteNumbers = new Array(slice.length);
|
||||||
|
for (let i = 0; i < slice.length; i++) {
|
||||||
|
byteNumbers[i] = slice.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const byteArray = new Uint8Array(byteNumbers);
|
||||||
|
byteArrays.push(byteArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Blob(byteArrays, { type: contentType });
|
||||||
|
};
|
||||||
21
src/Utils/MessageValidation.test.ts
Normal file
21
src/Utils/MessageValidation.test.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { isInvalidParentFrameOrigin } from "./MessageValidation";
|
||||||
|
|
||||||
|
test.each`
|
||||||
|
domain | expected
|
||||||
|
${"https://cosmos.azure.com"} | ${false}
|
||||||
|
${"https://cosmos.azure.us"} | ${false}
|
||||||
|
${"https://cosmos.azure.cn"} | ${false}
|
||||||
|
${"https://cosmos.microsoftazure.de"} | ${false}
|
||||||
|
${"https://subdomain.portal.azure.com"} | ${false}
|
||||||
|
${"https://subdomain.portal.azure.us"} | ${false}
|
||||||
|
${"https://subdomain.portal.azure.cn"} | ${false}
|
||||||
|
${"https://subdomain.microsoftazure.de"} | ${false}
|
||||||
|
${"https://main.documentdb.ext.azure.com"} | ${false}
|
||||||
|
${"https://main.documentdb.ext.azure.us"} | ${false}
|
||||||
|
${"https://main.documentdb.ext.azure.cn"} | ${false}
|
||||||
|
${"https://main.documentdb.ext.microsoftazure.de"} | ${false}
|
||||||
|
${"https://random.domain"} | ${true}
|
||||||
|
${"https://malicious.cloudapp.azure.com"} | ${true}
|
||||||
|
`("returns $expected when called with $domain", ({ domain, expected }) => {
|
||||||
|
expect(isInvalidParentFrameOrigin({ origin: domain } as MessageEvent)).toBe(expected);
|
||||||
|
});
|
||||||
@@ -4,13 +4,18 @@ export function isInvalidParentFrameOrigin(event: MessageEvent): boolean {
|
|||||||
return !isValidOrigin(configContext.allowedParentFrameOrigins, event);
|
return !isValidOrigin(configContext.allowedParentFrameOrigins, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidOrigin(allowedOrigins: RegExp, event: MessageEvent): boolean {
|
function isValidOrigin(allowedOrigins: string[], event: MessageEvent): boolean {
|
||||||
const eventOrigin = (event && event.origin) || "";
|
const eventOrigin = (event && event.origin) || "";
|
||||||
const windowOrigin = (window && window.origin) || "";
|
const windowOrigin = (window && window.origin) || "";
|
||||||
if (eventOrigin === windowOrigin) {
|
if (eventOrigin === windowOrigin) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = allowedOrigins && allowedOrigins.test(eventOrigin);
|
for (const origin of allowedOrigins) {
|
||||||
return result;
|
const result = new RegExp(origin).test(eventOrigin);
|
||||||
|
if (result) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,30 @@ Instead, generate ARM clients that consume this function with stricter typing.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import promiseRetry, { AbortError } from "p-retry";
|
import promiseRetry, { AbortError } from "p-retry";
|
||||||
import { ErrorResponse } from "./generatedClients/2020-04-01/types";
|
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
|
|
||||||
interface ARMError extends Error {
|
interface ErrorResponse {
|
||||||
code: string;
|
code: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ARM sometimes returns an error wrapped in a top level error object
|
||||||
|
// Example: 409 Conflict error when trying to delete a locked resource
|
||||||
|
interface WrappedErrorResponse {
|
||||||
|
error: ErrorResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParsedErrorResponse = ErrorResponse | WrappedErrorResponse;
|
||||||
|
|
||||||
|
export class ARMError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
// Set the prototype explicitly.
|
||||||
|
// https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-extending-built-ins-like-error-array-and-map-work
|
||||||
|
Object.setPrototypeOf(this, ARMError.prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
public code: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
@@ -33,9 +52,20 @@ export async function armRequest<T>({ host, path, apiVersion, method, body: requ
|
|||||||
body: requestBody ? JSON.stringify(requestBody) : undefined
|
body: requestBody ? JSON.stringify(requestBody) : undefined
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorResponse = (await response.json()) as ErrorResponse;
|
let error: ARMError;
|
||||||
const error = new Error(errorResponse.message) as ARMError;
|
try {
|
||||||
|
const errorResponse = (await response.json()) as ParsedErrorResponse;
|
||||||
|
if ("error" in errorResponse) {
|
||||||
|
error = new ARMError(errorResponse.error.message);
|
||||||
|
error.code = errorResponse.error.code;
|
||||||
|
} else {
|
||||||
|
error = new ARMError(errorResponse.message);
|
||||||
error.code = errorResponse.code;
|
error.code = errorResponse.code;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(await response.text());
|
||||||
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,24 +56,27 @@ describe('Collection Add and Delete SQL spec', () => {
|
|||||||
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
||||||
|
|
||||||
await frame.click(`div[data-test="${dbId}"]`);
|
await frame.click(`div[data-test="${dbId}"]`);
|
||||||
await frame.waitFor(`span[title="${collectionId}"]`);
|
await frame.waitFor(`span[title="${collectionId}"]`, { visible: true });
|
||||||
|
await frame.waitFor(3000)
|
||||||
|
await frame.waitFor(`span[title="${collectionId}"]`, { visible: true });
|
||||||
|
|
||||||
// delete container
|
// delete container
|
||||||
|
|
||||||
// click context menu for container
|
// click context menu for container
|
||||||
await frame.waitFor(`div[data-test="${collectionId}"] > div > button`);
|
await frame.waitFor(`div[data-test="${collectionId}"] > div > button`, { visible: true });
|
||||||
|
await frame.waitFor(`span[title="${collectionId}"]`, { visible: true });
|
||||||
await frame.click(`div[data-test="${collectionId}"] > div > button`);
|
await frame.click(`div[data-test="${collectionId}"] > div > button`);
|
||||||
|
await frame.waitFor(2000)
|
||||||
// click delete container
|
// click delete container
|
||||||
await frame.waitForSelector('body > div.ms-Layer.ms-Layer--fixed');
|
await frame.waitFor('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]', { visible: true });
|
||||||
await frame.waitFor(1000);
|
await frame.click('span[class="treeComponentMenuItemLabel deleteCollectionMenuItemLabel"]');
|
||||||
const elements = await frame.$$('span[class="treeComponentMenuItemLabel"]')
|
|
||||||
await elements[4].click();
|
|
||||||
|
|
||||||
// confirm delete container
|
// confirm delete container
|
||||||
|
await frame.waitFor('input[data-test="confirmCollectionId"]', { visible: true })
|
||||||
await frame.type('input[data-test="confirmCollectionId"]', collectionId.trim());
|
await frame.type('input[data-test="confirmCollectionId"]', collectionId.trim());
|
||||||
|
|
||||||
// click delete
|
// click delete
|
||||||
|
await frame.waitFor('input[data-test="deleteCollection"]', { visible: true })
|
||||||
await frame.click('input[data-test="deleteCollection"]');
|
await frame.click('input[data-test="deleteCollection"]');
|
||||||
await frame.waitFor(5000);
|
await frame.waitFor(5000);
|
||||||
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
||||||
@@ -87,9 +90,8 @@ describe('Collection Add and Delete SQL spec', () => {
|
|||||||
await button.asElement().click();
|
await button.asElement().click();
|
||||||
|
|
||||||
// click delete database
|
// click delete database
|
||||||
await frame.waitFor(1000);
|
await frame.waitFor('span[class="treeComponentMenuItemLabel deleteDatabaseMenuItemLabel"]');
|
||||||
const dbElements = await frame.$$('span[class="treeComponentMenuItemLabel"]')
|
await frame.click('span[class="treeComponentMenuItemLabel deleteDatabaseMenuItemLabel"]');
|
||||||
await dbElements[1].click();
|
|
||||||
|
|
||||||
// confirm delete database
|
// confirm delete database
|
||||||
await frame.type('input[data-test="confirmDatabaseId"]', dbId.trim());
|
await frame.type('input[data-test="confirmDatabaseId"]', dbId.trim());
|
||||||
|
|||||||
Reference in New Issue
Block a user