mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-21 01:41:31 +00:00
resolve merge master conflict
This commit is contained in:
@@ -253,11 +253,8 @@ src/Terminal/NotebookAppContracts.d.ts
|
|||||||
src/Terminal/index.ts
|
src/Terminal/index.ts
|
||||||
src/TokenProviders/PortalTokenProvider.ts
|
src/TokenProviders/PortalTokenProvider.ts
|
||||||
src/TokenProviders/TokenProviderFactory.ts
|
src/TokenProviders/TokenProviderFactory.ts
|
||||||
src/Utils/DatabaseAccountUtils.test.ts
|
|
||||||
src/Utils/DatabaseAccountUtils.ts
|
|
||||||
src/Utils/PricingUtils.test.ts
|
src/Utils/PricingUtils.test.ts
|
||||||
src/Utils/QueryUtils.test.ts
|
src/Utils/QueryUtils.test.ts
|
||||||
src/Utils/QueryUtils.ts
|
|
||||||
src/applyExplorerBindings.ts
|
src/applyExplorerBindings.ts
|
||||||
src/global.d.ts
|
src/global.d.ts
|
||||||
src/setupTests.ts
|
src/setupTests.ts
|
||||||
|
|||||||
9
.github/dependabot.yml
vendored
Normal file
9
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
940
package-lock.json
generated
940
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -44,9 +44,7 @@
|
|||||||
"@types/node-fetch": "2.5.7",
|
"@types/node-fetch": "2.5.7",
|
||||||
"@uifabric/react-cards": "0.109.110",
|
"@uifabric/react-cards": "0.109.110",
|
||||||
"@uifabric/styling": "7.13.7",
|
"@uifabric/styling": "7.13.7",
|
||||||
"abort-controller": "3.0.0",
|
|
||||||
"applicationinsights": "1.8.0",
|
"applicationinsights": "1.8.0",
|
||||||
"babel-polyfill": "6.26.0",
|
|
||||||
"bootstrap": "3.4.1",
|
"bootstrap": "3.4.1",
|
||||||
"canvas": "file:./canvas",
|
"canvas": "file:./canvas",
|
||||||
"clean-webpack-plugin": "0.1.19",
|
"clean-webpack-plugin": "0.1.19",
|
||||||
@@ -61,8 +59,6 @@
|
|||||||
"date-fns": "1.29.0",
|
"date-fns": "1.29.0",
|
||||||
"dayjs": "1.8.19",
|
"dayjs": "1.8.19",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"es6-object-assign": "1.1.0",
|
|
||||||
"es6-symbol": "3.1.3",
|
|
||||||
"eslint-plugin-jest": "23.13.2",
|
"eslint-plugin-jest": "23.13.2",
|
||||||
"eslint-plugin-react": "7.20.0",
|
"eslint-plugin-react": "7.20.0",
|
||||||
"hasher": "1.2.0",
|
"hasher": "1.2.0",
|
||||||
@@ -80,12 +76,9 @@
|
|||||||
"monaco-editor": "0.18.1",
|
"monaco-editor": "0.18.1",
|
||||||
"ms": "2.1.3",
|
"ms": "2.1.3",
|
||||||
"msal": "1.4.4",
|
"msal": "1.4.4",
|
||||||
"object.entries": "1.1.0",
|
|
||||||
"office-ui-fabric-react": "7.164.2",
|
"office-ui-fabric-react": "7.164.2",
|
||||||
"p-retry": "4.2.0",
|
"p-retry": "4.2.0",
|
||||||
"plotly.js-cartesian-dist-min": "1.52.3",
|
"plotly.js-cartesian-dist-min": "1.52.3",
|
||||||
"promise-polyfill": "8.1.0",
|
|
||||||
"promise.prototype.finally": "3.1.0",
|
|
||||||
"q": "1.5.1",
|
"q": "1.5.1",
|
||||||
"react": "16.13.1",
|
"react": "16.13.1",
|
||||||
"react-animate-height": "2.0.8",
|
"react-animate-height": "2.0.8",
|
||||||
@@ -102,13 +95,9 @@
|
|||||||
"rxjs": "6.6.3",
|
"rxjs": "6.6.3",
|
||||||
"styled-components": "4.3.2",
|
"styled-components": "4.3.2",
|
||||||
"swr": "0.4.0",
|
"swr": "0.4.0",
|
||||||
"text-encoding": "0.7.0",
|
"terser-webpack-plugin": "3.1.0",
|
||||||
"underscore": "1.9.1",
|
"underscore": "1.9.1",
|
||||||
"url-polyfill": "1.1.7",
|
"utility-types": "3.10.0"
|
||||||
"utility-types": "3.10.0",
|
|
||||||
"webcrypto-liner": "1.1.4",
|
|
||||||
"webfontloader": "1.6.28",
|
|
||||||
"whatwg-fetch": "3.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.9.0",
|
"@babel/core": "7.9.0",
|
||||||
@@ -138,9 +127,7 @@
|
|||||||
"@types/react-redux": "7.1.7",
|
"@types/react-redux": "7.1.7",
|
||||||
"@types/sinon": "2.3.3",
|
"@types/sinon": "2.3.3",
|
||||||
"@types/styled-components": "5.1.1",
|
"@types/styled-components": "5.1.1",
|
||||||
"@types/text-encoding": "0.0.33",
|
|
||||||
"@types/underscore": "1.7.36",
|
"@types/underscore": "1.7.36",
|
||||||
"@types/webfontloader": "1.6.29",
|
|
||||||
"@typescript-eslint/eslint-plugin": "4.0.1",
|
"@typescript-eslint/eslint-plugin": "4.0.1",
|
||||||
"@typescript-eslint/parser": "4.0.1",
|
"@typescript-eslint/parser": "4.0.1",
|
||||||
"axe-puppeteer": "1.1.0",
|
"axe-puppeteer": "1.1.0",
|
||||||
@@ -165,7 +152,6 @@
|
|||||||
"html-loader": "0.5.5",
|
"html-loader": "0.5.5",
|
||||||
"html-loader-jest": "0.2.1",
|
"html-loader-jest": "0.2.1",
|
||||||
"html-webpack-plugin": "3.2.0",
|
"html-webpack-plugin": "3.2.0",
|
||||||
"inline-css": "2.2.5",
|
|
||||||
"jest": "25.5.4",
|
"jest": "25.5.4",
|
||||||
"jest-canvas-mock": "2.1.0",
|
"jest-canvas-mock": "2.1.0",
|
||||||
"jest-puppeteer": "4.4.0",
|
"jest-puppeteer": "4.4.0",
|
||||||
@@ -182,7 +168,6 @@
|
|||||||
"rimraf": "3.0.0",
|
"rimraf": "3.0.0",
|
||||||
"sinon": "3.2.1",
|
"sinon": "3.2.1",
|
||||||
"style-loader": "0.23.0",
|
"style-loader": "0.23.0",
|
||||||
"terser-webpack-plugin": "3.0.5",
|
|
||||||
"ts-loader": "6.2.2",
|
"ts-loader": "6.2.2",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"tslint-microsoft-contrib": "6.0.0",
|
"tslint-microsoft-contrib": "6.0.0",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Explorer from "../Explorer/Explorer";
|
|||||||
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
|
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
|
||||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||||
import { QueryUtils } from "../Utils/QueryUtils";
|
import * as QueryUtils from "../Utils/QueryUtils";
|
||||||
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
|
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"_closeSynapseLinkModalDialog": [Function],
|
"_closeSynapseLinkModalDialog": [Function],
|
||||||
"_isAfecFeatureRegistered": [Function],
|
"_isAfecFeatureRegistered": [Function],
|
||||||
"_isInitializingNotebooks": false,
|
"_isInitializingNotebooks": false,
|
||||||
"_isSystemDatabasePredicate": [Function],
|
|
||||||
"_panes": Array [
|
"_panes": Array [
|
||||||
AddDatabasePane {
|
AddDatabasePane {
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
@@ -974,7 +973,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"title": [Function],
|
"title": [Function],
|
||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
"nonSystemDatabases": [Function],
|
|
||||||
"notebookBasePath": [Function],
|
"notebookBasePath": [Function],
|
||||||
"notebookServerInfo": [Function],
|
"notebookServerInfo": [Function],
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
@@ -1228,7 +1226,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"_closeSynapseLinkModalDialog": [Function],
|
"_closeSynapseLinkModalDialog": [Function],
|
||||||
"_isAfecFeatureRegistered": [Function],
|
"_isAfecFeatureRegistered": [Function],
|
||||||
"_isInitializingNotebooks": false,
|
"_isInitializingNotebooks": false,
|
||||||
"_isSystemDatabasePredicate": [Function],
|
|
||||||
"_panes": Array [
|
"_panes": Array [
|
||||||
AddDatabasePane {
|
AddDatabasePane {
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
@@ -2170,7 +2167,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"title": [Function],
|
"title": [Function],
|
||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
"nonSystemDatabases": [Function],
|
|
||||||
"notebookBasePath": [Function],
|
"notebookBasePath": [Function],
|
||||||
"notebookServerInfo": [Function],
|
"notebookServerInfo": [Function],
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
@@ -2437,7 +2433,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"_closeSynapseLinkModalDialog": [Function],
|
"_closeSynapseLinkModalDialog": [Function],
|
||||||
"_isAfecFeatureRegistered": [Function],
|
"_isAfecFeatureRegistered": [Function],
|
||||||
"_isInitializingNotebooks": false,
|
"_isInitializingNotebooks": false,
|
||||||
"_isSystemDatabasePredicate": [Function],
|
|
||||||
"_panes": Array [
|
"_panes": Array [
|
||||||
AddDatabasePane {
|
AddDatabasePane {
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
@@ -3379,7 +3374,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"title": [Function],
|
"title": [Function],
|
||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
"nonSystemDatabases": [Function],
|
|
||||||
"notebookBasePath": [Function],
|
"notebookBasePath": [Function],
|
||||||
"notebookServerInfo": [Function],
|
"notebookServerInfo": [Function],
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
@@ -3633,7 +3627,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"_closeSynapseLinkModalDialog": [Function],
|
"_closeSynapseLinkModalDialog": [Function],
|
||||||
"_isAfecFeatureRegistered": [Function],
|
"_isAfecFeatureRegistered": [Function],
|
||||||
"_isInitializingNotebooks": false,
|
"_isInitializingNotebooks": false,
|
||||||
"_isSystemDatabasePredicate": [Function],
|
|
||||||
"_panes": Array [
|
"_panes": Array [
|
||||||
AddDatabasePane {
|
AddDatabasePane {
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
@@ -4575,7 +4568,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"title": [Function],
|
"title": [Function],
|
||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
"nonSystemDatabases": [Function],
|
|
||||||
"notebookBasePath": [Function],
|
"notebookBasePath": [Function],
|
||||||
"notebookServerInfo": [Function],
|
"notebookServerInfo": [Function],
|
||||||
"onRefreshDatabasesKeyPress": [Function],
|
"onRefreshDatabasesKeyPress": [Function],
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
|
||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
|
import React from "react";
|
||||||
|
import { DescriptionType, NumberUiType, SmartUiInput } from "../../../SelfServe/SelfServeTypes";
|
||||||
import { SmartUiComponent, SmartUiDescriptor } from "./SmartUiComponent";
|
import { SmartUiComponent, SmartUiDescriptor } from "./SmartUiComponent";
|
||||||
import { NumberUiType, SmartUiInput, DescriptionType } from "../../../SelfServe/SelfServeTypes";
|
|
||||||
|
|
||||||
describe("SmartUiComponent", () => {
|
describe("SmartUiComponent", () => {
|
||||||
const exampleData: SmartUiDescriptor = {
|
const exampleData: SmartUiDescriptor = {
|
||||||
@@ -97,9 +97,9 @@ describe("SmartUiComponent", () => {
|
|||||||
dataFieldName: "database",
|
dataFieldName: "database",
|
||||||
type: "object",
|
type: "object",
|
||||||
choices: [
|
choices: [
|
||||||
{ label: "Database 1", key: "db1" },
|
{ labelTKey: "Database 1", key: "db1" },
|
||||||
{ label: "Database 2", key: "db2" },
|
{ labelTKey: "Database 2", key: "db2" },
|
||||||
{ label: "Database 3", key: "db3" },
|
{ labelTKey: "Database 3", key: "db3" },
|
||||||
],
|
],
|
||||||
defaultKey: "db2",
|
defaultKey: "db2",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||||||
dropdownWidth="auto"
|
dropdownWidth="auto"
|
||||||
options={choices.map((c) => ({
|
options={choices.map((c) => ({
|
||||||
key: c.key,
|
key: c.key,
|
||||||
text: this.props.getTranslation(c.label),
|
text: this.props.getTranslation(c.labelTKey),
|
||||||
}))}
|
}))}
|
||||||
styles={{
|
styles={{
|
||||||
root: { width: 400 },
|
root: { width: 400 },
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ jest.mock("../Graph/GraphExplorerComponent/GremlinClient");
|
|||||||
jest.mock("../../Common/dataAccess/createCollection");
|
jest.mock("../../Common/dataAccess/createCollection");
|
||||||
jest.mock("../../Common/dataAccess/createDocument");
|
jest.mock("../../Common/dataAccess/createDocument");
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
|
||||||
import { createDocument } from "../../Common/dataAccess/createDocument";
|
import { createDocument } from "../../Common/dataAccess/createDocument";
|
||||||
import Explorer from "../Explorer";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { updateUserContext } from "../../UserContext";
|
import { updateUserContext } from "../../UserContext";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||||
|
|
||||||
describe("ContainerSampleGenerator", () => {
|
describe("ContainerSampleGenerator", () => {
|
||||||
const createExplorerStub = (database: ViewModels.Database): Explorer => {
|
const createExplorerStub = (database: ViewModels.Database): Explorer => {
|
||||||
const explorerStub = {} as Explorer;
|
const explorerStub = {} as Explorer;
|
||||||
explorerStub.nonSystemDatabases = ko.computed(() => [database]);
|
explorerStub.databases = ko.observableArray<ViewModels.Database>([database]);
|
||||||
explorerStub.isPreferredApiGraph = ko.computed<boolean>(() => false);
|
explorerStub.isPreferredApiGraph = ko.computed<boolean>(() => false);
|
||||||
explorerStub.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
explorerStub.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||||
explorerStub.isPreferredApiDocumentDB = ko.computed<boolean>(() => false);
|
explorerStub.isPreferredApiDocumentDB = ko.computed<boolean>(() => false);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { DataSamplesUtil } from "./DataSamplesUtil";
|
|
||||||
import * as sinon from "sinon";
|
|
||||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
|
import * as sinon from "sinon";
|
||||||
|
import { Collection, Database } from "../../Contracts/ViewModels";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { Database, Collection } from "../../Contracts/ViewModels";
|
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||||
|
import { DataSamplesUtil } from "./DataSamplesUtil";
|
||||||
|
|
||||||
describe("DataSampleUtils", () => {
|
describe("DataSampleUtils", () => {
|
||||||
const sampleCollectionId = "sampleCollectionId";
|
const sampleCollectionId = "sampleCollectionId";
|
||||||
@@ -16,7 +16,7 @@ describe("DataSampleUtils", () => {
|
|||||||
collections: ko.observableArray<Collection>([collection]),
|
collections: ko.observableArray<Collection>([collection]),
|
||||||
} as Database;
|
} as Database;
|
||||||
const explorer = {} as Explorer;
|
const explorer = {} as Explorer;
|
||||||
explorer.nonSystemDatabases = ko.computed(() => [database]);
|
explorer.databases = ko.observableArray<Database>([database]);
|
||||||
explorer.showOkModalDialog = () => {};
|
explorer.showOkModalDialog = () => {};
|
||||||
const dataSamplesUtil = new DataSamplesUtil(explorer);
|
const dataSamplesUtil = new DataSamplesUtil(explorer);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
|
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||||
|
|
||||||
export class DataSamplesUtil {
|
export class DataSamplesUtil {
|
||||||
private static readonly DialogTitle = "Create Sample Container";
|
private static readonly DialogTitle = "Create Sample Container";
|
||||||
@@ -17,7 +17,7 @@ export class DataSamplesUtil {
|
|||||||
|
|
||||||
const databaseName = generator.getDatabaseId();
|
const databaseName = generator.getDatabaseId();
|
||||||
const containerName = generator.getCollectionId();
|
const containerName = generator.getCollectionId();
|
||||||
if (this.hasContainer(databaseName, containerName, this.container.nonSystemDatabases())) {
|
if (this.hasContainer(databaseName, containerName, this.container.databases())) {
|
||||||
const msg = `The container ${containerName} in database ${databaseName} already exists. Please delete it and retry.`;
|
const msg = `The container ${containerName} in database ${databaseName} already exists. Please delete it and retry.`;
|
||||||
this.container.showOkModalDialog(DataSamplesUtil.DialogTitle, msg);
|
this.container.showOkModalDialog(DataSamplesUtil.DialogTitle, msg);
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
|
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
|
||||||
|
|||||||
@@ -180,7 +180,6 @@ export default class Explorer {
|
|||||||
|
|
||||||
// Resource Tree
|
// Resource Tree
|
||||||
public databases: ko.ObservableArray<ViewModels.Database>;
|
public databases: ko.ObservableArray<ViewModels.Database>;
|
||||||
public nonSystemDatabases: ko.Computed<ViewModels.Database[]>;
|
|
||||||
public selectedDatabaseId: ko.Computed<string>;
|
public selectedDatabaseId: ko.Computed<string>;
|
||||||
public selectedCollectionId: ko.Computed<string>;
|
public selectedCollectionId: ko.Computed<string>;
|
||||||
public isLeftPaneExpanded: ko.Observable<boolean>;
|
public isLeftPaneExpanded: ko.Observable<boolean>;
|
||||||
@@ -256,7 +255,6 @@ export default class Explorer {
|
|||||||
public closeDialog: ExplorerParams["closeDialog"];
|
public closeDialog: ExplorerParams["closeDialog"];
|
||||||
|
|
||||||
private _panes: ContextualPaneBase[] = [];
|
private _panes: ContextualPaneBase[] = [];
|
||||||
private _isSystemDatabasePredicate: (database: ViewModels.Database) => boolean = (database) => false;
|
|
||||||
private _isInitializingNotebooks: boolean;
|
private _isInitializingNotebooks: boolean;
|
||||||
private notebookBasePath: ko.Observable<string>;
|
private notebookBasePath: ko.Observable<string>;
|
||||||
private _arcadiaManager: ArcadiaResourceManager;
|
private _arcadiaManager: ArcadiaResourceManager;
|
||||||
@@ -527,17 +525,6 @@ export default class Explorer {
|
|||||||
configContext.platform === Platform.Portal && !this.isRunningOnNationalCloud() && !this.isPreferredApiGraph()
|
configContext.platform === Platform.Portal && !this.isRunningOnNationalCloud() && !this.isPreferredApiGraph()
|
||||||
);
|
);
|
||||||
this.isRightPanelV2Enabled = ko.computed<boolean>(() => userContext.features.enableRightPanelV2);
|
this.isRightPanelV2Enabled = ko.computed<boolean>(() => userContext.features.enableRightPanelV2);
|
||||||
this.defaultExperience.subscribe((defaultExperience: string) => {
|
|
||||||
if (
|
|
||||||
defaultExperience &&
|
|
||||||
defaultExperience.toLowerCase() === Constants.DefaultAccountExperience.Cassandra.toLowerCase()
|
|
||||||
) {
|
|
||||||
this._isSystemDatabasePredicate = (database: ViewModels.Database): boolean => {
|
|
||||||
return database.id() === "system";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.selectedDatabaseId = ko.computed<string>(() => {
|
this.selectedDatabaseId = ko.computed<string>(() => {
|
||||||
const selectedNode = this.selectedNode();
|
const selectedNode = this.selectedNode();
|
||||||
if (!selectedNode) {
|
if (!selectedNode) {
|
||||||
@@ -559,10 +546,6 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.nonSystemDatabases = ko.computed(() => {
|
|
||||||
return this.databases().filter((database: ViewModels.Database) => !this._isSystemDatabasePredicate(database));
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addCollectionPane = new AddCollectionPane({
|
this.addCollectionPane = new AddCollectionPane({
|
||||||
isPreferredApiTable: ko.computed(() => this.isPreferredApiTable()),
|
isPreferredApiTable: ko.computed(() => this.isPreferredApiTable()),
|
||||||
id: "addcollectionpane",
|
id: "addcollectionpane",
|
||||||
|
|||||||
@@ -4,11 +4,8 @@
|
|||||||
* - inspired from gremlin-javascript for nodejs: https://github.com/jbmusso/gremlin-javascript
|
* - inspired from gremlin-javascript for nodejs: https://github.com/jbmusso/gremlin-javascript
|
||||||
* - tested on cosmosdb gremlin server
|
* - tested on cosmosdb gremlin server
|
||||||
* - only supports sessionless gremlin requests
|
* - only supports sessionless gremlin requests
|
||||||
* - Relies on text-encoding polyfill (github.com/inexorabletash/text-encoding) for TextEncoder/TextDecoder on IE, Edge.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TextEncoder, TextDecoder } from "text-encoding";
|
|
||||||
|
|
||||||
export interface GremlinSimpleClientParameters {
|
export interface GremlinSimpleClientParameters {
|
||||||
endpoint: string; // The websocket endpoint
|
endpoint: string; // The websocket endpoint
|
||||||
user: string;
|
user: string;
|
||||||
|
|||||||
@@ -114,7 +114,7 @@
|
|||||||
aria-label="Keyspace id"
|
aria-label="Keyspace id"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<datalist id="keyspacesList" data-bind="foreach: container.nonSystemDatabases">
|
<datalist id="keyspacesList" data-bind="foreach: container.databases">
|
||||||
<option data-bind="value: $data.id"></option>
|
<option data-bind="value: $data.id"></option>
|
||||||
</datalist>
|
</datalist>
|
||||||
|
|
||||||
|
|||||||
@@ -261,10 +261,8 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
});
|
});
|
||||||
this.keyspaceIds(cachedKeyspaceIdsList);
|
this.keyspaceIds(cachedKeyspaceIdsList);
|
||||||
};
|
};
|
||||||
this.container.nonSystemDatabases.subscribe((newDatabases: ViewModels.Database[]) =>
|
this.container.databases.subscribe((newDatabases: ViewModels.Database[]) => updateKeyspaceIds(newDatabases));
|
||||||
updateKeyspaceIds(newDatabases)
|
updateKeyspaceIds(this.container.databases());
|
||||||
);
|
|
||||||
updateKeyspaceIds(this.container.nonSystemDatabases());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.autoPilotUsageCost = ko.pureComputed<string>(() => {
|
this.autoPilotUsageCost = ko.pureComputed<string>(() => {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import editable from "../../Common/EditableUtility";
|
|||||||
import * as HeadersUtility from "../../Common/HeadersUtility";
|
import * as HeadersUtility from "../../Common/HeadersUtility";
|
||||||
import TabsBase from "./TabsBase";
|
import TabsBase from "./TabsBase";
|
||||||
import { DocumentsGridMetrics } from "../../Common/Constants";
|
import { DocumentsGridMetrics } from "../../Common/Constants";
|
||||||
import { QueryUtils } from "../../Utils/QueryUtils";
|
import * as QueryUtils from "../../Utils/QueryUtils";
|
||||||
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import NewDocumentIcon from "../../../images/NewDocument.svg";
|
import NewDocumentIcon from "../../../images/NewDocument.svg";
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
<div
|
|
||||||
class="tab-pane"
|
|
||||||
data-bind="
|
|
||||||
attr:{
|
|
||||||
id: tabId
|
|
||||||
}"
|
|
||||||
role="tabpanel"
|
|
||||||
>
|
|
||||||
<!-- Query Tab Command Bar - Start -->
|
|
||||||
<div class="contentdiv">
|
|
||||||
<div class="tabCommandButton">
|
|
||||||
<!-- Execute Query - Start -->
|
|
||||||
<span
|
|
||||||
class="commandButton"
|
|
||||||
data-bind="
|
|
||||||
click: onExecuteQueryClick,
|
|
||||||
visible: executeQueryButton.visible() && executeQueryButton.enabled()"
|
|
||||||
>
|
|
||||||
<img class="imgiconwidth" src="/ExecuteQuery.svg" />Run
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="commandButton tabCommandDisabled"
|
|
||||||
data-bind="
|
|
||||||
visible: executeQueryButton.visible() && !executeQueryButton.enabled()"
|
|
||||||
>
|
|
||||||
<img class="imgiconwidth" src="/ExecuteQuery-disabled.svg" />Run
|
|
||||||
</span>
|
|
||||||
<!-- Execute Query - End -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Query Tab Command Bar - End -->
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="queryEditor"
|
|
||||||
data-bind="
|
|
||||||
attr: {
|
|
||||||
id: queryEditorId
|
|
||||||
},
|
|
||||||
css: {
|
|
||||||
mongoQueryEditor:$root.isPreferredApiMongoDB()
|
|
||||||
}"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
style="margin-left: 50px; margin-top: -75px"
|
|
||||||
data-bind="
|
|
||||||
visible: $root.isPreferredApiMongoDB() && sqlQueryEditorContent().length == 0"
|
|
||||||
>
|
|
||||||
Start by writing a Mongo query, for example: <strong>{'id':'foo'}</strong> or <strong>{ }</strong> to get all the
|
|
||||||
documents.
|
|
||||||
</div>
|
|
||||||
<!-- Query Errors Tab - Start-->
|
|
||||||
<div class="active queryErrorsHeaderContainer" data-bind="visible: errors().length > 0">
|
|
||||||
<span
|
|
||||||
class="queryErrors"
|
|
||||||
data-toggle="tab"
|
|
||||||
data-bind="
|
|
||||||
attr: {
|
|
||||||
href: '#queryerrors' + tabId
|
|
||||||
}"
|
|
||||||
>Errors</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<!-- Query Errors Tab - End -->
|
|
||||||
|
|
||||||
<!-- Query Results & Errors Content Container - Start-->
|
|
||||||
<div class="queryResultErrorContentContainer">
|
|
||||||
<!-- Query Results Content - Start-->
|
|
||||||
<div
|
|
||||||
class="tab-pane active"
|
|
||||||
data-bind="
|
|
||||||
id: {
|
|
||||||
href: 'queryresults' + tabId
|
|
||||||
},
|
|
||||||
visible: allResultsMetadata().length > 0 && !errors().length > 0"
|
|
||||||
>
|
|
||||||
<div class="queryResultsValue">
|
|
||||||
<span class="queryResults"> Results: </span> <span data-bind="text: showingDocumentsDisplayText"></span>
|
|
||||||
<span class="queryResultDivider"> | </span> <span> Request Charge: </span>
|
|
||||||
<span data-bind="text: requestChargeDisplayText"></span> <span class="queryResultDivider"> | </span>
|
|
||||||
<span class="queryResultNextEnable" data-bind="visible: fetchNextPageButton.enabled">
|
|
||||||
<a
|
|
||||||
data-bind="
|
|
||||||
click: onFetchNextPageClick"
|
|
||||||
>
|
|
||||||
<span>Next</span> <img class="queryResultnextImg" src="/Query-Editor-Next.svg" />
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
<span class="queryResultNextDisable" data-bind="visible: !fetchNextPageButton.enabled()">
|
|
||||||
<span>Next</span> <img class="queryResultnextImg" src="/Query-Editor-Next-Disabled.svg" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="height: 600px"
|
|
||||||
data-bind="
|
|
||||||
attr: {
|
|
||||||
id: resultsEditorId
|
|
||||||
}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<!-- Query Results Content - Start-->
|
|
||||||
|
|
||||||
<!-- Query Errors Content - Start-->
|
|
||||||
<div
|
|
||||||
class="tab-pane active"
|
|
||||||
data-bind="
|
|
||||||
id: {
|
|
||||||
href: 'queryerrors' + tabId
|
|
||||||
},
|
|
||||||
visible: errors().length > 0"
|
|
||||||
>
|
|
||||||
<!-- ko foreach: errors -->
|
|
||||||
<div style="margin-left: 17px; font-size: 12px">
|
|
||||||
<span data-bind="text: $data.code"></span> : <span data-bind="text: $data.message"></span>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
</div>
|
|
||||||
<!-- Query Errors Content - End-->
|
|
||||||
</div>
|
|
||||||
<!-- Results & Errors Content Container - Endt-->
|
|
||||||
</div>
|
|
||||||
@@ -5,10 +5,8 @@ import QueryTab from "./QueryTab";
|
|||||||
import * as HeadersUtility from "../../Common/HeadersUtility";
|
import * as HeadersUtility from "../../Common/HeadersUtility";
|
||||||
import { queryIterator } from "../../Common/MongoProxyClient";
|
import { queryIterator } from "../../Common/MongoProxyClient";
|
||||||
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
||||||
import template from "./MongoQueryTab.html";
|
|
||||||
|
|
||||||
export default class MongoQueryTab extends QueryTab {
|
export default class MongoQueryTab extends QueryTab {
|
||||||
public static readonly component = { name: "mongo-query-tab", template };
|
|
||||||
public collection: ViewModels.Collection;
|
public collection: ViewModels.Collection;
|
||||||
|
|
||||||
constructor(options: ViewModels.QueryTabOptions) {
|
constructor(options: ViewModels.QueryTabOptions) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import * as HeadersUtility from "../../Common/HeadersUtility";
|
|||||||
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
||||||
import { QueryUtils } from "../../Utils/QueryUtils";
|
import * as QueryUtils from "../../Utils/QueryUtils";
|
||||||
import SaveQueryIcon from "../../../images/save-cosmos.svg";
|
import SaveQueryIcon from "../../../images/save-cosmos.svg";
|
||||||
|
|
||||||
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
import { MinimalQueryIterator } from "../../Common/IteratorUtilities";
|
||||||
|
|||||||
@@ -76,10 +76,10 @@
|
|||||||
|
|
||||||
<!-- Tabs Panes -- Start -->
|
<!-- Tabs Panes -- Start -->
|
||||||
<div class="tabPanesContainer">
|
<div class="tabPanesContainer">
|
||||||
<!-- ko if: activeTab && activeTab() -->
|
<!-- ko foreach: openedTabs -->
|
||||||
<div class="tabs-container" data-bind="visible: activeTab().isActive">
|
<div class="tabs-container" data-bind="visible: $data.isActive">
|
||||||
<span
|
<span
|
||||||
data-bind="class: activeTab().constructor.component.name, component: { name: activeTab().constructor.component.name, params: activeTab }"
|
data-bind="class: $data.constructor.component.name, component: { name: $data.constructor.component.name, params: $data }"
|
||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Explorer from "../Explorer";
|
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import { ResourceTreeAdapter } from "./ResourceTreeAdapter";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
import TabsBase from "../Tabs/TabsBase";
|
import TabsBase from "../Tabs/TabsBase";
|
||||||
|
import { ResourceTreeAdapter } from "./ResourceTreeAdapter";
|
||||||
|
|
||||||
describe("ResourceTreeAdapter", () => {
|
describe("ResourceTreeAdapter", () => {
|
||||||
const mockContainer = (): Explorer =>
|
const mockContainer = (): Explorer =>
|
||||||
@@ -18,7 +18,7 @@ describe("ResourceTreeAdapter", () => {
|
|||||||
} as TabsBase),
|
} as TabsBase),
|
||||||
},
|
},
|
||||||
isNotebookEnabled: ko.observable<boolean>(true),
|
isNotebookEnabled: ko.observable<boolean>(true),
|
||||||
nonSystemDatabases: ko.observable<ViewModels.Database[]>([]),
|
databases: ko.observable<ViewModels.Database[]>([]),
|
||||||
} as unknown) as Explorer);
|
} as unknown) as Explorer);
|
||||||
|
|
||||||
// TODO isDataNodeSelected needs a better design and refactor, but for now, we protect some of the code paths
|
// TODO isDataNodeSelected needs a better design and refactor, but for now, we protect some of the code paths
|
||||||
|
|||||||
@@ -1,39 +1,38 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
|
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
|
||||||
import { AccordionComponent, AccordionItemComponent } from "../Controls/Accordion/AccordionComponent";
|
|
||||||
import { TreeComponent, TreeNode, TreeNodeMenuItem, TreeNodeComponent } from "../Controls/TreeComponent/TreeComponent";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import { NotebookContentItem, NotebookContentItemType } from "../Notebook/NotebookContentItem";
|
|
||||||
import { ResourceTreeContextMenuButtonFactory } from "../ContextMenuButtonFactory";
|
|
||||||
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
|
||||||
import CopyIcon from "../../../images/notebook/Notebook-copy.svg";
|
|
||||||
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
|
||||||
import CollectionIcon from "../../../images/tree-collection.svg";
|
|
||||||
import DeleteIcon from "../../../images/delete.svg";
|
import DeleteIcon from "../../../images/delete.svg";
|
||||||
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
|
||||||
import RefreshIcon from "../../../images/refresh-cosmos.svg";
|
|
||||||
import NewNotebookIcon from "../../../images/notebook/Notebook-new.svg";
|
|
||||||
import FileIcon from "../../../images/notebook/file-cosmos.svg";
|
|
||||||
import PublishIcon from "../../../images/notebook/publish_content.svg";
|
|
||||||
import { ArrayHashMap } from "../../Common/ArrayHashMap";
|
|
||||||
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
|
||||||
import _ from "underscore";
|
|
||||||
import { IPinnedRepo } from "../../Juno/JunoClient";
|
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
|
|
||||||
import { Areas } from "../../Common/Constants";
|
|
||||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
|
||||||
import GalleryIcon from "../../../images/GalleryIcon.svg";
|
import GalleryIcon from "../../../images/GalleryIcon.svg";
|
||||||
import { Callout, Text, Link, DirectionalHint, Stack, ICalloutProps, ILinkProps } from "office-ui-fabric-react";
|
import FileIcon from "../../../images/notebook/file-cosmos.svg";
|
||||||
|
import CopyIcon from "../../../images/notebook/Notebook-copy.svg";
|
||||||
|
import NewNotebookIcon from "../../../images/notebook/Notebook-new.svg";
|
||||||
|
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
||||||
|
import PublishIcon from "../../../images/notebook/publish_content.svg";
|
||||||
|
import RefreshIcon from "../../../images/refresh-cosmos.svg";
|
||||||
|
import CollectionIcon from "../../../images/tree-collection.svg";
|
||||||
|
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
||||||
|
import { ArrayHashMap } from "../../Common/ArrayHashMap";
|
||||||
|
import { Areas } from "../../Common/Constants";
|
||||||
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
|
import { IPinnedRepo } from "../../Juno/JunoClient";
|
||||||
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
||||||
|
import { Action, ActionModifiers, Source } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
|
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||||
|
import { ResourceTreeContextMenuButtonFactory } from "../ContextMenuButtonFactory";
|
||||||
|
import { AccordionComponent, AccordionItemComponent } from "../Controls/Accordion/AccordionComponent";
|
||||||
|
import { TreeComponent, TreeNode, TreeNodeMenuItem } from "../Controls/TreeComponent/TreeComponent";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import UserDefinedFunction from "./UserDefinedFunction";
|
import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
|
||||||
|
import { NotebookContentItem, NotebookContentItemType } from "../Notebook/NotebookContentItem";
|
||||||
|
import { NotebookUtil } from "../Notebook/NotebookUtil";
|
||||||
|
import TabsBase from "../Tabs/TabsBase";
|
||||||
import StoredProcedure from "./StoredProcedure";
|
import StoredProcedure from "./StoredProcedure";
|
||||||
import Trigger from "./Trigger";
|
import Trigger from "./Trigger";
|
||||||
import TabsBase from "../Tabs/TabsBase";
|
import UserDefinedFunction from "./UserDefinedFunction";
|
||||||
import { userContext } from "../../UserContext";
|
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
|
||||||
|
|
||||||
export class ResourceTreeAdapter implements ReactAdapter {
|
export class ResourceTreeAdapter implements ReactAdapter {
|
||||||
public static readonly MyNotebooksTitle = "My Notebooks";
|
public static readonly MyNotebooksTitle = "My Notebooks";
|
||||||
@@ -64,7 +63,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
this.koSubsCollectionIdMap = new ArrayHashMap();
|
this.koSubsCollectionIdMap = new ArrayHashMap();
|
||||||
this.databaseCollectionIdMap = new ArrayHashMap();
|
this.databaseCollectionIdMap = new ArrayHashMap();
|
||||||
|
|
||||||
this.container.nonSystemDatabases.subscribe((databases: ViewModels.Database[]) => {
|
this.container.databases.subscribe((databases: ViewModels.Database[]) => {
|
||||||
// Clean up old databases
|
// Clean up old databases
|
||||||
this.cleanupDatabasesKoSubs();
|
this.cleanupDatabasesKoSubs();
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
this.triggerRender();
|
this.triggerRender();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.container.nonSystemDatabases().forEach((database: ViewModels.Database) => this.watchDatabase(database));
|
this.container.databases().forEach((database: ViewModels.Database) => this.watchDatabase(database));
|
||||||
this.triggerRender();
|
this.triggerRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +189,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private buildDataTree(): TreeNode {
|
private buildDataTree(): TreeNode {
|
||||||
const databaseTreeNodes: TreeNode[] = this.container.nonSystemDatabases().map((database: ViewModels.Database) => {
|
const databaseTreeNodes: TreeNode[] = this.container.databases().map((database: ViewModels.Database) => {
|
||||||
const databaseNode: TreeNode = {
|
const databaseNode: TreeNode = {
|
||||||
label: database.id(),
|
label: database.id(),
|
||||||
iconSrc: CosmosDBIcon,
|
iconSrc: CosmosDBIcon,
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
Number.isInteger =
|
|
||||||
Number.isInteger ||
|
|
||||||
function(value) {
|
|
||||||
return typeof value === "number" && isFinite(value) && Math.floor(value) === value;
|
|
||||||
};
|
|
||||||
5
src/Localization/en/Common.json
Normal file
5
src/Localization/en/Common.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"Save": "Save",
|
||||||
|
"Discard": "Discard",
|
||||||
|
"Refresh": "Refesh"
|
||||||
|
}
|
||||||
31
src/Localization/en/SelfServeExample.json
Normal file
31
src/Localization/en/SelfServeExample.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"NorthCentralUS": "North Central US",
|
||||||
|
"WestUS": "West US",
|
||||||
|
"EastUS2": "East US 2",
|
||||||
|
"Current Region": "Current Region",
|
||||||
|
"RegionDropdownInfo": "More regions can be added in the future.",
|
||||||
|
"RegionsAndAccountNameValidationError": "Regions and account name should not be empty.",
|
||||||
|
"DbThroughputValidationError": "Please update throughput for database.",
|
||||||
|
"DescriptionLabel": "Description",
|
||||||
|
"DescriptionText": "This class sets collection and database throughput.",
|
||||||
|
"DecriptionLinkText": "Click here for more information",
|
||||||
|
"Regions": "Regions",
|
||||||
|
"RegionsPlaceholder": "Select a region",
|
||||||
|
"Enable Logging": "Enable Logging",
|
||||||
|
"Enable": "Enable",
|
||||||
|
"Disable": "Disable",
|
||||||
|
"Account Name": "Account Name",
|
||||||
|
"AccountNamePlaceHolder": "Enter the account name",
|
||||||
|
"Collection Throughput": "Collection Throughput",
|
||||||
|
"Enable DB level throughput": "Enable Database Level Throughput",
|
||||||
|
"Database Throughput": "Database Throughput",
|
||||||
|
"UpdateInProgressMessage": "Data is being updated",
|
||||||
|
"UpdateCompletedMessageTitle": "Update succeeded",
|
||||||
|
"UpdateCompletedMessageText": "Data update completed.",
|
||||||
|
"SubmissionMessageSuccessTitle": "Update started",
|
||||||
|
"SubmissionMessageForNewRegionText": "Data update started. Region changed.",
|
||||||
|
"SubmissionMessageForSameRegionText": "Data update started. Region not changed.",
|
||||||
|
"SubmissionMessageErrorTitle": "Data update failed",
|
||||||
|
"SubmissionMessageErrorText": "Data update failed because of errors.",
|
||||||
|
"OnSaveFailureMessage": "Data save operation not currently permitted."
|
||||||
|
}
|
||||||
52
src/Localization/en/SqlX.json
Normal file
52
src/Localization/en/SqlX.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"DedicatedGatewayDescription": "Provision a dedicated gateway cluster for your Azure Cosmos DB account. A dedicated gateway is compute that is a front-end to data in your Azure Cosmos DB account. Your dedicated gateway automatically includes the integrated cache, which can improve read performance.",
|
||||||
|
"DedicatedGateway": "Dedicated Gateway",
|
||||||
|
"Enable": "Enable",
|
||||||
|
"Disable": "Disable",
|
||||||
|
"LearnAboutDedicatedGateway": "Learn more about dedicated gateway.",
|
||||||
|
"DeprovisioningDetailsText": "Learn more about deprovisioning the dedicated gateway.",
|
||||||
|
"DedicatedGatewayPricing": "Learn more about dedicated gateway pricing.",
|
||||||
|
"SKUs": "SKUs",
|
||||||
|
"SKUsPlaceHolder": "Select SKUs",
|
||||||
|
"NumberOfInstances": "Number of instances",
|
||||||
|
"CosmosD4s": "Cosmos.D4s (General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory)",
|
||||||
|
"CosmosD8s": "Cosmos.D8s (General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory)",
|
||||||
|
"CosmosD16s": "Cosmos.D16s (General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory)",
|
||||||
|
"CosmosD32s": "Cosmos.D32s (General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory)",
|
||||||
|
"CreateMessage": "Dedicated gateway resource is being created.",
|
||||||
|
"CreateInitializeTitle": "Provisioning resource",
|
||||||
|
"CreateInitializeMessage": "Dedicated gateway resource will be provisioned.",
|
||||||
|
"CreateSuccessTitle": "Resource provisioned",
|
||||||
|
"CreateSuccesseMessage": "Dedicated gateway resource provisioned.",
|
||||||
|
"CreateFailureTitle": "Failed to provision resource",
|
||||||
|
"CreateFailureMessage": "Dedicated gateway resource provisioning failed.",
|
||||||
|
"UpdateMessage": "Dedicated gateway resource is being updated.",
|
||||||
|
"UpdateInitializeTitle": "Updating resource",
|
||||||
|
"UpdateInitializeMessage": "Dedicated gateway resource will be updated.",
|
||||||
|
"UpdateSuccessTitle": "Resource updated",
|
||||||
|
"UpdateSuccesseMessage": "Dedicated gateway resource updated.",
|
||||||
|
"UpdateFailureTitle": "Failed to update resource",
|
||||||
|
"UpdateFailureMessage": "Dedicated gateway resource updation failed.",
|
||||||
|
"DeleteMessage": "Dedicated gateway resource is being deleted.",
|
||||||
|
"DeleteInitializeTitle": "Deleting resource",
|
||||||
|
"DeleteInitializeMessage": "Dedicated gateway resource will be deleted.",
|
||||||
|
"DeleteSuccessTitle": "Resource deleted",
|
||||||
|
"DeleteSuccesseMessage": "Dedicated gateway resource deleted.",
|
||||||
|
"DeleteFailureTitle": "Failed to delete resource",
|
||||||
|
"DeleteFailureMessage": "Dedicated gateway resource deletion failed.",
|
||||||
|
"CannotSave": "Cannot save the changes to the Dedicated gateway resource at the moment.",
|
||||||
|
"DedicatedGatewayEndpoint": "Dedicated gatewayEndpoint",
|
||||||
|
"NoValue": "",
|
||||||
|
"SKUDetails": "SKU Details:",
|
||||||
|
"CosmosD4Details": "General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory",
|
||||||
|
"CosmosD8Details": "General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory",
|
||||||
|
"CosmosD16Details": "General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory",
|
||||||
|
"CosmosD32Details": "General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory",
|
||||||
|
"Cost": "Cost",
|
||||||
|
"CostText": "Hourly cost of the dedicated gateway resource depends on the SKU selection, number of instances per region, and number of regions.",
|
||||||
|
"ConnectionString": "Connection String",
|
||||||
|
"ConnectionStringText": "To use the dedicated gateway, use the connection string shown in ",
|
||||||
|
"KeysBlade": "the keys blade",
|
||||||
|
"WarningBannerOnUpdate": "Adding or modifying dedicated gateway instances may affect your bill.",
|
||||||
|
"WarningBannerOnDelete": "After deprovisioning the dedicated gateway, you must update any applications using the old dedicated gateway connection string."
|
||||||
|
}
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
{
|
|
||||||
"translations": {
|
|
||||||
"Common": {
|
|
||||||
"Save": "Save",
|
|
||||||
"Discard": "Discard",
|
|
||||||
"Refresh": "Refesh"
|
|
||||||
},
|
|
||||||
"SelfServeExample": {
|
|
||||||
"North Central US": "North Central US",
|
|
||||||
"West US": "West US",
|
|
||||||
"East US 2": "East US 2",
|
|
||||||
"Current Region": "Current Region",
|
|
||||||
"RegionDropdownInfo": "More regions can be added in the future.",
|
|
||||||
"RegionsAndAccountNameValidationError": "Regions and account name should not be empty.",
|
|
||||||
"DbThroughputValidationError": "Please update throughput for database.",
|
|
||||||
"DescriptionLabel": "Description",
|
|
||||||
"DescriptionText": "This class sets collection and database throughput.",
|
|
||||||
"DecriptionLinkText": "Click here for more information",
|
|
||||||
"Regions": "Regions",
|
|
||||||
"RegionsPlaceholder": "Select a region",
|
|
||||||
"Enable Logging": "Enable Logging",
|
|
||||||
"Enable": "Enable",
|
|
||||||
"Disable": "Disable",
|
|
||||||
"Account Name": "Account Name",
|
|
||||||
"AccountNamePlaceHolder": "Enter the account name",
|
|
||||||
"Collection Throughput": "Collection Throughput",
|
|
||||||
"Enable DB level throughput": "Enable Database Level Throughput",
|
|
||||||
"Database Throughput": "Database Throughput",
|
|
||||||
"UpdateInProgressMessage": "Data is being updated",
|
|
||||||
"UpdateCompletedMessageTitle": "Update succeeded",
|
|
||||||
"UpdateCompletedMessageText": "Data update completed.",
|
|
||||||
"SubmissionMessageSuccessTitle": "Update started",
|
|
||||||
"SubmissionMessageForNewRegionText": "Data update started. Region changed.",
|
|
||||||
"SubmissionMessageForSameRegionText": "Data update started. Region not changed.",
|
|
||||||
"SubmissionMessageErrorTitle": "Data update failed",
|
|
||||||
"SubmissionMessageErrorText": "Data update failed because of errors.",
|
|
||||||
"OnSaveFailureMessage": "Data save operation not currently permitted."
|
|
||||||
},
|
|
||||||
"SqlX": {
|
|
||||||
"DedicatedGatewayDescription": "Provision a dedicated gateway cluster for your Azure Cosmos DB account. A dedicated gateway is compute that is a front-end to data in your Azure Cosmos DB account. Your dedicated gateway automatically includes the integrated cache, which can improve read performance. ",
|
|
||||||
"DedicatedGateway": "Dedicated Gateway",
|
|
||||||
"Enable": "Enable",
|
|
||||||
"Disable": "Disable",
|
|
||||||
"LearnAboutDedicatedGateway": "Learn more about dedicated gateway.",
|
|
||||||
"DeprovisioningDetailsText": "Learn more about deprovisioning the dedicated gateway.",
|
|
||||||
"DedicatedGatewayPricing": "Learn more about dedicated gateway pricing",
|
|
||||||
"SKUs": "SKUs",
|
|
||||||
"SKUsPlaceHolder": "Select SKUs",
|
|
||||||
"NumberOfInstances": "Number of instances",
|
|
||||||
"CosmosD4s": "Cosmos.D4s (General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory)",
|
|
||||||
"CosmosD8s": "Cosmos.D8s (General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory)",
|
|
||||||
"CosmosD16s": "Cosmos.D16s (General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory)",
|
|
||||||
"CosmosD32s": "Cosmos.D32s (General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory)",
|
|
||||||
"CreateMessage": "Dedicated gateway resource is being created.",
|
|
||||||
"CreateInitializeTitle": "Provisioning resource",
|
|
||||||
"CreateInitializeMessage": "Dedicated gateway resource will be provisioned.",
|
|
||||||
"CreateSuccessTitle": "Resource provisioned",
|
|
||||||
"CreateSuccesseMessage": "Dedicated gateway resource provisioned.",
|
|
||||||
"CreateFailureTitle": "Failed to provision resource",
|
|
||||||
"CreateFailureMessage": "Dedicated gateway resource provisioning failed.",
|
|
||||||
"UpdateMessage": "Dedicated gateway resource is being updated.",
|
|
||||||
"UpdateInitializeTitle": "Updating resource",
|
|
||||||
"UpdateInitializeMessage": "Dedicated gateway resource will be updated.",
|
|
||||||
"UpdateSuccessTitle": "Resource updated",
|
|
||||||
"UpdateSuccesseMessage": "Dedicated gateway resource updated.",
|
|
||||||
"UpdateFailureTitle": "Failed to update resource",
|
|
||||||
"UpdateFailureMessage": "Dedicated gateway resource updation failed.",
|
|
||||||
"DeleteMessage": "Dedicated gateway resource is being deleted.",
|
|
||||||
"DeleteInitializeTitle": "Deleting resource",
|
|
||||||
"DeleteInitializeMessage": "Dedicated gateway resource will be deleted.",
|
|
||||||
"DeleteSuccessTitle": "Resource deleted",
|
|
||||||
"DeleteSuccesseMessage": "Dedicated gateway resource deleted.",
|
|
||||||
"DeleteFailureTitle": "Failed to delete resource",
|
|
||||||
"DeleteFailureMessage": "Dedicated gateway resource deletion failed.",
|
|
||||||
"CannotSave": "Cannot save the changes to the Dedicated gateway resource at the moment",
|
|
||||||
"DedicatedGatewayEndpoint": "Dedicated gatewayEndpoint",
|
|
||||||
"NoValue": "",
|
|
||||||
"SKUDetails": "SKU Details: ",
|
|
||||||
"CosmosD4Details": "General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory",
|
|
||||||
"CosmosD8Details": "General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory",
|
|
||||||
"CosmosD16Details": "General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory",
|
|
||||||
"CosmosD32Details": "General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory",
|
|
||||||
"Cost": "Cost",
|
|
||||||
"CostText": "Hourly cost of the dedicated gateway resource depends on the SKU selection, number of instances per region, and number of regions.",
|
|
||||||
"ConnectionString": "Connection String",
|
|
||||||
"ConnectionStringText": "To use the dedicated gateway, use the connection string shown in ",
|
|
||||||
"KeysBlade": "the keys blade",
|
|
||||||
"WarningBannerOnUpdate": "Adding or modifying dedicated gateway instances may affect your bill.",
|
|
||||||
"WarningBannerOnDelete": "After deprovisioning the dedicated gateway, you must update any applications using the old dedicated gateway connection string."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
src/Main.tsx
11
src/Main.tsx
@@ -1,18 +1,8 @@
|
|||||||
// CSS Dependencies
|
// CSS Dependencies
|
||||||
import "abort-controller/polyfill";
|
|
||||||
import "babel-polyfill";
|
|
||||||
import "bootstrap/dist/css/bootstrap.css";
|
import "bootstrap/dist/css/bootstrap.css";
|
||||||
import "es6-object-assign/auto";
|
|
||||||
import "es6-symbol/implement";
|
|
||||||
import "object.entries/auto";
|
|
||||||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||||
import "promise-polyfill/src/polyfill";
|
|
||||||
import "promise.prototype.finally/auto";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import "url-polyfill/url-polyfill.min";
|
|
||||||
import "webcrypto-liner/build/webcrypto-liner.shim.min";
|
|
||||||
import "whatwg-fetch";
|
|
||||||
import "../externals/jquery-ui.min.css";
|
import "../externals/jquery-ui.min.css";
|
||||||
import "../externals/jquery-ui.min.js";
|
import "../externals/jquery-ui.min.js";
|
||||||
import "../externals/jquery-ui.structure.min.css";
|
import "../externals/jquery-ui.structure.min.css";
|
||||||
@@ -64,7 +54,6 @@ import { useConfig } from "./hooks/useConfig";
|
|||||||
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
||||||
import { useSidePanel } from "./hooks/useSidePanel";
|
import { useSidePanel } from "./hooks/useSidePanel";
|
||||||
import { KOCommentEnd, KOCommentIfStart } from "./koComment";
|
import { KOCommentEnd, KOCommentIfStart } from "./koComment";
|
||||||
import "./Libs/is-integer-polyfill";
|
|
||||||
import "./Libs/jquery";
|
import "./Libs/jquery";
|
||||||
import "./Shared/appInsights";
|
import "./Shared/appInsights";
|
||||||
import { userContext } from "./UserContext";
|
import { userContext } from "./UserContext";
|
||||||
|
|||||||
@@ -23,11 +23,18 @@ export type Features = {
|
|||||||
readonly ttl90Days: boolean;
|
readonly ttl90Days: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function extractFeatures(params?: URLSearchParams): Features {
|
export function extractFeatures(given = new URLSearchParams()): Features {
|
||||||
params = params || new URLSearchParams(window.location.search);
|
|
||||||
const downcased = new URLSearchParams();
|
const downcased = new URLSearchParams();
|
||||||
params.forEach((value, key) => downcased.append(key.toLocaleLowerCase(), value));
|
const set = (value: string, key: string) => downcased.set(key.toLowerCase(), value);
|
||||||
const get = (key: string) => downcased.get("feature." + key.toLocaleLowerCase()) ?? undefined;
|
const get = (key: string) => downcased.get("feature." + key) ?? undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
new URLSearchParams(window.parent.location.search).forEach(set);
|
||||||
|
} catch {
|
||||||
|
//
|
||||||
|
} finally {
|
||||||
|
given.forEach(set);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canExceedMaximumValue: "true" === get("canexceedmaximumvalue"),
|
canExceedMaximumValue: "true" === get("canexceedmaximumvalue"),
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ import {
|
|||||||
} from "./SelfServeExample.rp";
|
} from "./SelfServeExample.rp";
|
||||||
|
|
||||||
const regionDropdownItems: ChoiceItem[] = [
|
const regionDropdownItems: ChoiceItem[] = [
|
||||||
{ label: "North Central US", key: Regions.NorthCentralUS },
|
{ labelTKey: "NorthCentralUS", key: Regions.NorthCentralUS },
|
||||||
{ label: "West US", key: Regions.WestUS },
|
{ labelTKey: "WestUS", key: Regions.WestUS },
|
||||||
{ label: "East US 2", key: Regions.EastUS2 },
|
{ labelTKey: "EastUS2", key: Regions.EastUS2 },
|
||||||
];
|
];
|
||||||
|
|
||||||
const regionDropdownInfo: Info = {
|
const regionDropdownInfo: Info = {
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { Spinner, SpinnerSize } from "office-ui-fabric-react";
|
|||||||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
|
import { withTranslation } from "react-i18next";
|
||||||
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
|
import { normalizeArmEndpoint } from "../Common/EnvironmentUtility";
|
||||||
import { sendReadyMessage } from "../Common/MessageHandler";
|
import { sendReadyMessage } from "../Common/MessageHandler";
|
||||||
import { configContext, updateConfigContext } from "../ConfigContext";
|
import { configContext, updateConfigContext } from "../ConfigContext";
|
||||||
import { SelfServeFrameInputs } from "../Contracts/ViewModels";
|
import { SelfServeFrameInputs } from "../Contracts/ViewModels";
|
||||||
|
import i18n from "../i18n";
|
||||||
import { updateUserContext } from "../UserContext";
|
import { updateUserContext } from "../UserContext";
|
||||||
import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
|
import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
|
||||||
import "./SelfServe.less";
|
import "./SelfServe.less";
|
||||||
@@ -14,14 +16,35 @@ import { SelfServeDescriptor } from "./SelfServeTypes";
|
|||||||
import { SelfServeType } from "./SelfServeUtils";
|
import { SelfServeType } from "./SelfServeUtils";
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
|
const loadTranslationFile = async (className: string): Promise<void> => {
|
||||||
|
const language = i18n.languages[0];
|
||||||
|
const fileName = `${className}.json`;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
let translations: any;
|
||||||
|
try {
|
||||||
|
translations = await import(`../Localization/${language}/${fileName}`);
|
||||||
|
} catch (e) {
|
||||||
|
translations = await import(`../Localization/en/${fileName}`);
|
||||||
|
}
|
||||||
|
i18n.addResourceBundle(language, className, translations.default, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadTranslations = async (className: string): Promise<void> => {
|
||||||
|
await loadTranslationFile("Common");
|
||||||
|
await loadTranslationFile(className);
|
||||||
|
};
|
||||||
|
|
||||||
const getDescriptor = async (selfServeType: SelfServeType): Promise<SelfServeDescriptor> => {
|
const getDescriptor = async (selfServeType: SelfServeType): Promise<SelfServeDescriptor> => {
|
||||||
switch (selfServeType) {
|
switch (selfServeType) {
|
||||||
case SelfServeType.example: {
|
case SelfServeType.example: {
|
||||||
const SelfServeExample = await import(/* webpackChunkName: "SelfServeExample" */ "./Example/SelfServeExample");
|
const SelfServeExample = await import(/* webpackChunkName: "SelfServeExample" */ "./Example/SelfServeExample");
|
||||||
|
await loadTranslations(SelfServeExample.default.name);
|
||||||
return new SelfServeExample.default().toSelfServeDescriptor();
|
return new SelfServeExample.default().toSelfServeDescriptor();
|
||||||
}
|
}
|
||||||
case SelfServeType.sqlx: {
|
case SelfServeType.sqlx: {
|
||||||
const SqlX = await import(/* webpackChunkName: "SqlX" */ "./SqlX/SqlX");
|
const SqlX = await import(/* webpackChunkName: "SqlX" */ "./SqlX/SqlX");
|
||||||
|
await loadTranslations(SqlX.default.name);
|
||||||
return new SqlX.default().toSelfServeDescriptor();
|
return new SqlX.default().toSelfServeDescriptor();
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -33,7 +56,8 @@ const renderComponent = (selfServeDescriptor: SelfServeDescriptor): JSX.Element
|
|||||||
if (!selfServeDescriptor) {
|
if (!selfServeDescriptor) {
|
||||||
return <h1>Invalid self serve type!</h1>;
|
return <h1>Invalid self serve type!</h1>;
|
||||||
}
|
}
|
||||||
return <SelfServeComponent descriptor={selfServeDescriptor} />;
|
const SelfServeComponentTranslated = withTranslation()(SelfServeComponent);
|
||||||
|
return <SelfServeComponentTranslated descriptor={selfServeDescriptor} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSpinner = (): JSX.Element => {
|
const renderSpinner = (): JSX.Element => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
|
||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
|
import React from "react";
|
||||||
import { SelfServeComponent, SelfServeComponentState } from "./SelfServeComponent";
|
import { SelfServeComponent, SelfServeComponentState } from "./SelfServeComponent";
|
||||||
import { NumberUiType, OnSaveResult, SelfServeDescriptor, SmartUiInput } from "./SelfServeTypes";
|
import { NumberUiType, OnSaveResult, SelfServeDescriptor, SmartUiInput } from "./SelfServeTypes";
|
||||||
|
|
||||||
@@ -87,9 +87,9 @@ describe("SelfServeComponent", () => {
|
|||||||
dataFieldName: "database",
|
dataFieldName: "database",
|
||||||
type: "object",
|
type: "object",
|
||||||
choices: [
|
choices: [
|
||||||
{ label: "Database 1", key: "db1" },
|
{ labelTKey: "Database 1", key: "db1" },
|
||||||
{ label: "Database 2", key: "db2" },
|
{ labelTKey: "Database 2", key: "db2" },
|
||||||
{ label: "Database 3", key: "db3" },
|
{ labelTKey: "Database 3", key: "db3" },
|
||||||
],
|
],
|
||||||
defaultKey: "db2",
|
defaultKey: "db2",
|
||||||
},
|
},
|
||||||
@@ -106,7 +106,9 @@ describe("SelfServeComponent", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
it("should render and honor save, discard, refresh actions", async () => {
|
it("should render and honor save, discard, refresh actions", async () => {
|
||||||
const wrapper = shallow(<SelfServeComponent descriptor={exampleData} />);
|
const wrapper = shallow(
|
||||||
|
<SelfServeComponent descriptor={exampleData} t={undefined} i18n={undefined} tReady={undefined} />
|
||||||
|
);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
|
||||||
@@ -158,7 +160,9 @@ describe("SelfServeComponent", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("getResolvedValue", async () => {
|
it("getResolvedValue", async () => {
|
||||||
const wrapper = shallow(<SelfServeComponent descriptor={exampleData} />);
|
const wrapper = shallow(
|
||||||
|
<SelfServeComponent descriptor={exampleData} t={undefined} i18n={undefined} tReady={undefined} />
|
||||||
|
);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
const selfServeComponent = wrapper.instance() as SelfServeComponent;
|
const selfServeComponent = wrapper.instance() as SelfServeComponent;
|
||||||
|
|
||||||
@@ -179,7 +183,9 @@ describe("SelfServeComponent", () => {
|
|||||||
|
|
||||||
it("message bar and spinner snapshots", async () => {
|
it("message bar and spinner snapshots", async () => {
|
||||||
const newDescriptor = { ...exampleData, onRefresh: onRefreshIsUpdatingMock };
|
const newDescriptor = { ...exampleData, onRefresh: onRefreshIsUpdatingMock };
|
||||||
let wrapper = shallow(<SelfServeComponent descriptor={newDescriptor} />);
|
let wrapper = shallow(
|
||||||
|
<SelfServeComponent descriptor={newDescriptor} t={undefined} i18n={undefined} tReady={undefined} />
|
||||||
|
);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
let selfServeComponent = wrapper.instance() as SelfServeComponent;
|
let selfServeComponent = wrapper.instance() as SelfServeComponent;
|
||||||
selfServeComponent.onSaveButtonClick();
|
selfServeComponent.onSaveButtonClick();
|
||||||
@@ -187,7 +193,9 @@ describe("SelfServeComponent", () => {
|
|||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
|
||||||
newDescriptor.onRefresh = onRefreshMock;
|
newDescriptor.onRefresh = onRefreshMock;
|
||||||
wrapper = shallow(<SelfServeComponent descriptor={newDescriptor} />);
|
wrapper = shallow(
|
||||||
|
<SelfServeComponent descriptor={newDescriptor} t={undefined} i18n={undefined} tReady={undefined} />
|
||||||
|
);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
selfServeComponent = wrapper.instance() as SelfServeComponent;
|
selfServeComponent = wrapper.instance() as SelfServeComponent;
|
||||||
selfServeComponent.onSaveButtonClick();
|
selfServeComponent.onSaveButtonClick();
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ import {
|
|||||||
Spinner,
|
Spinner,
|
||||||
SpinnerSize,
|
SpinnerSize,
|
||||||
Stack,
|
Stack,
|
||||||
|
Text,
|
||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
import promiseRetry, { AbortError } from "p-retry";
|
import promiseRetry, { AbortError } from "p-retry";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Translation } from "react-i18next";
|
import { WithTranslation } from "react-i18next";
|
||||||
import * as _ from "underscore";
|
import * as _ from "underscore";
|
||||||
import { sendMessage } from "../Common/MessageHandler";
|
import { sendMessage } from "../Common/MessageHandler";
|
||||||
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
|
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
|
||||||
import { SmartUiComponent, SmartUiDescriptor } from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
import { SmartUiComponent, SmartUiDescriptor } from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
||||||
import "../i18n";
|
|
||||||
import { commandBarItemStyles, commandBarStyles, containerStackTokens, separatorStyles } from "./SelfServeStyles";
|
import { commandBarItemStyles, commandBarStyles, containerStackTokens, separatorStyles } from "./SelfServeStyles";
|
||||||
import {
|
import {
|
||||||
AnyDisplay,
|
AnyDisplay,
|
||||||
@@ -57,7 +57,7 @@ interface PortalNotificationContent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelfServeComponentProps {
|
export interface SelfServeComponentProps extends WithTranslation {
|
||||||
descriptor: SelfServeDescriptor;
|
descriptor: SelfServeDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +108,9 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
|
|||||||
this.retryIntervalInMs = SelfServeComponent.defaultRetryIntervalInMs;
|
this.retryIntervalInMs = SelfServeComponent.defaultRetryIntervalInMs;
|
||||||
}
|
}
|
||||||
this.retryOptions = { forever: true, maxTimeout: this.retryIntervalInMs, minTimeout: this.retryIntervalInMs };
|
this.retryOptions = { forever: true, maxTimeout: this.retryIntervalInMs, minTimeout: this.retryIntervalInMs };
|
||||||
|
|
||||||
|
// translation function passed to SelfServeComponent
|
||||||
|
this.translationFunction = this.props.t;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onError = (hasErrors: boolean): void => {
|
private onError = (hasErrors: boolean): void => {
|
||||||
@@ -391,8 +394,8 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
|
|||||||
return this.getTranslation(key, "Common");
|
return this.getTranslation(key, "Common");
|
||||||
};
|
};
|
||||||
|
|
||||||
private getTranslation = (messageKey: string, prefix = `${this.smartUiGeneratorClassName}`): string => {
|
private getTranslation = (messageKey: string, namespace = `${this.smartUiGeneratorClassName}`): string => {
|
||||||
const translationKey = `${prefix}.${messageKey}`;
|
const translationKey = `${namespace}:${messageKey}`;
|
||||||
const translation = this.translationFunction ? this.translationFunction(translationKey) : messageKey;
|
const translation = this.translationFunction ? this.translationFunction(translationKey) : messageKey;
|
||||||
if (translation === translationKey) {
|
if (translation === translationKey) {
|
||||||
return messageKey;
|
return messageKey;
|
||||||
@@ -441,53 +444,45 @@ export class SelfServeComponent extends React.Component<SelfServeComponentProps,
|
|||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
if (this.state.compileErrorMessage) {
|
if (this.state.compileErrorMessage) {
|
||||||
return <MessageBar messageBarType={MessageBarType.error}>{this.state.compileErrorMessage}</MessageBar>;
|
return (
|
||||||
|
<MessageBar messageBarType={MessageBarType.error}>
|
||||||
|
<Text>{this.state.compileErrorMessage}</Text>
|
||||||
|
</MessageBar>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Translation>
|
<div style={{ overflowX: "auto" }}>
|
||||||
{(translate) => {
|
<Stack tokens={containerStackTokens}>
|
||||||
if (!this.translationFunction) {
|
<Stack.Item>
|
||||||
this.translationFunction = translate;
|
<CommandBar styles={commandBarStyles} items={this.getCommandBarItems()} />
|
||||||
}
|
<Separator styles={separatorStyles} />
|
||||||
|
</Stack.Item>
|
||||||
return (
|
{this.state.isInitializing ? (
|
||||||
<div style={{ overflowX: "auto" }}>
|
<Spinner size={SpinnerSize.large} />
|
||||||
<Stack tokens={containerStackTokens}>
|
) : (
|
||||||
<Stack.Item>
|
<>
|
||||||
<CommandBar styles={commandBarStyles} items={this.getCommandBarItems()} />
|
{this.state.notification && (
|
||||||
<Separator styles={separatorStyles} />
|
<MessageBar
|
||||||
</Stack.Item>
|
messageBarType={this.state.notification.type}
|
||||||
{this.state.isInitializing ? (
|
onDismiss={
|
||||||
<Spinner size={SpinnerSize.large} />
|
this.state.notification.isCancellable ? () => this.setState({ notification: undefined }) : undefined
|
||||||
) : (
|
}
|
||||||
<>
|
>
|
||||||
{this.state.notification && (
|
<Text>{this.state.notification.message}</Text>
|
||||||
<MessageBar
|
</MessageBar>
|
||||||
messageBarType={this.state.notification.type}
|
)}
|
||||||
onDismiss={
|
<SmartUiComponent
|
||||||
this.state.notification.isCancellable
|
disabled={this.state.refreshResult?.isUpdateInProgress || this.state.isSaving}
|
||||||
? () => this.setState({ notification: undefined })
|
descriptor={this.state.root as SmartUiDescriptor}
|
||||||
: undefined
|
currentValues={this.state.currentValues}
|
||||||
}
|
onInputChange={this.onInputChange}
|
||||||
>
|
onError={this.onError}
|
||||||
{this.state.notification.message}
|
getTranslation={this.getTranslation}
|
||||||
</MessageBar>
|
/>
|
||||||
)}
|
</>
|
||||||
<SmartUiComponent
|
)}
|
||||||
disabled={this.state.refreshResult?.isUpdateInProgress || this.state.isSaving}
|
</Stack>
|
||||||
descriptor={this.state.root as SmartUiDescriptor}
|
</div>
|
||||||
currentValues={this.state.currentValues}
|
|
||||||
onInputChange={this.onInputChange}
|
|
||||||
onError={this.onError}
|
|
||||||
getTranslation={this.getTranslation}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Translation>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
69
src/SelfServe/SelfServeTelemetryProcessor.ts
Normal file
69
src/SelfServe/SelfServeTelemetryProcessor.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { sendMessage } from "../Common/MessageHandler";
|
||||||
|
import { configContext } from "../ConfigContext";
|
||||||
|
import { SelfServeMessageTypes } from "../Contracts/SelfServeContracts";
|
||||||
|
import { appInsights } from "../Shared/appInsights";
|
||||||
|
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import { userContext } from "../UserContext";
|
||||||
|
import { SelfServeTelemetryMessage } from "./SelfServeTypes";
|
||||||
|
|
||||||
|
const action = Action.SelfServe;
|
||||||
|
|
||||||
|
export const trace = (data: SelfServeTelemetryMessage): void => {
|
||||||
|
sendSelfServeTelemetryMessage(ActionModifiers.Mark, data);
|
||||||
|
appInsights.trackEvent({ name: Action[action] }, decorateData(data, ActionModifiers.Mark));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const traceStart = (data: SelfServeTelemetryMessage): number => {
|
||||||
|
const timestamp: number = Date.now();
|
||||||
|
sendSelfServeTelemetryMessage(ActionModifiers.Start, data);
|
||||||
|
appInsights.startTrackEvent(Action[action]);
|
||||||
|
return timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const traceSuccess = (data: SelfServeTelemetryMessage, timestamp?: number): void => {
|
||||||
|
sendSelfServeTelemetryMessage(ActionModifiers.Success, data, timestamp || Date.now());
|
||||||
|
appInsights.stopTrackEvent(Action[action], decorateData(data, ActionModifiers.Success));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const traceFailure = (data: SelfServeTelemetryMessage, timestamp?: number): void => {
|
||||||
|
sendSelfServeTelemetryMessage(ActionModifiers.Failed, data, timestamp || Date.now());
|
||||||
|
appInsights.stopTrackEvent(Action[action], decorateData(data, ActionModifiers.Failed));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const traceCancel = (data: SelfServeTelemetryMessage, timestamp?: number): void => {
|
||||||
|
sendSelfServeTelemetryMessage(ActionModifiers.Cancel, data, timestamp || Date.now());
|
||||||
|
appInsights.stopTrackEvent(Action[action], decorateData(data, ActionModifiers.Cancel));
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendSelfServeTelemetryMessage = (
|
||||||
|
actionModifier: string,
|
||||||
|
data: SelfServeTelemetryMessage,
|
||||||
|
timeStamp?: number
|
||||||
|
): void => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const dataToSend: any = {
|
||||||
|
type: SelfServeMessageTypes.TelemetryInfo,
|
||||||
|
data: {
|
||||||
|
action: Action[action],
|
||||||
|
actionModifier: actionModifier,
|
||||||
|
data: JSON.stringify(decorateData(data)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (timeStamp) {
|
||||||
|
dataToSend.data.timeStamp = timeStamp;
|
||||||
|
}
|
||||||
|
sendMessage(dataToSend);
|
||||||
|
};
|
||||||
|
|
||||||
|
const decorateData = (data: SelfServeTelemetryMessage, actionModifier?: string) => {
|
||||||
|
return {
|
||||||
|
databaseAccountName: userContext.databaseAccount?.name,
|
||||||
|
defaultExperience: userContext.defaultExperience,
|
||||||
|
authType: userContext.authType,
|
||||||
|
subscriptionId: userContext.subscriptionId,
|
||||||
|
platform: configContext.platform,
|
||||||
|
env: process.env.NODE_ENV,
|
||||||
|
actionModifier,
|
||||||
|
...data,
|
||||||
|
} as { [key: string]: string };
|
||||||
|
};
|
||||||
@@ -98,7 +98,7 @@ export enum NumberUiType {
|
|||||||
Slider = "Slider",
|
Slider = "Slider",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChoiceItem = { label: string; key: string };
|
export type ChoiceItem = { labelTKey: string; key: string };
|
||||||
|
|
||||||
export type InputType = number | string | boolean | ChoiceItem | Description;
|
export type InputType = number | string | boolean | ChoiceItem | Description;
|
||||||
|
|
||||||
@@ -157,3 +157,9 @@ export interface RefreshResult {
|
|||||||
export interface RefreshParams {
|
export interface RefreshParams {
|
||||||
retryIntervalInMs: number;
|
retryIntervalInMs: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SelfServeTelemetryMessage {
|
||||||
|
selfServeClassName: string;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
data?: any;
|
||||||
|
}
|
||||||
|
|||||||
@@ -131,9 +131,9 @@ describe("SelfServeUtils", () => {
|
|||||||
type: "object",
|
type: "object",
|
||||||
labelTKey: "Regions",
|
labelTKey: "Regions",
|
||||||
choices: [
|
choices: [
|
||||||
{ label: "South West US", key: "SWUS" },
|
{ labelTKey: "South West US", key: "SWUS" },
|
||||||
{ label: "North Central US", key: "NCUS" },
|
{ labelTKey: "North Central US", key: "NCUS" },
|
||||||
{ label: "East US 2", key: "EUS2" },
|
{ labelTKey: "East US 2", key: "EUS2" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -238,9 +238,9 @@ describe("SelfServeUtils", () => {
|
|||||||
type: "object",
|
type: "object",
|
||||||
labelTKey: "Regions",
|
labelTKey: "Regions",
|
||||||
choices: [
|
choices: [
|
||||||
{ label: "South West US", key: "SWUS" },
|
{ labelTKey: "South West US", key: "SWUS" },
|
||||||
{ label: "North Central US", key: "NCUS" },
|
{ labelTKey: "North Central US", key: "NCUS" },
|
||||||
{ label: "East US 2", key: "EUS2" },
|
{ labelTKey: "East US 2", key: "EUS2" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
children: [] as Node[],
|
children: [] as Node[],
|
||||||
|
|||||||
@@ -195,5 +195,5 @@ export const generateBladeLink = (blade: BladeType): string => {
|
|||||||
const subscriptionId = userContext.subscriptionId;
|
const subscriptionId = userContext.subscriptionId;
|
||||||
const resourceGroupName = userContext.resourceGroup;
|
const resourceGroupName = userContext.resourceGroup;
|
||||||
const databaseAccountName = userContext.databaseAccount.name;
|
const databaseAccountName = userContext.databaseAccount.name;
|
||||||
return `https://portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}/${blade}`;
|
return `${document.referrer}#@microsoft.onmicrosoft.com/resource/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}/${blade}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { IsDisplayable, OnChange, RefreshOptions, Values } from "../Decorators";
|
import { IsDisplayable, OnChange, RefreshOptions, Values } from "../Decorators";
|
||||||
|
import { trace } from "../SelfServeTelemetryProcessor";
|
||||||
import {
|
import {
|
||||||
ChoiceItem,
|
ChoiceItem,
|
||||||
Description,
|
Description,
|
||||||
@@ -147,10 +148,10 @@ const onEnableDedicatedGatewayChange = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const skuDropDownItems: ChoiceItem[] = [
|
const skuDropDownItems: ChoiceItem[] = [
|
||||||
{ label: "CosmosD4s", key: CosmosD4s },
|
{ labelTKey: "CosmosD4s", key: CosmosD4s },
|
||||||
{ label: "CosmosD8s", key: CosmosD8s },
|
{ labelTKey: "CosmosD8s", key: CosmosD8s },
|
||||||
{ label: "CosmosD16s", key: CosmosD16s },
|
{ labelTKey: "CosmosD16s", key: CosmosD16s },
|
||||||
{ label: "CosmosD32s", key: CosmosD32s },
|
{ labelTKey: "CosmosD32s", key: CosmosD32s },
|
||||||
];
|
];
|
||||||
|
|
||||||
const getSkus = async (): Promise<ChoiceItem[]> => {
|
const getSkus = async (): Promise<ChoiceItem[]> => {
|
||||||
@@ -176,6 +177,8 @@ export default class SqlX extends SelfServeBaseClass {
|
|||||||
currentValues: Map<string, SmartUiInput>,
|
currentValues: Map<string, SmartUiInput>,
|
||||||
baselineValues: Map<string, SmartUiInput>
|
baselineValues: Map<string, SmartUiInput>
|
||||||
): Promise<OnSaveResult> => {
|
): Promise<OnSaveResult> => {
|
||||||
|
trace({ selfServeClassName: "SqlX" });
|
||||||
|
|
||||||
const dedicatedGatewayCurrentlyEnabled = currentValues.get("enableDedicatedGateway")?.value as boolean;
|
const dedicatedGatewayCurrentlyEnabled = currentValues.get("enableDedicatedGateway")?.value as boolean;
|
||||||
const dedicatedGatewayOriginallyEnabled = baselineValues.get("enableDedicatedGateway")?.value as boolean;
|
const dedicatedGatewayOriginallyEnabled = baselineValues.get("enableDedicatedGateway")?.value as boolean;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -114,6 +114,7 @@ export enum Action {
|
|||||||
NotebooksGalleryPublicGalleryCount,
|
NotebooksGalleryPublicGalleryCount,
|
||||||
NotebooksGalleryFavoritesCount,
|
NotebooksGalleryFavoritesCount,
|
||||||
NotebooksGalleryPublishedCount,
|
NotebooksGalleryPublishedCount,
|
||||||
|
SelfServe,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ActionModifiers = {
|
export const ActionModifiers = {
|
||||||
|
|||||||
@@ -1,58 +1,11 @@
|
|||||||
import { Action, ActionModifiers } from "./TelemetryConstants";
|
import { sendMessage } from "../../Common/MessageHandler";
|
||||||
import { sendMessage } from "../../Common/MessageHandler";
|
|
||||||
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
|
||||||
import { appInsights } from "../appInsights";
|
|
||||||
import { configContext } from "../../ConfigContext";
|
import { configContext } from "../../ConfigContext";
|
||||||
|
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
|
import { appInsights } from "../appInsights";
|
||||||
|
import { Action, ActionModifiers } from "./TelemetryConstants";
|
||||||
|
|
||||||
// TODO: Remove this. It is perfectly find to pass any data to telemtry methods.
|
type TelemetryData = { [key: string]: unknown };
|
||||||
// This was added only to maintain stability while removing dependencies on explorer.databaseAccount and explorer.defaultExperience
|
|
||||||
type allowedKeys =
|
|
||||||
| "notebookId"
|
|
||||||
| "collectionId"
|
|
||||||
| "collectionName"
|
|
||||||
| "error"
|
|
||||||
| "isSample"
|
|
||||||
| "downloadCount"
|
|
||||||
| "baseUrl"
|
|
||||||
| "source"
|
|
||||||
| "description"
|
|
||||||
| "dataExplorerArea"
|
|
||||||
| "databaseName"
|
|
||||||
| "downloadCount"
|
|
||||||
| "favoriteCount"
|
|
||||||
| "abuseCategory"
|
|
||||||
| "errorStack"
|
|
||||||
| "tabTitle"
|
|
||||||
| "tabId"
|
|
||||||
| "conflictResourceType"
|
|
||||||
| "conflictOperationType"
|
|
||||||
| "conflictResourceId"
|
|
||||||
| "message"
|
|
||||||
| "files"
|
|
||||||
| "notebooks"
|
|
||||||
| "directories"
|
|
||||||
| "tabName"
|
|
||||||
| "databaseId"
|
|
||||||
| "queryName"
|
|
||||||
| "isPublishPending"
|
|
||||||
| "label"
|
|
||||||
| "scopesSelected"
|
|
||||||
| "title"
|
|
||||||
| "level"
|
|
||||||
| "changedSelectedValueTo"
|
|
||||||
| "area"
|
|
||||||
| "area"
|
|
||||||
| "paneTitle"
|
|
||||||
| "notebookUrl"
|
|
||||||
| "isNotebookEnabled"
|
|
||||||
| "commandButtonClicked"
|
|
||||||
| "count"
|
|
||||||
| "publishedCount"
|
|
||||||
| "underReviewCount"
|
|
||||||
| "removedCount";
|
|
||||||
|
|
||||||
type TelemetryData = { [key in allowedKeys]?: unknown };
|
|
||||||
|
|
||||||
export function trace(action: Action, actionModifier: string = ActionModifiers.Mark, data: TelemetryData = {}): void {
|
export function trace(action: Action, actionModifier: string = ActionModifiers.Mark, data: TelemetryData = {}): void {
|
||||||
sendMessage({
|
sendMessage({
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import "babel-polyfill";
|
|
||||||
import "promise-polyfill/src/polyfill"; // polyfill Promise on IE
|
|
||||||
import "@jupyterlab/terminal/style/index.css";
|
import "@jupyterlab/terminal/style/index.css";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { ServerConnection } from "@jupyterlab/services";
|
import { ServerConnection } from "@jupyterlab/services";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as DataModels from "../Contracts/DataModels";
|
|||||||
import * as Q from "q";
|
import * as Q from "q";
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import { QueryUtils } from "./QueryUtils";
|
import * as QueryUtils from "./QueryUtils";
|
||||||
|
|
||||||
describe("Query Utils", () => {
|
describe("Query Utils", () => {
|
||||||
function generatePartitionKeyForPath(path: string): DataModels.PartitionKey {
|
function generatePartitionKeyForPath(path: string): DataModels.PartitionKey {
|
||||||
|
|||||||
@@ -1,118 +1,114 @@
|
|||||||
import Q from "q";
|
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
|
|
||||||
export class QueryUtils {
|
export function buildDocumentsQuery(
|
||||||
public static buildDocumentsQuery(
|
filter: string,
|
||||||
filter: string,
|
partitionKeyProperty: string,
|
||||||
partitionKeyProperty: string,
|
partitionKey: DataModels.PartitionKey
|
||||||
partitionKey: DataModels.PartitionKey
|
): string {
|
||||||
): string {
|
let query = partitionKeyProperty
|
||||||
let query: string = partitionKeyProperty
|
? `select c.id, c._self, c._rid, c._ts, ${buildDocumentsQueryPartitionProjections(
|
||||||
? `select c.id, c._self, c._rid, c._ts, ${QueryUtils.buildDocumentsQueryPartitionProjections(
|
"c",
|
||||||
"c",
|
partitionKey
|
||||||
partitionKey
|
)} as _partitionKeyValue from c`
|
||||||
)} as _partitionKeyValue from c`
|
: `select c.id, c._self, c._rid, c._ts from c`;
|
||||||
: `select c.id, c._self, c._rid, c._ts from c`;
|
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
query += " " + filter;
|
query += " " + filter;
|
||||||
}
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static buildDocumentsQueryPartitionProjections(
|
return query;
|
||||||
collectionAlias: string,
|
|
||||||
partitionKey: DataModels.PartitionKey
|
|
||||||
): string {
|
|
||||||
if (!partitionKey) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// e.g., path /order/id will be projected as c["order"]["id"],
|
|
||||||
// to escape any property names that match a keyword
|
|
||||||
let projections = [];
|
|
||||||
for (let index in partitionKey.paths) {
|
|
||||||
// TODO: Handle "/" in partition key definitions
|
|
||||||
const projectedProperties: string[] = partitionKey.paths[index].split("/").slice(1);
|
|
||||||
let projectedProperty: string = "";
|
|
||||||
|
|
||||||
projectedProperties.forEach((property: string) => {
|
|
||||||
const projection = property.trim();
|
|
||||||
if (projection.length > 0 && projection.charAt(0) != "'" && projection.charAt(0) != '"') {
|
|
||||||
projectedProperty = projectedProperty + `["${projection}"]`;
|
|
||||||
} else if (projection.length > 0 && projection.charAt(0) == "'") {
|
|
||||||
// trim single quotes and escape double quotes
|
|
||||||
const projectionSlice = projection.slice(1, projection.length - 1);
|
|
||||||
projectedProperty =
|
|
||||||
projectedProperty + `["${projectionSlice.replace(/\\"/g, '"').replace(/"/g, '\\\\\\"')}"]`;
|
|
||||||
} else {
|
|
||||||
projectedProperty = projectedProperty + `[${projection}]`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
projections.push(`${collectionAlias}${projectedProperty}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return projections.join(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async queryPagesUntilContentPresent(
|
|
||||||
firstItemIndex: number,
|
|
||||||
queryItems: (itemIndex: number) => Promise<ViewModels.QueryResults>
|
|
||||||
): Promise<ViewModels.QueryResults> {
|
|
||||||
let roundTrips: number = 0;
|
|
||||||
let netRequestCharge: number = 0;
|
|
||||||
const doRequest = async (itemIndex: number): Promise<ViewModels.QueryResults> => {
|
|
||||||
const results: ViewModels.QueryResults = await queryItems(itemIndex);
|
|
||||||
roundTrips = roundTrips + 1;
|
|
||||||
results.roundTrips = roundTrips;
|
|
||||||
results.requestCharge = Number(results.requestCharge) + netRequestCharge;
|
|
||||||
netRequestCharge = Number(results.requestCharge);
|
|
||||||
const resultsMetadata: ViewModels.QueryResultsMetadata = {
|
|
||||||
hasMoreResults: results.hasMoreResults,
|
|
||||||
itemCount: results.itemCount,
|
|
||||||
firstItemIndex: results.firstItemIndex,
|
|
||||||
lastItemIndex: results.lastItemIndex,
|
|
||||||
};
|
|
||||||
if (resultsMetadata.itemCount === 0 && resultsMetadata.hasMoreResults) {
|
|
||||||
return await doRequest(resultsMetadata.lastItemIndex);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
};
|
|
||||||
|
|
||||||
return await doRequest(firstItemIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async queryAllPages(
|
|
||||||
queryItems: (itemIndex: number) => Promise<ViewModels.QueryResults>
|
|
||||||
): Promise<ViewModels.QueryResults> {
|
|
||||||
const queryResults: ViewModels.QueryResults = {
|
|
||||||
documents: [],
|
|
||||||
activityId: undefined,
|
|
||||||
hasMoreResults: false,
|
|
||||||
itemCount: 0,
|
|
||||||
firstItemIndex: 0,
|
|
||||||
lastItemIndex: 0,
|
|
||||||
requestCharge: 0,
|
|
||||||
roundTrips: 0,
|
|
||||||
};
|
|
||||||
const doRequest = async (itemIndex: number): Promise<ViewModels.QueryResults> => {
|
|
||||||
const results: ViewModels.QueryResults = await queryItems(itemIndex);
|
|
||||||
const { requestCharge, hasMoreResults, itemCount, lastItemIndex, documents } = results;
|
|
||||||
queryResults.roundTrips = queryResults.roundTrips + 1;
|
|
||||||
queryResults.requestCharge = Number(queryResults.requestCharge) + Number(requestCharge);
|
|
||||||
queryResults.hasMoreResults = hasMoreResults;
|
|
||||||
queryResults.itemCount = queryResults.itemCount + itemCount;
|
|
||||||
queryResults.lastItemIndex = lastItemIndex;
|
|
||||||
queryResults.documents = queryResults.documents.concat(documents);
|
|
||||||
if (queryResults.hasMoreResults) {
|
|
||||||
return doRequest(queryResults.lastItemIndex + 1);
|
|
||||||
}
|
|
||||||
return queryResults;
|
|
||||||
};
|
|
||||||
|
|
||||||
return doRequest(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildDocumentsQueryPartitionProjections(
|
||||||
|
collectionAlias: string,
|
||||||
|
partitionKey?: DataModels.PartitionKey
|
||||||
|
): string {
|
||||||
|
if (!partitionKey) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// e.g., path /order/id will be projected as c["order"]["id"],
|
||||||
|
// to escape any property names that match a keyword
|
||||||
|
const projections = [];
|
||||||
|
for (const index in partitionKey.paths) {
|
||||||
|
// TODO: Handle "/" in partition key definitions
|
||||||
|
const projectedProperties: string[] = partitionKey.paths[index].split("/").slice(1);
|
||||||
|
let projectedProperty = "";
|
||||||
|
|
||||||
|
projectedProperties.forEach((property: string) => {
|
||||||
|
const projection = property.trim();
|
||||||
|
if (projection.length > 0 && projection.charAt(0) !== "'" && projection.charAt(0) !== '"') {
|
||||||
|
projectedProperty += `["${projection}"]`;
|
||||||
|
} else if (projection.length > 0 && projection.charAt(0) === "'") {
|
||||||
|
// trim single quotes and escape double quotes
|
||||||
|
const projectionSlice = projection.slice(1, projection.length - 1);
|
||||||
|
projectedProperty += `["${projectionSlice.replace(/\\"/g, '"').replace(/"/g, '\\\\\\"')}"]`;
|
||||||
|
} else {
|
||||||
|
projectedProperty += `[${projection}]`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
projections.push(`${collectionAlias}${projectedProperty}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return projections.join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryPagesUntilContentPresent = async (
|
||||||
|
firstItemIndex: number,
|
||||||
|
queryItems: (itemIndex: number) => Promise<ViewModels.QueryResults>
|
||||||
|
): Promise<ViewModels.QueryResults> => {
|
||||||
|
let roundTrips = 0;
|
||||||
|
let netRequestCharge = 0;
|
||||||
|
const doRequest = async (itemIndex: number): Promise<ViewModels.QueryResults> => {
|
||||||
|
const results = await queryItems(itemIndex);
|
||||||
|
roundTrips = roundTrips + 1;
|
||||||
|
results.roundTrips = roundTrips;
|
||||||
|
results.requestCharge = Number(results.requestCharge) + netRequestCharge;
|
||||||
|
netRequestCharge = Number(results.requestCharge);
|
||||||
|
const resultsMetadata = {
|
||||||
|
hasMoreResults: results.hasMoreResults,
|
||||||
|
itemCount: results.itemCount,
|
||||||
|
firstItemIndex: results.firstItemIndex,
|
||||||
|
lastItemIndex: results.lastItemIndex,
|
||||||
|
};
|
||||||
|
if (resultsMetadata.itemCount === 0 && resultsMetadata.hasMoreResults) {
|
||||||
|
return await doRequest(resultsMetadata.lastItemIndex);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
return await doRequest(firstItemIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const queryAllPages = async (
|
||||||
|
queryItems: (itemIndex: number) => Promise<ViewModels.QueryResults>
|
||||||
|
): Promise<ViewModels.QueryResults> => {
|
||||||
|
const queryResults: ViewModels.QueryResults = {
|
||||||
|
documents: [],
|
||||||
|
activityId: undefined,
|
||||||
|
hasMoreResults: false,
|
||||||
|
itemCount: 0,
|
||||||
|
firstItemIndex: 0,
|
||||||
|
lastItemIndex: 0,
|
||||||
|
requestCharge: 0,
|
||||||
|
roundTrips: 0,
|
||||||
|
};
|
||||||
|
const doRequest = async (itemIndex: number): Promise<ViewModels.QueryResults> => {
|
||||||
|
const results = await queryItems(itemIndex);
|
||||||
|
const { requestCharge, hasMoreResults, itemCount, lastItemIndex, documents } = results;
|
||||||
|
queryResults.roundTrips = queryResults.roundTrips + 1;
|
||||||
|
queryResults.requestCharge = Number(queryResults.requestCharge) + Number(requestCharge);
|
||||||
|
queryResults.hasMoreResults = hasMoreResults;
|
||||||
|
queryResults.itemCount = queryResults.itemCount + itemCount;
|
||||||
|
queryResults.lastItemIndex = lastItemIndex;
|
||||||
|
queryResults.documents = queryResults.documents.concat(documents);
|
||||||
|
if (queryResults.hasMoreResults) {
|
||||||
|
return doRequest(queryResults.lastItemIndex + 1);
|
||||||
|
}
|
||||||
|
return queryResults;
|
||||||
|
};
|
||||||
|
|
||||||
|
return doRequest(0);
|
||||||
|
};
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ async function configurePortal(explorerParams: ExplorerParams): Promise<Explorer
|
|||||||
explorer.configure(inputs);
|
explorer.configure(inputs);
|
||||||
resolve(explorer);
|
resolve(explorer);
|
||||||
if (openAction) {
|
if (openAction) {
|
||||||
handleOpenAction(openAction, explorer.nonSystemDatabases(), explorer);
|
handleOpenAction(openAction, explorer.databases(), explorer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
10
src/i18n.ts
10
src/i18n.ts
@@ -1,22 +1,14 @@
|
|||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import LanguageDetector from "i18next-browser-languagedetector";
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
import { initReactI18next } from "react-i18next";
|
import { initReactI18next } from "react-i18next";
|
||||||
import XHR from "i18next-http-backend";
|
|
||||||
import EnglishTranslations from "./Localization/en/translations.json";
|
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(XHR)
|
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
resources: {
|
|
||||||
en: EnglishTranslations,
|
|
||||||
},
|
|
||||||
fallbackLng: "en",
|
fallbackLng: "en",
|
||||||
detection: { order: ["navigator", "cookie", "localStorage", "sessionStorage", "querystring", "htmlTag"] },
|
detection: { order: ["navigator", "cookie", "localStorage", "sessionStorage", "querystring", "htmlTag"] },
|
||||||
debug: process.env.NODE_ENV === "development",
|
debug: process.env.NODE_ENV === "development",
|
||||||
ns: ["translations"],
|
|
||||||
defaultNS: "translations",
|
|
||||||
keySeparator: ".",
|
keySeparator: ".",
|
||||||
interpolation: {
|
interpolation: {
|
||||||
formatSeparator: ",",
|
formatSeparator: ",",
|
||||||
@@ -29,3 +21,5 @@ i18n
|
|||||||
useSuspense: false,
|
useSuspense: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import Adapter from "enzyme-adapter-react-16";
|
|
||||||
import { configure } from "enzyme";
|
import { configure } from "enzyme";
|
||||||
|
import Adapter from "enzyme-adapter-react-16";
|
||||||
import "jest-canvas-mock";
|
import "jest-canvas-mock";
|
||||||
import { initializeIcons } from "office-ui-fabric-react";
|
import { initializeIcons } from "office-ui-fabric-react";
|
||||||
|
import { TextDecoder, TextEncoder } from "util";
|
||||||
configure({ adapter: new Adapter() });
|
configure({ adapter: new Adapter() });
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
|
|
||||||
@@ -13,3 +14,5 @@ if (typeof window.URL.createObjectURL === "undefined") {
|
|||||||
(<any>window).$ = (<any>window).jQuery = require("jquery");
|
(<any>window).$ = (<any>window).jQuery = require("jquery");
|
||||||
(<any>global).$ = (<any>global).$.jQuery = require("jquery");
|
(<any>global).$ = (<any>global).$.jQuery = require("jquery");
|
||||||
require("jquery-ui-dist/jquery-ui");
|
require("jquery-ui-dist/jquery-ui");
|
||||||
|
(<any>global).TextEncoder = TextEncoder;
|
||||||
|
(<any>global).TextDecoder = TextDecoder;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import "babel-polyfill";
|
|
||||||
import { DocumentClientParams, UploadDetailsRecord, UploadDetails } from "./definitions";
|
import { DocumentClientParams, UploadDetailsRecord, UploadDetails } from "./definitions";
|
||||||
import { client } from "../../Common/CosmosClient";
|
import { client } from "../../Common/CosmosClient";
|
||||||
import { configContext, updateConfigContext } from "../../ConfigContext";
|
import { updateConfigContext } from "../../ConfigContext";
|
||||||
import { updateUserContext } from "../../UserContext";
|
import { updateUserContext } from "../../UserContext";
|
||||||
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"target": "es5",
|
"target": "es2017",
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"lib": ["es5", "es6", "dom", "webworker.importscripts"],
|
"lib": ["es5", "es6", "dom", "webworker.importscripts"],
|
||||||
|
|||||||
6
utils/deployment-status/package-lock.json
generated
6
utils/deployment-status/package-lock.json
generated
@@ -51,9 +51,9 @@
|
|||||||
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
||||||
},
|
},
|
||||||
"node-fetch": {
|
"node-fetch": {
|
||||||
"version": "2.6.0",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||||
},
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
|
|||||||
@@ -12,6 +12,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"moment": "^2.27.0",
|
"moment": "^2.27.0",
|
||||||
"node-fetch": "^2.6.0"
|
"node-fetch": "^2.6.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,23 +81,6 @@ const typescriptRule = {
|
|||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Third party modules are compiled with babel since using ts-loader that much causes webpack to run out of memory
|
|
||||||
const ModulesRule = {
|
|
||||||
test: /\.js$/,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: "babel-loader",
|
|
||||||
options: {
|
|
||||||
cacheDirectory: ".cache/babel",
|
|
||||||
presets: [["@babel/preset-env", { targets: { ie: "11" }, useBuiltIns: false }]],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
include: /node_modules/,
|
|
||||||
// Exclude large modules we know don't need transpiling
|
|
||||||
exclude: /vega|monaco|plotly/,
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function (env = {}, argv = {}) {
|
module.exports = function (env = {}, argv = {}) {
|
||||||
const mode = argv.mode || "development";
|
const mode = argv.mode || "development";
|
||||||
const rules = [fontRule, lessRule, imagesRule, cssRule, htmlRule, typescriptRule];
|
const rules = [fontRule, lessRule, imagesRule, cssRule, htmlRule, typescriptRule];
|
||||||
@@ -107,7 +90,6 @@ module.exports = function (env = {}, argv = {}) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (mode === "production") {
|
if (mode === "production") {
|
||||||
rules.push(ModulesRule);
|
|
||||||
envVars.NODE_ENV = "production";
|
envVars.NODE_ENV = "production";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +217,6 @@ module.exports = function (env = {}, argv = {}) {
|
|||||||
minimize: mode === "production" ? true : false,
|
minimize: mode === "production" ? true : false,
|
||||||
minimizer: [
|
minimizer: [
|
||||||
new TerserPlugin({
|
new TerserPlugin({
|
||||||
cache: ".cache/terser",
|
|
||||||
terserOptions: {
|
terserOptions: {
|
||||||
// These options increase our initial bundle size by ~5% but the builds are significantly faster and won't run out of memory
|
// These options increase our initial bundle size by ~5% but the builds are significantly faster and won't run out of memory
|
||||||
compress: false,
|
compress: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user