Compare commits

..

3 Commits

Author SHA1 Message Date
Steve Faulkner
a636c70ce8 Remove old panel component 2021-02-24 13:07:15 -06:00
Steve Faulkner
92891a4878 Remove unused SparkMasterTab 2020-10-26 22:15:19 -05:00
Steve Faulkner
fe58722002 Remove cached resource calls (#297) 2020-10-26 17:17:41 -05:00
59 changed files with 580 additions and 675 deletions

View File

@@ -206,7 +206,6 @@ src/Explorer/Tabs/QueryTablesTab.ts
src/Explorer/Tabs/ScriptTabBase.ts
src/Explorer/Tabs/SettingsTab.test.ts
src/Explorer/Tabs/SettingsTab.ts
src/Explorer/Tabs/SparkMasterTab.ts
src/Explorer/Tabs/StoredProcedureTab.ts
src/Explorer/Tabs/TabComponents.ts
src/Explorer/Tabs/TabsBase.ts

View File

@@ -7,6 +7,7 @@ import {
Resource
} from "@azure/cosmos";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import Q from "q";
import { configContext, Platform } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels";
import { MessageTypes } from "../Contracts/ExplorerContracts";
@@ -38,17 +39,18 @@ export function getCommonQueryOptions(options: FeedOptions): any {
return options;
}
export async function queryDocuments(
export function queryDocuments(
databaseId: string,
containerId: string,
query: string,
options: any
): Promise<QueryIterator<ItemDefinition & Resource>> {
): Q.Promise<QueryIterator<ItemDefinition & Resource>> {
options = getCommonQueryOptions(options);
return client()
const documentsIterator = client()
.database(databaseId)
.container(containerId)
.items.query(query, options);
return Q(documentsIterator);
}
export function getPartitionKeyHeaderForConflict(conflictId: ConflictId): Object {
@@ -74,15 +76,17 @@ export function updateDocument(
collection: ViewModels.CollectionBase,
documentId: DocumentId,
newDocument: any
): Promise<any> {
): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.replace(newDocument)
.then(response => response.resource);
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.replace(newDocument)
.then(response => response.resource)
);
}
export function executeStoredProcedure(
@@ -90,89 +94,89 @@ export function executeStoredProcedure(
storedProcedure: StoredProcedure,
partitionKeyValue: any,
params: any[]
): Promise<any> {
return Promise.race([
): Q.Promise<any> {
// TODO remove this deferred. Kept it because of timeout code at bottom of function
const deferred = Q.defer<any>();
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedure(storedProcedure.id())
.execute(partitionKeyValue, params, { enableScriptLogging: true })
.then(response =>
deferred.resolve({
result: response.resource,
scriptLogs: response.headers[Constants.HttpHeaders.scriptLogResults]
})
)
.catch(error => deferred.reject(error));
return deferred.promise.timeout(
Constants.ClientDefaults.requestTimeoutMs,
`Request timed out while executing stored procedure ${storedProcedure.id()}`
);
}
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedure(storedProcedure.id())
.execute(partitionKeyValue, params, { enableScriptLogging: true })
.then(response => ({
result: response.resource,
scriptLogs: response.headers[Constants.HttpHeaders.scriptLogResults]
})),
new Promise((_, reject) =>
setTimeout(
() => reject(`Request timed out while executing stored procedure ${storedProcedure.id()}`),
Constants.ClientDefaults.requestTimeoutMs
)
)
]);
.items.create(newDocument)
.then(response => response.resource)
);
}
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Promise<any> {
return client()
.database(collection.databaseId)
.container(collection.id())
.items.create(newDocument)
.then(response => response.resource);
}
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Promise<any> {
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.read()
.then(response => response.resource);
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.read()
.then(response => response.resource)
);
}
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Promise<any> {
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.delete();
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.delete()
);
}
export function deleteConflict(
collection: ViewModels.CollectionBase,
conflictId: ConflictId,
options: any = {}
): Promise<any> {
): Q.Promise<any> {
options.partitionKey = options.partitionKey || getPartitionKeyHeaderForConflict(conflictId);
return client()
.database(collection.databaseId)
.container(collection.id())
.conflict(conflictId.id())
.delete(options);
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.conflict(conflictId.id())
.delete(options)
);
}
export async function refreshCachedOffers(): Promise<void> {
if (configContext.platform === Platform.Portal) {
sendCachedDataMessage(MessageTypes.RefreshOffers, []);
}
}
export async function refreshCachedResources(options?: any): Promise<void> {
if (configContext.platform === Platform.Portal) {
sendCachedDataMessage(MessageTypes.RefreshResources, []);
}
}
export async function queryConflicts(
export function queryConflicts(
databaseId: string,
containerId: string,
query: string,
options: any
): Promise<QueryIterator<ConflictDefinition & Resource>> {
return client()
): Q.Promise<QueryIterator<ConflictDefinition & Resource>> {
const documentsIterator = client()
.database(databaseId)
.container(containerId)
.conflicts.query(query, options);
return Q(documentsIterator);
}

View File

@@ -1,4 +1,5 @@
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import Q from "q";
import * as ViewModels from "../Contracts/ViewModels";
import ConflictId from "../Explorer/Tree/ConflictId";
import DocumentId from "../Explorer/Tree/DocumentId";
@@ -15,7 +16,7 @@ export function queryDocuments(
containerId: string,
query: string,
options: any
): Promise<QueryIterator<ItemDefinition & Resource>> {
): Q.Promise<QueryIterator<ItemDefinition & Resource>> {
return DataAccessUtilityBase.queryDocuments(databaseId, containerId, query, options);
}
@@ -24,7 +25,7 @@ export function queryConflicts(
containerId: string,
query: string,
options: any
): Promise<QueryIterator<ConflictDefinition & Resource>> {
): Q.Promise<QueryIterator<ConflictDefinition & Resource>> {
return DataAccessUtilityBase.queryConflicts(databaseId, containerId, query, options);
}
@@ -42,15 +43,17 @@ export function executeStoredProcedure(
storedProcedure: StoredProcedure,
partitionKeyValue: any,
params: any[]
): Promise<any> {
): Q.Promise<any> {
var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Executing stored procedure ${storedProcedure.id()}`);
return DataAccessUtilityBase.executeStoredProcedure(collection, storedProcedure, partitionKeyValue, params)
DataAccessUtilityBase.executeStoredProcedure(collection, storedProcedure, partitionKeyValue, params)
.then(
(response: any) => {
deferred.resolve(response);
logConsoleInfo(
`Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
return response;
},
(error: any) => {
handleError(
@@ -58,10 +61,14 @@ export function executeStoredProcedure(
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`,
"ExecuteStoredProcedure"
);
throw error;
deferred.reject(error);
}
)
.finally(clearMessage);
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function queryDocumentsPage(
@@ -69,114 +76,142 @@ export function queryDocumentsPage(
documentsIterator: MinimalQueryIterator,
firstItemIndex: number,
options: any
): Promise<ViewModels.QueryResults> {
): Q.Promise<ViewModels.QueryResults> {
var deferred = Q.defer<ViewModels.QueryResults>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
return nextPage(documentsIterator, firstItemIndex)
Q(nextPage(documentsIterator, firstItemIndex))
.then(
(result: ViewModels.QueryResults) => {
const itemCount = (result.documents && result.documents.length) || 0;
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
return result;
deferred.resolve(result);
},
(error: any) => {
handleError(error, `Failed to query ${entityName} for container ${resourceName}`, "QueryDocumentsPage");
throw error;
deferred.reject(error);
}
)
.finally(clearMessage);
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Promise<any> {
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Reading ${entityName} ${documentId.id()}`);
return DataAccessUtilityBase.readDocument(collection, documentId)
.catch((error: any) => {
handleError(error, `Failed to read ${entityName} ${documentId.id()}`, "ReadDocument");
throw error;
})
.finally(clearMessage);
DataAccessUtilityBase.readDocument(collection, documentId)
.then(
(document: any) => {
deferred.resolve(document);
},
(error: any) => {
handleError(error, `Failed to read ${entityName} ${documentId.id()}`, "ReadDocument");
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function updateDocument(
collection: ViewModels.CollectionBase,
documentId: DocumentId,
newDocument: any
): Promise<any> {
): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Updating ${entityName} ${documentId.id()}`);
return DataAccessUtilityBase.updateDocument(collection, documentId, newDocument)
DataAccessUtilityBase.updateDocument(collection, documentId, newDocument)
.then(
(updatedDocument: any) => {
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
return updatedDocument;
deferred.resolve(updatedDocument);
},
(error: any) => {
handleError(error, `Failed to update ${entityName} ${documentId.id()}`, "UpdateDocument");
throw error;
deferred.reject(error);
}
)
.finally(clearMessage);
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Promise<any> {
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Creating new ${entityName} for container ${collection.id()}`);
return DataAccessUtilityBase.createDocument(collection, newDocument)
DataAccessUtilityBase.createDocument(collection, newDocument)
.then(
(savedDocument: any) => {
logConsoleInfo(`Successfully created new ${entityName} for container ${collection.id()}`);
return savedDocument;
deferred.resolve(savedDocument);
},
(error: any) => {
handleError(error, `Error while creating new ${entityName} for container ${collection.id()}`, "CreateDocument");
throw error;
deferred.reject(error);
}
)
.finally(clearMessage);
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Promise<any> {
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`);
return DataAccessUtilityBase.deleteDocument(collection, documentId)
DataAccessUtilityBase.deleteDocument(collection, documentId)
.then(
(response: any) => {
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
return response;
deferred.resolve(response);
},
(error: any) => {
handleError(error, `Error while deleting ${entityName} ${documentId.id()}`, "DeleteDocument");
throw error;
deferred.reject(error);
}
)
.finally(clearMessage);
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function deleteConflict(
collection: ViewModels.CollectionBase,
conflictId: ConflictId,
options?: any
): Promise<any> {
): Q.Promise<any> {
var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Deleting conflict ${conflictId.id()}`);
return DataAccessUtilityBase.deleteConflict(collection, conflictId, options)
DataAccessUtilityBase.deleteConflict(collection, conflictId, options)
.then(
(response: any) => {
logConsoleInfo(`Successfully deleted conflict ${conflictId.id()}`);
return response;
deferred.resolve(response);
},
(error: any) => {
handleError(error, `Error while deleting conflict ${conflictId.id()}`, "DeleteConflict");
throw error;
deferred.reject(error);
}
)
.finally(clearMessage);
}
.finally(() => {
clearMessage();
});
export function refreshCachedResources(options: any = {}): Promise<void> {
return DataAccessUtilityBase.refreshCachedResources(options);
}
export function refreshCachedOffers(): Promise<void> {
return DataAccessUtilityBase.refreshCachedOffers();
return deferred.promise;
}

View File

@@ -136,7 +136,7 @@ export class QueriesClient {
return queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
.then(
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
const fetchQueries = (firstItemIndex: number): Promise<ViewModels.QueryResults> =>
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex, options);
return QueryUtils.queryAllPages(fetchQueries).then(
(results: ViewModels.QueryResults) => {

View File

@@ -23,7 +23,6 @@ import {
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
import { refreshCachedResources } from "../DataAccessUtilityBase";
import { userContext } from "../../UserContext";
import { createDatabase } from "./createDatabase";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
@@ -54,7 +53,6 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
}
logConsoleInfo(`Successfully created container ${params.collectionId}`);
await refreshCachedResources();
return collection;
} catch (error) {
handleError(error, `Error while creating container ${params.collectionId}`, "CreateCollection");

View File

@@ -26,7 +26,6 @@ import {
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
import { refreshCachedOffers, refreshCachedResources } from "../DataAccessUtilityBase";
import { userContext } from "../../UserContext";
export async function createDatabase(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
@@ -39,8 +38,6 @@ export async function createDatabase(params: DataModels.CreateDatabaseParams): P
? createDatabaseWithARM(params)
: createDatabaseWithSDK(params));
await refreshCachedResources();
await refreshCachedOffers();
logConsoleInfo(`Successfully created database ${params.databaseId}`);
return database;
} catch (error) {

View File

@@ -7,7 +7,6 @@ import { AuthType } from "../../AuthType";
import { client } from "../CosmosClient";
import { updateUserContext } from "../../UserContext";
import { DatabaseAccount } from "../../Contracts/DataModels";
import { sendCachedDataMessage } from "../MessageHandler";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
describe("deleteCollection", () => {
@@ -18,7 +17,6 @@ describe("deleteCollection", () => {
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
(sendCachedDataMessage as jest.Mock).mockResolvedValue(undefined);
});
it("should call ARM if logged in with AAD", async () => {

View File

@@ -9,7 +9,6 @@ import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { userContext } from "../../UserContext";
import { client } from "../CosmosClient";
import { refreshCachedResources } from "../DataAccessUtilityBase";
export async function deleteCollection(databaseId: string, collectionId: string): Promise<void> {
const clearMessage = logConsoleProgress(`Deleting container ${collectionId}`);
@@ -23,7 +22,6 @@ export async function deleteCollection(databaseId: string, collectionId: string)
.delete();
}
logConsoleInfo(`Successfully deleted container ${collectionId}`);
await refreshCachedResources();
} catch (error) {
handleError(error, `Error while deleting container ${collectionId}`, "DeleteCollection");
throw error;

View File

@@ -7,7 +7,6 @@ import { AuthType } from "../../AuthType";
import { client } from "../CosmosClient";
import { updateUserContext } from "../../UserContext";
import { DatabaseAccount } from "../../Contracts/DataModels";
import { sendCachedDataMessage } from "../MessageHandler";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
describe("deleteDatabase", () => {
@@ -18,7 +17,6 @@ describe("deleteDatabase", () => {
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
(sendCachedDataMessage as jest.Mock).mockResolvedValue(undefined);
});
it("should call ARM if logged in with AAD", async () => {

View File

@@ -8,7 +8,6 @@ import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { userContext } from "../../UserContext";
import { client } from "../CosmosClient";
import { refreshCachedResources } from "../DataAccessUtilityBase";
export async function deleteDatabase(databaseId: string): Promise<void> {
const clearMessage = logConsoleProgress(`Deleting database ${databaseId}`);
@@ -25,7 +24,6 @@ export async function deleteDatabase(databaseId: string): Promise<void> {
.delete();
}
logConsoleInfo(`Successfully deleted database ${databaseId}`);
await refreshCachedResources();
} catch (error) {
handleError(error, `Error while deleting database ${databaseId}`, "DeleteDatabase");
throw error;

View File

@@ -1,28 +1,10 @@
import { Offer } from "../../Contracts/DataModels";
import { ClientDefaults } from "../Constants";
import { MessageTypes } from "../../Contracts/ExplorerContracts";
import { Platform, configContext } from "../../ConfigContext";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { sendCachedDataMessage } from "../MessageHandler";
import { userContext } from "../../UserContext";
export const readOffers = async (): Promise<Offer[]> => {
const clearMessage = logConsoleProgress(`Querying offers`);
try {
if (configContext.platform === Platform.Portal) {
const offers = sendCachedDataMessage<Offer[]>(MessageTypes.AllOffers, [
userContext.databaseAccount.id,
ClientDefaults.portalCacheTimeoutMs
]);
clearMessage();
return offers;
}
} catch (error) {
// If error getting cached Offers, continue on and read via SDK
}
try {
const response = await client()

View File

@@ -28,7 +28,6 @@ import {
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { refreshCachedResources } from "../DataAccessUtilityBase";
import { userContext } from "../../UserContext";
export async function updateCollection(
@@ -58,7 +57,6 @@ export async function updateCollection(
}
logConsoleInfo(`Successfully updated container ${collectionId}`);
await refreshCachedResources();
return collection;
} catch (error) {
handleError(error, `Failed to update container ${collectionId}`, "UpdateCollection");

View File

@@ -10,7 +10,6 @@ import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { readCollectionOffer } from "./readCollectionOffer";
import { readDatabaseOffer } from "./readDatabaseOffer";
import { refreshCachedOffers, refreshCachedResources } from "../DataAccessUtilityBase";
import {
updateSqlDatabaseThroughput,
migrateSqlDatabaseToAutoscale,
@@ -70,8 +69,6 @@ export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> =>
} else {
updatedOffer = await updateOfferWithSDK(params);
}
await refreshCachedOffers();
await refreshCachedResources();
logConsoleInfo(`Successfully updated offer for ${offerResourceText}`);
return updatedOffer;
} catch (error) {

View File

@@ -5,6 +5,7 @@ import {
TriggerDefinition,
UserDefinedFunctionDefinition
} from "@azure/cosmos";
import Q from "q";
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
import Explorer from "../Explorer/Explorer";
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
@@ -106,7 +107,7 @@ export interface CollectionBase extends TreeNode {
onDocumentDBDocumentsClick(): void;
onNewQueryClick(source: any, event: MouseEvent, queryText?: string): void;
expandCollection(): Promise<any>;
expandCollection(): Q.Promise<any>;
collapseCollection(): void;
getDatabase(): Database;
}
@@ -171,7 +172,7 @@ export interface Collection extends CollectionBase {
onDragOver(source: Collection, event: { originalEvent: DragEvent }): void;
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
uploadFiles(fileList: FileList): Promise<UploadDetails>;
uploadFiles(fileList: FileList): Q.Promise<UploadDetails>;
getLabel(): string;
}
@@ -289,7 +290,7 @@ export interface DocumentsTabOptions extends TabOptions {
}
export interface SettingsTabV2Options extends TabOptions {
getPendingNotification: Promise<DataModels.Notification>;
getPendingNotification: Q.Promise<DataModels.Notification>;
}
export interface ConflictsTabOptions extends TabOptions {
@@ -354,7 +355,6 @@ export enum CollectionTabKind {
Notebook = 13 /* Deprecated */,
Terminal = 14,
NotebookV2 = 15,
SparkMasterTab = 16,
Gallery = 17,
NotebookViewer = 18,
SettingsV2 = 19

View File

@@ -20,10 +20,6 @@ describe("Component Registerer", () => {
expect(ko.components.isRegistered("graph-style")).toBe(true);
});
it("should register collapsible-panel component", () => {
expect(ko.components.isRegistered("collapsible-panel")).toBe(true);
});
it("should register json-editor component", () => {
expect(ko.components.isRegistered("json-editor")).toBe(true);
});

View File

@@ -1,7 +1,6 @@
import * as ko from "knockout";
import * as PaneComponents from "./Panes/PaneComponents";
import * as TabComponents from "./Tabs/TabComponents";
import { CollapsiblePanelComponent } from "./Controls/CollapsiblePanel/CollapsiblePanelComponent";
import { DiffEditorComponent } from "./Controls/DiffEditor/DiffEditorComponent";
import { DynamicListComponent } from "./Controls/DynamicList/DynamicListComponent";
import { EditorComponent } from "./Controls/Editor/EditorComponent";
@@ -18,7 +17,6 @@ ko.components.register("input-typeahead", new InputTypeaheadComponent());
ko.components.register("new-vertex-form", NewVertexComponent);
ko.components.register("error-display", new ErrorDisplayComponent());
ko.components.register("graph-style", GraphStyleComponent);
ko.components.register("collapsible-panel", new CollapsiblePanelComponent());
ko.components.register("editor", new EditorComponent());
ko.components.register("json-editor", new JsonEditorComponent());
ko.components.register("diff-editor", new DiffEditorComponent());
@@ -42,7 +40,6 @@ ko.components.register("mongo-shell-tab", new TabComponents.MongoShellTab());
ko.components.register("conflicts-tab", new TabComponents.ConflictsTab());
ko.components.register("notebookv2-tab", new TabComponents.NotebookV2Tab());
ko.components.register("terminal-tab", new TabComponents.TerminalTab());
ko.components.register("spark-master-tab", new TabComponents.SparkMasterTab());
ko.components.register("gallery-tab", new TabComponents.GalleryTab());
ko.components.register("notebook-viewer-tab", new TabComponents.NotebookViewerTab());

View File

@@ -1,56 +0,0 @@
import * as ko from "knockout";
import template from "./collapsible-panel-component.html";
/**
* Helper class for ko component registration
*/
export class CollapsiblePanelComponent {
constructor() {
return {
viewModel: CollapsiblePanelViewModel,
template
};
}
}
/**
* Parameters for this component
*/
interface CollapsiblePanelParams {
collapsedTitle: ko.Observable<string>;
expandedTitle: ko.Observable<string>;
isCollapsed?: ko.Observable<boolean>;
collapseToLeft?: boolean;
}
/**
* Collapsible panel:
* Contains a header with [>] button to collapse and an title ("expandedTitle").
* Collapsing the panel:
* - shrinks width to narrow amount
* - hides children
* - shows [<]
* - shows vertical title ("collapsedTitle")
* - the default behavior is to collapse to the right (ie, place this component on the right or use "collapseToLeft" parameter)
*
* How to use in your markup:
* <collapsible-panel params="{ collapsedTitle:'Properties', expandedTitle:'Expanded properties' }">
* <!-- add your markup here: the ko context is the same as outside of collapsible-panel (ie $data) -->
* </collapsible-panel>
*
* Use the optional "isCollapsed" parameter to programmatically collapse/expand the pane from outside the component.
* Use the optional "collapseToLeft" parameter to collapse to the left.
*/
class CollapsiblePanelViewModel {
private params: CollapsiblePanelParams;
private isCollapsed: ko.Observable<boolean>;
public constructor(params: CollapsiblePanelParams) {
this.params = params;
this.isCollapsed = params.isCollapsed || ko.observable(false);
}
private toggleCollapse(): void {
this.isCollapsed(!this.isCollapsed());
}
}

View File

@@ -1,44 +0,0 @@
<div class="collapsiblePanel" data-bind="css: { paneCollapsed:isCollapsed() }">
<div class="panelHeader" data-bind="visible: !isCollapsed()">
<span
class="collapsedIconContainer collapseExpandButton"
data-bind="click:toggleCollapse, css: { 'pull-right':params.collapseToLeft }"
>
<img
class="collapsedIcon imgVerticalAlignment"
src="/imgarrowlefticon.svg"
alt="Collapse"
data-bind="css: { expanded:!isCollapsed(), iconMirror:params.collapseToLeft }"
/>
</span>
<span
class="expandedTitle"
data-bind="text: params.expandedTitle, css:{ iconSpacer:!params.collapseToLeft }"
></span>
</div>
<div class="collapsibleNav nav" data-bind="visible:isCollapsed">
<ul class="nav">
<li class="collapsedBtn collapseExpandButton">
<span class="collapsedIconContainer" data-bind="click: toggleCollapse">
<img
class="collapsedIcon"
src="/imgarrowlefticon.svg"
data-bind="css: { expanded:!isCollapsed(), iconMirror:params.collapseToLeft }"
alt="Expand"
/>
</span>
<span class="rotatedInner" data-bind="click: toggleCollapse">
<span data-bind="text: params.collapsedTitle"></span>
</span>
</li>
</ul>
</div>
<div class="panelContent" data-bind="visible:!isCollapsed()">
<!-- ko with:$parent -->
<!-- ko template: { nodes: $componentTemplateNodes } -->
<!-- /ko -->
<!-- /ko -->
</div>
</div>

View File

@@ -57,7 +57,7 @@ class EditorViewModel extends JsonEditorViewModel {
}
}
protected async getErrorMarkers(input: string): Promise<monaco.editor.IMarkerData[]> {
protected getErrorMarkers(input: string): Q.Promise<monaco.editor.IMarkerData[]> {
return ErrorMarkProvider.getErrorMark(input);
}
}

View File

@@ -1,3 +1,4 @@
import Q from "q";
import * as monaco from "monaco-editor";
import * as ViewModels from "../../../Contracts/ViewModels";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
@@ -106,8 +107,8 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
protected registerCompletionItemProvider() {}
// Interface. Will be implemented in children editor view model such as EditorViewModel.
protected getErrorMarkers(input: string): Promise<monaco.editor.IMarkerData[]> {
return new Promise(() => {});
protected getErrorMarkers(input: string): Q.Promise<monaco.editor.IMarkerData[]> {
return Q.Promise(() => {});
}
protected getEditorLanguage(): string {

View File

@@ -31,6 +31,7 @@ jest.mock("../../../Common/dataAccess/updateCollection", () => ({
}));
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
import { MongoDBCollectionResource } from "../../../Utils/arm/generatedClients/2020-04-01/types";
import Q from "q";
jest.mock("../../../Common/dataAccess/updateOffer", () => ({
updateOffer: jest.fn().mockReturnValue({} as DataModels.Offer)
}));
@@ -46,7 +47,9 @@ describe("SettingsComponent", () => {
hashLocation: "settings",
isActive: ko.observable(false),
onUpdateTabsButtons: undefined,
getPendingNotification: Promise.resolve(undefined)
getPendingNotification: Q.Promise<DataModels.Notification>(() => {
return;
})
})
};

View File

@@ -3,6 +3,7 @@ jest.mock("../Graph/GraphExplorerComponent/GremlinClient");
jest.mock("../../Common/dataAccess/createCollection");
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import Q from "q";
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
import { createDocument } from "../../Common/DocumentClientUtilityBase";
import Explorer from "../Explorer";
@@ -20,7 +21,7 @@ describe("ContainerSampleGenerator", () => {
explorerStub.canExceedMaximumValue = ko.computed<boolean>(() => false);
explorerStub.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
explorerStub.findDatabaseWithId = () => database;
explorerStub.refreshAllDatabases = () => Promise.resolve();
explorerStub.refreshAllDatabases = () => Q.resolve();
return explorerStub;
};

View File

@@ -70,7 +70,7 @@ export class ContainerSampleGenerator {
if (!collection) {
throw new Error("No container to populate");
}
const promises: Promise<any>[] = [];
const promises: Q.Promise<any>[] = [];
if (this.container.isPreferredApiGraph()) {
// For Gremlin, all queries are executed sequentially, because some queries might be dependent on other queries

View File

@@ -15,7 +15,6 @@ import CassandraAddCollectionPane from "./Panes/CassandraAddCollectionPane";
import Database from "./Tree/Database";
import DeleteCollectionConfirmationPane from "./Panes/DeleteCollectionConfirmationPane";
import DeleteDatabaseConfirmationPane from "./Panes/DeleteDatabaseConfirmationPane";
import { refreshCachedResources } from "../Common/DocumentClientUtilityBase";
import { readCollection } from "../Common/dataAccess/readCollection";
import { readDatabases } from "../Common/dataAccess/readDatabases";
import EditTableEntityPane from "./Panes/Tables/EditTableEntityPane";
@@ -217,7 +216,7 @@ export default class Explorer {
public shouldShowShareDialogContents: ko.Observable<boolean>;
public shareAccessData: ko.Observable<AdHocAccessData>;
public renewExplorerShareAccess: (explorer: Explorer, token: string) => Promise<void>;
public renewExplorerShareAccess: (explorer: Explorer, token: string) => Q.Promise<void>;
public renewTokenError: ko.Observable<string>;
public tokenForRenewal: ko.Observable<string>;
public shareAccessToggleState: ko.Observable<ShareAccessToggleState>;
@@ -1120,7 +1119,7 @@ export default class Explorer {
"Initiating connection to account"
);
this.renewExplorerShareAccess(this, this.tokenForRenewal())
.catch((error: any) => {
.fail((error: any) => {
const stringifiedError: string = error.message;
this.renewTokenError("Invalid connection string specified");
NotificationConsoleUtils.logConsoleMessage(
@@ -1157,27 +1156,33 @@ export default class Explorer {
);
}
public async renewShareAccess(token: string): Promise<void> {
public renewShareAccess(token: string): Q.Promise<void> {
if (!this.renewExplorerShareAccess) {
throw "Not implemented";
return Q.reject("Not implemented");
}
const deferred: Q.Deferred<void> = Q.defer<void>();
const id: string = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
"Initiating connection to account"
);
return this.renewExplorerShareAccess(this, token)
this.renewExplorerShareAccess(this, token)
.then(
() => {
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, "Connection successful");
this.renewAdHocAccessPane && this.renewAdHocAccessPane.close();
deferred.resolve();
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, `Failed to connect: ${error.message}`);
throw error;
deferred.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
public displayGuestAccessTokenRenewalPrompt(): void {
@@ -1372,19 +1377,24 @@ export default class Explorer {
}
}
public async refreshDatabaseForResourceToken(): Promise<any> {
public refreshDatabaseForResourceToken(): Q.Promise<any> {
const databaseId = this.resourceTokenDatabaseId();
const collectionId = this.resourceTokenCollectionId();
if (!databaseId || !collectionId) {
throw new Error();
return Q.reject();
}
const collection = await readCollection(databaseId, collectionId);
this.resourceTokenCollection(new ResourceTokenCollection(this, databaseId, collection));
this.selectedNode(this.resourceTokenCollection());
const deferred: Q.Deferred<void> = Q.defer();
readCollection(databaseId, collectionId).then((collection: DataModels.Collection) => {
this.resourceTokenCollection(new ResourceTokenCollection(this, databaseId, collection));
this.selectedNode(this.resourceTokenCollection());
deferred.resolve();
});
return deferred.promise;
}
public refreshAllDatabases(isInitialLoad?: boolean): Promise<any> {
public refreshAllDatabases(isInitialLoad?: boolean): Q.Promise<any> {
this.isRefreshingExplorer(true);
const startKey: number = TelemetryProcessor.traceStart(Action.LoadDatabases, {
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
@@ -1401,85 +1411,89 @@ export default class Explorer {
}
// TODO: Refactor
const deferred: Q.Deferred<any> = Q.defer();
this._setLoadingStatusText("Fetching databases...");
return readDatabases()
.then(
(databases: DataModels.Database[]) => {
this._setLoadingStatusText("Successfully fetched databases.");
readDatabases().then(
(databases: DataModels.Database[]) => {
this._setLoadingStatusText("Successfully fetched databases.");
TelemetryProcessor.traceSuccess(
Action.LoadDatabases,
{
databaseAccountName: this.databaseAccount().name,
defaultExperience: this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree
},
startKey
);
const currentlySelectedNode: ViewModels.TreeNode = this.selectedNode();
const deltaDatabases = this.getDeltaDatabases(databases);
this.addDatabasesToList(deltaDatabases.toAdd);
this.deleteDatabasesFromList(deltaDatabases.toDelete);
this.selectedNode(currentlySelectedNode);
this._setLoadingStatusText("Fetching containers...");
this.refreshAndExpandNewDatabases(deltaDatabases.toAdd)
.then(
() => {
this._setLoadingStatusText("Successfully fetched containers.");
deferred.resolve();
},
reason => {
this._setLoadingStatusText("Failed to fetch containers.");
deferred.reject(reason);
}
)
.finally(() => this.isRefreshingExplorer(false));
},
error => {
this._setLoadingStatusText("Failed to fetch databases.");
this.isRefreshingExplorer(false);
deferred.reject(error);
TelemetryProcessor.traceFailure(
Action.LoadDatabases,
{
databaseAccountName: this.databaseAccount().name,
defaultExperience: this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: error.message
},
startKey
);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while refreshing databases: ${error.message}`
);
}
);
return deferred.promise.then(
() => {
if (resourceTreeStartKey != null) {
TelemetryProcessor.traceSuccess(
Action.LoadDatabases,
Action.LoadResourceTree,
{
databaseAccountName: this.databaseAccount().name,
defaultExperience: this.defaultExperience(),
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
defaultExperience: this.defaultExperience && this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree
},
startKey
resourceTreeStartKey
);
const currentlySelectedNode: ViewModels.TreeNode = this.selectedNode();
const deltaDatabases = this.getDeltaDatabases(databases);
this.addDatabasesToList(deltaDatabases.toAdd);
this.deleteDatabasesFromList(deltaDatabases.toDelete);
this.selectedNode(currentlySelectedNode);
this._setLoadingStatusText("Fetching containers...");
this.refreshAndExpandNewDatabases(deltaDatabases.toAdd)
.then(
() => this._setLoadingStatusText("Successfully fetched containers."),
reason => {
this._setLoadingStatusText("Failed to fetch containers.");
throw reason;
}
)
.finally(() => this.isRefreshingExplorer(false));
},
error => {
this._setLoadingStatusText("Failed to fetch databases.");
this.isRefreshingExplorer(false);
}
},
reason => {
if (resourceTreeStartKey != null) {
TelemetryProcessor.traceFailure(
Action.LoadDatabases,
Action.LoadResourceTree,
{
databaseAccountName: this.databaseAccount().name,
defaultExperience: this.defaultExperience(),
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
defaultExperience: this.defaultExperience && this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: error.message
error: reason
},
startKey
resourceTreeStartKey
);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while refreshing databases: ${error.message}`
);
throw error;
}
)
.then(
() => {
if (resourceTreeStartKey != null) {
TelemetryProcessor.traceSuccess(
Action.LoadResourceTree,
{
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
defaultExperience: this.defaultExperience && this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree
},
resourceTreeStartKey
);
}
},
reason => {
if (resourceTreeStartKey != null) {
TelemetryProcessor.traceFailure(
Action.LoadResourceTree,
{
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
defaultExperience: this.defaultExperience && this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: reason
},
resourceTreeStartKey
);
}
}
);
}
);
}
public onRefreshDatabasesKeyPress = (source: any, event: KeyboardEvent): boolean => {
@@ -1498,41 +1512,7 @@ export default class Explorer {
dataExplorerArea: Constants.Areas.ResourceTree
});
this.isRefreshingExplorer(true);
refreshCachedResources().then(
() => {
TelemetryProcessor.traceSuccess(
Action.LoadDatabases,
{
description: "Refresh successful",
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
defaultExperience: this.defaultExperience && this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree
},
startKey
);
this.isAuthWithResourceToken() ? this.refreshDatabaseForResourceToken() : this.refreshAllDatabases();
},
(error: any) => {
this.isRefreshingExplorer(false);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while refreshing data: ${error.message}`
);
TelemetryProcessor.traceFailure(
Action.LoadDatabases,
{
description: "Unable to refresh cached resources",
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
defaultExperience: this.defaultExperience && this.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree,
error: error
},
startKey
);
throw error;
}
);
this.isAuthWithResourceToken() ? this.refreshDatabaseForResourceToken() : this.refreshAllDatabases();
this.refreshNotebookList();
};
@@ -1779,7 +1759,7 @@ export default class Explorer {
inputs.extensionEndpoint = configContext.PROXY_PATH;
}
const initPromise: Promise<void> = inputs ? this.initDataExplorerWithFrameInputs(inputs) : Promise.resolve();
const initPromise: Q.Promise<void> = inputs ? this.initDataExplorerWithFrameInputs(inputs) : Q();
initPromise.then(() => {
const openAction: ActionContracts.DataExplorerAction = message.openAction;
@@ -1873,7 +1853,7 @@ export default class Explorer {
return false;
}
public async initDataExplorerWithFrameInputs(inputs: ViewModels.DataExplorerInputsFrame): Promise<void> {
public initDataExplorerWithFrameInputs(inputs: ViewModels.DataExplorerInputsFrame): Q.Promise<void> {
if (inputs != null) {
const authorizationToken = inputs.authorizationToken || "";
const masterKey = inputs.masterKey || "";
@@ -1923,6 +1903,7 @@ export default class Explorer {
this.isAccountReady(true);
}
return Q();
}
public setFeatureFlagsFromFlights(flights: readonly string[]): void {
@@ -2040,7 +2021,7 @@ export default class Explorer {
// we reload collections for all databases so the resource tree reflects any collection-level changes
// i.e addition of stored procedures, etc.
const deferred: Q.Deferred<void> = Q.defer<void>();
let loadCollectionPromises: Promise<void>[] = [];
let loadCollectionPromises: Q.Promise<void>[] = [];
// If the user has a lot of databases, only load expanded databases.
const databasesToLoad =
@@ -2424,7 +2405,7 @@ export default class Explorer {
return true;
}
public async renameNotebook(notebookFile: NotebookContentItem): Promise<NotebookContentItem> {
public renameNotebook(notebookFile: NotebookContentItem): Q.Promise<NotebookContentItem> {
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
const error = "Attempt to rename notebook, but notebook is not enabled";
Logger.logError(error, "Explorer/renameNotebook");
@@ -2441,35 +2422,39 @@ export default class Explorer {
);
if (openedNotebookTabs.length > 0) {
this.showOkModalDialog("Unable to rename file", "This file is being edited. Please close the tab and try again.");
throw new Error();
return Q.reject();
}
const originalPath = notebookFile.path;
const newNotebookFile = await this.stringInputPane.openWithOptions<NotebookContentItem>({
errorMessage: "Could not rename notebook",
inProgressMessage: "Renaming notebook to",
successMessage: "Renamed notebook to",
inputLabel: "Enter new notebook name",
paneTitle: "Rename Notebook",
submitButtonLabel: "Rename",
defaultInput: FileSystemUtil.stripExtension(notebookFile.name, "ipynb"),
onSubmit: (input: string) => this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input)
});
const notebookTabs = this.tabsManager.getTabs(
ViewModels.CollectionTabKind.NotebookV2,
(tab: NotebookV2Tab) => tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), originalPath)
);
notebookTabs.forEach(tab => {
tab.tabTitle(newNotebookFile.name);
tab.tabPath(newNotebookFile.path);
(tab as NotebookV2Tab).notebookPath(newNotebookFile.path);
});
const result = this.stringInputPane
.openWithOptions<NotebookContentItem>({
errorMessage: "Could not rename notebook",
inProgressMessage: "Renaming notebook to",
successMessage: "Renamed notebook to",
inputLabel: "Enter new notebook name",
paneTitle: "Rename Notebook",
submitButtonLabel: "Rename",
defaultInput: FileSystemUtil.stripExtension(notebookFile.name, "ipynb"),
onSubmit: (input: string) => this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input)
})
.then(newNotebookFile => {
const notebookTabs = this.tabsManager.getTabs(
ViewModels.CollectionTabKind.NotebookV2,
(tab: NotebookV2Tab) => tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), originalPath)
);
notebookTabs.forEach(tab => {
tab.tabTitle(newNotebookFile.name);
tab.tabPath(newNotebookFile.path);
(tab as NotebookV2Tab).notebookPath(newNotebookFile.path);
});
this.resourceTree.triggerRender();
return newNotebookFile;
return newNotebookFile;
});
result.then(() => this.resourceTree.triggerRender());
return result;
}
public async onCreateDirectory(parent: NotebookContentItem): Promise<NotebookContentItem> {
public onCreateDirectory(parent: NotebookContentItem): Q.Promise<NotebookContentItem> {
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
const error = "Attempt to create notebook directory, but notebook is not enabled";
Logger.logError(error, "Explorer/onCreateDirectory");
@@ -2477,7 +2462,7 @@ export default class Explorer {
throw new Error(error);
}
const result = await this.stringInputPane.openWithOptions<NotebookContentItem>({
const result = this.stringInputPane.openWithOptions<NotebookContentItem>({
errorMessage: "Could not create directory ",
inProgressMessage: "Creating directory ",
successMessage: "Created directory ",
@@ -2487,7 +2472,7 @@ export default class Explorer {
defaultInput: "",
onSubmit: (input: string) => this.notebookManager?.notebookContentClient.createDirectory(parent, input)
});
this.resourceTree.triggerRender();
result.then(() => this.resourceTree.triggerRender());
return result;
}

View File

@@ -378,7 +378,8 @@ export class D3ForceGraph implements GraphRenderer {
* @param targetPosition
* @return promise with shift offset
*/
private async shiftGraph(targetPosition: Point2D): Promise<Point2D> {
private shiftGraph(targetPosition: Point2D): Q.Promise<Point2D> {
const deferred: Q.Deferred<Point2D> = Q.defer<Point2D>();
const offset = { x: this.width / 2 - targetPosition.x, y: this.height / 2 - targetPosition.y };
this.viewCenter = targetPosition;
@@ -390,15 +391,18 @@ export class D3ForceGraph implements GraphRenderer {
.translate(-targetPosition.x, -targetPosition.y);
};
const transition = this.zoomBackground
this.zoomBackground
.transition()
.duration(D3ForceGraph.TRANSITION_STEP1_MS)
.call(this.zoom.transform, transform);
await new Promise(resolve => transition.on("end", resolve));
return offset;
.call(this.zoom.transform, transform)
.on("end", () => {
deferred.resolve(offset);
});
} else {
deferred.resolve(null);
}
return null;
return deferred.promise;
}
private onGraphDataUpdate(graph: GraphData<D3Node, D3Link>) {
@@ -431,7 +435,7 @@ export class D3ForceGraph implements GraphRenderer {
}
}
private animateRemoveExitSelections(): Promise<void> {
private animateRemoveExitSelections(): Q.Promise<void> {
const deferred1 = Q.defer<void>();
const deferred2 = Q.defer<void>();
const linkExitSelection = this.linkSelection.exit();
@@ -504,7 +508,7 @@ export class D3ForceGraph implements GraphRenderer {
deferred2.resolve();
}
return Promise.all([deferred1.promise, deferred2.promise]).then(undefined);
return Q.allSettled([deferred1.promise, deferred2.promise]).then(undefined);
}
/**

View File

@@ -1,5 +1,6 @@
import React from "react";
import { mount, ReactWrapper } from "enzyme";
import * as Q from "q";
import { NodePropertiesComponent, NodePropertiesComponentProps, Mode } from "./NodePropertiesComponent";
import { GraphHighlightedNodeData, EditedProperties, EditedEdges, PossibleVertex } from "./GraphExplorer";
@@ -40,11 +41,11 @@ describe("Property pane", () => {
node: highlightedNode,
getPkIdFromNodeData: (v: GraphHighlightedNodeData): string => null,
collectionPartitionKeyProperty: null,
updateVertexProperties: (editedProperties: EditedProperties): Promise<void> => Promise.resolve(),
updateVertexProperties: (editedProperties: EditedProperties): Q.Promise<void> => Q.resolve(),
selectNode: (id: string): void => {},
updatePossibleVertices: async (): Promise<PossibleVertex[]> => null,
updatePossibleVertices: (): Q.Promise<PossibleVertex[]> => Q.resolve(null),
possibleEdgeLabels: null,
editGraphEdges: async (editedEdges: EditedEdges): Promise<any> => undefined,
editGraphEdges: (editedEdges: EditedEdges): Q.Promise<any> => Q.resolve(),
deleteHighlightedNode: (): void => {},
onModeChanged: (newMode: Mode): void => {},
viewMode: Mode.READONLY_PROP

View File

@@ -36,11 +36,11 @@ export interface NodePropertiesComponentProps {
node: GraphHighlightedNodeData;
getPkIdFromNodeData: (v: GraphHighlightedNodeData) => string;
collectionPartitionKeyProperty: string;
updateVertexProperties: (editedProperties: EditedProperties) => Promise<void>;
updateVertexProperties: (editedProperties: EditedProperties) => Q.Promise<void>;
selectNode: (id: string) => void;
updatePossibleVertices: () => Promise<PossibleVertex[]>;
updatePossibleVertices: () => Q.Promise<PossibleVertex[]>;
possibleEdgeLabels: Item[];
editGraphEdges: (editedEdges: EditedEdges) => Promise<any>;
editGraphEdges: (editedEdges: EditedEdges) => Q.Promise<any>;
deleteHighlightedNode: () => void;
onModeChanged: (newMode: Mode) => void;
viewMode: Mode; // If viewMode is specified in parent, keep state in sync with it

View File

@@ -14,7 +14,6 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
import { configContext, Platform } from "../../ConfigContext";
import { ContextualPaneBase } from "./ContextualPaneBase";
import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent";
import { refreshCachedResources } from "../../Common/DocumentClientUtilityBase";
import { createCollection } from "../../Common/dataAccess/createCollection";
export interface AddCollectionPaneOptions extends ViewModels.PaneOptions {
@@ -896,9 +895,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
};
TelemetryProcessor.traceSuccess(Action.CreateCollection, addCollectionPaneSuccessMessage, startKey);
this.resetData();
return refreshCachedResources().then(() => {
this.container.refreshAllDatabases();
});
this.container.refreshAllDatabases();
},
(reason: any) => {
this.isExecuting(false);

View File

@@ -350,7 +350,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
}
this.isExecuting(true);
const autoPilotCommand = `cosmosdb_autoscale_max_throughput`;
let createTableAndKeyspacePromise: Promise<any>;
let createTableAndKeyspacePromise: Q.Promise<any>;
const toCreateKeyspace: boolean = this.keyspaceCreateNew();
const useAutoPilotForKeyspace: boolean =
(!this.hasAutoPilotV2FeatureFlag() && this.isSharedAutoPilotSelected() && !!this.sharedAutoPilotThroughput()) ||

View File

@@ -1,6 +1,7 @@
jest.mock("../../Common/dataAccess/deleteCollection");
import * as ko from "knockout";
import * as sinon from "sinon";
import Q from "q";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
@@ -57,7 +58,7 @@ describe("Delete Collection Confirmation Pane", () => {
it("should return true if last collection and database does not have shared throughput else false", () => {
let fakeExplorer = new Explorer();
fakeExplorer.isNotificationConsoleExpanded = ko.observable<boolean>(false);
fakeExplorer.refreshAllDatabases = () => Promise.resolve();
fakeExplorer.refreshAllDatabases = () => Q.resolve();
let pane = new DeleteCollectionConfirmationPane({
id: "deletecollectionconfirmationpane",
@@ -118,7 +119,7 @@ describe("Delete Collection Confirmation Pane", () => {
fakeExplorer.selectedNode = ko.observable<TreeNode>();
fakeExplorer.isLastCollection = () => true;
fakeExplorer.isSelectedDatabaseShared = () => false;
fakeExplorer.refreshAllDatabases = () => Promise.resolve();
fakeExplorer.refreshAllDatabases = () => Q.resolve();
let pane = new DeleteCollectionConfirmationPane({
id: "deletecollectionconfirmationpane",

View File

@@ -1,4 +1,5 @@
import * as ko from "knockout";
import Q from "q";
import * as ViewModels from "../../Contracts/ViewModels";
import * as Constants from "../../Common/Constants";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";

View File

@@ -1,6 +1,7 @@
jest.mock("../../Common/dataAccess/deleteDatabase");
jest.mock("../../Shared/Telemetry/TelemetryProcessor");
import * as ko from "knockout";
import Q from "q";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
@@ -90,7 +91,7 @@ describe("Delete Database Confirmation Pane", () => {
collections: ko.observableArray<ViewModels.Collection>()
} as ViewModels.Database;
};
fakeExplorer.refreshAllDatabases = () => Promise.resolve();
fakeExplorer.refreshAllDatabases = () => Q.resolve();
fakeExplorer.isNotificationConsoleExpanded = ko.observable<boolean>(false);
fakeExplorer.selectedDatabaseId = ko.computed<string>(() => selectedDatabaseId);
fakeExplorer.isSelectedDatabaseShared = () => false;

View File

@@ -1,4 +1,5 @@
import * as ko from "knockout";
import Q from "q";
import * as Constants from "../../Common/Constants";
import * as ViewModels from "../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
@@ -30,7 +31,7 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
this.resetData();
}
public async submit(): Promise<any> {
public submit(): Q.Promise<any> {
if (!this._isValid()) {
const selectedDatabase: ViewModels.Database = this.container.findSelectedDatabase();
this.formErrors("Input database name does not match the selected database");
@@ -38,7 +39,7 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
ConsoleDataType.Error,
`Error while deleting collection ${selectedDatabase && selectedDatabase.id()}: ${this.formErrors()}`
);
return;
return Q.resolve();
}
this.formErrors("");
@@ -52,7 +53,7 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
paneTitle: this.title()
});
// TODO: Should not be a Q promise anymore, but the Cassandra code requires it
let promise: Promise<any>;
let promise: Q.Promise<any>;
if (this.container.isPreferredApiCassandra()) {
promise = (<CassandraAPIDataClient>this.container.tableDataClient).deleteTableOrKeyspace(
this.container.databaseAccount().properties.cassandraEndpoint,
@@ -61,7 +62,7 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
this.container
);
} else {
promise = deleteDatabase(selectedDatabase.id());
promise = Q(deleteDatabase(selectedDatabase.id()));
}
return promise.then(
() => {

View File

@@ -1,4 +1,5 @@
import * as ko from "knockout";
import * as Q from "q";
import * as Constants from "../../Common/Constants";
import * as ViewModels from "../../Contracts/ViewModels";
import { ContextualPaneBase } from "./ContextualPaneBase";
@@ -96,30 +97,33 @@ export class LoadQueryPane extends ContextualPaneBase {
return true;
};
public async loadQueryFromFile(file: File): Promise<void> {
public loadQueryFromFile(file: File): Q.Promise<void> {
const selectedCollection: ViewModels.Collection = this.container && this.container.findSelectedCollection();
if (!selectedCollection) {
// should never get into this state
Logger.logError("No collection was selected", "LoadQueryPane.loadQueryFromFile");
throw new Error("No collection was selected");
return Q.reject("No collection was selected");
} else if (this.container.isPreferredApiMongoDB()) {
selectedCollection.onNewMongoQueryClick(selectedCollection, null);
} else {
selectedCollection.onNewQueryClick(selectedCollection, null);
}
const deferred: Q.Deferred<void> = Q.defer<void>();
const reader = new FileReader();
reader.onload = (evt: any): void => {
const fileData: string = evt.target.result;
const queryTab = this.container.tabsManager.activeTab() as QueryTab;
queryTab.initialEditorContent(fileData);
queryTab.sqlQueryEditorContent(fileData);
deferred.resolve();
};
reader.onerror = (evt: ProgressEvent): void => {
throw new Error((evt as any).error.message);
deferred.reject((evt as any).error.message);
};
reader.readAsText(file);
return deferred.promise;
}
private updateSelectedFilesTitle(fileList: FileList) {

View File

@@ -1,4 +1,5 @@
import _ from "underscore";
import Q from "q";
import * as Entities from "../Entities";
import * as QueryBuilderConstants from "../Constants";
@@ -83,11 +84,11 @@ export function filterColumns(table: DataTables.DataTable, settings: boolean[]):
* Reorder columns based on current order.
* If no current order is specified, reorder the columns based on intial order.
*/
export async function reorderColumns(
export function reorderColumns(
table: DataTables.DataTable,
targetOrder: number[],
currentOrder?: number[]
): Promise<any> {
): Q.Promise<any> {
var columnsCount: number = targetOrder.length;
var isCurrentOrderPassedIn: boolean = !!currentOrder;
if (!isCurrentOrderPassedIn) {
@@ -106,9 +107,13 @@ export async function reorderColumns(
var transformationOrder: number[] = isCurrentOrderPassedIn
? calculateTransformationOrder(currentOrder, targetOrder)
: targetOrder;
$.fn.dataTable.ColReorder(table).fnOrder(transformationOrder);
try {
$.fn.dataTable.ColReorder(table).fnOrder(transformationOrder);
} catch (err) {
return Q.reject(err);
}
}
return null;
return Q.resolve(null);
}
export function resetColumns(table: DataTables.DataTable): void {

View File

@@ -1,4 +1,5 @@
import _ from "underscore";
import Q from "q";
import * as DataTableUtilities from "./DataTableUtilities";
import * as DataTableOperations from "./DataTableOperations";
import TableEntityListViewModel from "./TableEntityListViewModel";
@@ -48,7 +49,7 @@ export default class TableCommands {
/**
* Edit entity
*/
public editEntityCommand(viewModel: TableEntityListViewModel): Promise<any> {
public editEntityCommand(viewModel: TableEntityListViewModel): Q.Promise<any> {
if (!viewModel) {
return null; // Error
}
@@ -67,7 +68,7 @@ export default class TableCommands {
return null;
}
public deleteEntitiesCommand(viewModel: TableEntityListViewModel): Promise<any> {
public deleteEntitiesCommand(viewModel: TableEntityListViewModel): Q.Promise<any> {
if (!viewModel) {
return null; // Error
}
@@ -91,7 +92,7 @@ export default class TableCommands {
return null;
}
public customizeColumnsCommand(viewModel: TableEntityListViewModel): Promise<any> {
public customizeColumnsCommand(viewModel: TableEntityListViewModel): Q.Promise<any> {
var table: DataTables.DataTable = viewModel.table;
var displayedColumnNames: string[] = DataTableOperations.getDataTableHeaders(table);
var columnsCount: number = displayedColumnNames.length;
@@ -119,7 +120,7 @@ export default class TableCommands {
return null;
}
public reorderColumnsBasedOnSelectedEntities(viewModel: TableEntityListViewModel): Promise<boolean> {
public reorderColumnsBasedOnSelectedEntities(viewModel: TableEntityListViewModel): Q.Promise<boolean> {
var selected = viewModel.selected();
if (!selected || !selected.length) {
return null;

View File

@@ -15,13 +15,7 @@ import * as ViewModels from "../../Contracts/ViewModels";
import { MessageTypes } from "../../Contracts/ExplorerContracts";
import { sendMessage } from "../../Common/MessageHandler";
import Explorer from "../Explorer";
import {
queryDocuments,
refreshCachedResources,
deleteDocument,
updateDocument,
createDocument
} from "../../Common/DocumentClientUtilityBase";
import { queryDocuments, deleteDocument, updateDocument, createDocument } from "../../Common/DocumentClientUtilityBase";
import { configContext } from "../../ConfigContext";
export interface CassandraTableKeys {
@@ -424,7 +418,7 @@ export class CassandraAPIDataClient extends TableDataClient {
ConsoleDataType.Info,
`Successfully created a keyspace with query ${createKeyspaceQuery}`
);
refreshCachedResources().finally(() => deferred.resolve());
deferred.resolve();
},
reason => {
NotificationConsoleUtils.logConsoleMessage(
@@ -471,15 +465,7 @@ export class CassandraAPIDataClient extends TableDataClient {
ConsoleDataType.Info,
`Successfully created a table with query ${createTableQuery}`
);
refreshCachedResources(null).then(
() => {
deferred.resolve();
},
reason => {
// Still resolve since the keyspace/table was successfully created at this point.
deferred.resolve();
}
);
deferred.resolve();
},
reason => {
NotificationConsoleUtils.logConsoleMessage(
@@ -520,15 +506,7 @@ export class CassandraAPIDataClient extends TableDataClient {
ConsoleDataType.Info,
`Successfully deleted resource with query ${deleteQuery}`
);
refreshCachedResources(null).then(
() => {
deferred.resolve();
},
reason => {
// Still resolve since the keyspace/table was successfully deleted at this point.
deferred.resolve();
}
);
deferred.resolve();
},
reason => {
NotificationConsoleUtils.logConsoleMessage(

View File

@@ -1,4 +1,5 @@
import * as _ from "underscore";
import Q from "q";
import * as Entities from "./Entities";
import { CassandraTableKey } from "./TableDataClient";
import * as Constants from "./Constants";
@@ -18,8 +19,8 @@ export function guid() {
/**
* Returns a promise that resolves in the specified number of milliseconds.
*/
export function delay(milliseconds: number): Promise<any> {
return new Promise(resolve => setTimeout(resolve, milliseconds));
export function delay(milliseconds: number): Q.Promise<any> {
return Q.delay(milliseconds);
}
/**

View File

@@ -1,4 +1,5 @@
import * as ko from "knockout";
import Q from "q";
import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
@@ -224,7 +225,7 @@ export default class ConflictsTab extends TabsBase {
});
}
public refreshDocumentsGrid(): Promise<any> {
public refreshDocumentsGrid(): Q.Promise<any> {
// clear documents grid
this.conflictIds([]);
return this.createIterator()
@@ -255,17 +256,19 @@ export default class ConflictsTab extends TabsBase {
return true;
};
public async onConflictIdClick(clickedDocumentId: ConflictId): Promise<any> {
public onConflictIdClick(clickedDocumentId: ConflictId): Q.Promise<any> {
if (this.editorState() !== ViewModels.DocumentExplorerState.noDocumentSelected) {
return;
return Q();
}
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
return Q();
}
public onAcceptChangesClick = async (): Promise<any> => {
public onAcceptChangesClick = (): Q.Promise<any> => {
if (this.isEditorDirty() && !this._isIgnoreDirtyEditor()) {
return;
return Q();
}
this.isExecutionError(false);
@@ -283,7 +286,7 @@ export default class ConflictsTab extends TabsBase {
conflictResourceId: selectedConflict.resourceId
});
let operationPromise: Promise<any>;
let operationPromise: Q.Promise<any> = Q();
if (selectedConflict.operationType === Constants.ConflictOperationType.Replace) {
const documentContent = JSON.parse(this.selectedConflictContent());
@@ -355,7 +358,7 @@ export default class ConflictsTab extends TabsBase {
.finally(() => this.isExecuting(false));
};
public onDeleteClick = (): Promise<any> => {
public onDeleteClick = (): Q.Promise<any> => {
this.isExecutionError(false);
this.isExecuting(true);
@@ -415,34 +418,40 @@ export default class ConflictsTab extends TabsBase {
.finally(() => this.isExecuting(false));
};
public onDiscardClick = async (): Promise<any> => {
public onDiscardClick = (): Q.Promise<any> => {
this.selectedConflictContent(this.selectedConflictContent.getEditableOriginalValue());
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
return Q();
};
public async onValidDocumentEdit(): Promise<any> {
public onValidDocumentEdit(): Q.Promise<any> {
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid);
return Q();
}
public async onInvalidDocumentEdit(): Promise<any> {
public onInvalidDocumentEdit(): Q.Promise<any> {
if (
this.editorState() === ViewModels.DocumentExplorerState.exisitingDocumentNoEdits ||
this.editorState() === ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid
) {
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentDirtyInvalid);
return Q();
}
return Q();
}
public onTabClick(): Promise<any> {
public onTabClick(): Q.Promise<any> {
return super.onTabClick().then(() => {
this.collection && this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Conflicts);
});
}
public onActivate(): Promise<any> {
public onActivate(): Q.Promise<any> {
return super.onActivate().then(() => {
if (this._documentsIterator) {
return this._documentsIterator;
return Q.resolve(this._documentsIterator);
}
return this.createIterator().then(
@@ -472,7 +481,7 @@ export default class ConflictsTab extends TabsBase {
});
}
public onRefreshClick(): Promise<any> {
public onRefreshClick(): Q.Promise<any> {
return this.refreshDocumentsGrid().then(() => {
this.selectedConflictContent("");
this.selectedConflictId(null);
@@ -480,7 +489,7 @@ export default class ConflictsTab extends TabsBase {
});
}
public createIterator(): Promise<QueryIterator<ConflictDefinition & Resource>> {
public createIterator(): Q.Promise<QueryIterator<ConflictDefinition & Resource>> {
// TODO: Conflict Feed does not allow filtering atm
const query: string = undefined;
let options: any = {};
@@ -488,7 +497,7 @@ export default class ConflictsTab extends TabsBase {
return queryConflicts(this.collection.databaseId, this.collection.id(), query, options);
}
public loadNextPage(): Promise<any> {
public loadNextPage(): Q.Promise<any> {
this.isExecuting(true);
this.isExecutionError(false);
return this._loadNextPageInternal()
@@ -555,8 +564,8 @@ export default class ConflictsTab extends TabsBase {
}
};
protected _loadNextPageInternal(): Promise<DataModels.ConflictId[]> {
return this._documentsIterator.fetchNext().then(response => response.resources);
protected _loadNextPageInternal(): Q.Promise<DataModels.ConflictId[]> {
return Q(this._documentsIterator.fetchNext().then(response => response.resources));
}
protected _onEditorContentChange(newContent: string) {
@@ -568,20 +577,22 @@ export default class ConflictsTab extends TabsBase {
}
}
public async initDocumentEditorForCreate(documentId: ConflictId, documentToInsert: any): Promise<any> {
public initDocumentEditorForCreate(documentId: ConflictId, documentToInsert: any): Q.Promise<any> {
if (documentId) {
let parsedConflictContent: any = JSON.parse(documentToInsert);
const renderedConflictContent: string = this.renderObjectForEditor(parsedConflictContent, null, 4);
this.selectedConflictContent.setBaseline(renderedConflictContent);
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
}
return Q();
}
public async initDocumentEditorForReplace(
public initDocumentEditorForReplace(
documentId: ConflictId,
conflictContent: any,
currentContent: any
): Promise<any> {
): Q.Promise<any> {
if (documentId) {
currentContent = ConflictsTab.removeSystemProperties(currentContent);
const renderedCurrentContent: string = this.renderObjectForEditor(currentContent, null, 4);
@@ -594,9 +605,11 @@ export default class ConflictsTab extends TabsBase {
this.selectedConflictContent.setBaseline(renderedConflictContent);
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
}
return Q();
}
public async initDocumentEditorForDelete(documentId: ConflictId, documentToDelete: any): Promise<any> {
public initDocumentEditorForDelete(documentId: ConflictId, documentToDelete: any): Q.Promise<any> {
if (documentId) {
let parsedDocumentToDelete: any = JSON.parse(documentToDelete);
parsedDocumentToDelete = ConflictsTab.removeSystemProperties(parsedDocumentToDelete);
@@ -604,12 +617,15 @@ export default class ConflictsTab extends TabsBase {
this.selectedConflictContent.setBaseline(renderedDocumentToDelete);
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
}
return Q();
}
public async initDocumentEditorForNoOp(documentId: ConflictId): Promise<any> {
public initDocumentEditorForNoOp(documentId: ConflictId): Q.Promise<any> {
this.selectedConflictContent(null);
this.selectedConflictCurrent(null);
this.editorState(ViewModels.DocumentExplorerState.noDocumentSelected);
return Q();
}
protected getTabsButtons(): CommandButtonComponentProps[] {

View File

@@ -8,6 +8,7 @@ import * as SharedConstants from "../../Shared/Constants";
import * as ViewModels from "../../Contracts/ViewModels";
import DiscardIcon from "../../../images/discard.svg";
import editable from "../../Common/EditableUtility";
import Q from "q";
import SaveIcon from "../../../images/save-cosmos.svg";
import TabsBase from "./TabsBase";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
@@ -21,12 +22,12 @@ import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/update
import { configContext, Platform } from "../../ConfigContext";
const updateThroughputBeyondLimitWarningMessage: string = `
You are about to request an increase in throughput beyond the pre-allocated capacity.
The service will scale out and increase throughput for the selected database.
You are about to request an increase in throughput beyond the pre-allocated capacity.
The service will scale out and increase throughput for the selected database.
This operation will take 1-3 business days to complete. You can track the status of this request in Notifications.`;
const updateThroughputDelayedApplyWarningMessage: string = `
You are about to request an increase in throughput beyond the pre-allocated capacity.
You are about to request an increase in throughput beyond the pre-allocated capacity.
This operation will take some time to complete.`;
const currentThroughput: (isAutoscale: boolean, throughput: number) => string = (isAutoscale, throughput) =>
@@ -35,17 +36,17 @@ const currentThroughput: (isAutoscale: boolean, throughput: number) => string =
: `Current manual throughput: ${throughput} RU/s`;
const throughputApplyDelayedMessage = (isAutoscale: boolean, throughput: number, databaseName: string) =>
`The request to increase the throughput has successfully been submitted.
`The request to increase the throughput has successfully been submitted.
This operation will take 1-3 business days to complete. View the latest status in Notifications.<br />
Database: ${databaseName}, ${currentThroughput(isAutoscale, throughput)}`;
const throughputApplyShortDelayMessage = (isAutoscale: boolean, throughput: number, databaseName: string) =>
`A request to increase the throughput is currently in progress.
`A request to increase the throughput is currently in progress.
This operation will take some time to complete.<br />
Database: ${databaseName}, ${currentThroughput(isAutoscale, throughput)}`;
const throughputApplyLongDelayMessage = (isAutoscale: boolean, throughput: number, databaseName: string) =>
`A request to increase the throughput is currently in progress.
`A request to increase the throughput is currently in progress.
This operation will take 1-3 business days to complete. View the latest status in Notifications.<br />
Database: ${databaseName}, ${currentThroughput(isAutoscale, throughput)}`;
@@ -543,7 +544,7 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
}
};
public onRevertClick = async (): Promise<any> => {
public onRevertClick = (): Q.Promise<any> => {
this.throughput.setBaseline(this.throughput.getEditableOriginalValue());
this.isAutoPilotSelected.setBaseline(this.isAutoPilotSelected.getEditableOriginalValue());
if (!this.hasAutoPilotV2FeatureFlag()) {
@@ -551,9 +552,11 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
} else {
this.selectedAutoPilotTier.setBaseline(this.selectedAutoPilotTier.getEditableOriginalValue());
}
return Q();
};
public onActivate(): Promise<any> {
public onActivate(): Q.Promise<any> {
return super.onActivate().then(async () => {
this.database.selectedSubnodeKind(ViewModels.CollectionTabKind.DatabaseSettings);
await this.database.loadOffer();

View File

@@ -1,4 +1,5 @@
import * as ko from "knockout";
import Q from "q";
import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
@@ -339,21 +340,24 @@ export default class DocumentsTab extends TabsBase {
return true;
}
public async onShowFilterClick(): Promise<any> {
public onShowFilterClick(): Q.Promise<any> {
this.isFilterCreated(true);
this.isFilterExpanded(true);
$(".filterDocExpanded").addClass("active");
$("#content").addClass("active");
$(".querydropdown").focus();
return Q();
}
public async onHideFilterClick(): Promise<any> {
public onHideFilterClick(): Q.Promise<any> {
this.isFilterExpanded(false);
$(".filterDocExpanded").removeClass("active");
$("#content").removeClass("active");
$(".queryButton").focus();
return Q();
}
public onCloseButtonKeyDown = (source: any, event: KeyboardEvent): boolean => {
@@ -365,7 +369,7 @@ export default class DocumentsTab extends TabsBase {
return true;
};
public onApplyFilterClick(): Promise<any> {
public onApplyFilterClick(): Q.Promise<any> {
// clear documents grid
this.documentIds([]);
return this.createIterator()
@@ -394,7 +398,7 @@ export default class DocumentsTab extends TabsBase {
});
}
public refreshDocumentsGrid(): Promise<any> {
public refreshDocumentsGrid(): Q.Promise<any> {
return this.onApplyFilterClick();
}
@@ -407,17 +411,19 @@ export default class DocumentsTab extends TabsBase {
return true;
};
public async onDocumentIdClick(clickedDocumentId: DocumentId): Promise<any> {
public onDocumentIdClick(clickedDocumentId: DocumentId): Q.Promise<any> {
if (this.editorState() !== ViewModels.DocumentExplorerState.noDocumentSelected) {
return;
return Q();
}
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
return Q();
}
public onNewDocumentClick = async (): Promise<any> => {
public onNewDocumentClick = (): Q.Promise<any> => {
if (this.isEditorDirty() && !this._isIgnoreDirtyEditor()) {
return;
return Q();
}
this.selectedDocumentId(null);
@@ -425,9 +431,11 @@ export default class DocumentsTab extends TabsBase {
this.initialDocumentContent(defaultDocument);
this.selectedDocumentContent.setBaseline(defaultDocument);
this.editorState(ViewModels.DocumentExplorerState.newDocumentValid);
return Q();
};
public onSaveNewDocumentClick = (): Promise<any> => {
public onSaveNewDocumentClick = (): Q.Promise<any> => {
this.isExecutionError(false);
const startKey: number = TelemetryProcessor.traceStart(Action.CreateDocument, {
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
@@ -485,13 +493,15 @@ export default class DocumentsTab extends TabsBase {
.finally(() => this.isExecuting(false));
};
public onRevertNewDocumentClick = async (): Promise<any> => {
public onRevertNewDocumentClick = (): Q.Promise<any> => {
this.initialDocumentContent("");
this.selectedDocumentContent("");
this.editorState(ViewModels.DocumentExplorerState.noDocumentSelected);
return Q();
};
public onSaveExisitingDocumentClick = (): Promise<any> => {
public onSaveExisitingDocumentClick = (): Q.Promise<any> => {
const selectedDocumentId = this.selectedDocumentId();
const documentContent = JSON.parse(this.selectedDocumentContent());
@@ -550,13 +560,15 @@ export default class DocumentsTab extends TabsBase {
.finally(() => this.isExecuting(false));
};
public onRevertExisitingDocumentClick = async (): Promise<any> => {
public onRevertExisitingDocumentClick = (): Q.Promise<any> => {
this.selectedDocumentContent.setBaseline(this.initialDocumentContent());
this.initialDocumentContent.valueHasMutated();
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentNoEdits);
return Q();
};
public onDeleteExisitingDocumentClick = async (): Promise<any> => {
public onDeleteExisitingDocumentClick = (): Q.Promise<any> => {
const selectedDocumentId = this.selectedDocumentId();
const msg = !this.isPreferredApiMongoDB
? "Are you sure you want to delete the selected item ?"
@@ -565,27 +577,30 @@ export default class DocumentsTab extends TabsBase {
if (window.confirm(msg)) {
return this._deleteDocument(selectedDocumentId);
}
return Q();
};
public async onValidDocumentEdit(): Promise<any> {
public onValidDocumentEdit(): Q.Promise<any> {
if (
this.editorState() === ViewModels.DocumentExplorerState.newDocumentInvalid ||
this.editorState() === ViewModels.DocumentExplorerState.newDocumentValid
) {
this.editorState(ViewModels.DocumentExplorerState.newDocumentValid);
return;
return Q();
}
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid);
return Q();
}
public async onInvalidDocumentEdit(): Promise<any> {
public onInvalidDocumentEdit(): Q.Promise<any> {
if (
this.editorState() === ViewModels.DocumentExplorerState.newDocumentInvalid ||
this.editorState() === ViewModels.DocumentExplorerState.newDocumentValid
) {
this.editorState(ViewModels.DocumentExplorerState.newDocumentInvalid);
return;
return Q();
}
if (
@@ -593,20 +608,22 @@ export default class DocumentsTab extends TabsBase {
this.editorState() === ViewModels.DocumentExplorerState.exisitingDocumentDirtyValid
) {
this.editorState(ViewModels.DocumentExplorerState.exisitingDocumentDirtyInvalid);
return;
return Q();
}
return Q();
}
public onTabClick(): Promise<any> {
public onTabClick(): Q.Promise<any> {
return super.onTabClick().then(() => {
this.collection && this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
});
}
public onActivate(): Promise<any> {
public onActivate(): Q.Promise<any> {
return super.onActivate().then(() => {
if (this._documentsIterator) {
return this._documentsIterator;
return Q.resolve(this._documentsIterator);
}
return this.createIterator().then(
@@ -636,7 +653,7 @@ export default class DocumentsTab extends TabsBase {
});
}
public onRefreshClick(): Promise<any> {
public onRefreshClick(): Q.Promise<any> {
return this.refreshDocumentsGrid().then(() => {
this.selectedDocumentContent("");
this.selectedDocumentId(null);
@@ -648,11 +665,11 @@ export default class DocumentsTab extends TabsBase {
return window.confirm(msg);
};
protected __deleteDocument(documentId: DocumentId): Promise<any> {
protected __deleteDocument(documentId: DocumentId): Q.Promise<any> {
return deleteDocument(this.collection, documentId);
}
private _deleteDocument(selectedDocumentId: DocumentId): Promise<any> {
private _deleteDocument(selectedDocumentId: DocumentId): Q.Promise<any> {
this.isExecutionError(false);
const startKey: number = TelemetryProcessor.traceStart(Action.DeleteDocument, {
databaseAccountName: this.collection && this.collection.container.databaseAccount().name,
@@ -697,7 +714,7 @@ export default class DocumentsTab extends TabsBase {
.finally(() => this.isExecuting(false));
}
public createIterator(): Promise<QueryIterator<ItemDefinition & Resource>> {
public createIterator(): Q.Promise<QueryIterator<ItemDefinition & Resource>> {
let filters = this.lastFilterContents();
const filter: string = this.filterContent().trim();
const query: string = this.buildQuery(filter);
@@ -711,14 +728,14 @@ export default class DocumentsTab extends TabsBase {
return queryDocuments(this.collection.databaseId, this.collection.id(), query, options);
}
public selectDocument(documentId: DocumentId): Promise<any> {
public selectDocument(documentId: DocumentId): Q.Promise<any> {
this.selectedDocumentId(documentId);
return readDocument(this.collection, documentId).then((content: any) => {
this.initDocumentEditor(documentId, content);
});
}
public loadNextPage(): Promise<any> {
public loadNextPage(): Q.Promise<any> {
this.isExecuting(true);
this.isExecutionError(false);
return this._loadNextPageInternal()
@@ -792,8 +809,8 @@ export default class DocumentsTab extends TabsBase {
}
};
protected _loadNextPageInternal(): Promise<DataModels.DocumentId[]> {
return this._documentsIterator.fetchNext().then(response => response.resources);
protected _loadNextPageInternal(): Q.Promise<DataModels.DocumentId[]> {
return Q(this._documentsIterator.fetchNext().then(response => response.resources));
}
protected _onEditorContentChange(newContent: string) {
@@ -805,7 +822,7 @@ export default class DocumentsTab extends TabsBase {
}
}
public async initDocumentEditor(documentId: DocumentId, documentContent: any): Promise<any> {
public initDocumentEditor(documentId: DocumentId, documentContent: any): Q.Promise<any> {
if (documentId) {
const content: string = this.renderObjectForEditor(documentContent, null, 4);
this.selectedDocumentContent.setBaseline(content);
@@ -815,6 +832,8 @@ export default class DocumentsTab extends TabsBase {
: ViewModels.DocumentExplorerState.newDocumentValid;
this.editorState(newState);
}
return Q();
}
public buildQuery(filter: string): string {

View File

@@ -1,4 +1,5 @@
import * as ko from "knockout";
import * as Q from "q";
import * as ViewModels from "../../Contracts/ViewModels";
import TabsBase from "./TabsBase";
import { GraphExplorerAdapter } from "../Graph/GraphExplorerComponent/GraphExplorerAdapter";
@@ -113,7 +114,7 @@ export default class GraphTab extends TabsBase {
: `${account.name}.graphs.azure.com:443/`;
}
public onTabClick(): Promise<any> {
public onTabClick(): Q.Promise<any> {
return super.onTabClick().then(() => {
this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Graph);
});

View File

@@ -1,4 +1,5 @@
import * as ViewModels from "../../Contracts/ViewModels";
import Q from "q";
import MongoUtility from "../../Common/MongoUtility";
import QueryTab from "./QueryTab";
import * as HeadersUtility from "../../Common/HeadersUtility";
@@ -19,10 +20,10 @@ export default class MongoQueryTab extends QueryTab {
return MongoUtility.tojson(value, null, false);
}
protected async _initIterator(): Promise<MinimalQueryIterator> {
protected _initIterator(): Q.Promise<MinimalQueryIterator> {
let options: any = {};
options.enableCrossPartitionQuery = HeadersUtility.shouldEnableCrossPartitionKey();
this._iterator = queryIterator(this.collection.databaseId, this.collection, this.sqlStatementToExecute());
return this._iterator;
return Q(this._iterator);
}
}

View File

@@ -3,6 +3,7 @@ import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import AuthHeadersUtil from "../../Platform/Hosted/Authorization";
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
import Q from "q";
import TabsBase from "./TabsBase";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
@@ -52,7 +53,7 @@ export default class MongoShellTab extends TabsBase {
// }
}
public onTabClick(): Promise<any> {
public onTabClick(): Q.Promise<any> {
return super.onTabClick().then(() => {
this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Documents);
});

View File

@@ -1,4 +1,5 @@
import * as _ from "underscore";
import * as Q from "q";
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
@@ -83,7 +84,7 @@ export default class NotebookTabV2 extends TabsBase {
});
}
public async onCloseTabButtonClick(): Promise<any> {
public onCloseTabButtonClick(): Q.Promise<any> {
const cleanup = () => {
this.notebookComponentAdapter.notebookShutdown();
this.isActive(false);
@@ -99,11 +100,11 @@ export default class NotebookTabV2 extends TabsBase {
"Cancel",
undefined
);
return Q.resolve(null);
} else {
cleanup();
return Q.resolve(null);
}
return null;
}
public async reconfigureServiceEndpoints() {

View File

@@ -1,4 +1,5 @@
import * as ko from "knockout";
import Q from "q";
import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
@@ -162,13 +163,13 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
this._buildCommandBarOptions();
}
public onTabClick(): Promise<any> {
public onTabClick(): Q.Promise<any> {
return super.onTabClick().then(() => {
this.collection && this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Query);
});
}
public onExecuteQueryClick = (): Promise<any> => {
public onExecuteQueryClick = (): Q.Promise<any> => {
const sqlStatement: string = this.selectedContent() || this.sqlQueryEditorContent();
this.sqlStatementToExecute(sqlStatement);
this.allResultsMetadata([]);
@@ -190,7 +191,7 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
this.collection && this.collection.container && this.collection.container.browseQueriesPane.open();
};
public onFetchNextPageClick(): Promise<any> {
public onFetchNextPageClick(): Q.Promise<any> {
const allResultsMetadata = (this.allResultsMetadata && this.allResultsMetadata()) || [];
const metadata: ViewModels.QueryResultsMetadata = allResultsMetadata[allResultsMetadata.length - 1];
const firstResultIndex: number = (metadata && Number(metadata.firstItemIndex)) || 1;
@@ -264,7 +265,7 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
return true;
};
private _executeQueryDocumentsPage(firstItemIndex: number): Promise<any> {
private _executeQueryDocumentsPage(firstItemIndex: number): Q.Promise<any> {
this.errors([]);
this.roundTrips(undefined);
if (this._iterator == null) {
@@ -276,7 +277,7 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
}
// TODO: Position and enable spinner when request is in progress
private _queryDocumentsPage(firstItemIndex: number): Promise<any> {
private _queryDocumentsPage(firstItemIndex: number): Q.Promise<any> {
this.isExecutionError(false);
this._resetAggregateQueryMetrics();
const startKey: number = TelemetryProcessor.traceStart(Action.ExecuteQuery, {
@@ -484,14 +485,16 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
}
}
protected _initIterator(): Promise<MinimalQueryIterator> {
protected _initIterator(): Q.Promise<MinimalQueryIterator> {
const options: any = QueryTab.getIteratorOptions(this.collection);
if (this._resourceTokenPartitionKey) {
options.partitionKey = this._resourceTokenPartitionKey;
}
return queryDocuments(this.collection.databaseId, this.collection.id(), this.sqlStatementToExecute(), options).then(
iterator => (this._iterator = iterator)
return Q(
queryDocuments(this.collection.databaseId, this.collection.id(), this.sqlStatementToExecute(), options).then(
iterator => (this._iterator = iterator)
)
);
}

View File

@@ -1,4 +1,5 @@
import * as ko from "knockout";
import Q from "q";
import * as ViewModels from "../../Contracts/ViewModels";
import TabsBase from "./TabsBase";
import TableEntityListViewModel from "../Tables/DataTable/TableEntityListViewModel";
@@ -129,38 +130,38 @@ export default class QueryTablesTab extends TabsBase {
this.buildCommandBarOptions();
}
public onExecuteQueryClick = (): Promise<any> => {
public onExecuteQueryClick = (): Q.Promise<any> => {
this.queryViewModel().runQuery();
return null;
};
public onQueryBuilderClick = (): Promise<any> => {
public onQueryBuilderClick = (): Q.Promise<any> => {
this.queryViewModel().selectHelper();
return null;
};
public onQueryTextClick = (): Promise<any> => {
public onQueryTextClick = (): Q.Promise<any> => {
this.queryViewModel().selectEditor();
return null;
};
public onAddEntityClick = (): Promise<any> => {
public onAddEntityClick = (): Q.Promise<any> => {
this.container.addTableEntityPane.tableViewModel = this.tableEntityListViewModel();
this.container.addTableEntityPane.open();
return null;
};
public onEditEntityClick = (): Promise<any> => {
public onEditEntityClick = (): Q.Promise<any> => {
this.tableCommands.editEntityCommand(this.tableEntityListViewModel());
return null;
};
public onDeleteEntityClick = (): Promise<any> => {
public onDeleteEntityClick = (): Q.Promise<any> => {
this.tableCommands.deleteEntitiesCommand(this.tableEntityListViewModel());
return null;
};
public onActivate(): Promise<any> {
public onActivate(): Q.Promise<any> {
return super.onActivate().then(() => {
const columns =
!!this.tableEntityListViewModel() &&

View File

@@ -1,5 +1,6 @@
import * as ko from "knockout";
import * as monaco from "monaco-editor";
import Q from "q";
import DiscardIcon from "../../../images/discard.svg";
import SaveIcon from "../../../images/save-cosmos.svg";
import * as Constants from "../../Common/Constants";
@@ -185,7 +186,7 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
this._setBaselines();
}
public onTabClick(): Promise<any> {
public onTabClick(): Q.Promise<any> {
return super.onTabClick().then(() => {
if (this.isNew()) {
this.collection.selectedSubnodeKind(this.tabKind);
@@ -196,11 +197,13 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
public abstract onSaveClick: () => Promise<any>;
public abstract onUpdateClick: () => Promise<any>;
public onDiscard = async (): Promise<any> => {
public onDiscard = (): Q.Promise<any> => {
this.setBaselines();
const original = this.editorContent.getEditableOriginalValue();
const editorModel = this.editor() && this.editor().getModel();
editorModel && editorModel.setValue(original);
return Q();
};
public onSaveOrUpdateClick(): Promise<any> {

View File

@@ -9,6 +9,7 @@ import * as SharedConstants from "../../Shared/Constants";
import * as ViewModels from "../../Contracts/ViewModels";
import DiscardIcon from "../../../images/discard.svg";
import editable from "../../Common/EditableUtility";
import Q from "q";
import SaveIcon from "../../../images/save-cosmos.svg";
import TabsBase from "./TabsBase";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
@@ -23,21 +24,21 @@ import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/update
import { configContext, Platform } from "../../ConfigContext";
const ttlWarning: string = `
The system will automatically delete items based on the TTL value (in seconds) you provide, without needing a delete operation explicitly issued by a client application.
The system will automatically delete items based on the TTL value (in seconds) you provide, without needing a delete operation explicitly issued by a client application.
For more information see, <a target="_blank" href="https://aka.ms/cosmos-db-ttl">Time to Live (TTL) in Azure Cosmos DB</a>.`;
const indexingPolicyTTLWarningMessage: string = `
Changing the Indexing Policy impacts query results while the index transformation occurs.
When a change is made and the indexing mode is set to consistent or lazy, queries return eventual results until the operation completes.
Changing the Indexing Policy impacts query results while the index transformation occurs.
When a change is made and the indexing mode is set to consistent or lazy, queries return eventual results until the operation completes.
For more information see, <a target="_blank" href="https://aka.ms/cosmosdb/modify-index-policy">Modifying Indexing Policies</a>.`;
const updateThroughputBeyondLimitWarningMessage: string = `
You are about to request an increase in throughput beyond the pre-allocated capacity.
The service will scale out and increase throughput for the selected container.
You are about to request an increase in throughput beyond the pre-allocated capacity.
The service will scale out and increase throughput for the selected container.
This operation will take 1-3 business days to complete. You can track the status of this request in Notifications.`;
const updateThroughputDelayedApplyWarningMessage: string = `
You are about to request an increase in throughput beyond the pre-allocated capacity.
You are about to request an increase in throughput beyond the pre-allocated capacity.
This operation will take some time to complete.`;
// TODO: move to a utility classs and add unit tests
@@ -81,7 +82,7 @@ const throughputApplyDelayedMessage = (
collectionName: string,
requestedThroughput: number
): string => `
The request to increase the throughput has successfully been submitted.
The request to increase the throughput has successfully been submitted.
This operation will take 1-3 business days to complete. View the latest status in Notifications.<br />
Database: ${databaseName}, Container: ${collectionName} ${currentThroughput(
isAutoscale,
@@ -114,7 +115,7 @@ const throughputApplyLongDelayMessage = (
collectionName: string,
requestedThroughput: number
): string => `
A request to increase the throughput is currently in progress.
A request to increase the throughput is currently in progress.
This operation will take 1-3 business days to complete. View the latest status in Notifications.<br />
Database: ${databaseName}, Container: ${collectionName} ${currentThroughput(
isAutoscale,
@@ -1230,7 +1231,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
this.isExecuting(false);
};
public onRevertClick = async (): Promise<any> => {
public onRevertClick = (): Q.Promise<any> => {
TelemetryProcessor.trace(Action.DiscardSettings, ActionModifiers.Mark, {
message: "Settings Discarded"
});
@@ -1273,17 +1274,21 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
this.selectedAutoPilotTier(originalAutoPilotTier);
}
}
return Q();
};
public async onValidIndexingPolicyEdit(): Promise<any> {
public onValidIndexingPolicyEdit(): Q.Promise<any> {
this.indexingPolicyContent.editableIsValid(true);
return Q();
}
public async onInvalidIndexingPolicyEdit(): Promise<any> {
public onInvalidIndexingPolicyEdit(): Q.Promise<any> {
this.indexingPolicyContent.editableIsValid(false);
return Q();
}
public onActivate(): Promise<any> {
public onActivate(): Q.Promise<any> {
return super.onActivate().then(async () => {
this.collection.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
const database: ViewModels.Database = this.collection.getDatabase();

View File

@@ -41,7 +41,7 @@ export default class SettingsTabV2 extends TabsBase {
});
}
public onActivate(): Promise<unknown> {
public onActivate(): Q.Promise<unknown> {
this.isExecuting(true);
this.currentCollection.loadOffer().then(
() => {

View File

@@ -1,7 +0,0 @@
<div style="width: 100%; height: 100%; margin-left: 3px;" data-bind="attr: { id: tabId }">
<!-- This runs the NotebookApp hosted by DataExplorer -->
<iframe
style="width:100%; height: 100%; border:none"
data-bind="setTemplateReady: true, attr: { src: sparkMasterSrc }, visible: !!sparkMasterSrc()"
></iframe>
</div>

View File

@@ -1,35 +0,0 @@
import * as ko from "knockout";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import TabsBase from "./TabsBase";
import Explorer from "../Explorer";
interface SparkMasterTabOptions extends ViewModels.TabOptions {
clusterConnectionInfo: DataModels.SparkClusterConnectionInfo;
container: Explorer;
}
export default class SparkMasterTab extends TabsBase {
public sparkMasterSrc: ko.Observable<string>;
private _clusterConnectionInfo: DataModels.SparkClusterConnectionInfo;
private _container: Explorer;
constructor(options: SparkMasterTabOptions) {
super(options);
super.onActivate.bind(this);
this._container = options.container;
this._clusterConnectionInfo = options.clusterConnectionInfo;
const sparkMasterEndpoint =
this._clusterConnectionInfo &&
this._clusterConnectionInfo.endpoints &&
this._clusterConnectionInfo.endpoints.find(
endpoint => endpoint.kind === DataModels.SparkClusterEndpointKind.SparkUI
);
this.sparkMasterSrc = ko.observable<string>(sparkMasterEndpoint && sparkMasterEndpoint.endpoint);
}
protected getContainer() {
return this._container;
}
}

View File

@@ -1,5 +1,6 @@
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import * as ko from "knockout";
import Q from "q";
import * as _ from "underscore";
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
import * as Constants from "../../Common/Constants";
@@ -60,11 +61,13 @@ export default class StoredProcedureTab extends ScriptTabBase {
});
};
public onDiscard = async (): Promise<any> => {
public onDiscard = (): Q.Promise<any> => {
this.setBaselines();
const original = this.editorContent.getEditableOriginalValue();
this.originalSprocBody(original);
this.originalSprocBody.valueHasMutated(); // trigger a re-render of the editor
return Q();
};
public onUpdateClick = (): Promise<any> => {
@@ -281,7 +284,8 @@ export default class StoredProcedureTab extends ScriptTabBase {
.finally(() => this.isExecuting(false));
}
public async onDelete(): Promise<any> {
public onDelete(): Q.Promise<any> {
// TODO
return Q();
}
}

View File

@@ -1,7 +1,6 @@
import DocumentsTabTemplate from "./DocumentsTab.html";
import ConflictsTabTemplate from "./ConflictsTab.html";
import GraphTabTemplate from "./GraphTab.html";
import SparkMasterTabTemplate from "./SparkMasterTab.html";
import NotebookV2TabTemplate from "./NotebookV2Tab.html";
import TerminalTabTemplate from "./TerminalTab.html";
import MongoDocumentsTabTemplate from "./MongoDocumentsTab.html";
@@ -61,15 +60,6 @@ export class GraphTab {
}
}
export class SparkMasterTab {
constructor() {
return {
viewModel: TabComponent,
template: SparkMasterTabTemplate
};
}
}
export class NotebookV2Tab {
constructor() {
return {

View File

@@ -1,4 +1,5 @@
import * as ko from "knockout";
import Q from "q";
import * as Constants from "../../Common/Constants";
import * as ViewModels from "../../Contracts/ViewModels";
import * as DataModels from "../../Contracts/DataModels";
@@ -92,8 +93,9 @@ export default class TabsBase extends WaitsForTemplateViewModel {
});
}
public async onTabClick(): Promise<any> {
public onTabClick(): Q.Promise<any> {
this.getContainer().tabsManager.activateTab(this);
return Q();
}
protected updateSelectedNode(): void {
@@ -125,7 +127,7 @@ export default class TabsBase extends WaitsForTemplateViewModel {
return this.onSpaceOrEnterKeyPress(event, () => this.onCloseTabButtonClick());
};
public async onActivate(): Promise<any> {
public onActivate(): Q.Promise<any> {
this.updateSelectedNode();
if (!!this.collection) {
this.collection.selectedSubnodeKind(this.tabKind);
@@ -147,6 +149,7 @@ export default class TabsBase extends WaitsForTemplateViewModel {
tabTitle: this.tabTitle(),
tabId: this.tabId
});
return Q();
}
public onErrorDetailsClick = (src: any, event: MouseEvent): boolean => {
@@ -169,8 +172,9 @@ export default class TabsBase extends WaitsForTemplateViewModel {
return true;
};
public async refresh(): Promise<any> {
public refresh(): Q.Promise<any> {
location.reload();
return Q();
}
protected getContainer(): Explorer {

View File

@@ -1,3 +1,4 @@
import Q from "q";
import * as ko from "knockout";
import * as Constants from "../../Common/Constants";
import DocumentId from "./DocumentId";
@@ -58,13 +59,13 @@ export default class ConflictId {
return;
}
public async loadConflict(): Promise<any> {
public loadConflict(): Q.Promise<any> {
const conflictsTab = this.container;
this.container.selectedConflictId(this);
if (this.operationType === Constants.ConflictOperationType.Create) {
this.container.initDocumentEditorForCreate(this, this.content);
return;
return Q();
}
this.container.loadingConflictData(true);
@@ -87,10 +88,10 @@ export default class ConflictId {
this.operationType === Constants.ConflictOperationType.Delete
) {
this.container.initDocumentEditorForNoOp(this);
return;
return Q();
}
throw reason;
return Q.reject(reason);
}
);
}

View File

@@ -65,7 +65,7 @@ export default class DocumentId {
return JSON.stringify(partitionKeyValue);
}
public loadDocument(): Promise<any> {
public loadDocument(): Q.Promise<any> {
return this.container.selectDocument(this);
}
}

View File

@@ -5,6 +5,7 @@ import * as ViewModels from "../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import DocumentId from "./DocumentId";
import DocumentsTab from "../Tabs/DocumentsTab";
import Q from "q";
import QueryTab from "../Tabs/QueryTab";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer";
@@ -40,9 +41,9 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
this.isCollectionExpanded = ko.observable<boolean>(true);
}
public async expandCollection(): Promise<void> {
public expandCollection(): Q.Promise<void> {
if (this.isCollectionExpanded()) {
return;
return Q();
}
this.isCollectionExpanded(true);
@@ -54,6 +55,8 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree
});
return Q.resolve();
}
public collapseCollection() {

View File

@@ -1,3 +1,4 @@
import Q from "q";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
@@ -59,34 +60,39 @@ export class QueryUtils {
public static queryPagesUntilContentPresent(
firstItemIndex: number,
queryItems: (itemIndex: number) => Promise<ViewModels.QueryResults>
): Promise<ViewModels.QueryResults> {
queryItems: (itemIndex: number) => Q.Promise<ViewModels.QueryResults>
): Q.Promise<ViewModels.QueryResults> {
let roundTrips: number = 0;
let netRequestCharge: number = 0;
const doRequest = async (itemIndex: number): Promise<ViewModels.QueryResults> =>
queryItems(itemIndex).then((results: ViewModels.QueryResults) => {
roundTrips = roundTrips + 1;
results.roundTrips = roundTrips;
results.requestCharge = Number(results.requestCharge) + netRequestCharge;
netRequestCharge = Number(results.requestCharge);
const resultsMetadata: ViewModels.QueryResultsMetadata = {
hasMoreResults: results.hasMoreResults,
itemCount: results.itemCount,
firstItemIndex: results.firstItemIndex,
lastItemIndex: results.lastItemIndex
};
if (resultsMetadata.itemCount === 0 && resultsMetadata.hasMoreResults) {
return doRequest(resultsMetadata.lastItemIndex);
const doRequest = (itemIndex: number): Q.Promise<ViewModels.QueryResults> =>
queryItems(itemIndex).then(
(results: ViewModels.QueryResults) => {
roundTrips = roundTrips + 1;
results.roundTrips = roundTrips;
results.requestCharge = Number(results.requestCharge) + netRequestCharge;
netRequestCharge = Number(results.requestCharge);
const resultsMetadata: ViewModels.QueryResultsMetadata = {
hasMoreResults: results.hasMoreResults,
itemCount: results.itemCount,
firstItemIndex: results.firstItemIndex,
lastItemIndex: results.lastItemIndex
};
if (resultsMetadata.itemCount === 0 && resultsMetadata.hasMoreResults) {
return doRequest(resultsMetadata.lastItemIndex);
}
return Q.resolve(results);
},
(error: any) => {
return Q.reject(error);
}
return results;
});
);
return doRequest(firstItemIndex);
}
public static queryAllPages(
queryItems: (itemIndex: number) => Promise<ViewModels.QueryResults>
): Promise<ViewModels.QueryResults> {
queryItems: (itemIndex: number) => Q.Promise<ViewModels.QueryResults>
): Q.Promise<ViewModels.QueryResults> {
const queryResults: ViewModels.QueryResults = {
documents: [],
activityId: undefined,
@@ -97,20 +103,25 @@ export class QueryUtils {
requestCharge: 0,
roundTrips: 0
};
const doRequest = async (itemIndex: number): Promise<ViewModels.QueryResults> =>
queryItems(itemIndex).then((results: ViewModels.QueryResults) => {
const { requestCharge, hasMoreResults, itemCount, lastItemIndex, documents } = results;
queryResults.roundTrips = queryResults.roundTrips + 1;
queryResults.requestCharge = Number(queryResults.requestCharge) + Number(requestCharge);
queryResults.hasMoreResults = hasMoreResults;
queryResults.itemCount = queryResults.itemCount + itemCount;
queryResults.lastItemIndex = lastItemIndex;
queryResults.documents = queryResults.documents.concat(documents);
if (queryResults.hasMoreResults) {
return doRequest(queryResults.lastItemIndex + 1);
const doRequest = (itemIndex: number): Q.Promise<ViewModels.QueryResults> =>
queryItems(itemIndex).then(
(results: ViewModels.QueryResults) => {
const { requestCharge, hasMoreResults, itemCount, lastItemIndex, documents } = results;
queryResults.roundTrips = queryResults.roundTrips + 1;
queryResults.requestCharge = Number(queryResults.requestCharge) + Number(requestCharge);
queryResults.hasMoreResults = hasMoreResults;
queryResults.itemCount = queryResults.itemCount + itemCount;
queryResults.lastItemIndex = lastItemIndex;
queryResults.documents = queryResults.documents.concat(documents);
if (queryResults.hasMoreResults) {
return doRequest(queryResults.lastItemIndex + 1);
}
return Q.resolve(queryResults);
},
(error: any) => {
return Q.reject(error);
}
return queryResults;
});
);
return doRequest(0);
}