Refactor DocumentClientUtilityBase to not be a class (#115)

This commit is contained in:
Steve Faulkner 2020-07-27 12:58:27 -05:00 committed by GitHub
parent 6d142f16f9
commit 2e49ed45c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1567 additions and 1754 deletions

View File

@ -52,6 +52,7 @@ jobs:
- run: npm run test
build:
runs-on: ubuntu-latest
needs: [lint, format, compile, unittest]
name: "Build"
steps:
- uses: actions/checkout@v2
@ -75,6 +76,7 @@ jobs:
path: dist/
endtoendemulator:
name: "End To End Tests | Emulator | SQL"
needs: [lint, format, compile, unittest]
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
@ -101,6 +103,7 @@ jobs:
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
endtoendsql:
name: "End To End Tests | SQL"
needs: [lint, format, compile, unittest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@ -129,6 +132,7 @@ jobs:
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
endtoendmongo:
name: "End To End Tests | Mongo"
needs: [lint, format, compile, unittest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@ -157,6 +161,7 @@ jobs:
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
accessibility:
name: "Accessibility | Hosted"
needs: [lint, format, compile, unittest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

View File

@ -39,10 +39,10 @@ module.exports = {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 18,
functions: 22,
lines: 28,
statements: 27
branches: 19.5,
functions: 24,
lines: 29.5,
statements: 28.5
}
},

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,13 @@ import * as Logger from "./Logger";
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
import { QueryUtils } from "../Utils/QueryUtils";
import Explorer from "../Explorer/Explorer";
import {
getOrCreateDatabaseAndCollection,
createDocument,
queryDocuments,
queryDocumentsPage,
deleteDocument
} from "./DocumentClientUtilityBase";
export class QueriesClient implements ViewModels.QueriesClient {
private static readonly PartitionKey: DataModels.PartitionKey = {
@ -33,14 +40,13 @@ export class QueriesClient implements ViewModels.QueriesClient {
ConsoleDataType.InProgress,
"Setting up account for saving queries"
);
return this.container.documentClientUtility
.getOrCreateDatabaseAndCollection({
collectionId: SavedQueries.CollectionName,
databaseId: SavedQueries.DatabaseName,
partitionKey: QueriesClient.PartitionKey,
offerThroughput: SavedQueries.OfferThroughput,
databaseLevelThroughput: undefined
})
return getOrCreateDatabaseAndCollection({
collectionId: SavedQueries.CollectionName,
databaseId: SavedQueries.DatabaseName,
partitionKey: QueriesClient.PartitionKey,
offerThroughput: SavedQueries.OfferThroughput,
databaseLevelThroughput: undefined
})
.then(
(collection: DataModels.Collection) => {
NotificationConsoleUtils.logConsoleMessage(
@ -89,8 +95,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
`Saving query ${query.queryName}`
);
query.id = query.queryName;
return this.container.documentClientUtility
.createDocument(queriesCollection, query)
return createDocument(queriesCollection, query)
.then(
(savedQuery: DataModels.Query) => {
NotificationConsoleUtils.logConsoleMessage(
@ -131,17 +136,11 @@ export class QueriesClient implements ViewModels.QueriesClient {
const options: any = { enableCrossPartitionQuery: true };
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
return this.container.documentClientUtility
.queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
return queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
.then(
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
this.container.documentClientUtility.queryDocumentsPage(
queriesCollection.id(),
queryIterator,
firstItemIndex,
options
);
queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex, options);
return QueryUtils.queryAllPages(fetchQueries).then(
(results: ViewModels.QueryResults) => {
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
@ -226,8 +225,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
query.queryName
); // TODO: Remove DocumentId's dependency on DocumentsTab
const options: any = { partitionKey: query.resourceId };
return this.container.documentClientUtility
.deleteDocument(queriesCollection, documentId)
return deleteDocument(queriesCollection, documentId)
.then(
() => {
NotificationConsoleUtils.logConsoleMessage(

View File

@ -1,6 +1,5 @@
import * as DataModels from "./DataModels";
import * as monaco from "monaco-editor";
import DocumentClientUtilityBase from "../Common/DocumentClientUtilityBase";
import Q from "q";
import { AccessibleVerticalList } from "../Explorer/Tree/AccessibleVerticalList";
import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient";
@ -18,7 +17,6 @@ import ConflictsTab from "../Explorer/Tabs/ConflictsTab";
import Trigger from "../Explorer/Tree/Trigger";
export interface ExplorerOptions {
documentClientUtility: DocumentClientUtilityBase;
notificationsClient: NotificationsClient;
isEmulator: boolean;
}
@ -245,13 +243,11 @@ export interface ConflictId {
*/
export interface PaneOptions {
id: string;
documentClientUtility: DocumentClientUtilityBase;
visible: ko.Observable<boolean>;
container?: Explorer;
}
export interface ContextualPane {
documentClientUtility: DocumentClientUtilityBase;
formErrors: ko.Observable<string>;
formErrorsDetails: ko.Observable<string>;
id: string;
@ -406,7 +402,6 @@ export interface TabOptions {
tabKind: CollectionTabKind;
title: string;
tabPath: string;
documentClientUtility: DocumentClientUtilityBase;
selfLink: string;
isActive: ko.Observable<boolean>;
hashLocation: string;
@ -493,11 +488,9 @@ export interface ScriptTabOption extends TabOptions {
// Tabs
export interface Tab {
documentClientUtility: DocumentClientUtilityBase;
node: TreeNode; // Can be null
collection: CollectionBase;
rid: string;
tabKind: CollectionTabKind;
tabId: string;
isActive: ko.Observable<boolean>;

View File

@ -1,10 +1,11 @@
jest.mock("../../Common/DocumentClientUtilityBase");
import * as ko from "knockout";
import * as sinon from "sinon";
import * as ViewModels from "../../Contracts/ViewModels";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
import Q from "q";
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
import { CosmosClient } from "../../Common/CosmosClient";
import * as DocumentClientUtility from "../../Common/DocumentClientUtilityBase";
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
import Explorer from "../Explorer";
@ -62,19 +63,12 @@ describe("ContainerSampleGenerator", () => {
const explorerStub = createExplorerStub(database);
explorerStub.isPreferredApiDocumentDB = ko.computed<boolean>(() => true);
const fakeDocumentClientUtility = sinon.createStubInstance(DocumentClientUtilityBase);
fakeDocumentClientUtility.getOrCreateDatabaseAndCollection.returns(Q.resolve(collection));
fakeDocumentClientUtility.createDocument.returns(Q.resolve());
explorerStub.documentClientUtility = fakeDocumentClientUtility;
const generator = await ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub);
generator.setData(sampleData);
await generator.createSampleContainerAsync();
expect(fakeDocumentClientUtility.createDocument.called).toBe(true);
expect(DocumentClientUtility.createDocument).toHaveBeenCalled();
});
it("should send gremlin queries for Graph API account", async () => {
@ -109,18 +103,12 @@ describe("ContainerSampleGenerator", () => {
const explorerStub = createExplorerStub(database);
explorerStub.isPreferredApiGraph = ko.computed<boolean>(() => true);
const fakeDocumentClientUtility = sinon.createStubInstance(DocumentClientUtilityBase);
fakeDocumentClientUtility.getOrCreateDatabaseAndCollection.returns(Q.resolve(collection));
fakeDocumentClientUtility.createDocument.returns(Q.resolve());
explorerStub.documentClientUtility = fakeDocumentClientUtility;
const generator = await ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub);
generator.setData(sampleData);
await generator.createSampleContainerAsync();
expect(fakeDocumentClientUtility.createDocument.called).toBe(false);
expect(DocumentClientUtility.createDocument).toHaveBeenCalled();
expect(executeStub.called).toBe(true);
});

View File

@ -7,6 +7,7 @@ import { CosmosClient } from "../../Common/CosmosClient";
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
import Explorer from "../Explorer";
import { createDocument, getOrCreateDatabaseAndCollection } from "../../Common/DocumentClientUtilityBase";
interface SampleDataFile extends DataModels.CreateDatabaseAndCollectionRequest {
data: any[];
@ -64,7 +65,7 @@ export class ContainerSampleGenerator {
options.initialHeaders[Constants.HttpHeaders.usePolygonsSmallerThanAHemisphere] = true;
}
await this.container.documentClientUtility.getOrCreateDatabaseAndCollection(createRequest, options);
await getOrCreateDatabaseAndCollection(createRequest, options);
await this.container.refreshAllDatabases();
const database = this.container.findDatabaseWithId(this.sampleDataFile.databaseId);
if (!database) {
@ -103,7 +104,7 @@ export class ContainerSampleGenerator {
} else {
// For SQL all queries are executed at the same time
this.sampleDataFile.data.forEach(doc => {
const subPromise = this.container.documentClientUtility.createDocument(collection, doc);
const subPromise = createDocument(collection, doc);
subPromise.catch(reason => NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, reason));
promises.push(subPromise);
});

View File

@ -15,7 +15,7 @@ import CassandraAddCollectionPane from "./Panes/CassandraAddCollectionPane";
import Database from "./Tree/Database";
import DeleteCollectionConfirmationPane from "./Panes/DeleteCollectionConfirmationPane";
import DeleteDatabaseConfirmationPane from "./Panes/DeleteDatabaseConfirmationPane";
import DocumentClientUtilityBase from "../Common/DocumentClientUtilityBase";
import { readDatabases, readCollection, readOffers, refreshCachedResources } from "../Common/DocumentClientUtilityBase";
import EditTableEntityPane from "./Panes/Tables/EditTableEntityPane";
import EnvironmentUtility from "../Common/EnvironmentUtility";
import GraphStylingPane from "./Panes/GraphStylingPane";
@ -127,7 +127,6 @@ export default class Explorer {
public extensionEndpoint: ko.Observable<string>;
public armEndpoint: ko.Observable<string>;
public isTryCosmosDBSubscription: ko.Observable<boolean>;
public documentClientUtility: DocumentClientUtilityBase;
public notificationsClient: ViewModels.NotificationsClient;
public queriesClient: ViewModels.QueriesClient;
public tableDataClient: TableDataClient;
@ -358,7 +357,6 @@ export default class Explorer {
}
});
this.memoryUsageInfo = ko.observable<DataModels.MemoryUsageInfo>();
this.documentClientUtility = options.documentClientUtility;
this.notificationsClient = options.notificationsClient;
this.isEmulator = options.isEmulator;
@ -584,7 +582,6 @@ export default class Explorer {
});
this.addDatabasePane = new AddDatabasePane({
documentClientUtility: this.documentClientUtility,
id: "adddatabasepane",
visible: ko.observable<boolean>(false),
@ -593,7 +590,6 @@ export default class Explorer {
this.addCollectionPane = new AddCollectionPane({
isPreferredApiTable: ko.computed(() => this.isPreferredApiTable()),
documentClientUtility: this.documentClientUtility,
id: "addcollectionpane",
visible: ko.observable<boolean>(false),
@ -601,7 +597,6 @@ export default class Explorer {
});
this.deleteCollectionConfirmationPane = new DeleteCollectionConfirmationPane({
documentClientUtility: this.documentClientUtility,
id: "deletecollectionconfirmationpane",
visible: ko.observable<boolean>(false),
@ -609,7 +604,6 @@ export default class Explorer {
});
this.deleteDatabaseConfirmationPane = new DeleteDatabaseConfirmationPane({
documentClientUtility: this.documentClientUtility,
id: "deletedatabaseconfirmationpane",
visible: ko.observable<boolean>(false),
@ -617,7 +611,6 @@ export default class Explorer {
});
this.graphStylingPane = new GraphStylingPane({
documentClientUtility: this.documentClientUtility,
id: "graphstylingpane",
visible: ko.observable<boolean>(false),
@ -625,7 +618,6 @@ export default class Explorer {
});
this.addTableEntityPane = new AddTableEntityPane({
documentClientUtility: this.documentClientUtility,
id: "addtableentitypane",
visible: ko.observable<boolean>(false),
@ -633,7 +625,6 @@ export default class Explorer {
});
this.editTableEntityPane = new EditTableEntityPane({
documentClientUtility: this.documentClientUtility,
id: "edittableentitypane",
visible: ko.observable<boolean>(false),
@ -641,7 +632,6 @@ export default class Explorer {
});
this.tableColumnOptionsPane = new TableColumnOptionsPane({
documentClientUtility: this.documentClientUtility,
id: "tablecolumnoptionspane",
visible: ko.observable<boolean>(false),
@ -649,7 +639,6 @@ export default class Explorer {
});
this.querySelectPane = new QuerySelectPane({
documentClientUtility: this.documentClientUtility,
id: "queryselectpane",
visible: ko.observable<boolean>(false),
@ -657,7 +646,6 @@ export default class Explorer {
});
this.newVertexPane = new NewVertexPane({
documentClientUtility: this.documentClientUtility,
id: "newvertexpane",
visible: ko.observable<boolean>(false),
@ -665,7 +653,6 @@ export default class Explorer {
});
this.cassandraAddCollectionPane = new CassandraAddCollectionPane({
documentClientUtility: this.documentClientUtility,
id: "cassandraaddcollectionpane",
visible: ko.observable<boolean>(false),
@ -673,7 +660,6 @@ export default class Explorer {
});
this.settingsPane = new SettingsPane({
documentClientUtility: this.documentClientUtility,
id: "settingspane",
visible: ko.observable<boolean>(false),
@ -681,7 +667,6 @@ export default class Explorer {
});
this.executeSprocParamsPane = new ExecuteSprocParamsPane({
documentClientUtility: this.documentClientUtility,
id: "executesprocparamspane",
visible: ko.observable<boolean>(false),
@ -689,7 +674,6 @@ export default class Explorer {
});
this.renewAdHocAccessPane = new RenewAdHocAccessPane({
documentClientUtility: this.documentClientUtility,
id: "renewadhocaccesspane",
visible: ko.observable<boolean>(false),
@ -697,7 +681,6 @@ export default class Explorer {
});
this.uploadItemsPane = new UploadItemsPane({
documentClientUtility: this.documentClientUtility,
id: "uploaditemspane",
visible: ko.observable<boolean>(false),
@ -707,7 +690,6 @@ export default class Explorer {
this.uploadItemsPaneAdapter = new UploadItemsPaneAdapter(this);
this.loadQueryPane = new LoadQueryPane({
documentClientUtility: this.documentClientUtility,
id: "loadquerypane",
visible: ko.observable<boolean>(false),
@ -715,7 +697,6 @@ export default class Explorer {
});
this.saveQueryPane = new SaveQueryPane({
documentClientUtility: this.documentClientUtility,
id: "savequerypane",
visible: ko.observable<boolean>(false),
@ -723,7 +704,6 @@ export default class Explorer {
});
this.browseQueriesPane = new BrowseQueriesPane({
documentClientUtility: this.documentClientUtility,
id: "browsequeriespane",
visible: ko.observable<boolean>(false),
@ -731,7 +711,6 @@ export default class Explorer {
});
this.uploadFilePane = new UploadFilePane({
documentClientUtility: this.documentClientUtility,
id: "uploadfilepane",
visible: ko.observable<boolean>(false),
@ -739,7 +718,6 @@ export default class Explorer {
});
this.stringInputPane = new StringInputPane({
documentClientUtility: this.documentClientUtility,
id: "stringinputpane",
visible: ko.observable<boolean>(false),
@ -747,7 +725,6 @@ export default class Explorer {
});
this.setupNotebooksPane = new SetupNotebooksPane({
documentClientUtility: this.documentClientUtility,
id: "setupnotebookspane",
visible: ko.observable<boolean>(false),
@ -780,7 +757,6 @@ export default class Explorer {
this.setupNotebooksPane
];
this.addDatabaseText.subscribe((addDatabaseText: string) => this.addDatabasePane.title(addDatabaseText));
this.rebindDocumentClientUtility.bind(this);
this.isTabsContentExpanded = ko.observable(false);
document.addEventListener(
@ -862,7 +838,7 @@ export default class Explorer {
this.editTableEntityPane.title("Edit Table Entity");
this.deleteCollectionConfirmationPane.title("Delete Table");
this.deleteCollectionConfirmationPane.collectionIdConfirmationText("Confirm by typing the table id");
this.tableDataClient = new TablesAPIDataClient(this.documentClientUtility);
this.tableDataClient = new TablesAPIDataClient();
break;
case Constants.DefaultAccountExperience.Cassandra.toLowerCase():
this.addCollectionText("New Table");
@ -881,7 +857,7 @@ export default class Explorer {
this.deleteCollectionConfirmationPane.collectionIdConfirmationText("Confirm by typing the table id");
this.deleteDatabaseConfirmationPane.title("Delete Keyspace");
this.deleteDatabaseConfirmationPane.databaseIdConfirmationText("Confirm by typing the keyspace id");
this.tableDataClient = new CassandraAPIDataClient(this.documentClientUtility);
this.tableDataClient = new CassandraAPIDataClient();
break;
}
});
@ -1066,13 +1042,6 @@ export default class Explorer {
// TODO: return result
}
public rebindDocumentClientUtility(documentClientUtility: DocumentClientUtilityBase): void {
this.documentClientUtility = documentClientUtility;
this._panes.forEach((pane: ViewModels.ContextualPane) => {
pane.documentClientUtility = documentClientUtility;
});
}
public copyUrlLink(src: any, event: MouseEvent): void {
const urlLinkInput: HTMLInputElement = document.getElementById("shareUrlLink") as HTMLInputElement;
urlLinkInput && urlLinkInput.select();
@ -1391,7 +1360,7 @@ export default class Explorer {
}
const deferred: Q.Deferred<void> = Q.defer();
this.documentClientUtility.readCollection(databaseId, collectionId).then((collection: DataModels.Collection) => {
readCollection(databaseId, collectionId).then((collection: DataModels.Collection) => {
this.resourceTokenCollection(new ResourceTokenCollection(this, databaseId, collection));
this.selectedNode(this.resourceTokenCollection());
deferred.resolve();
@ -1421,7 +1390,7 @@ export default class Explorer {
const refreshDatabases = (offers?: DataModels.Offer[]) => {
this._setLoadingStatusText("Fetching databases...");
this.documentClientUtility.readDatabases(null /*options*/).then(
readDatabases(null /*options*/).then(
(databases: DataModels.Database[]) => {
this._setLoadingStatusText("Successfully fetched databases.");
TelemetryProcessor.traceSuccess(
@ -1478,7 +1447,7 @@ export default class Explorer {
// Serverless accounts don't support offers call
refreshDatabases();
} else {
const offerPromise: Q.Promise<DataModels.Offer[]> = this.documentClientUtility.readOffers();
const offerPromise: Q.Promise<DataModels.Offer[]> = readOffers();
this._setLoadingStatusText("Fetching offers...");
offerPromise.then(
(offers: DataModels.Offer[]) => {
@ -1554,7 +1523,7 @@ export default class Explorer {
dataExplorerArea: Constants.Areas.ResourceTree
});
this.isRefreshingExplorer(true);
this.documentClientUtility.refreshCachedResources().then(
refreshCachedResources().then(
() => {
TelemetryProcessor.traceSuccess(
Action.LoadDatabases,
@ -2480,8 +2449,6 @@ export default class Explorer {
node: null,
title: notebookContentItem.name,
tabPath: notebookContentItem.path,
documentClientUtility: null,
collection: null,
selfLink: null,
masterKey: CosmosClient.masterKey() || "",
@ -2923,8 +2890,6 @@ export default class Explorer {
node: null,
title: title,
tabPath: title,
documentClientUtility: null,
collection: null,
selfLink: null,
hashLocation: hashLocation,

View File

@ -1,3 +1,4 @@
jest.mock("../../../Common/DocumentClientUtilityBase");
import React from "react";
import * as sinon from "sinon";
import { mount, ReactWrapper } from "enzyme";
@ -7,11 +8,11 @@ import { GraphExplorer, GraphExplorerProps, GraphAccessor, GraphHighlightedNodeD
import * as D3ForceGraph from "./D3ForceGraph";
import { GraphData } from "./GraphData";
import { TabComponent } from "../../Controls/Tabs/TabComponent";
import * as ViewModels from "../../../Contracts/ViewModels";
import * as DataModels from "../../../Contracts/DataModels";
import * as StorageUtility from "../../../Shared/StorageUtility";
import GraphTab from "../../Tabs/GraphTab";
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
import { queryDocuments, queryDocumentsPage } from "../../../Common/DocumentClientUtilityBase";
describe("Check whether query result is vertex array", () => {
it("should reject null as vertex array", () => {
@ -134,7 +135,7 @@ describe("GraphExplorer", () => {
const COLLECTION_SELF_LINK = "collectionSelfLink";
const gremlinRU = 789.12;
const createMockProps = (documentClientUtility?: any): GraphExplorerProps => {
const createMockProps = (): GraphExplorerProps => {
const graphConfig = GraphTab.createGraphConfig();
const graphConfigUi = GraphTab.createGraphConfigUiData(graphConfig);
@ -149,7 +150,6 @@ describe("GraphExplorer", () => {
onIsValidQueryChange: (isValidQuery: boolean): void => {},
collectionPartitionKeyProperty: "collectionPartitionKeyProperty",
documentClientUtility: documentClientUtility,
collectionRid: COLLECTION_RID,
collectionSelfLink: COLLECTION_SELF_LINK,
graphBackendEndpoint: "graphBackendEndpoint",
@ -188,7 +188,6 @@ describe("GraphExplorer", () => {
let wrapper: ReactWrapper;
let connectStub: sinon.SinonSpy;
let queryDocStub: sinon.SinonSpy;
let submitToBackendSpy: sinon.SinonSpy;
let renderResultAsJsonStub: sinon.SinonSpy;
let onMiddlePaneInitializedStub: sinon.SinonSpy;
@ -215,46 +214,6 @@ describe("GraphExplorer", () => {
[query: string]: AjaxResponse;
}
const createDocumentClientUtilityMock = (docDBResponse: AjaxResponse) => {
const mock = {
queryDocuments: () => {},
queryDocumentsPage: (
rid: string,
iterator: any,
firstItemIndex: number,
options: any
): Q.Promise<ViewModels.QueryResults> => {
const qresult = {
hasMoreResults: false,
firstItemIndex: firstItemIndex,
lastItemIndex: 0,
itemCount: 0,
documents: docDBResponse.response,
activityId: "",
headers: [] as any[],
requestCharge: gVRU
};
return Q.resolve(qresult);
}
};
const fakeIterator: any = {
nextItem: (callback: (error: any, document: DataModels.DocumentId) => void): void => {},
hasMoreResults: () => false,
executeNext: (callback: (error: any, documents: DataModels.DocumentId[], headers: any) => void): void => {}
};
queryDocStub = sinon.stub(mock, "queryDocuments").callsFake(
(container: ViewModels.DocumentRequestContainer, query: string, options: any): Q.Promise<any> => {
(fakeIterator as any)._query = query;
return Q.resolve(fakeIterator);
}
);
return mock;
};
const setupMocks = (
graphExplorer: GraphExplorer,
backendResponses: BackendResponses,
@ -333,7 +292,29 @@ describe("GraphExplorer", () => {
done: any,
ignoreD3Update: boolean
): GraphExplorer => {
const props: GraphExplorerProps = createMockProps(createDocumentClientUtilityMock(docDBResponse));
(queryDocuments as jest.Mock).mockImplementation((container: any, query: string, options: any) => {
return Q.resolve({
_query: query,
nextItem: (callback: (error: any, document: DataModels.DocumentId) => void): void => {},
hasMoreResults: () => false,
executeNext: (callback: (error: any, documents: DataModels.DocumentId[], headers: any) => void): void => {}
});
});
(queryDocumentsPage as jest.Mock).mockImplementation(
(rid: string, iterator: any, firstItemIndex: number, options: any) => {
return Q.resolve({
hasMoreResults: false,
firstItemIndex: firstItemIndex,
lastItemIndex: 0,
itemCount: 0,
documents: docDBResponse.response,
activityId: "",
headers: [] as any[],
requestCharge: gVRU
});
}
);
const props: GraphExplorerProps = createMockProps();
wrapper = mount(<GraphExplorer {...props} />);
graphExplorerInstance = wrapper.instance() as GraphExplorer;
setupMocks(graphExplorerInstance, backendResponses, done, ignoreD3Update);
@ -341,7 +322,7 @@ describe("GraphExplorer", () => {
};
const cleanUpStubsWrapper = () => {
queryDocStub.restore();
jest.resetAllMocks();
connectStub.restore();
submitToBackendSpy.restore();
renderResultAsJsonStub.restore();
@ -378,22 +359,11 @@ describe("GraphExplorer", () => {
expect((graphExplorerInstance.submitToBackend as sinon.SinonSpy).calledWith("g.V()")).toBe(false);
});
it("should submit g.V() as docdb query with proper query", () => {
expect(
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[2]
).toBe(DOCDB_G_DOT_V_QUERY);
});
it("should submit g.V() as docdb query with proper parameters", () => {
expect(
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[0]
).toEqual("databaseId");
expect(
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[1]
).toEqual("collectionId");
expect(
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[3]
).toEqual({ maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE, enableCrossPartitionQuery: true });
expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
enableCrossPartitionQuery: true
});
});
it("should call backend thrice (user query, fetch outE, then fetch inE)", () => {
@ -426,22 +396,11 @@ describe("GraphExplorer", () => {
expect((graphExplorerInstance.submitToBackend as sinon.SinonSpy).calledWith("g.V()")).toBe(false);
});
it("should submit g.V() as docdb query with proper query", () => {
expect(
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[2]
).toBe(DOCDB_G_DOT_V_QUERY);
});
it("should submit g.V() as docdb query with proper parameters", () => {
expect(
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[0]
).toEqual("databaseId");
expect(
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[1]
).toEqual("collectionId");
expect(
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[3]
).toEqual({ maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE, enableCrossPartitionQuery: true });
expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
enableCrossPartitionQuery: true
});
});
it("should call backend thrice (user query, fetch outE, then fetch inE)", () => {

View File

@ -28,7 +28,7 @@ import * as Constants from "../../../Common/Constants";
import { InputProperty } from "../../../Contracts/ViewModels";
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
import DocumentClientUtilityBase from "../../../Common/DocumentClientUtilityBase";
import { queryDocuments, queryDocumentsPage } from "../../../Common/DocumentClientUtilityBase";
export interface GraphAccessor {
applyFilter: () => void;
@ -47,7 +47,6 @@ export interface GraphExplorerProps {
onIsValidQueryChange: (isValidQuery: boolean) => void;
collectionPartitionKeyProperty: string;
documentClientUtility: DocumentClientUtilityBase;
collectionRid: string;
collectionSelfLink: string;
graphBackendEndpoint: string;
@ -697,7 +696,6 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
* @param cmd
*/
public submitToBackend(cmd: string): Q.Promise<GremlinClient.GremlinRequestResult> {
console.log("submit:", cmd);
const id = GraphExplorer.reportToConsole(ConsoleDataType.InProgress, `Executing: ${cmd}`);
this.setExecuteCounter(this.executeCounter + 1);
@ -730,26 +728,24 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
*/
public executeNonPagedDocDbQuery(query: string): Q.Promise<DataModels.DocumentId[]> {
// TODO maxItemCount: this reduces throttling, but won't cap the # of results
return this.props.documentClientUtility
.queryDocuments(this.props.databaseId, this.props.collectionId, query, {
maxItemCount: GraphExplorer.PAGE_ALL,
enableCrossPartitionQuery:
StorageUtility.LocalStorageUtility.getEntryString(StorageUtility.StorageKey.IsCrossPartitionQueryEnabled) ===
"true"
})
.then(
(iterator: QueryIterator<ItemDefinition & Resource>) => {
return iterator.fetchNext().then(response => response.resources);
},
(reason: any) => {
GraphExplorer.reportToConsole(
ConsoleDataType.Error,
`Failed to execute non-paged query ${query}. Reason:${reason}`,
reason
);
return null;
}
);
return queryDocuments(this.props.databaseId, this.props.collectionId, query, {
maxItemCount: GraphExplorer.PAGE_ALL,
enableCrossPartitionQuery:
StorageUtility.LocalStorageUtility.getEntryString(StorageUtility.StorageKey.IsCrossPartitionQueryEnabled) ===
"true"
}).then(
(iterator: QueryIterator<ItemDefinition & Resource>) => {
return iterator.fetchNext().then(response => response.resources);
},
(reason: any) => {
GraphExplorer.reportToConsole(
ConsoleDataType.Error,
`Failed to execute non-paged query ${query}. Reason:${reason}`,
reason
);
return null;
}
);
}
/**
@ -1732,12 +1728,10 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
query = `select root.id, root.${this.props.collectionPartitionKeyProperty} from root where IS_DEFINED(root._isEdge) = false order by root._ts asc`;
}
return this.props.documentClientUtility
.queryDocuments(this.props.databaseId, this.props.collectionId, query, {
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
enableCrossPartitionQuery:
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
})
return queryDocuments(this.props.databaseId, this.props.collectionId, query, {
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
enableCrossPartitionQuery: LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
})
.then(
(iterator: QueryIterator<ItemDefinition & Resource>) => {
this.currentDocDBQueryInfo = {
@ -1766,16 +1760,15 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
.currentDocDBQueryInfo.index + GraphExplorer.ROOT_LIST_PAGE_SIZE})`;
const id = GraphExplorer.reportToConsole(ConsoleDataType.InProgress, `Executing: ${queryInfoStr}`);
return this.props.documentClientUtility
.queryDocumentsPage(
this.props.collectionRid,
this.currentDocDBQueryInfo.iterator,
this.currentDocDBQueryInfo.index,
{
enableCrossPartitionQuery:
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
}
)
return queryDocumentsPage(
this.props.collectionRid,
this.currentDocDBQueryInfo.iterator,
this.currentDocDBQueryInfo.index,
{
enableCrossPartitionQuery:
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
}
)
.then((results: ViewModels.QueryResults) => {
GraphExplorer.clearConsoleProgress(id);
this.currentDocDBQueryInfo.index = results.lastItemIndex + 1;

View File

@ -3,7 +3,6 @@ import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
import { GraphConfig } from "../../Tabs/GraphTab";
import * as ViewModels from "../../../Contracts/ViewModels";
import { GraphExplorer, GraphAccessor } from "./GraphExplorer";
import DocumentClientUtilityBase from "../../../Common/DocumentClientUtilityBase";
interface Parameter {
onIsNewVertexDisabledChange: (isEnabled: boolean) => void;
@ -18,7 +17,6 @@ interface Parameter {
graphConfig?: GraphConfig;
collectionPartitionKeyProperty: string;
documentClientUtility: DocumentClientUtilityBase;
collectionRid: string;
collectionSelfLink: string;
graphBackendEndpoint: string;
@ -51,7 +49,6 @@ export class GraphExplorerAdapter implements ReactAdapter {
onIsGraphDisplayed={this.params.onIsGraphDisplayed}
onResetDefaultGraphConfigValues={this.params.onResetDefaultGraphConfigValues}
collectionPartitionKeyProperty={this.params.collectionPartitionKeyProperty}
documentClientUtility={this.params.documentClientUtility}
collectionRid={this.params.collectionRid}
collectionSelfLink={this.params.collectionSelfLink}
graphBackendEndpoint={this.params.graphBackendEndpoint}

View File

@ -57,7 +57,6 @@ export default class NotebookManager {
this.gitHubOAuthService = new GitHubOAuthService(this.junoClient);
this.gitHubClient = new GitHubClient(this.onGitHubClientError);
this.gitHubReposPane = new GitHubReposPane({
documentClientUtility: this.params.container.documentClientUtility,
id: "gitHubReposPane",
visible: ko.observable<boolean>(false),
container: this.params.container,

View File

@ -41,7 +41,7 @@ describe("Add Collection Pane", () => {
};
beforeEach(() => {
explorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
});

View File

@ -20,6 +20,7 @@ import { createMongoCollectionWithARM, createMongoCollectionWithProxy } from "..
import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent";
import { HashMap } from "../../Common/HashMap";
import { PlatformType } from "../../PlatformType";
import { refreshCachedResources, getOrCreateDatabaseAndCollection } from "../../Common/DocumentClientUtilityBase";
export default class AddCollectionPane extends ContextualPaneBase {
public defaultExperience: ko.Computed<string>;
@ -941,8 +942,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
)
);
} else {
createCollectionFunc = () =>
this.container.documentClientUtility.getOrCreateDatabaseAndCollection(createRequest, options);
createCollectionFunc = () => getOrCreateDatabaseAndCollection(createRequest, options);
}
createCollectionFunc().then(
@ -978,7 +978,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
};
TelemetryProcessor.traceSuccess(Action.CreateCollection, addCollectionPaneSuccessMessage, startKey);
this.resetData();
return this.container.documentClientUtility.refreshCachedResources().then(() => {
return refreshCachedResources().then(() => {
this.container.refreshAllDatabases();
});
},

View File

@ -40,7 +40,6 @@ describe("Add Database Pane", () => {
beforeEach(() => {
explorer = new Explorer({
documentClientUtility: null,
notificationsClient: null,
isEmulator: false
});

View File

@ -16,6 +16,7 @@ import { CassandraAPIDataClient } from "../Tables/TableDataClient";
import { ContextualPaneBase } from "./ContextualPaneBase";
import { CosmosClient } from "../../Common/CosmosClient";
import { PlatformType } from "../../PlatformType";
import { refreshCachedOffers, refreshCachedResources, createDatabase } from "../../Common/DocumentClientUtilityBase";
export default class AddDatabasePane extends ContextualPaneBase {
public defaultExperience: ko.Computed<string>;
@ -334,10 +335,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
) {
AddDbUtilities.createSqlDatabase(this.container.armEndpoint(), createDatabaseParameters, autoPilotSettings).then(
() => {
Promise.all([
this.container.documentClientUtility.refreshCachedOffers(),
this.container.documentClientUtility.refreshCachedResources()
]).then(() => {
Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => {
this._onCreateDatabaseSuccess(createDatabaseParameters.offerThroughput, startKey);
});
}
@ -354,10 +352,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
createDatabaseParameters,
autoPilotSettings
).then(() => {
Promise.all([
this.container.documentClientUtility.refreshCachedOffers(),
this.container.documentClientUtility.refreshCachedResources()
]).then(() => {
Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => {
this._onCreateDatabaseSuccess(createDatabaseParameters.offerThroughput, startKey);
});
});
@ -373,10 +368,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
createDatabaseParameters,
autoPilotSettings
).then(() => {
Promise.all([
this.container.documentClientUtility.refreshCachedOffers(),
this.container.documentClientUtility.refreshCachedResources()
]).then(() => {
Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => {
this._onCreateDatabaseSuccess(createDatabaseParameters.offerThroughput, startKey);
});
});
@ -413,7 +405,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
autoPilot,
hasAutoPilotV2FeatureFlag: this.hasAutoPilotV2FeatureFlag()
};
this.container.documentClientUtility.createDatabase(createRequest).then(
createDatabase(createRequest).then(
(database: DataModels.Database) => {
this._onCreateDatabaseSuccess(offerThroughput, telemetryStartKey);
},
@ -464,10 +456,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
startKey: number
): void {
AddDbUtilities.createCassandraKeyspace(armEndpoint, createKeyspaceParameters, autoPilotSettings).then(() => {
Promise.all([
this.container.documentClientUtility.refreshCachedOffers(),
this.container.documentClientUtility.refreshCachedResources()
]).then(() => {
Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => {
this._onCreateDatabaseSuccess(createKeyspaceParameters.offerThroughput, startKey);
});
});

View File

@ -5,7 +5,6 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
import { KeyCodes } from "../../Common/Constants";
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
import Explorer from "../Explorer";
// TODO: Use specific actions for logging telemetry data
@ -17,14 +16,12 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel imple
public formErrors: ko.Observable<string>;
public title: ko.Observable<string>;
public visible: ko.Observable<boolean>;
public documentClientUtility: DocumentClientUtilityBase;
public isExecuting: ko.Observable<boolean>;
constructor(options: ViewModels.PaneOptions) {
super();
this.id = options.id;
this.container = options.container;
this.documentClientUtility = options.documentClientUtility;
this.visible = options.visible || ko.observable(false);
this.firstFieldHasFocus = ko.observable<boolean>(false);
this.formErrors = ko.observable<string>();

View File

@ -1,3 +1,4 @@
jest.mock("../../Common/DocumentClientUtilityBase");
import * as ko from "knockout";
import * as sinon from "sinon";
import Q from "q";
@ -6,7 +7,7 @@ import * as ViewModels from "../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import DeleteCollectionConfirmationPane from "./DeleteCollectionConfirmationPane";
import DeleteFeedback from "../../Common/DeleteFeedback";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
import { deleteCollection } from "../../Common/DocumentClientUtilityBase";
import Explorer from "../Explorer";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { TreeNode } from "../../Contracts/ViewModels";
@ -16,7 +17,7 @@ describe("Delete Collection Confirmation Pane", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be true if 1 database and 1 collection", () => {
@ -55,15 +56,11 @@ describe("Delete Collection Confirmation Pane", () => {
describe("shouldRecordFeedback()", () => {
it("should return true if last collection and database does not have shared throughput else false", () => {
let fakeDocumentClientUtility = sinon.createStubInstance<DocumentClientUtilityBase>(
DocumentClientUtilityBase as any
);
let fakeExplorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
let fakeExplorer = new Explorer({ notificationsClient: null, isEmulator: false });
fakeExplorer.isNotificationConsoleExpanded = ko.observable<boolean>(false);
fakeExplorer.refreshAllDatabases = () => Q.resolve();
let pane = new DeleteCollectionConfirmationPane({
documentClientUtility: fakeDocumentClientUtility as any,
id: "deletecollectionconfirmationpane",
visible: ko.observable<boolean>(false),
container: fakeExplorer
@ -96,8 +93,7 @@ describe("Delete Collection Confirmation Pane", () => {
it("it should log feedback if last collection and database is not shared", () => {
let selectedCollectionId = "testCol";
let fakeDocumentClientUtility = {} as DocumentClientUtilityBase;
fakeDocumentClientUtility.deleteCollection = () => Q(null);
(deleteCollection as jest.Mock).mockResolvedValue(null);
let fakeExplorer = {} as Explorer;
fakeExplorer.findSelectedCollection = () => {
return {
@ -120,14 +116,12 @@ describe("Delete Collection Confirmation Pane", () => {
return false;
});
fakeExplorer.documentClientUtility = fakeDocumentClientUtility;
fakeExplorer.selectedNode = ko.observable<TreeNode>();
fakeExplorer.isLastCollection = () => true;
fakeExplorer.isSelectedDatabaseShared = () => false;
fakeExplorer.refreshAllDatabases = () => Q.resolve();
let pane = new DeleteCollectionConfirmationPane({
documentClientUtility: fakeDocumentClientUtility as any,
id: "deletecollectionconfirmationpane",
visible: ko.observable<boolean>(false),
container: fakeExplorer as any

View File

@ -11,6 +11,7 @@ import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility"
import DeleteFeedback from "../../Common/DeleteFeedback";
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { deleteCollection } from "../../Common/DocumentClientUtilityBase";
export default class DeleteCollectionConfirmationPane extends ContextualPaneBase {
public collectionIdConfirmationText: ko.Observable<string>;
@ -58,7 +59,7 @@ export default class DeleteCollectionConfirmationPane extends ContextualPaneBase
this.container
);
} else {
promise = this.container.documentClientUtility.deleteCollection(selectedCollection);
promise = deleteCollection(selectedCollection);
}
return promise.then(
() => {

View File

@ -1,23 +1,28 @@
jest.mock("../../Common/DocumentClientUtilityBase");
jest.mock("../../Shared/Telemetry/TelemetryProcessor");
import * as ko from "knockout";
import * as sinon from "sinon";
import Q from "q";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import DeleteDatabaseConfirmationPane from "./DeleteDatabaseConfirmationPane";
import DeleteFeedback from "../../Common/DeleteFeedback";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
import Explorer from "../Explorer";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { TreeNode } from "../../Contracts/ViewModels";
import { TabsManager } from "../Tabs/TabsManager";
import { deleteDatabase } from "../../Common/DocumentClientUtilityBase";
describe("Delete Database Confirmation Pane", () => {
describe("Explorer.isLastDatabase() and Explorer.isLastNonEmptyDatabase()", () => {
let explorer: Explorer;
beforeAll(() => {
(deleteDatabase as jest.Mock).mockResolvedValue(undefined);
});
beforeEach(() => {
explorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be true if only 1 database", () => {
@ -49,12 +54,10 @@ describe("Delete Database Confirmation Pane", () => {
describe("shouldRecordFeedback()", () => {
it("should return true if last non empty database or is last database that has shared throughput, else false", () => {
let fakeDocumentClientUtility = {} as DocumentClientUtilityBase;
let fakeExplorer = {} as Explorer;
fakeExplorer.isNotificationConsoleExpanded = ko.observable<boolean>(false);
let pane = new DeleteDatabaseConfirmationPane({
documentClientUtility: fakeDocumentClientUtility as any,
id: "deletedatabaseconfirmationpane",
visible: ko.observable<boolean>(false),
container: fakeExplorer as any
@ -78,20 +81,8 @@ describe("Delete Database Confirmation Pane", () => {
});
describe("submit()", () => {
let telemetryProcessorSpy: sinon.SinonSpy;
beforeEach(() => {
telemetryProcessorSpy = sinon.spy(TelemetryProcessor, "trace");
});
afterEach(() => {
telemetryProcessorSpy.restore();
});
it("on submit() it should log feedback if last non empty database or is last database that has shared throughput", () => {
let selectedDatabaseId = "testDB";
let fakeDocumentClientUtility = {} as DocumentClientUtilityBase;
fakeDocumentClientUtility.deleteDatabase = () => Q.resolve(null);
let fakeExplorer = {} as Explorer;
fakeExplorer.findSelectedDatabase = () => {
return {
@ -114,13 +105,11 @@ describe("Delete Database Confirmation Pane", () => {
fakeExplorer.isPreferredApiCassandra = ko.computed(() => {
return false;
});
fakeExplorer.documentClientUtility = fakeDocumentClientUtility;
fakeExplorer.selectedNode = ko.observable<TreeNode>();
fakeExplorer.tabsManager = new TabsManager();
fakeExplorer.isLastNonEmptyDatabase = () => true;
let pane = new DeleteDatabaseConfirmationPane({
documentClientUtility: fakeDocumentClientUtility as any,
id: "deletedatabaseconfirmationpane",
visible: ko.observable<boolean>(false),
container: fakeExplorer as any
@ -130,15 +119,12 @@ describe("Delete Database Confirmation Pane", () => {
pane.databaseDeleteFeedback(Feedback);
return pane.submit().then(() => {
expect(telemetryProcessorSpy.called).toBe(true);
let deleteFeedback = new DeleteFeedback(SubscriptionId, AccountName, DataModels.ApiKind.SQL, Feedback);
expect(
telemetryProcessorSpy.calledWith(
Action.DeleteDatabase,
ActionModifiers.Mark,
JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
)
).toBe(true);
expect(TelemetryProcessor.trace).toHaveBeenCalledWith(
Action.DeleteDatabase,
ActionModifiers.Mark,
JSON.stringify(deleteFeedback, Object.getOwnPropertyNames(deleteFeedback))
);
});
});
});

View File

@ -12,6 +12,7 @@ import DeleteFeedback from "../../Common/DeleteFeedback";
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { deleteDatabase } from "../../Common/DocumentClientUtilityBase";
export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
public databaseIdConfirmationText: ko.Observable<string>;
@ -59,7 +60,7 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
this.container
);
} else {
promise = this.container.documentClientUtility.deleteDatabase(selectedDatabase);
promise = deleteDatabase(selectedDatabase);
}
return promise.then(
() => {

View File

@ -7,7 +7,7 @@ describe("Settings Pane", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be true for SQL API", () => {

View File

@ -15,8 +15,14 @@ import * as TableEntityProcessor from "./TableEntityProcessor";
import * as ViewModels from "../../Contracts/ViewModels";
import { MessageTypes } from "../../Contracts/ExplorerContracts";
import { sendMessage } from "../../Common/MessageHandler";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
import Explorer from "../Explorer";
import {
queryDocuments,
refreshCachedResources,
deleteDocument,
updateDocument,
createDocument
} from "../../Common/DocumentClientUtilityBase";
export interface CassandraTableKeys {
partitionKeys: CassandraTableKey[];
@ -29,11 +35,7 @@ export interface CassandraTableKey {
}
export abstract class TableDataClient {
public documentClientUtility: DocumentClientUtilityBase;
constructor(documentClientUtility: DocumentClientUtilityBase) {
this.documentClientUtility = documentClientUtility;
}
constructor() {}
public abstract createDocument(
collection: ViewModels.Collection,
@ -65,20 +67,18 @@ export class TablesAPIDataClient extends TableDataClient {
entity: Entities.ITableEntity
): Q.Promise<Entities.ITableEntity> {
const deferred = Q.defer<Entities.ITableEntity>();
this.documentClientUtility
.createDocument(
collection,
TableEntityProcessor.convertEntityToNewDocument(<Entities.ITableEntityForTablesAPI>entity)
)
.then(
(newDocument: any) => {
const newEntity = TableEntityProcessor.convertDocumentsToEntities([newDocument])[0];
deferred.resolve(newEntity);
},
reason => {
deferred.reject(reason);
}
);
createDocument(
collection,
TableEntityProcessor.convertEntityToNewDocument(<Entities.ITableEntityForTablesAPI>entity)
).then(
(newDocument: any) => {
const newEntity = TableEntityProcessor.convertDocumentsToEntities([newDocument])[0];
deferred.resolve(newEntity);
},
reason => {
deferred.reject(reason);
}
);
return deferred.promise;
}
@ -88,21 +88,20 @@ export class TablesAPIDataClient extends TableDataClient {
entity: Entities.ITableEntity
): Q.Promise<Entities.ITableEntity> {
const deferred = Q.defer<Entities.ITableEntity>();
this.documentClientUtility
.updateDocument(
collection,
originalDocument,
TableEntityProcessor.convertEntityToNewDocument(<Entities.ITableEntityForTablesAPI>entity)
)
.then(
(newDocument: any) => {
const newEntity = TableEntityProcessor.convertDocumentsToEntities([newDocument])[0];
deferred.resolve(newEntity);
},
reason => {
deferred.reject(reason);
}
);
updateDocument(
collection,
originalDocument,
TableEntityProcessor.convertEntityToNewDocument(<Entities.ITableEntityForTablesAPI>entity)
).then(
(newDocument: any) => {
const newEntity = TableEntityProcessor.convertDocumentsToEntities([newDocument])[0];
deferred.resolve(newEntity);
},
reason => {
deferred.reject(reason);
}
);
return deferred.promise;
}
@ -114,7 +113,7 @@ export class TablesAPIDataClient extends TableDataClient {
let options: any = {};
options.enableCrossPartitionQuery = HeadersUtility.shouldEnableCrossPartitionKey();
this.documentClientUtility.queryDocuments(collection.databaseId, collection.id(), query, options).then(
queryDocuments(collection.databaseId, collection.id(), query, options).then(
iterator => {
iterator
.fetchNext()
@ -150,7 +149,7 @@ export class TablesAPIDataClient extends TableDataClient {
documentsToDelete &&
documentsToDelete.forEach(document => {
document.id = ko.observable<string>(document.id);
let promise: Q.Promise<any> = this.documentClientUtility.deleteDocument(collection, document);
let promise: Q.Promise<any> = deleteDocument(collection, document);
promiseArray.push(promise);
});
return Q.all(promiseArray);
@ -425,7 +424,7 @@ export class CassandraAPIDataClient extends TableDataClient {
ConsoleDataType.Info,
`Successfully created a keyspace with query ${createKeyspaceQuery}`
);
explorer.documentClientUtility.refreshCachedResources().finally(() => deferred.resolve());
refreshCachedResources().finally(() => deferred.resolve());
},
reason => {
NotificationConsoleUtils.logConsoleMessage(
@ -472,7 +471,7 @@ export class CassandraAPIDataClient extends TableDataClient {
ConsoleDataType.Info,
`Successfully created a table with query ${createTableQuery}`
);
this.documentClientUtility.refreshCachedResources(null).then(
refreshCachedResources(null).then(
() => {
deferred.resolve();
},
@ -521,7 +520,7 @@ export class CassandraAPIDataClient extends TableDataClient {
ConsoleDataType.Info,
`Successfully deleted resource with query ${deleteQuery}`
);
this.documentClientUtility.refreshCachedResources(null).then(
refreshCachedResources(null).then(
() => {
deferred.resolve();
},

View File

@ -21,6 +21,13 @@ import DeleteIcon from "../../../images/delete.svg";
import { QueryIterator, ItemDefinition, Resource, ConflictDefinition } from "@azure/cosmos";
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
import Explorer from "../Explorer";
import {
queryConflicts,
deleteConflict,
deleteDocument,
createDocument,
updateDocument
} from "../../Common/DocumentClientUtilityBase";
export default class ConflictsTab extends TabsBase {
public selectedConflictId: ko.Observable<ViewModels.ConflictId>;
@ -286,7 +293,7 @@ export default class ConflictsTab extends TabsBase {
if (selectedConflict.operationType === Constants.ConflictOperationType.Replace) {
const documentContent = JSON.parse(this.selectedConflictContent());
operationPromise = this._container.documentClientUtility.updateDocument(
operationPromise = updateDocument(
this.collection,
selectedConflict.buildDocumentIdFromConflict(documentContent[selectedConflict.partitionKeyProperty]),
documentContent
@ -296,13 +303,13 @@ export default class ConflictsTab extends TabsBase {
if (selectedConflict.operationType === Constants.ConflictOperationType.Create) {
const documentContent = JSON.parse(this.selectedConflictContent());
operationPromise = this._container.documentClientUtility.createDocument(this.collection, documentContent);
operationPromise = createDocument(this.collection, documentContent);
}
if (selectedConflict.operationType === Constants.ConflictOperationType.Delete && !!this.selectedConflictContent()) {
const documentContent = JSON.parse(this.selectedConflictContent());
operationPromise = this._container.documentClientUtility.deleteDocument(
operationPromise = deleteDocument(
this.collection,
selectedConflict.buildDocumentIdFromConflict(documentContent[selectedConflict.partitionKeyProperty])
);
@ -311,7 +318,7 @@ export default class ConflictsTab extends TabsBase {
return operationPromise
.then(
() => {
return this._container.documentClientUtility.deleteConflict(this.collection, selectedConflict).then(() => {
return deleteConflict(this.collection, selectedConflict).then(() => {
this.conflictIds.remove((conflictId: ViewModels.ConflictId) => conflictId.rid === selectedConflict.rid);
this.selectedConflictContent("");
this.selectedConflictCurrent("");
@ -370,8 +377,7 @@ export default class ConflictsTab extends TabsBase {
conflictResourceId: selectedConflict.resourceId
});
return this._container.documentClientUtility
.deleteConflict(this.collection, selectedConflict)
return deleteConflict(this.collection, selectedConflict)
.then(
() => {
this.conflictIds.remove((conflictId: ViewModels.ConflictId) => conflictId.rid === selectedConflict.rid);
@ -491,7 +497,7 @@ export default class ConflictsTab extends TabsBase {
const query: string = undefined;
let options: any = {};
options.enableCrossPartitionQuery = HeadersUtility.shouldEnableCrossPartitionKey();
return this.documentClientUtility.queryConflicts(this.collection.databaseId, this.collection.id(), query, options);
return queryConflicts(this.collection.databaseId, this.collection.id(), query, options);
}
public loadNextPage(): Q.Promise<any> {

View File

@ -18,6 +18,7 @@ import { CosmosClient } from "../../Common/CosmosClient";
import { PlatformType } from "../../PlatformType";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import Explorer from "../Explorer";
import { updateOfferThroughputBeyondLimit, updateOffer } from "../../Common/DocumentClientUtilityBase";
const updateThroughputBeyondLimitWarningMessage: string = `
You are about to request an increase in throughput beyond the pre-allocated capacity.
@ -499,13 +500,13 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
delete newOffer.content.offerAutopilotSettings;
}
const updateOfferPromise = this.container.documentClientUtility
.updateOffer(this.database.offer(), newOffer, headerOptions)
.then((updatedOffer: DataModels.Offer) => {
const updateOfferPromise = updateOffer(this.database.offer(), newOffer, headerOptions).then(
(updatedOffer: DataModels.Offer) => {
this.database.offer(updatedOffer);
this.database.offer.valueHasMutated();
this._wasAutopilotOriginallySet(this.isAutoPilotSelected());
});
}
);
promises.push(updateOfferPromise);
} else {
if (this.throughput.editableIsDirty() || this.isAutoPilotSelected.editableIsDirty()) {
@ -527,32 +528,30 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
throughput: newThroughput,
offerIsRUPerMinuteThroughputEnabled: false
};
const updateOfferBeyondLimitPromise: Q.Promise<void> = this.documentClientUtility
.updateOfferThroughputBeyondLimit(requestPayload)
.then(
() => {
this.database.offer().content.offerThroughput = originalThroughputValue;
this.throughput(originalThroughputValue);
this.notificationStatusInfo(
throughputApplyDelayedMessage(this.isAutoPilotSelected(), newThroughput, this.database.id())
);
this.throughput.valueHasMutated(); // force component re-render
},
(error: any) => {
TelemetryProcessor.traceFailure(
Action.UpdateSettings,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.database && this.database.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
},
startKey
);
}
);
const updateOfferBeyondLimitPromise: Q.Promise<void> = updateOfferThroughputBeyondLimit(requestPayload).then(
() => {
this.database.offer().content.offerThroughput = originalThroughputValue;
this.throughput(originalThroughputValue);
this.notificationStatusInfo(
throughputApplyDelayedMessage(this.isAutoPilotSelected(), newThroughput, this.database.id())
);
this.throughput.valueHasMutated(); // force component re-render
},
(error: any) => {
TelemetryProcessor.traceFailure(
Action.UpdateSettings,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.database && this.database.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
},
startKey
);
}
);
promises.push(updateOfferBeyondLimitPromise);
} else {
const newOffer: DataModels.Offer = {
@ -577,13 +576,13 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
newOffer.content.offerAutopilotSettings = { maxThroughput: 0 };
}
const updateOfferPromise = this.container.documentClientUtility
.updateOffer(this.database.offer(), newOffer, headerOptions)
.then((updatedOffer: DataModels.Offer) => {
const updateOfferPromise = updateOffer(this.database.offer(), newOffer, headerOptions).then(
(updatedOffer: DataModels.Offer) => {
this._wasAutopilotOriginallySet(this.isAutoPilotSelected());
this.database.offer(updatedOffer);
this.database.offer.valueHasMutated();
});
}
);
promises.push(updateOfferPromise);
}

View File

@ -3,7 +3,6 @@ import * as ViewModels from "../../Contracts/ViewModels";
import * as Constants from "../../Common/Constants";
import DocumentsTab from "./DocumentsTab";
import Explorer from "../Explorer";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
describe("Documents tab", () => {
describe("buildQuery", () => {
@ -14,7 +13,6 @@ describe("Documents tab", () => {
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
@ -28,13 +26,11 @@ describe("Documents tab", () => {
describe("showPartitionKey", () => {
const explorer = new Explorer({
documentClientUtility: null,
notificationsClient: null,
isEmulator: false
});
const mongoExplorer = new Explorer({
documentClientUtility: null,
notificationsClient: null,
isEmulator: false
});
@ -97,7 +93,6 @@ describe("Documents tab", () => {
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
@ -116,7 +111,6 @@ describe("Documents tab", () => {
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
@ -135,7 +129,6 @@ describe("Documents tab", () => {
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
@ -154,7 +147,6 @@ describe("Documents tab", () => {
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),
@ -173,7 +165,6 @@ describe("Documents tab", () => {
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),

View File

@ -26,6 +26,13 @@ import { extractPartitionKey, PartitionKeyDefinition, QueryIterator, ItemDefinit
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
import Explorer from "../Explorer";
import {
readDocument,
queryDocuments,
deleteDocument,
updateDocument,
createDocument
} from "../../Common/DocumentClientUtilityBase";
export default class DocumentsTab extends TabsBase implements ViewModels.DocumentsTab {
public selectedDocumentId: ko.Observable<ViewModels.DocumentId>;
@ -442,8 +449,7 @@ export default class DocumentsTab extends TabsBase implements ViewModels.Documen
});
const document = JSON.parse(this.selectedDocumentContent());
this.isExecuting(true);
return this.documentClientUtility
.createDocument(this.collection, document)
return createDocument(this.collection, document)
.then(
(savedDocument: any) => {
const value: string = this.renderObjectForEditor(savedDocument || {}, null, 4);
@ -516,8 +522,7 @@ export default class DocumentsTab extends TabsBase implements ViewModels.Documen
tabTitle: this.tabTitle()
});
this.isExecuting(true);
return this.documentClientUtility
.updateDocument(this.collection, selectedDocumentId, documentContent)
return updateDocument(this.collection, selectedDocumentId, documentContent)
.then(
(updatedDocument: any) => {
const value: string = this.renderObjectForEditor(updatedDocument || {}, null, 4);
@ -665,7 +670,7 @@ export default class DocumentsTab extends TabsBase implements ViewModels.Documen
};
protected __deleteDocument(documentId: ViewModels.DocumentId): Q.Promise<any> {
return this.documentClientUtility.deleteDocument(this.collection, documentId);
return deleteDocument(this.collection, documentId);
}
private _deleteDocument(selectedDocumentId: ViewModels.DocumentId): Q.Promise<any> {
@ -724,12 +729,12 @@ export default class DocumentsTab extends TabsBase implements ViewModels.Documen
options.partitionKey = this._resourceTokenPartitionKey;
}
return this.documentClientUtility.queryDocuments(this.collection.databaseId, this.collection.id(), query, options);
return queryDocuments(this.collection.databaseId, this.collection.id(), query, options);
}
public selectDocument(documentId: ViewModels.DocumentId): Q.Promise<any> {
this.selectedDocumentId(documentId);
return this.documentClientUtility.readDocument(this.collection, documentId).then((content: any) => {
return readDocument(this.collection, documentId).then((content: any) => {
this.initDocumentEditor(documentId, content);
});
}

View File

@ -83,7 +83,6 @@ export default class GraphTab extends TabsBase implements ViewModels.Tab {
onIsFilterQueryLoading: (isFilterQueryLoading: boolean): void => this.isFilterQueryLoading(isFilterQueryLoading),
onIsValidQuery: (isValidQuery: boolean): void => this.isValidQuery(isValidQuery),
collectionPartitionKeyProperty: options.collectionPartitionKeyProperty,
documentClientUtility: this.documentClientUtility,
collectionRid: this.rid,
collectionSelfLink: options.selfLink,
graphBackendEndpoint: GraphTab.getGremlinEndpoint(options.account),
@ -101,7 +100,6 @@ export default class GraphTab extends TabsBase implements ViewModels.Tab {
this.isFilterQueryLoading = ko.observable(false);
this.isValidQuery = ko.observable(true);
this.documentClientUtility = options.documentClientUtility;
this.toolbarViewModel = ko.observable<Toolbar>();
}

View File

@ -25,7 +25,6 @@ describe("Query Tab", () => {
database: database,
title: "",
tabPath: "",
documentClientUtility: container.documentClientUtility,
selfLink: "",
isActive: ko.observable<boolean>(false),
hashLocation: "",
@ -51,7 +50,7 @@ describe("Query Tab", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be true for accounts using SQL API", () => {
@ -71,7 +70,7 @@ describe("Query Tab", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be visible when using a supported API", () => {

View File

@ -16,6 +16,7 @@ import { QueryUtils } from "../../Utils/QueryUtils";
import SaveQueryIcon from "../../../images/save-cosmos.svg";
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
import { queryDocuments, queryDocumentsPage } from "../../Common/DocumentClientUtilityBase";
enum ToggleState {
Result,
@ -290,12 +291,7 @@ export default class QueryTab extends TabsBase implements ViewModels.QueryTab, V
options.enableCrossPartitionQuery = HeadersUtility.shouldEnableCrossPartitionKey();
const queryDocuments = (firstItemIndex: number) =>
this.documentClientUtility.queryDocumentsPage(
this.collection && this.collection.id(),
this._iterator,
firstItemIndex,
options
);
queryDocumentsPage(this.collection && this.collection.id(), this._iterator, firstItemIndex, options);
this.isExecuting(true);
return QueryUtils.queryPagesUntilContentPresent(firstItemIndex, queryDocuments)
.then(
@ -497,9 +493,9 @@ export default class QueryTab extends TabsBase implements ViewModels.QueryTab, V
}
return Q(
this.documentClientUtility
.queryDocuments(this.collection.databaseId, this.collection.id(), this.sqlStatementToExecute(), options)
.then(iterator => (this._iterator = iterator))
queryDocuments(this.collection.databaseId, this.collection.id(), this.sqlStatementToExecute(), options).then(
iterator => (this._iterator = iterator)
)
);
}

View File

@ -4,7 +4,6 @@ import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import Collection from "../Tree/Collection";
import Database from "../Tree/Database";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
import Explorer from "../Explorer";
import SettingsTab from "../Tabs/SettingsTab";
@ -63,8 +62,6 @@ describe("Settings tab", () => {
tabKind: ViewModels.CollectionTabKind.Settings,
title: "Scale & Settings",
tabPath: "",
documentClientUtility: undefined,
selfLink: "",
hashLocation: "",
isActive: ko.observable(false),
@ -80,7 +77,7 @@ describe("Settings tab", () => {
};
beforeEach(() => {
explorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
});
@ -179,7 +176,7 @@ describe("Settings tab", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
});
@ -188,8 +185,6 @@ describe("Settings tab", () => {
tabKind: ViewModels.CollectionTabKind.Settings,
title: "Scale & Settings",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
isActive: ko.observable(false),
@ -212,7 +207,6 @@ describe("Settings tab", () => {
tabKind: ViewModels.CollectionTabKind.Settings,
title: "Scale & Settings",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
@ -231,7 +225,6 @@ describe("Settings tab", () => {
tabKind: ViewModels.CollectionTabKind.Settings,
title: "Scale & Settings",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
@ -261,7 +254,7 @@ describe("Settings tab", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer({ documentClientUtility: null, notificationsClient: null, isEmulator: false });
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
});
@ -270,7 +263,6 @@ describe("Settings tab", () => {
tabKind: ViewModels.CollectionTabKind.Settings,
title: "Scale & Settings",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
@ -287,7 +279,6 @@ describe("Settings tab", () => {
tabKind: ViewModels.CollectionTabKind.Settings,
title: "Scale & Settings",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
@ -313,7 +304,6 @@ describe("Settings tab", () => {
tabKind: ViewModels.CollectionTabKind.Settings,
title: "Scale & Settings",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
@ -346,7 +336,6 @@ describe("Settings tab", () => {
function getCollection(defaultApi: string, partitionKeyOption: PartitionKeyOption) {
const explorer = new Explorer({
documentClientUtility: null,
notificationsClient: null,
isEmulator: false
});
@ -394,7 +383,6 @@ describe("Settings tab", () => {
tabKind: ViewModels.CollectionTabKind.Settings,
title: "Scale & Settings",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
@ -483,7 +471,6 @@ describe("Settings tab", () => {
describe("AutoPilot", () => {
function getCollection(autoPilotTier: DataModels.AutopilotTier) {
const explorer = new Explorer({
documentClientUtility: null,
notificationsClient: null,
isEmulator: false
});
@ -540,7 +527,6 @@ describe("Settings tab", () => {
tabKind: ViewModels.CollectionTabKind.Settings,
title: "Scale & Settings",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",

View File

@ -18,6 +18,11 @@ import { CosmosClient } from "../../Common/CosmosClient";
import { PlatformType } from "../../PlatformType";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import Explorer from "../Explorer";
import {
updateOfferThroughputBeyondLimit,
updateOffer,
updateCollection
} from "../../Common/DocumentClientUtilityBase";
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.
@ -1062,9 +1067,8 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
}
const newCollection: DataModels.Collection = _.extend({}, this.collection.rawDataModel, newCollectionAttributes);
const updateCollectionPromise = this.container.documentClientUtility
.updateCollection(this.collection.databaseId, this.collection, newCollection)
.then((updatedCollection: DataModels.Collection) => {
const updateCollectionPromise = updateCollection(this.collection.databaseId, this.collection, newCollection).then(
(updatedCollection: DataModels.Collection) => {
this.collection.rawDataModel = updatedCollection;
this.collection.defaultTtl(updatedCollection.defaultTtl);
this.collection.analyticalStorageTtl(updatedCollection.analyticalStorageTtl);
@ -1073,7 +1077,8 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
this.collection.conflictResolutionPolicy(updatedCollection.conflictResolutionPolicy);
this.collection.changeFeedPolicy(updatedCollection.changeFeedPolicy);
this.collection.geospatialConfig(updatedCollection.geospatialConfig);
});
}
);
promises.push(updateCollectionPromise);
}
@ -1147,48 +1152,46 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
throughput: newThroughput,
offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled
};
const updateOfferBeyondLimitPromise: Q.Promise<void> = this.documentClientUtility
.updateOfferThroughputBeyondLimit(requestPayload)
.then(
() => {
this.collection.offer().content.offerThroughput = originalThroughputValue;
this.throughput(originalThroughputValue);
this.notificationStatusInfo(
throughputApplyDelayedMessage(
this.isAutoPilotSelected(),
originalThroughputValue,
this._getThroughputUnit(),
this.collection.databaseId,
this.collection.id(),
newThroughput
)
);
this.throughput.valueHasMutated(); // force component re-render
},
(error: any) => {
TelemetryProcessor.traceFailure(
Action.UpdateSettings,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.collection && this.collection.databaseId,
collectionName: this.collection && this.collection.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
},
startKey
);
}
);
const updateOfferBeyondLimitPromise: Q.Promise<void> = updateOfferThroughputBeyondLimit(requestPayload).then(
() => {
this.collection.offer().content.offerThroughput = originalThroughputValue;
this.throughput(originalThroughputValue);
this.notificationStatusInfo(
throughputApplyDelayedMessage(
this.isAutoPilotSelected(),
originalThroughputValue,
this._getThroughputUnit(),
this.collection.databaseId,
this.collection.id(),
newThroughput
)
);
this.throughput.valueHasMutated(); // force component re-render
},
(error: any) => {
TelemetryProcessor.traceFailure(
Action.UpdateSettings,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.collection && this.collection.databaseId,
collectionName: this.collection && this.collection.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle(),
error: error
},
startKey
);
}
);
promises.push(updateOfferBeyondLimitPromise);
} else {
const updateOfferPromise = this.documentClientUtility
.updateOffer(this.collection.offer(), newOffer, headerOptions)
.then((updatedOffer: DataModels.Offer) => {
const updateOfferPromise = updateOffer(this.collection.offer(), newOffer, headerOptions).then(
(updatedOffer: DataModels.Offer) => {
this.collection.offer(updatedOffer);
this.collection.offer.valueHasMutated();
});
}
);
promises.push(updateOfferPromise);
}

View File

@ -10,6 +10,7 @@ import ScriptTabBase from "./ScriptTabBase";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
import StoredProcedure from "../Tree/StoredProcedure";
import { createStoredProcedure, updateStoredProcedure } from "../../Common/DocumentClientUtilityBase";
enum ToggleState {
Result = "result",
@ -81,8 +82,7 @@ export default class StoredProcedureTab extends ScriptTabBase implements ViewMod
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.tabTitle()
});
return this.documentClientUtility
.updateStoredProcedure(this.collection, data)
return updateStoredProcedure(this.collection, data)
.then(
(updatedResource: DataModels.StoredProcedure) => {
this.resource(updatedResource);
@ -240,8 +240,7 @@ export default class StoredProcedureTab extends ScriptTabBase implements ViewMod
tabTitle: this.tabTitle()
});
return this.documentClientUtility
.createStoredProcedure(this.collection, resource)
return createStoredProcedure(this.collection, resource)
.then(
createdResource => {
this.tabTitle(createdResource.id);

View File

@ -7,13 +7,11 @@ import { RouteHandler } from "../../RouteHandlers/RouteHandler";
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import ThemeUtility from "../../Common/ThemeUtility";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
import Explorer from "../Explorer";
// TODO: Use specific actions for logging telemetry data
export default class TabsBase extends WaitsForTemplateViewModel implements ViewModels.Tab {
public closeTabButton: ViewModels.Button;
public documentClientUtility: DocumentClientUtilityBase;
public node: ViewModels.TreeNode;
public collection: ViewModels.CollectionBase;
public database: ViewModels.Database;
@ -39,7 +37,6 @@ export default class TabsBase extends WaitsForTemplateViewModel implements ViewM
const id = new Date().getTime().toString();
this._theme = ThemeUtility.getMonacoTheme(options.theme);
this.documentClientUtility = options.documentClientUtility;
this.node = options.node;
this.collection = options.collection;
this.database = options.database;

View File

@ -1,7 +1,6 @@
import * as ko from "knockout";
import * as ViewModels from "../../Contracts/ViewModels";
import { TabsManager } from "./TabsManager";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
import DocumentsTab from "./DocumentsTab";
import Explorer from "../Explorer";
import QueryTab from "./QueryTab";
@ -15,7 +14,7 @@ describe("Tabs manager tests", () => {
let documentsTab: DocumentsTab;
beforeAll(() => {
explorer = new Explorer({ documentClientUtility: undefined, notificationsClient: undefined, isEmulator: false });
explorer = new Explorer({ notificationsClient: undefined, isEmulator: false });
explorer.databaseAccount = ko.observable<ViewModels.DatabaseAccount>({
id: "test",
name: "test",
@ -49,7 +48,6 @@ describe("Tabs manager tests", () => {
database,
title: "",
tabPath: "",
documentClientUtility: explorer.documentClientUtility,
selfLink: "",
isActive: ko.observable<boolean>(false),
hashLocation: "",
@ -63,7 +61,6 @@ describe("Tabs manager tests", () => {
collection,
title: "",
tabPath: "",
documentClientUtility: new DocumentClientUtilityBase(),
selfLink: "",
hashLocation: "",
isActive: ko.observable<boolean>(false),

View File

@ -7,6 +7,7 @@ import ScriptTabBase from "./ScriptTabBase";
import editable from "../../Common/EditableUtility";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Trigger from "../Tree/Trigger";
import { createTrigger, updateTrigger } from "../../Common/DocumentClientUtilityBase";
export default class TriggerTab extends ScriptTabBase implements ViewModels.TriggerTab {
public collection: ViewModels.Collection;
@ -41,8 +42,7 @@ export default class TriggerTab extends ScriptTabBase implements ViewModels.Trig
tabTitle: this.tabTitle()
});
return this.documentClientUtility
.updateTrigger(this.collection, data)
return updateTrigger(this.collection, data)
.then(
(createdResource: DataModels.Trigger) => {
this.resource(createdResource);
@ -119,8 +119,7 @@ export default class TriggerTab extends ScriptTabBase implements ViewModels.Trig
tabTitle: this.tabTitle()
});
return this.documentClientUtility
.createTrigger(this.collection, resource)
return createTrigger(this.collection, resource)
.then(
(createdResource: DataModels.Trigger) => {
this.tabTitle(createdResource.id);

View File

@ -6,6 +6,7 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import ScriptTabBase from "./ScriptTabBase";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import UserDefinedFunction from "../Tree/UserDefinedFunction";
import { createUserDefinedFunction, updateUserDefinedFunction } from "../../Common/DocumentClientUtilityBase";
export default class UserDefinedFunctionTab extends ScriptTabBase implements ViewModels.UserDefinedFunctionTab {
public collection: ViewModels.Collection;
@ -34,8 +35,7 @@ export default class UserDefinedFunctionTab extends ScriptTabBase implements Vie
tabTitle: this.tabTitle()
});
return this.documentClientUtility
.updateUserDefinedFunction(this.collection, data)
return updateUserDefinedFunction(this.collection, data)
.then(
(createdResource: DataModels.UserDefinedFunction) => {
this.resource(createdResource);
@ -104,8 +104,7 @@ export default class UserDefinedFunctionTab extends ScriptTabBase implements Vie
tabTitle: this.tabTitle()
});
return this.documentClientUtility
.createUserDefinedFunction(this.collection, resource)
return createUserDefinedFunction(this.collection, resource)
.then(
(createdResource: DataModels.UserDefinedFunction) => {
this.tabTitle(createdResource.id);

View File

@ -33,6 +33,15 @@ import Trigger from "./Trigger";
import UserDefinedFunction from "./UserDefinedFunction";
import { config } from "../../Config";
import Explorer from "../Explorer";
import {
createDocument,
readTriggers,
readUserDefinedFunctions,
readStoredProcedures,
readCollectionQuotaInfo,
readOffer,
readOffers
} from "../../Common/DocumentClientUtilityBase";
export default class Collection implements ViewModels.Collection {
public nodeKind: string;
@ -306,7 +315,6 @@ export default class Collection implements ViewModels.Collection {
documentIds: ko.observableArray<DocumentId>([]),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "Items",
documentClientUtility: this.container.documentClientUtility,
selfLink: this.self,
isActive: ko.observable<boolean>(false),
@ -358,7 +366,6 @@ export default class Collection implements ViewModels.Collection {
conflictIds: ko.observableArray<ConflictId>([]),
tabKind: ViewModels.CollectionTabKind.Conflicts,
title: "Conflicts",
documentClientUtility: this.container.documentClientUtility,
selfLink: this.self,
isActive: ko.observable<boolean>(false),
@ -419,7 +426,7 @@ export default class Collection implements ViewModels.Collection {
tabKind: ViewModels.CollectionTabKind.QueryTables,
title: title,
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this,
node: this,
@ -472,7 +479,7 @@ export default class Collection implements ViewModels.Collection {
node: this,
title: title,
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this,
selfLink: this.self,
masterKey: CosmosClient.masterKey() || "",
@ -527,7 +534,7 @@ export default class Collection implements ViewModels.Collection {
tabKind: ViewModels.CollectionTabKind.Documents,
title: "Documents",
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this,
node: this,
@ -580,7 +587,7 @@ export default class Collection implements ViewModels.Collection {
tabKind: ViewModels.CollectionTabKind.Settings,
title: !this.offer() ? "Settings" : "Scale & Settings",
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this,
node: this,
selfLink: this.self,
@ -645,10 +652,8 @@ export default class Collection implements ViewModels.Collection {
defaultExperience: this.container.defaultExperience()
});
// TODO: Use the collection entity cache to get quota info
const quotaInfoPromise: Q.Promise<DataModels.CollectionQuotaInfo> = this.container.documentClientUtility.readCollectionQuotaInfo(
this
);
const offerInfoPromise: Q.Promise<DataModels.Offer[]> = this.container.documentClientUtility.readOffers();
const quotaInfoPromise: Q.Promise<DataModels.CollectionQuotaInfo> = readCollectionQuotaInfo(this);
const offerInfoPromise: Q.Promise<DataModels.Offer[]> = readOffers();
Q.all([quotaInfoPromise, offerInfoPromise]).then(
() => {
this.container.isRefreshingExplorer(false);
@ -675,41 +680,39 @@ export default class Collection implements ViewModels.Collection {
return;
}
this.container.documentClientUtility
.readOffer(collectionOffer)
.then((offerDetail: DataModels.OfferWithHeaders) => {
if (OfferUtils.isNotOfferV1(collectionOffer)) {
const offerThroughputInfo: DataModels.OfferThroughputInfo = {
minimumRUForCollection:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.minimumRUForCollection,
numPhysicalPartitions:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.numPhysicalPartitions
};
readOffer(collectionOffer).then((offerDetail: DataModels.OfferWithHeaders) => {
if (OfferUtils.isNotOfferV1(collectionOffer)) {
const offerThroughputInfo: DataModels.OfferThroughputInfo = {
minimumRUForCollection:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.minimumRUForCollection,
numPhysicalPartitions:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.numPhysicalPartitions
};
collectionOffer.content.collectionThroughputInfo = offerThroughputInfo;
}
collectionOffer.content.collectionThroughputInfo = offerThroughputInfo;
}
(collectionOffer as DataModels.OfferWithHeaders).headers = offerDetail.headers;
this.offer(collectionOffer);
this.offer.valueHasMutated();
this.quotaInfo(quotaInfo);
TelemetryProcessor.traceSuccess(
Action.LoadOffers,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience(),
offerVersion: collectionOffer && collectionOffer.offerVersion
},
startKey
);
deferred.resolve();
});
(collectionOffer as DataModels.OfferWithHeaders).headers = offerDetail.headers;
this.offer(collectionOffer);
this.offer.valueHasMutated();
this.quotaInfo(quotaInfo);
TelemetryProcessor.traceSuccess(
Action.LoadOffers,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience(),
offerVersion: collectionOffer && collectionOffer.offerVersion
},
startKey
);
deferred.resolve();
});
},
(error: any) => {
this.container.isRefreshingExplorer(false);
@ -747,7 +750,6 @@ export default class Collection implements ViewModels.Collection {
tabKind: ViewModels.CollectionTabKind.Query,
title: title,
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this,
node: this,
selfLink: this.self,
@ -780,7 +782,6 @@ export default class Collection implements ViewModels.Collection {
tabKind: ViewModels.CollectionTabKind.Query,
title: title,
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this,
node: this,
selfLink: this.self,
@ -813,7 +814,6 @@ export default class Collection implements ViewModels.Collection {
node: this,
title: title,
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this,
selfLink: this.self,
masterKey: CosmosClient.masterKey() || "",
@ -836,7 +836,6 @@ export default class Collection implements ViewModels.Collection {
tabKind: ViewModels.CollectionTabKind.MongoShell,
title: "Shell " + id,
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(this.databaseId, this.id())}/mongoShell`,
@ -1075,40 +1074,34 @@ export default class Collection implements ViewModels.Collection {
}
public loadStoredProcedures(): Q.Promise<any> {
return this.container.documentClientUtility
.readStoredProcedures(this)
.then((storedProcedures: DataModels.StoredProcedure[]) => {
const storedProceduresNodes: ViewModels.TreeNode[] = storedProcedures.map(
storedProcedure => new StoredProcedure(this.container, this, storedProcedure)
);
const otherNodes = this.children().filter(node => node.nodeKind !== "StoredProcedure");
const allNodes = otherNodes.concat(storedProceduresNodes);
this.children(allNodes);
});
return readStoredProcedures(this).then((storedProcedures: DataModels.StoredProcedure[]) => {
const storedProceduresNodes: ViewModels.TreeNode[] = storedProcedures.map(
storedProcedure => new StoredProcedure(this.container, this, storedProcedure)
);
const otherNodes = this.children().filter(node => node.nodeKind !== "StoredProcedure");
const allNodes = otherNodes.concat(storedProceduresNodes);
this.children(allNodes);
});
}
public loadUserDefinedFunctions(): Q.Promise<any> {
return this.container.documentClientUtility
.readUserDefinedFunctions(this)
.then((userDefinedFunctions: DataModels.UserDefinedFunction[]) => {
const userDefinedFunctionsNodes: ViewModels.TreeNode[] = userDefinedFunctions.map(
udf => new UserDefinedFunction(this.container, this, udf)
);
const otherNodes = this.children().filter(node => node.nodeKind !== "UserDefinedFunction");
const allNodes = otherNodes.concat(userDefinedFunctionsNodes);
this.children(allNodes);
});
return readUserDefinedFunctions(this).then((userDefinedFunctions: DataModels.UserDefinedFunction[]) => {
const userDefinedFunctionsNodes: ViewModels.TreeNode[] = userDefinedFunctions.map(
udf => new UserDefinedFunction(this.container, this, udf)
);
const otherNodes = this.children().filter(node => node.nodeKind !== "UserDefinedFunction");
const allNodes = otherNodes.concat(userDefinedFunctionsNodes);
this.children(allNodes);
});
}
public loadTriggers(): Q.Promise<any> {
return this.container.documentClientUtility
.readTriggers(this, null /*options*/)
.then((triggers: DataModels.Trigger[]) => {
const triggerNodes: ViewModels.TreeNode[] = triggers.map(trigger => new Trigger(this.container, this, trigger));
const otherNodes = this.children().filter(node => node.nodeKind !== "Trigger");
const allNodes = otherNodes.concat(triggerNodes);
this.children(allNodes);
});
return readTriggers(this, null /*options*/).then((triggers: DataModels.Trigger[]) => {
const triggerNodes: ViewModels.TreeNode[] = triggers.map(trigger => new Trigger(this.container, this, trigger));
const otherNodes = this.children().filter(node => node.nodeKind !== "Trigger");
const allNodes = otherNodes.concat(triggerNodes);
this.children(allNodes);
});
}
public onDragOver(source: Collection, event: { originalEvent: DragEvent }) {
@ -1269,7 +1262,7 @@ export default class Collection implements ViewModels.Collection {
const promises: Array<Q.Promise<any>> = [];
const triggerCreateDocument: (documentContent: any) => Q.Promise<any> = (documentContent: any) => {
return this.container.documentClientUtility.createDocument(this, documentContent).then(
return createDocument(this, documentContent).then(
doc => {
record.numSucceeded++;
return Q.resolve();

View File

@ -6,6 +6,7 @@ import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { extractPartitionKey } from "@azure/cosmos";
import ConflictsTab from "../Tabs/ConflictsTab";
import { readDocument } from "../../Common/DocumentClientUtilityBase";
export default class ConflictId implements ViewModels.ConflictId {
public container: ConflictsTab;
@ -68,33 +69,31 @@ export default class ConflictId implements ViewModels.ConflictId {
}
this.container.loadingConflictData(true);
return conflictsTab.documentClientUtility
.readDocument(this.container.collection, this.buildDocumentIdFromConflict(this.partitionKeyValue))
.then(
(currentDocumentContent: any) => {
this.container.loadingConflictData(false);
if (this.operationType === Constants.ConflictOperationType.Replace) {
this.container.initDocumentEditorForReplace(this, this.content, currentDocumentContent);
} else {
this.container.initDocumentEditorForDelete(this, currentDocumentContent);
}
},
(reason: any) => {
this.container.loadingConflictData(false);
// Document could be deleted
if (
reason &&
reason.code === Constants.HttpStatusCodes.NotFound &&
this.operationType === Constants.ConflictOperationType.Delete
) {
this.container.initDocumentEditorForNoOp(this);
return Q();
}
return Q.reject(reason);
return readDocument(this.container.collection, this.buildDocumentIdFromConflict(this.partitionKeyValue)).then(
(currentDocumentContent: any) => {
this.container.loadingConflictData(false);
if (this.operationType === Constants.ConflictOperationType.Replace) {
this.container.initDocumentEditorForReplace(this, this.content, currentDocumentContent);
} else {
this.container.initDocumentEditorForDelete(this, currentDocumentContent);
}
);
},
(reason: any) => {
this.container.loadingConflictData(false);
// Document could be deleted
if (
reason &&
reason.code === Constants.HttpStatusCodes.NotFound &&
this.operationType === Constants.ConflictOperationType.Delete
) {
this.container.initDocumentEditorForNoOp(this);
return Q();
}
return Q.reject(reason);
}
);
}
public getPartitionKeyValueAsString(): string {

View File

@ -12,6 +12,7 @@ import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import * as Logger from "../../Common/Logger";
import Explorer from "../Explorer";
import { readCollections, readOffers, readOffer } from "../../Common/DocumentClientUtilityBase";
export default class Database implements ViewModels.Database {
public nodeKind: string;
@ -71,7 +72,6 @@ export default class Database implements ViewModels.Database {
tabKind: ViewModels.CollectionTabKind.DatabaseSettings,
title: "Scale",
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
node: this,
rid: this.rid,
database: this,
@ -137,7 +137,7 @@ export default class Database implements ViewModels.Database {
defaultExperience: this.container.defaultExperience()
});
const offerInfoPromise: Q.Promise<DataModels.Offer[]> = this.container.documentClientUtility.readOffers();
const offerInfoPromise: Q.Promise<DataModels.Offer[]> = readOffers();
Q.all([offerInfoPromise]).then(
() => {
this.container.isRefreshingExplorer(false);
@ -146,35 +146,33 @@ export default class Database implements ViewModels.Database {
offerInfoPromise.valueOf(),
databaseDataModel
);
this.container.documentClientUtility
.readOffer(databaseOffer)
.then((offerDetail: DataModels.OfferWithHeaders) => {
const offerThroughputInfo: DataModels.OfferThroughputInfo = {
minimumRUForCollection:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.minimumRUForCollection,
numPhysicalPartitions:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.numPhysicalPartitions
};
readOffer(databaseOffer).then((offerDetail: DataModels.OfferWithHeaders) => {
const offerThroughputInfo: DataModels.OfferThroughputInfo = {
minimumRUForCollection:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.minimumRUForCollection,
numPhysicalPartitions:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.numPhysicalPartitions
};
databaseOffer.content.collectionThroughputInfo = offerThroughputInfo;
(databaseOffer as DataModels.OfferWithHeaders).headers = offerDetail.headers;
this.offer(databaseOffer);
this.offer.valueHasMutated();
databaseOffer.content.collectionThroughputInfo = offerThroughputInfo;
(databaseOffer as DataModels.OfferWithHeaders).headers = offerDetail.headers;
this.offer(databaseOffer);
this.offer.valueHasMutated();
TelemetryProcessor.traceSuccess(
Action.LoadOffers,
{
databaseAccountName: this.container.databaseAccount().name,
defaultExperience: this.container.defaultExperience()
},
startKey
);
deferred.resolve();
});
TelemetryProcessor.traceSuccess(
Action.LoadOffers,
{
databaseAccountName: this.container.databaseAccount().name,
defaultExperience: this.container.defaultExperience()
},
startKey
);
deferred.resolve();
});
},
(error: any) => {
this.container.isRefreshingExplorer(false);
@ -263,7 +261,7 @@ export default class Database implements ViewModels.Database {
let collectionVMs: Collection[] = [];
let deferred: Q.Deferred<void> = Q.defer<void>();
this.container.documentClientUtility.readCollections(this).then(
readCollections(this).then(
(collections: DataModels.Collection[]) => {
let collectionsToAddVMPromises: Q.Promise<any>[] = [];
let deltaCollections = this.getDeltaCollections(collections);

View File

@ -91,7 +91,6 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
tabKind: ViewModels.CollectionTabKind.Query,
title: title,
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this,
node: this,
selfLink: this.self,
@ -143,8 +142,6 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
documentIds: ko.observableArray<DocumentId>([]),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "Items",
documentClientUtility: this.container.documentClientUtility,
selfLink: this.self,
isActive: ko.observable<boolean>(false),
collection: this,

View File

@ -7,6 +7,7 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
import StoredProcedureTab from "../Tabs/StoredProcedureTab";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer";
import { deleteStoredProcedure, executeStoredProcedure } from "../../Common/DocumentClientUtilityBase";
const sampleStoredProcedureBody: string = `// SAMPLE STORED PROCEDURE
function sample(prefix) {
@ -69,7 +70,6 @@ export default class StoredProcedure {
tabKind: ViewModels.CollectionTabKind.StoredProcedures,
title: `New Stored Procedure ${id}`,
tabPath: `${source.databaseId}>${source.id()}>New Stored Procedure ${id}`,
documentClientUtility: source.container.documentClientUtility,
collection: source,
node: source,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/sproc`,
@ -116,7 +116,6 @@ export default class StoredProcedure {
tabKind: ViewModels.CollectionTabKind.StoredProcedures,
title: storedProcedureData.id,
tabPath: `${this.collection.databaseId}>${this.collection.id()}>${storedProcedureData.id}`,
documentClientUtility: this.container.documentClientUtility,
collection: this.collection,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(
@ -144,7 +143,7 @@ export default class StoredProcedure {
body: this.body()
};
this.container.documentClientUtility.deleteStoredProcedure(this.collection, storedProcedureData).then(
deleteStoredProcedure(this.collection, storedProcedureData).then(
() => {
this.container.tabsManager.removeTabByComparator(
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid
@ -163,8 +162,7 @@ export default class StoredProcedure {
const sprocTab: ViewModels.StoredProcedureTab = sprocTabs && sprocTabs.length > 0 && sprocTabs[0];
sprocTab.isExecuting(true);
this.container &&
this.container.documentClientUtility
.executeStoredProcedure(this.collection, this, partitionKeyValue, params)
executeStoredProcedure(this.collection, this, partitionKeyValue, params)
.then(
(result: any) => {
sprocTab.onExecuteSprocsResult(result, result.scriptLogs);

View File

@ -6,6 +6,7 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
import TriggerTab from "../Tabs/TriggerTab";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer";
import { deleteTrigger } from "../../Common/DocumentClientUtilityBase";
export default class Trigger {
public nodeKind: string;
@ -55,7 +56,6 @@ export default class Trigger {
tabKind: ViewModels.CollectionTabKind.Triggers,
title: `New Trigger ${id}`,
tabPath: "",
documentClientUtility: source.container.documentClientUtility,
collection: source,
node: source,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/trigger`,
@ -94,7 +94,6 @@ export default class Trigger {
tabKind: ViewModels.CollectionTabKind.Triggers,
title: triggerData.id,
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this.collection,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(
@ -124,7 +123,7 @@ export default class Trigger {
triggerType: this.triggerType()
};
this.container.documentClientUtility.deleteTrigger(this.collection, triggerData).then(
deleteTrigger(this.collection, triggerData).then(
() => {
this.container.tabsManager.removeTabByComparator(
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid

View File

@ -6,6 +6,7 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
import UserDefinedFunctionTab from "../Tabs/UserDefinedFunctionTab";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer";
import { deleteUserDefinedFunction } from "../../Common/DocumentClientUtilityBase";
export default class UserDefinedFunction {
public nodeKind: string;
@ -40,7 +41,6 @@ export default class UserDefinedFunction {
tabKind: ViewModels.CollectionTabKind.UserDefinedFunctions,
title: `New UDF ${id}`,
tabPath: "",
documentClientUtility: source.container.documentClientUtility,
collection: source,
node: source,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(source.databaseId, source.id())}/udf`,
@ -77,7 +77,6 @@ export default class UserDefinedFunction {
tabKind: ViewModels.CollectionTabKind.UserDefinedFunctions,
title: userDefinedFunctionData.id,
tabPath: "",
documentClientUtility: this.container.documentClientUtility,
collection: this.collection,
node: this,
hashLocation: `${Constants.HashRoutePrefixes.collectionsWithIds(
@ -114,7 +113,7 @@ export default class UserDefinedFunction {
id: this.id(),
body: this.body()
};
this.container.documentClientUtility.deleteUserDefinedFunction(this.collection, userDefinedFunctionData).then(
deleteUserDefinedFunction(this.collection, userDefinedFunctionData).then(
() => {
this.container.tabsManager.removeTabByComparator(
(tab: ViewModels.Tab) => tab.node && tab.node.rid === this.rid

View File

@ -3,15 +3,10 @@ import { AccountKind, TagNames, DefaultAccountExperience } from "../../Common/Co
import Explorer from "../../Explorer/Explorer";
import { NotificationsClient } from "./NotificationsClient";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
export default class EmulatorExplorerFactory {
public static createExplorer(): Explorer {
DocumentClientUtilityBase;
const documentClientUtility: DocumentClientUtilityBase = new DocumentClientUtilityBase();
const explorer: Explorer = new Explorer({
documentClientUtility: documentClientUtility,
notificationsClient: new NotificationsClient(),
isEmulator: true
});

View File

@ -1,13 +1,9 @@
import Explorer from "../../Explorer/Explorer";
import { NotificationsClient } from "./NotificationsClient";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
export default class HostedExplorerFactory {
public createExplorer(): Explorer {
var documentClientUtility = new DocumentClientUtilityBase();
const explorer = new Explorer({
documentClientUtility: documentClientUtility,
notificationsClient: new NotificationsClient(),
isEmulator: false
});
@ -17,8 +13,6 @@ export default class HostedExplorerFactory {
public static reInitializeDocumentClientUtilityForExplorer(explorer: Explorer): void {
if (!!explorer) {
const documentClientUtility = new DocumentClientUtilityBase();
explorer.rebindDocumentClientUtility(documentClientUtility);
explorer.notificationConsoleData([]);
}
}

View File

@ -1,14 +1,9 @@
import Explorer from "../../Explorer/Explorer";
import { NotificationsClient } from "./NotificationsClient";
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
export default class PortalExplorerFactory {
public createExplorer(): Explorer {
var documentClientUtility = new DocumentClientUtilityBase();
var explorer = new Explorer({
documentClientUtility: documentClientUtility,
notificationsClient: new NotificationsClient(),
isEmulator: false
});

View File

@ -10,7 +10,6 @@ describe("TabRouteHandler", () => {
beforeAll(() => {
(<any>window).dataExplorer = new Explorer({
documentClientUtility: null,
notificationsClient: null,
isEmulator: false
}); // create a mock to avoid null refs