diff --git a/src/Common/ErrorHandlingUtils.ts b/src/Common/ErrorHandlingUtils.ts
index 8a86bdf32..3a537683c 100644
--- a/src/Common/ErrorHandlingUtils.ts
+++ b/src/Common/ErrorHandlingUtils.ts
@@ -51,6 +51,11 @@ const replaceKnownError = (errorMessage: string): string => {
return "Database throughput is not supported for internal subscriptions.";
} else if (errorMessage?.indexOf("Partition key paths must contain only valid") >= 0) {
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
+ } else if (
+ errorMessage?.indexOf("The user aborted a request") >= 0 ||
+ errorMessage?.indexOf("The operation was aborted") >= 0
+ ) {
+ return "User aborted query.";
}
return errorMessage;
diff --git a/src/Explorer/Tabs/DocumentsTab.html b/src/Explorer/Tabs/DocumentsTab.html
index 096db482e..094c18e10 100644
--- a/src/Explorer/Tabs/DocumentsTab.html
+++ b/src/Explorer/Tabs/DocumentsTab.html
@@ -106,6 +106,18 @@
Apply Filter
+
+
+
;
private _resourceTokenPartitionKey: string;
private _isQueryCopilotSampleContainer: boolean;
+ private queryAbortController: AbortController;
constructor(options: ViewModels.DocumentsTabOptions) {
super(options);
@@ -401,6 +402,10 @@ export default class DocumentsTab extends TabsBase {
return true;
};
+ public onAbortQueryClick(): void {
+ this.queryAbortController.abort();
+ }
+
public onDocumentIdClick(clickedDocumentId: DocumentId): Q.Promise {
if (this.editorState() !== ViewModels.DocumentExplorerState.noDocumentSelected) {
return Q();
@@ -688,6 +693,7 @@ export default class DocumentsTab extends TabsBase {
}
public createIterator(): QueryIterator {
+ this.queryAbortController = new AbortController();
const filter: string = this.filterContent().trim();
const query: string = this.buildQuery(filter);
let options: any = {};
@@ -696,7 +702,7 @@ export default class DocumentsTab extends TabsBase {
if (this._resourceTokenPartitionKey) {
options.partitionKey = this._resourceTokenPartitionKey;
}
-
+ options.abortSignal = this.queryAbortController.signal;
return this._isQueryCopilotSampleContainer
? querySampleDocuments(query, options)
: queryDocuments(this.collection.databaseId, this.collection.id(), query, options);
diff --git a/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx b/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx
index e7b9bb07a..98384d92e 100644
--- a/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx
+++ b/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx
@@ -8,6 +8,7 @@ import React, { Fragment } from "react";
import SplitterLayout from "react-splitter-layout";
import "react-splitter-layout/lib/index.css";
import LaunchCopilot from "../../../../images/CopilotTabIcon.svg";
+import CancelQueryIcon from "../../../../images/Entity_cancel.svg";
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
import SaveQueryIcon from "../../../../images/save-cosmos.svg";
import { NormalizedEventKey, QueryCopilotSampleDatabaseId } from "../../../Common/Constants";
@@ -91,6 +92,7 @@ export default class QueryTabComponent extends React.Component {
+ this.queryAbortController = new AbortController();
if (this._iterator === undefined) {
this._iterator = this.props.isPreferredApiMongoDB
? queryIterator(
@@ -225,7 +228,10 @@ export default class QueryTabComponent extends React.Component this.queryAbortController.abort(),
+ commandButtonLabel: label,
+ ariaLabel: label,
+ hasPopup: false,
+ });
+ }
+
return buttons;
}