Remove Q from ViewModels (#390)

I got cold feet at the thought of merging #324 in one go, so I'm going to split it into smaller chunks and keep rebasing the large one until there's no more Q.
This commit is contained in:
Jordi Bunster 2021-01-28 10:13:26 -08:00 committed by GitHub
parent bddb288a89
commit f8ede0cc1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 115 deletions

View File

@ -5,7 +5,6 @@ import {
TriggerDefinition, TriggerDefinition,
UserDefinedFunctionDefinition, UserDefinedFunctionDefinition,
} from "@azure/cosmos"; } from "@azure/cosmos";
import Q from "q";
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
import Explorer from "../Explorer/Explorer"; import Explorer from "../Explorer/Explorer";
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent"; import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
@ -109,7 +108,7 @@ export interface CollectionBase extends TreeNode {
onDocumentDBDocumentsClick(): void; onDocumentDBDocumentsClick(): void;
onNewQueryClick(source: any, event: MouseEvent, queryText?: string): void; onNewQueryClick(source: any, event: MouseEvent, queryText?: string): void;
expandCollection(): Q.Promise<any>; expandCollection(): void;
collapseCollection(): void; collapseCollection(): void;
getDatabase(): Database; getDatabase(): Database;
} }
@ -176,7 +175,7 @@ export interface Collection extends CollectionBase {
onDragOver(source: Collection, event: { originalEvent: DragEvent }): void; onDragOver(source: Collection, event: { originalEvent: DragEvent }): void;
onDrop(source: Collection, event: { originalEvent: DragEvent }): void; onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
uploadFiles(fileList: FileList): Q.Promise<UploadDetails>; uploadFiles(fileList: FileList): Promise<UploadDetails>;
getLabel(): string; getLabel(): string;
} }
@ -294,7 +293,7 @@ export interface DocumentsTabOptions extends TabOptions {
} }
export interface SettingsTabV2Options extends TabOptions { export interface SettingsTabV2Options extends TabOptions {
getPendingNotification: Q.Promise<DataModels.Notification>; getPendingNotification: Promise<DataModels.Notification>;
} }
export interface ConflictsTabOptions extends TabOptions { export interface ConflictsTabOptions extends TabOptions {

View File

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

View File

@ -1,6 +1,5 @@
import { Resource, StoredProcedureDefinition, TriggerDefinition, UserDefinedFunctionDefinition } from "@azure/cosmos"; import { Resource, StoredProcedureDefinition, TriggerDefinition, UserDefinedFunctionDefinition } from "@azure/cosmos";
import * as ko from "knockout"; import * as ko from "knockout";
import Q from "q";
import * as _ from "underscore"; import * as _ from "underscore";
import UploadWorker from "worker-loader!../../workers/upload"; import UploadWorker from "worker-loader!../../workers/upload";
import { AuthType } from "../../AuthType"; import { AuthType } from "../../AuthType";
@ -254,9 +253,9 @@ export default class Collection implements ViewModels.Collection {
}); });
} }
public expandCollection(): Q.Promise<any> { public expandCollection(): void {
if (this.isCollectionExpanded()) { if (this.isCollectionExpanded()) {
return Q(); return;
} }
this.isCollectionExpanded(true); this.isCollectionExpanded(true);
@ -268,8 +267,6 @@ export default class Collection implements ViewModels.Collection {
defaultExperience: this.container.defaultExperience(), defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree, dataExplorerArea: Constants.Areas.ResourceTree,
}); });
return Q.resolve();
} }
public onDocumentDBDocumentsClick() { public onDocumentDBDocumentsClick() {
@ -547,7 +544,7 @@ export default class Collection implements ViewModels.Collection {
}); });
const tabTitle = !this.offer() ? "Settings" : "Scale & Settings"; const tabTitle = !this.offer() ? "Settings" : "Scale & Settings";
const pendingNotificationsPromise: Q.Promise<DataModels.Notification> = this._getPendingThroughputSplitNotification(); const pendingNotificationsPromise: Promise<DataModels.Notification> = this._getPendingThroughputSplitNotification();
const matchingTabs = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.SettingsV2, (tab) => { const matchingTabs = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.SettingsV2, (tab) => {
return tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id(); return tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id();
}); });
@ -580,7 +577,7 @@ export default class Collection implements ViewModels.Collection {
settingsTabV2: SettingsTabV2, settingsTabV2: SettingsTabV2,
traceStartData: any, traceStartData: any,
settingsTabOptions: ViewModels.TabOptions, settingsTabOptions: ViewModels.TabOptions,
getPendingNotification: Q.Promise<DataModels.Notification> getPendingNotification: Promise<DataModels.Notification>
): void => { ): void => {
const settingsTabV2Options: ViewModels.SettingsTabV2Options = { const settingsTabV2Options: ViewModels.SettingsTabV2Options = {
...settingsTabOptions, ...settingsTabOptions,
@ -980,19 +977,19 @@ export default class Collection implements ViewModels.Collection {
this.container.deleteCollectionConfirmationPane.open(); this.container.deleteCollectionConfirmationPane.open();
} }
public uploadFiles = (fileList: FileList): Q.Promise<UploadDetails> => { public uploadFiles = (fileList: FileList): Promise<UploadDetails> => {
// TODO: right now web worker is not working with AAD flow. Use main thread for upload for now until we have backend upload capability // TODO: right now web worker is not working with AAD flow. Use main thread for upload for now until we have backend upload capability
if (configContext.platform === Platform.Hosted && window.authType === AuthType.AAD) { if (configContext.platform === Platform.Hosted && window.authType === AuthType.AAD) {
return this._uploadFilesCors(fileList); return this._uploadFilesCors(fileList);
} }
const documentUploader: Worker = new UploadWorker(); const documentUploader: Worker = new UploadWorker();
const deferred: Q.Deferred<UploadDetails> = Q.defer<UploadDetails>();
let inProgressNotificationId: string = ""; let inProgressNotificationId: string = "";
if (!fileList || fileList.length === 0) { if (!fileList || fileList.length === 0) {
return Q.reject("No files specified"); return Promise.reject("No files specified");
} }
documentUploader.onmessage = (event: MessageEvent) => {
const onmessage = (resolve: (value: UploadDetails) => void, reject: (reason: any) => void, event: MessageEvent) => {
const numSuccessful: number = event.data.numUploadsSuccessful; const numSuccessful: number = event.data.numUploadsSuccessful;
const numFailed: number = event.data.numUploadsFailed; const numFailed: number = event.data.numUploadsFailed;
const runtimeError: string = event.data.runtimeError; const runtimeError: string = event.data.runtimeError;
@ -1001,31 +998,26 @@ export default class Collection implements ViewModels.Collection {
NotificationConsoleUtils.clearInProgressMessageWithId(inProgressNotificationId); NotificationConsoleUtils.clearInProgressMessageWithId(inProgressNotificationId);
documentUploader.terminate(); documentUploader.terminate();
if (!!runtimeError) { if (!!runtimeError) {
deferred.reject(runtimeError); reject(runtimeError);
} else if (numSuccessful === 0) { } else if (numSuccessful === 0) {
// all uploads failed // all uploads failed
NotificationConsoleUtils.logConsoleMessage( NotificationConsoleUtils.logConsoleError(`Failed to upload all documents to container ${this.id()}`);
ConsoleDataType.Error,
`Failed to upload all documents to container ${this.id()}`
);
} else if (numFailed > 0) { } else if (numFailed > 0) {
NotificationConsoleUtils.logConsoleMessage( NotificationConsoleUtils.logConsoleError(
ConsoleDataType.Error,
`Failed to upload ${numFailed} of ${numSuccessful + numFailed} documents to container ${this.id()}` `Failed to upload ${numFailed} of ${numSuccessful + numFailed} documents to container ${this.id()}`
); );
} else { } else {
NotificationConsoleUtils.logConsoleMessage( NotificationConsoleUtils.logConsoleInfo(
ConsoleDataType.Info,
`Successfully uploaded all ${numSuccessful} documents to container ${this.id()}` `Successfully uploaded all ${numSuccessful} documents to container ${this.id()}`
); );
} }
this._logUploadDetailsInConsole(uploadDetails); this._logUploadDetailsInConsole(uploadDetails);
deferred.resolve(uploadDetails); resolve(uploadDetails);
}; };
documentUploader.onerror = (event: ErrorEvent): void => { function onerror(reject: (reason: any) => void, event: ErrorEvent) {
documentUploader.terminate(); documentUploader.terminate();
deferred.reject(event.error); reject(event.error);
}; }
const uploaderMessage: StartUploadMessageParams = { const uploaderMessage: StartUploadMessageParams = {
files: fileList, files: fileList,
@ -1040,42 +1032,33 @@ export default class Collection implements ViewModels.Collection {
}, },
}; };
documentUploader.postMessage(uploaderMessage); return new Promise<UploadDetails>((resolve, reject) => {
inProgressNotificationId = NotificationConsoleUtils.logConsoleMessage( documentUploader.onmessage = onmessage.bind(null, resolve, reject);
ConsoleDataType.InProgress, documentUploader.onerror = onerror.bind(null, reject);
`Uploading and creating documents in container ${this.id()}`
);
return deferred.promise; documentUploader.postMessage(uploaderMessage);
inProgressNotificationId = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Uploading and creating documents in container ${this.id()}`
);
});
}; };
private _uploadFilesCors(files: FileList): Q.Promise<UploadDetails> { private async _uploadFilesCors(files: FileList): Promise<UploadDetails> {
const deferred: Q.Deferred<UploadDetails> = Q.defer<UploadDetails>(); const data = await Promise.all(Array.from(files).map((file) => this._uploadFile(file)));
const promises: Array<Q.Promise<UploadDetailsRecord>> = [];
for (let i = 0; i < files.length; i++) { return { data };
promises.push(this._uploadFile(files[i]));
}
Q.all(promises).then((uploadDetails: Array<UploadDetailsRecord>) => {
deferred.resolve({ data: uploadDetails });
});
return deferred.promise;
} }
private _uploadFile(file: File): Q.Promise<UploadDetailsRecord> { private _uploadFile(file: File): Promise<UploadDetailsRecord> {
const deferred: Q.Deferred<UploadDetailsRecord> = Q.defer();
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (evt: any): void => { const onload = (resolve: (value: UploadDetailsRecord) => void, evt: any): void => {
const fileData: string = evt.target.result; const fileData: string = evt.target.result;
this._createDocumentsFromFile(file.name, fileData).then((record) => { this._createDocumentsFromFile(file.name, fileData).then((record) => resolve(record));
deferred.resolve(record);
});
}; };
reader.onerror = (evt: ProgressEvent): void => { const onerror = (resolve: (value: UploadDetailsRecord) => void, evt: ProgressEvent): void => {
deferred.resolve({ resolve({
fileName: file.name, fileName: file.name,
numSucceeded: 0, numSucceeded: 0,
numFailed: 1, numFailed: 1,
@ -1083,9 +1066,11 @@ export default class Collection implements ViewModels.Collection {
}); });
}; };
reader.readAsText(file); return new Promise<UploadDetailsRecord>((resolve) => {
reader.onload = onload.bind(this, resolve);
return deferred.promise; reader.onerror = onerror.bind(this, resolve);
reader.readAsText(file);
});
} }
private async _createDocumentsFromFile(fileName: string, documentContent: string): Promise<UploadDetailsRecord> { private async _createDocumentsFromFile(fileName: string, documentContent: string): Promise<UploadDetailsRecord> {
@ -1119,46 +1104,35 @@ export default class Collection implements ViewModels.Collection {
} }
} }
private _getPendingThroughputSplitNotification(): Q.Promise<DataModels.Notification> { private async _getPendingThroughputSplitNotification(): Promise<DataModels.Notification> {
if (!this.container) { if (!this.container) {
return Q.resolve(undefined); return undefined;
} }
const deferred: Q.Deferred<DataModels.Notification> = Q.defer<DataModels.Notification>(); const throughputUpdateRegExp = new RegExp("Throughput update (.*) in progress");
fetchPortalNotifications().then( try {
(notifications: DataModels.Notification[]) => { const notifications = await fetchPortalNotifications();
if (!notifications || notifications.length === 0) { if (!notifications) {
deferred.resolve(undefined); return undefined;
return;
}
const pendingNotification = _.find(notifications, (notification: DataModels.Notification) => {
const throughputUpdateRegExp: RegExp = new RegExp("Throughput update (.*) in progress");
return (
notification.kind === "message" &&
notification.collectionName === this.id() &&
notification.description &&
throughputUpdateRegExp.test(notification.description)
);
});
deferred.resolve(pendingNotification);
},
(error: any) => {
Logger.logError(
JSON.stringify({
error: getErrorMessage(error),
accountName: this.container && this.container.databaseAccount(),
databaseName: this.databaseId,
collectionName: this.id(),
}),
"Settings tree node"
);
deferred.resolve(undefined);
} }
);
return deferred.promise; return notifications.find(
({ kind, collectionName, description = "" }) =>
kind === "message" && collectionName === this.id() && throughputUpdateRegExp.test(description)
);
} catch (error) {
Logger.logError(
JSON.stringify({
error: getErrorMessage(error),
accountName: this.container && this.container.databaseAccount(),
databaseName: this.databaseId,
collectionName: this.id(),
}),
"Settings tree node"
);
}
return undefined;
} }
private _logUploadDetailsInConsole(uploadDetails: UploadDetails): void { private _logUploadDetailsInConsole(uploadDetails: UploadDetails): void {

View File

@ -41,9 +41,9 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
this.isCollectionExpanded = ko.observable<boolean>(true); this.isCollectionExpanded = ko.observable<boolean>(true);
} }
public expandCollection(): Q.Promise<void> { public expandCollection(): void {
if (this.isCollectionExpanded()) { if (this.isCollectionExpanded()) {
return Q(); return;
} }
this.isCollectionExpanded(true); this.isCollectionExpanded(true);
@ -55,8 +55,6 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
defaultExperience: this.container.defaultExperience(), defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.ResourceTree, dataExplorerArea: Constants.Areas.ResourceTree,
}); });
return Q.resolve();
} }
public collapseCollection() { public collapseCollection() {

View File

@ -288,11 +288,9 @@ export class TabRouteHandler {
private _openSprocTabForResource(databaseId: string, collectionId: string, sprocId: string): void { private _openSprocTabForResource(databaseId: string, collectionId: string, sprocId: string): void {
this._executeActionHelper(() => { this._executeActionHelper(() => {
const collection: ViewModels.Collection = this._findMatchingCollectionForResource(databaseId, collectionId); const collection: ViewModels.Collection = this._findMatchingCollectionForResource(databaseId, collectionId);
collection && collection && collection.expandCollection();
collection.expandCollection().then(() => { const storedProcedure = collection && collection.findStoredProcedureWithId(sprocId);
const storedProcedure = collection && collection.findStoredProcedureWithId(sprocId); storedProcedure && storedProcedure.open();
storedProcedure && storedProcedure.open();
});
}); });
} }
@ -319,11 +317,9 @@ export class TabRouteHandler {
private _openTriggerTabForResource(databaseId: string, collectionId: string, triggerId: string): void { private _openTriggerTabForResource(databaseId: string, collectionId: string, triggerId: string): void {
this._executeActionHelper(() => { this._executeActionHelper(() => {
const collection: ViewModels.Collection = this._findMatchingCollectionForResource(databaseId, collectionId); const collection: ViewModels.Collection = this._findMatchingCollectionForResource(databaseId, collectionId);
collection && collection && collection.expandCollection();
collection.expandCollection().then(() => { const trigger = collection && collection.findTriggerWithId(triggerId);
const trigger = collection && collection.findTriggerWithId(triggerId); trigger && trigger.open();
trigger && trigger.open();
});
}); });
} }
@ -350,11 +346,9 @@ export class TabRouteHandler {
private _openUserDefinedFunctionTabForResource(databaseId: string, collectionId: string, udfId: string): void { private _openUserDefinedFunctionTabForResource(databaseId: string, collectionId: string, udfId: string): void {
this._executeActionHelper(() => { this._executeActionHelper(() => {
const collection: ViewModels.Collection = this._findMatchingCollectionForResource(databaseId, collectionId); const collection: ViewModels.Collection = this._findMatchingCollectionForResource(databaseId, collectionId);
collection && collection && collection.expandCollection();
collection.expandCollection().then(() => { const userDefinedFunction = collection && collection.findUserDefinedFunctionWithId(udfId);
const userDefinedFunction = collection && collection.findUserDefinedFunctionWithId(udfId); userDefinedFunction && userDefinedFunction.open();
userDefinedFunction && userDefinedFunction.open();
});
}); });
} }