mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-23 10:51:30 +00:00
Compare commits
28 Commits
languy-com
...
steve-self
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c857b9aab9 | ||
|
|
cd45f2943d | ||
|
|
385c9f216f | ||
|
|
d17508cc27 | ||
|
|
2ec2a891b4 | ||
|
|
b34628e9fc | ||
|
|
3fb4af53c8 | ||
|
|
41f37055ef | ||
|
|
aa925d8d54 | ||
|
|
c9cea86225 | ||
|
|
318842624f | ||
|
|
373327dc88 | ||
|
|
8333ee7ec4 | ||
|
|
97116175ab | ||
|
|
f770bb193e | ||
|
|
8cb8d10bc3 | ||
|
|
b298caf9ff | ||
|
|
a2022fbbac | ||
|
|
b3b57462ef | ||
|
|
95fc75cb23 | ||
|
|
c97eb6018b | ||
|
|
90fb7e7d8f | ||
|
|
2dbde9c31a | ||
|
|
4381ea447c | ||
|
|
69b17f1a00 | ||
|
|
8cf160d818 | ||
|
|
88d71d7070 | ||
|
|
84017660c1 |
@@ -4,8 +4,6 @@ PORTAL_RUNNER_SUBSCRIPTION=
|
||||
PORTAL_RUNNER_RESOURCE_GROUP=
|
||||
PORTAL_RUNNER_DATABASE_ACCOUNT=
|
||||
PORTAL_RUNNER_DATABASE_ACCOUNT_KEY=
|
||||
PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT=
|
||||
PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT_KEY=
|
||||
PORTAL_RUNNER_CONNECTION_STRING=
|
||||
NOTEBOOKS_TEST_RUNNER_TENANT_ID=
|
||||
NOTEBOOKS_TEST_RUNNER_CLIENT_ID=
|
||||
|
||||
@@ -11,9 +11,15 @@ src/Common/CosmosClient.test.ts
|
||||
src/Common/CosmosClient.ts
|
||||
src/Common/DataAccessUtilityBase.test.ts
|
||||
src/Common/DataAccessUtilityBase.ts
|
||||
src/Common/DeleteFeedback.ts
|
||||
src/Common/DocumentClientUtilityBase.ts
|
||||
src/Common/EditableUtility.ts
|
||||
src/Common/HashMap.test.ts
|
||||
src/Common/HashMap.ts
|
||||
src/Common/HeadersUtility.test.ts
|
||||
src/Common/HeadersUtility.ts
|
||||
src/Common/IteratorUtilities.test.ts
|
||||
src/Common/IteratorUtilities.ts
|
||||
src/Common/Logger.test.ts
|
||||
src/Common/MessageHandler.test.ts
|
||||
src/Common/MessageHandler.ts
|
||||
@@ -24,6 +30,7 @@ src/Common/ObjectCache.test.ts
|
||||
src/Common/ObjectCache.ts
|
||||
src/Common/QueriesClient.ts
|
||||
src/Common/Splitter.ts
|
||||
src/Common/ThemeUtility.ts
|
||||
src/Common/UrlUtility.ts
|
||||
src/Config.ts
|
||||
src/Contracts/ActionContracts.ts
|
||||
@@ -35,6 +42,7 @@ src/Contracts/ViewModels.ts
|
||||
src/Controls/Heatmap/Heatmap.test.ts
|
||||
src/Controls/Heatmap/Heatmap.ts
|
||||
src/Controls/Heatmap/HeatmapDatatypes.ts
|
||||
src/Definitions/adal.d.ts
|
||||
src/Definitions/datatables.d.ts
|
||||
src/Definitions/gif.d.ts
|
||||
src/Definitions/globals.d.ts
|
||||
@@ -51,6 +59,8 @@ src/Explorer/ComponentRegisterer.test.ts
|
||||
src/Explorer/ComponentRegisterer.ts
|
||||
src/Explorer/ContextMenuButtonFactory.ts
|
||||
src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts
|
||||
src/Explorer/Controls/CommandButton/CommandButton.test.ts
|
||||
src/Explorer/Controls/CommandButton/CommandButton.ts
|
||||
src/Explorer/Controls/DiffEditor/DiffEditorComponent.ts
|
||||
src/Explorer/Controls/DynamicList/DynamicList.test.ts
|
||||
src/Explorer/Controls/DynamicList/DynamicListComponent.ts
|
||||
@@ -78,7 +88,7 @@ src/Explorer/DataSamples/ContainerSampleGenerator.test.ts
|
||||
src/Explorer/DataSamples/ContainerSampleGenerator.ts
|
||||
src/Explorer/DataSamples/DataSamplesUtil.test.ts
|
||||
src/Explorer/DataSamples/DataSamplesUtil.ts
|
||||
src/Explorer/Explorer.tsx
|
||||
src/Explorer/Explorer.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.test.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.test.ts
|
||||
@@ -86,6 +96,8 @@ src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphData.test.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphData.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphUtil.test.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/GremlinClient.test.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/GremlinClient.ts
|
||||
src/Explorer/Graph/GraphExplorerComponent/GremlinSimpleClient.test.ts
|
||||
@@ -99,6 +111,7 @@ src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.ts
|
||||
src/Explorer/Menus/ContextMenu.ts
|
||||
src/Explorer/MostRecentActivity/MostRecentActivity.ts
|
||||
src/Explorer/Notebook/FileSystemUtil.ts
|
||||
src/Explorer/Notebook/NTeractUtil.ts
|
||||
src/Explorer/Notebook/NotebookClientV2.ts
|
||||
src/Explorer/Notebook/NotebookComponent/NotebookContentProvider.ts
|
||||
src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts
|
||||
@@ -150,7 +163,7 @@ src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts
|
||||
src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts
|
||||
src/Explorer/Panes/UploadFilePane.ts
|
||||
src/Explorer/Panes/UploadItemsPane.ts
|
||||
src/Explorer/SplashScreen/SplashScreen.test.ts
|
||||
src/Explorer/SplashScreen/SplashScreenComponentAdapter.test.ts
|
||||
src/Explorer/Tables/Constants.ts
|
||||
src/Explorer/Tables/DataTable/CacheBase.ts
|
||||
src/Explorer/Tables/DataTable/DataTableBindingManager.ts
|
||||
@@ -158,6 +171,7 @@ src/Explorer/Tables/DataTable/DataTableBuilder.ts
|
||||
src/Explorer/Tables/DataTable/DataTableContextMenu.ts
|
||||
src/Explorer/Tables/DataTable/DataTableOperationManager.ts
|
||||
src/Explorer/Tables/DataTable/DataTableOperations.ts
|
||||
src/Explorer/Tables/DataTable/DataTableUtilities.ts
|
||||
src/Explorer/Tables/DataTable/DataTableViewModel.ts
|
||||
src/Explorer/Tables/DataTable/TableCommands.ts
|
||||
src/Explorer/Tables/DataTable/TableEntityCache.ts
|
||||
@@ -166,6 +180,8 @@ src/Explorer/Tables/Entities.ts
|
||||
src/Explorer/Tables/QueryBuilder/ClauseGroup.ts
|
||||
src/Explorer/Tables/QueryBuilder/ClauseGroupViewModel.ts
|
||||
src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts
|
||||
src/Explorer/Tables/QueryBuilder/DateTimeUtilities.test.ts
|
||||
src/Explorer/Tables/QueryBuilder/DateTimeUtilities.ts
|
||||
src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts
|
||||
src/Explorer/Tables/QueryBuilder/QueryClauseViewModel.ts
|
||||
src/Explorer/Tables/QueryBuilder/QueryViewModel.ts
|
||||
@@ -185,6 +201,7 @@ src/Explorer/Tabs/QueryTab.test.ts
|
||||
src/Explorer/Tabs/QueryTab.ts
|
||||
src/Explorer/Tabs/QueryTablesTab.ts
|
||||
src/Explorer/Tabs/ScriptTabBase.ts
|
||||
src/Explorer/Tabs/SparkMasterTab.ts
|
||||
src/Explorer/Tabs/StoredProcedureTab.ts
|
||||
src/Explorer/Tabs/TabComponents.ts
|
||||
src/Explorer/Tabs/TabsBase.ts
|
||||
@@ -225,6 +242,9 @@ src/Platform/Hosted/Authorization.ts
|
||||
src/Platform/Hosted/DataAccessUtility.ts
|
||||
src/Platform/Hosted/ExplorerFactory.ts
|
||||
src/Platform/Hosted/Helpers/ConnectionStringParser.test.ts
|
||||
src/Platform/Hosted/Helpers/ConnectionStringParser.ts
|
||||
src/Platform/Hosted/HostedUtils.test.ts
|
||||
src/Platform/Hosted/HostedUtils.ts
|
||||
src/Platform/Hosted/Main.ts
|
||||
src/Platform/Hosted/Maint.test.ts
|
||||
src/Platform/Hosted/NotificationsClient.ts
|
||||
@@ -248,6 +268,8 @@ src/Shared/ExplorerSettings.ts
|
||||
src/Shared/PriceEstimateCalculator.ts
|
||||
src/Shared/StorageUtility.test.ts
|
||||
src/Shared/StorageUtility.ts
|
||||
src/Shared/StringUtility.test.ts
|
||||
src/Shared/StringUtility.ts
|
||||
src/Shared/appInsights.ts
|
||||
src/SparkClusterManager/ArcadiaResourceManager.ts
|
||||
src/SparkClusterManager/SparkClusterManager.ts
|
||||
@@ -256,14 +278,25 @@ src/Terminal/NotebookAppContracts.d.ts
|
||||
src/Terminal/index.ts
|
||||
src/TokenProviders/PortalTokenProvider.ts
|
||||
src/TokenProviders/TokenProviderFactory.ts
|
||||
src/Utils/AuthorizationUtils.test.ts
|
||||
src/Utils/AuthorizationUtils.ts
|
||||
src/Utils/AutoPilotUtils.test.ts
|
||||
src/Utils/AutoPilotUtils.ts
|
||||
src/Utils/DatabaseAccountUtils.test.ts
|
||||
src/Utils/DatabaseAccountUtils.ts
|
||||
src/Utils/JunoUtils.ts
|
||||
src/Utils/MessageValidation.ts
|
||||
src/Utils/NotebookConfigurationUtils.ts
|
||||
src/Utils/PricingUtils.test.ts
|
||||
src/Utils/QueryUtils.test.ts
|
||||
src/Utils/QueryUtils.ts
|
||||
src/Utils/StringUtils.test.ts
|
||||
src/Utils/StringUtils.ts
|
||||
src/applyExplorerBindings.ts
|
||||
src/global.d.ts
|
||||
src/quickstart.ts
|
||||
src/setupTests.ts
|
||||
src/workers/upload/definitions.ts
|
||||
src/workers/upload/index.ts
|
||||
src/Explorer/Controls/AccessibleElement/AccessibleElement.tsx
|
||||
src/Explorer/Controls/Accordion/AccordionComponent.tsx
|
||||
@@ -310,7 +343,15 @@ src/Explorer/Graph/GraphExplorerComponent/QueryContainerComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNeighborsComponent.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.tsx
|
||||
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx
|
||||
src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx
|
||||
src/Explorer/Menus/CommandBar/CommandBarUtil.test.tsx
|
||||
src/Explorer/Menus/CommandBar/CommandBarUtil.tsx
|
||||
src/Explorer/Menus/CommandBar/MemoryTrackerComponent.tsx
|
||||
src/Explorer/Menus/NavBar/ControlBarComponent.tsx
|
||||
src/Explorer/Menus/NavBar/ControlBarComponentAdapter.tsx
|
||||
src/Explorer/Menus/NavBar/MeControlComponent.test.tsx
|
||||
src/Explorer/Menus/NavBar/MeControlComponent.tsx
|
||||
src/Explorer/Menus/NavBar/MeControlComponentAdapter.tsx
|
||||
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.test.tsx
|
||||
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.tsx
|
||||
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponentAdapter.tsx
|
||||
@@ -340,7 +381,8 @@ src/Explorer/Notebook/temp/inputs/editor.tsx
|
||||
src/Explorer/Notebook/temp/markdown-cell.tsx
|
||||
src/Explorer/Notebook/temp/source.tsx
|
||||
src/Explorer/Notebook/temp/syntax-highlighter/index.tsx
|
||||
src/Explorer/SplashScreen/SplashScreen.tsx
|
||||
src/Explorer/SplashScreen/SplashScreenComponent.tsx
|
||||
src/Explorer/SplashScreen/SplashScreenComponentApdapter.tsx
|
||||
src/Explorer/Tabs/GalleryTab.tsx
|
||||
src/Explorer/Tabs/NotebookViewerTab.tsx
|
||||
src/Explorer/Tabs/TerminalTab.tsx
|
||||
|
||||
31
.eslintrc.js
31
.eslintrc.js
@@ -1,39 +1,41 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
es6: true
|
||||
},
|
||||
plugins: ["@typescript-eslint", "no-null", "prefer-arrow"],
|
||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
globals: {
|
||||
Atomics: "readonly",
|
||||
SharedArrayBuffer: "readonly",
|
||||
SharedArrayBuffer: "readonly"
|
||||
},
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
jsx: true
|
||||
},
|
||||
ecmaVersion: 2018,
|
||||
sourceType: "module",
|
||||
sourceType: "module"
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["**/*.tsx"],
|
||||
extends: ["plugin:react/recommended"], // TODO: Add react-hooks
|
||||
plugins: ["react"],
|
||||
env: {
|
||||
jest: true
|
||||
},
|
||||
extends: ["plugin:react/recommended"],
|
||||
plugins: ["react"]
|
||||
},
|
||||
{
|
||||
files: ["**/*.{test,spec}.{ts,tsx}"],
|
||||
env: {
|
||||
jest: true,
|
||||
jest: true
|
||||
},
|
||||
extends: ["plugin:jest/recommended"],
|
||||
plugins: ["jest"],
|
||||
},
|
||||
plugins: ["jest"]
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
"no-console": ["error", { allow: ["error", "warn", "dir"] }],
|
||||
curly: "error",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/no-extraneous-class": "error",
|
||||
@@ -41,13 +43,12 @@ module.exports = {
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }],
|
||||
eqeqeq: "error",
|
||||
"react/display-name": "off",
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.object.name='JSON'][callee.property.name='stringify'] Identifier[name=/$err/]",
|
||||
message: "Do not use JSON.stringify(error). It will print '{}'",
|
||||
},
|
||||
],
|
||||
},
|
||||
message: "Do not use JSON.stringify(error). It will print '{}'"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
154
.github/workflows/ci.yml
vendored
154
.github/workflows/ci.yml
vendored
@@ -9,29 +9,15 @@ on:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
codemetrics:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Log Code Metrics"
|
||||
if: github.ref == 'refs/heads/master'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: node utils/codeMetrics.js
|
||||
env:
|
||||
CODE_METRICS_APP_ID: ${{ secrets.CODE_METRICS_APP_ID }}
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Compile TypeScript"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
- name: Use Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 12.x
|
||||
- run: npm ci
|
||||
- run: npm run compile
|
||||
- run: npm run compile:strict
|
||||
@@ -40,10 +26,10 @@ jobs:
|
||||
name: "Check Format"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
- name: Use Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 12.x
|
||||
- run: npm ci
|
||||
- run: npm run format:check
|
||||
lint:
|
||||
@@ -51,10 +37,10 @@ jobs:
|
||||
name: "Lint"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
- name: Use Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 12.x
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
unittest:
|
||||
@@ -62,10 +48,10 @@ jobs:
|
||||
name: "Unit Tests"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
- name: Use Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 12.x
|
||||
- run: npm ci
|
||||
- run: npm run test
|
||||
build:
|
||||
@@ -74,10 +60,10 @@ jobs:
|
||||
name: "Build"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
- name: Use Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 12.x
|
||||
- run: npm ci
|
||||
- run: npm run build:contracts
|
||||
- name: Restore Build Cache
|
||||
@@ -94,21 +80,21 @@ jobs:
|
||||
path: dist/
|
||||
endtoendemulator:
|
||||
name: "End To End Emulator Tests"
|
||||
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
||||
needs: [lint, format, compile, unittest]
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
- name: Use Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 12.x
|
||||
- uses: southpolesteve/cosmos-emulator-github-action@v1
|
||||
- name: End to End Tests
|
||||
run: |
|
||||
npm ci
|
||||
npm start &
|
||||
npm run wait-for-server
|
||||
npx jest -c ./jest.config.e2e.js --detectOpenHandles test/sql/container.spec.ts
|
||||
npx jest -c ./jest.config.e2e.js --detectOpenHandles sql
|
||||
shell: bash
|
||||
env:
|
||||
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/explorer.html?platform=Emulator"
|
||||
@@ -125,10 +111,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
- name: Use Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 12.x
|
||||
- name: Accessibility Check
|
||||
run: |
|
||||
# Ubuntu gets mad when webpack runs too many files watchers
|
||||
@@ -143,72 +129,45 @@ jobs:
|
||||
env:
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||
endtoendhosted:
|
||||
name: "End to End Tests"
|
||||
needs: [cleanupaccounts]
|
||||
name: "End to End Hosted Tests"
|
||||
needs: [lint, format, compile, unittest]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||
PORTAL_RUNNER_SUBSCRIPTION: ${{ secrets.PORTAL_RUNNER_SUBSCRIPTION }}
|
||||
PORTAL_RUNNER_RESOURCE_GROUP: ${{ secrets.PORTAL_RUNNER_RESOURCE_GROUP }}
|
||||
PORTAL_RUNNER_DATABASE_ACCOUNT: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT }}
|
||||
PORTAL_RUNNER_DATABASE_ACCOUNT_KEY: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT_KEY }}
|
||||
PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT: ${{ secrets.PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT }}
|
||||
PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT_KEY: ${{ secrets.PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT_KEY }}
|
||||
NOTEBOOKS_TEST_RUNNER_TENANT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_TENANT_ID }}
|
||||
NOTEBOOKS_TEST_RUNNER_CLIENT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_ID }}
|
||||
NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }}
|
||||
PORTAL_RUNNER_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
|
||||
MONGO_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
|
||||
CASSANDRA_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_CASSANDRA }}
|
||||
TABLES_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_TABLE }}
|
||||
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/hostedExplorer.html"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test-file:
|
||||
- ./test/cassandra/container.spec.ts
|
||||
- ./test/mongo/mongoIndexPolicy.spec.ts
|
||||
- ./test/notebooks/uploadAndOpenNotebook.spec.ts
|
||||
- ./test/selfServe/selfServeExample.spec.ts
|
||||
- ./test/sql/container.spec.ts
|
||||
- ./test/sql/resourceToken.spec.ts
|
||||
- ./test/tables/container.spec.ts
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
- name: Use Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm start &
|
||||
- run: node utils/cleanupDBs.js
|
||||
- run: npm run wait-for-server
|
||||
- name: ${{ matrix['test-file'] }}
|
||||
run: npx jest -c ./jest.config.e2e.js --detectOpenHandles ${{ matrix['test-file'] }}
|
||||
node-version: 12.x
|
||||
- name: End to End Hosted Tests
|
||||
run: |
|
||||
npm ci
|
||||
npm start &
|
||||
npm run wait-for-server
|
||||
npm run test:e2e
|
||||
shell: bash
|
||||
env:
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||
PORTAL_RUNNER_SUBSCRIPTION: ${{ secrets.PORTAL_RUNNER_SUBSCRIPTION }}
|
||||
PORTAL_RUNNER_RESOURCE_GROUP: ${{ secrets.PORTAL_RUNNER_RESOURCE_GROUP }}
|
||||
PORTAL_RUNNER_DATABASE_ACCOUNT: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT }}
|
||||
PORTAL_RUNNER_DATABASE_ACCOUNT_KEY: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT_KEY }}
|
||||
NOTEBOOKS_TEST_RUNNER_TENANT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_TENANT_ID }}
|
||||
NOTEBOOKS_TEST_RUNNER_CLIENT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_ID }}
|
||||
NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }}
|
||||
PORTAL_RUNNER_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
|
||||
MONGO_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
|
||||
CASSANDRA_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_CASSANDRA }}
|
||||
TABLES_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_TABLE }}
|
||||
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/hostedExplorer.html"
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: screenshots
|
||||
path: failed-*
|
||||
cleanupaccounts:
|
||||
name: "Cleanup Test Database Accounts"
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NOTEBOOKS_TEST_RUNNER_CLIENT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_ID }}
|
||||
NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: node utils/cleanupDBs.js
|
||||
nuget:
|
||||
name: Publish Nuget
|
||||
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
||||
needs: [build]
|
||||
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted, accessibility]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
|
||||
@@ -224,7 +183,7 @@ jobs:
|
||||
- run: cp ./configs/prod.json config.json
|
||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
|
||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||
- uses: actions/upload-artifact@v2
|
||||
name: packages
|
||||
with:
|
||||
@@ -232,7 +191,7 @@ jobs:
|
||||
nugetmpac:
|
||||
name: Publish Nuget MPAC
|
||||
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
||||
needs: [build]
|
||||
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted, accessibility]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
|
||||
@@ -249,7 +208,32 @@ jobs:
|
||||
- run: sed -i 's/Azure.Cosmos.DB.Data.Explorer/Azure.Cosmos.DB.Data.Explorer.MPAC/g' DataExplorer.nuspec
|
||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
|
||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||
- uses: actions/upload-artifact@v2
|
||||
name: packages
|
||||
with:
|
||||
path: "*.nupkg"
|
||||
nugetie:
|
||||
name: Publish Nuget IE
|
||||
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
||||
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted, accessibility]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
|
||||
AZURE_DEVOPS_PAT: ${{ secrets.AZURE_DEVOPS_PAT }}
|
||||
steps:
|
||||
- uses: nuget/setup-nuget@v1
|
||||
with:
|
||||
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
|
||||
- name: Download Dist Folder
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: dist
|
||||
- run: cp ./configs/prod.json config.json
|
||||
- run: sed -i 's/Azure.Cosmos.DB.Data.Explorer/Azure.Cosmos.DB.Data.Explorer.IE/g' DataExplorer.nuspec
|
||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
|
||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||
- uses: actions/upload-artifact@v2
|
||||
name: packages
|
||||
with:
|
||||
|
||||
43
.vscode/settings.json
vendored
43
.vscode/settings.json
vendored
@@ -1,26 +1,21 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
".vs": true,
|
||||
".vscode/**": true,
|
||||
"*.trx": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/.git": true,
|
||||
"**/.hg": true,
|
||||
"**/.svn": true,
|
||||
"built/**": true,
|
||||
"coverage/**": true,
|
||||
"libs/**": true,
|
||||
"node_modules/**": true,
|
||||
"package-lock.json": true,
|
||||
"quickstart/**": true,
|
||||
"test/out/**": true,
|
||||
"workers/libs/**": true
|
||||
},
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true,
|
||||
"source.organizeImports": true
|
||||
}
|
||||
}
|
||||
"files.exclude": {
|
||||
".vs": true,
|
||||
".vscode/**": true,
|
||||
"*.trx": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/.git": true,
|
||||
"**/.hg": true,
|
||||
"**/.svn": true,
|
||||
"built/**": true,
|
||||
"coverage/**": true,
|
||||
"libs/**": true,
|
||||
"node_modules/**": true,
|
||||
"package-lock.json": true,
|
||||
"quickstart/**": true,
|
||||
"test/out/**": true,
|
||||
"workers/libs/**": true
|
||||
},
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
# Coding Guidelines and Recommendations
|
||||
|
||||
Cosmos Explorer has been under constant development for over 5 years. As a result, there are many different patterns and practices in the codebase. This document serves as a guide to how we write code and helps avoid propagating practices which are no longer preferred. Each requirement in this document is labeled and color-coded to show the relative importance. In order from highest to lowest importance:
|
||||
|
||||
✅ DO this. If you feel you need an exception, engage with the project owners _prior_ to implementation.
|
||||
|
||||
⛔️ DO NOT do this. If you feel you need an exception, engage with the project owners _prior_ to implementation.
|
||||
|
||||
☑️ YOU SHOULD strongly consider this but it is not a requirement. If not following this advice, please comment code with why and proactively begin a discussion as part of the PR process.
|
||||
|
||||
⚠️ YOU SHOULD NOT strongly consider not doing this. If not following this advice, please comment code with why and proactively begin a discussion as part of the PR process.
|
||||
|
||||
💭 YOU MAY consider this advice if appropriate to your situation. Other team members may comment on this as part of PR review, but there is no need to be proactive.
|
||||
|
||||
## Development Environment
|
||||
|
||||
☑️ YOU SHOULD
|
||||
|
||||
- Use VSCode and install the following extensions. This setup will catch most linting/formatting/type errors as you develop:
|
||||
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
|
||||
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
|
||||
|
||||
💭 YOU MAY
|
||||
|
||||
- Use the [GitHub CLI](https://cli.github.com/). It has helpful workflows for submitting PRs as well as for checking out other team member's PRs.
|
||||
- Use Windows, Linux (including WSL), or OSX. We have team members developing on all three environments.
|
||||
|
||||
✅ DO
|
||||
|
||||
- Maintain cross-platform compatibility when modifying any engineering or build systems
|
||||
|
||||
## Code Formatting
|
||||
|
||||
✅ DO
|
||||
|
||||
- Use [Prettier](https://prettier.io/) to format your code
|
||||
- This will occur automatically if using the recommended editor setup
|
||||
- `npm run format` will also format code
|
||||
|
||||
## Linting
|
||||
|
||||
✅ DO
|
||||
|
||||
- Use [ESLint](https://eslint.org/) to check for code errors.
|
||||
- This will occur automatically if using the recommended editor setup
|
||||
- `npm run lint` will also check for linting errors
|
||||
|
||||
💭 YOU MAY
|
||||
|
||||
- Consider adding new lint rules.
|
||||
- If you find yourself performing "nits" as part of PR review, consider adding a lint rule that will automatically catch the error in the future
|
||||
|
||||
⚠️ YOU SHOULD NOT
|
||||
|
||||
- Disable lint rules
|
||||
- Lint rules exist as guidance and to catch common mistakes
|
||||
- You will find places we disable specific lint rules however it should be exceptional.
|
||||
- If a rule does need to be disabled, prefer disabling a specific line instead of the entire file.
|
||||
|
||||
⛔️ DO NOT
|
||||
|
||||
- Add [TSLint](https://palantir.github.io/tslint/) rules
|
||||
- TSLint has been deprecated and is on track to be removed
|
||||
- Always prefer ESLint rules
|
||||
|
||||
## UI Components
|
||||
|
||||
☑️ YOU SHOULD
|
||||
|
||||
- Write new components using [React](https://reactjs.org/). We are actively migrating Cosmos Explorer off of [Knockout](https://knockoutjs.com/).
|
||||
- Use [Fluent](https://developer.microsoft.com/en-us/fluentui#/) components.
|
||||
- Fluent components are designed to be highly accessible and composable
|
||||
- Using Fluent allows us to build upon the work of the Fluent team and leads to a lower total cost of ownership for UI code
|
||||
|
||||
### React
|
||||
|
||||
☑️ YOU SHOULD
|
||||
|
||||
- Use pure functional components when no state is required
|
||||
|
||||
💭 YOU MAY
|
||||
|
||||
- Use functional (hooks) or class components
|
||||
- The project contains examples of both
|
||||
- Neither is strongly preferred at this time
|
||||
|
||||
⛔️ DO NOT
|
||||
|
||||
- Use inheritance for sharing component behavior.
|
||||
- React documentation covers this topic in detail https://reactjs.org/docs/composition-vs-inheritance.html
|
||||
- Suffix your file or component name with "Component"
|
||||
- Even though the code has examples of it, we are ending the practice.
|
||||
|
||||
## Libraries
|
||||
|
||||
⚠️ YOU SHOULD NOT
|
||||
|
||||
- Add new libraries to package.json.
|
||||
- Adding libraries may bring in code that explodes the bundled size or attempts to run NodeJS code in the browser
|
||||
- Consult with project owners for help with library selection if one is needed
|
||||
|
||||
⛔️ DO NOT
|
||||
|
||||
- Use underscore.js
|
||||
- Much of this library is now native to JS and will be automatically transpiled
|
||||
- Use jQuery
|
||||
- Much of this library is not native to the DOM.
|
||||
- We are planning to remove it
|
||||
|
||||
## Testing
|
||||
|
||||
⛔️ DO NOT
|
||||
|
||||
- Decrease test coverage
|
||||
- Unit/Functional test coverage is checked as part of the CI process
|
||||
|
||||
### Unit Tests
|
||||
|
||||
✅ DO
|
||||
|
||||
- Write unit tests for non-UI and utility code.
|
||||
- Write your tests using [Jest](https://jestjs.io/)
|
||||
|
||||
☑️ YOU SHOULD
|
||||
|
||||
- Abstract non-UI and utility code so it can run either the NodeJS or Browser environment
|
||||
|
||||
### Functional(Component) Tests
|
||||
|
||||
✅ DO
|
||||
|
||||
- Write tests for UI components
|
||||
- Write your tests using [Jest](https://jestjs.io/)
|
||||
- Use either Enzyme or React Testing Library to perform component tests.
|
||||
|
||||
### Mocking
|
||||
|
||||
✅ DO
|
||||
|
||||
- Use Jest's built-in mocking helpers
|
||||
|
||||
☑️ YOU SHOULD
|
||||
|
||||
- Write code that does not require mocking
|
||||
- Build components that do not require mocking extremely large or difficult to mock objects (like Explorer.ts). Pass _only_ what you need.
|
||||
|
||||
⛔️ DO NOT
|
||||
|
||||
- Use sinon.js for mocking
|
||||
- Sinon has been deprecated and planned for removal
|
||||
|
||||
### End to End Tests
|
||||
|
||||
✅ DO
|
||||
|
||||
- Use [Puppeteer](https://developers.google.com/web/tools/puppeteer) and [Jest](https://jestjs.io/)
|
||||
- Write or modify an existing E2E test that covers the primary use case of any major feature.
|
||||
- Use caution. Do not try to cover every case. End to End tests can be slow and brittle.
|
||||
|
||||
☑️ YOU SHOULD
|
||||
|
||||
- Write tests that use accessible attributes to perform actions. Role, Title, Label, etc
|
||||
- More information https://testing-library.com/docs/queries/about#priority
|
||||
|
||||
⚠️ YOU SHOULD NOT
|
||||
|
||||
- Add test specfic `data-*` attributes to dom elements
|
||||
- This is a common current practice, but one we would like to avoid in the future
|
||||
- End to end tests need to use semantic HTML and accesible attributes to be truely end to end
|
||||
- No user or screen reader actually navigates an app using `data-*` attributes
|
||||
- Add arbitrary time delays to wait for page to render or element to be ready.
|
||||
- All the time delays add up and slow down testing.
|
||||
- Prefer using the framework's "wait for..." functionality.
|
||||
|
||||
### Migrating Knockout to React
|
||||
|
||||
✅ DO
|
||||
|
||||
- Consult other team members before beginning migration work. There is a significant amount of flux in patterns we are using and it is important we do not propagate incorrect patterns.
|
||||
- Start by converting HTML to JSX: https://magic.reactjs.net/htmltojsx.htm. Add functionality as a second step.
|
||||
|
||||
☑️ YOU SHOULD
|
||||
|
||||
- Write React components that require no dependency on Knockout or observables to trigger rendering.
|
||||
|
||||
## Browser Support
|
||||
|
||||
✅ DO
|
||||
|
||||
- Support all [browsers supported by the Azure Portal](https://docs.microsoft.com/en-us/azure/azure-portal/azure-portal-supported-browsers-devices)
|
||||
- Support IE11
|
||||
- In practice, this should not need to be considered as part of a normal development workflow
|
||||
- Polyfills and transpilation are already provided by our engineering systems.
|
||||
- This requirement will be removed on March 30th, 2021 when Azure drops IE11 support.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Contribution guidelines to Data Explorer
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.microsoft.com.
|
||||
|
||||
@@ -13,7 +13,6 @@ For more information see the [Code of Conduct FAQ](https://opensource.microsoft.
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Microsoft Open Source Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
|
||||
Resources:
|
||||
@@ -21,3 +20,33 @@ Resources:
|
||||
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
||||
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
||||
|
||||
## Browser support
|
||||
Please make sure to support all modern browsers as well as Internet Explorer 11.
|
||||
For IE support, polyfill is preferred over new usage of lodash or underscore. We already polyfill almost everything by importing babel-polyfill at the top of entry points.
|
||||
|
||||
|
||||
## Coding guidelines, conventions and recommendations
|
||||
### Typescript
|
||||
* Follow this [typescript style guide](https://github.com/excelmicro/typescript) which is based on [airbnb's style guide](https://github.com/airbnb/javascript).
|
||||
* Conventions speficic to this project:
|
||||
- Use double-quotes for string
|
||||
- Don't use `null`, use `undefined`
|
||||
- Pascal case for private static readonly fields
|
||||
- Camel case for classnames in markup
|
||||
* Don't use class unless necessary
|
||||
* Code related to notebooks should be dynamically imported so that it is loaded from a separate bundle only if the account is notebook-enabled. There are already top-level notebook components which are dynamically imported and their dependencies can be statically imported from these files.
|
||||
* Prefer using [Fluent UI controls](https://developer.microsoft.com/en-us/fluentui#/controls/web) over creating your own, in order to maintain consistency and support a11y.
|
||||
|
||||
### React
|
||||
* Prefer using React class components over function components and hooks unless you have a simple component and require no nested functions:
|
||||
* Nested functions may be harder to test independently
|
||||
* Switching from function component to class component later mayb be painful
|
||||
|
||||
## Testing
|
||||
Any PR should not decrease testing coverage.
|
||||
|
||||
## Recommended Tools and VS Code extensions
|
||||
* [Bookmarks](https://github.com/alefragnani/vscode-bookmarks)
|
||||
* [Bracket pair colorizer](https://github.com/CoenraadS/Bracket-Pair-Colorizer-2)
|
||||
* [GitHub Pull Requests and Issues](https://github.com/Microsoft/vscode-pull-request-github)
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"],
|
||||
plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
|
||||
plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]]
|
||||
};
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"GITHUB_CLIENT_ID": "167ea4b09801db1de03d",
|
||||
"GITHUB_CLIENT_SECRET": "e7bb10a3a8da428815805c6fc483560a035a73c1"
|
||||
}
|
||||
1963
externals/adal.js
vendored
Normal file
1963
externals/adal.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.31449 2.01439L4.00103 5.31963L3.26105 4.57965L7.8407 0L12.4203 4.57965L11.6804 5.31963L8.36691 2.01439V12.8428H7.31449V2.01439ZM13.629 12.8428H14.6814V16H1V12.8428H2.05242V14.9476H13.629V12.8428Z" fill="#0078D4"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 329 B |
@@ -6,6 +6,6 @@ module.exports = {
|
||||
slowMo: 55,
|
||||
defaultViewport: null,
|
||||
ignoreHTTPSErrors: true,
|
||||
args: ["--disable-web-security"],
|
||||
},
|
||||
args: ["--disable-web-security"]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
preset: "jest-puppeteer",
|
||||
testMatch: ["<rootDir>/test/**/*.spec.[jt]s?(x)"],
|
||||
setupFiles: ["dotenv/config"],
|
||||
setupFiles: ["dotenv/config"]
|
||||
};
|
||||
|
||||
@@ -39,11 +39,11 @@ module.exports = {
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 22,
|
||||
functions: 28,
|
||||
lines: 33,
|
||||
statements: 31,
|
||||
},
|
||||
branches: 20,
|
||||
functions: 24,
|
||||
lines: 30,
|
||||
statements: 29.0
|
||||
}
|
||||
},
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
@@ -76,7 +76,7 @@ module.exports = {
|
||||
"office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes
|
||||
"^dnd-core$": "dnd-core/dist/cjs",
|
||||
"^react-dnd$": "react-dnd/dist/cjs",
|
||||
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs",
|
||||
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs"
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
@@ -164,11 +164,11 @@ module.exports = {
|
||||
// A map from regular expressions to paths to transformers
|
||||
transform: {
|
||||
"^.+\\.html?$": "html-loader-jest",
|
||||
"^.+\\.[t|j]sx?$": "babel-jest",
|
||||
"^.+\\.[t|j]sx?$": "babel-jest"
|
||||
},
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
transformIgnorePatterns: ["/node_modules/", "/externals/"],
|
||||
transformIgnorePatterns: ["/node_modules/", "/externals/"]
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
@@ -57,13 +57,6 @@
|
||||
|
||||
@FocusColor: #605e5c;
|
||||
|
||||
@GalleryBackgroundColor: #fdfdfd;
|
||||
|
||||
//Icons
|
||||
@InfoIconColor: #0072c6;
|
||||
@WarningIconColor: #db7500;
|
||||
@ErrorIconColor: #b91f26;
|
||||
|
||||
/******************************************************************************
|
||||
METRICS
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -1523,6 +1523,10 @@ p {
|
||||
.tooltipVisible();
|
||||
}
|
||||
|
||||
.infoTooltip a {
|
||||
color: @AccentHigh;
|
||||
}
|
||||
|
||||
.inputTooltip {
|
||||
.inputTooltip();
|
||||
}
|
||||
@@ -1538,10 +1542,6 @@ p {
|
||||
.inputTooltipTextAfter();
|
||||
}
|
||||
|
||||
.infoTooltip a {
|
||||
color: @AccentHigh;
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -1709,7 +1709,6 @@ input::-webkit-calendar-picker-indicator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.contextual-pane .paneErrorDetailsContainer {
|
||||
@@ -2099,7 +2098,7 @@ a:link {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
overflow-y: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -3045,11 +3044,11 @@ settings-pane {
|
||||
}
|
||||
|
||||
.messageBarInfoIcon {
|
||||
color: @InfoIconColor;
|
||||
color: #0072c6;
|
||||
}
|
||||
|
||||
.messageBarWarningIcon {
|
||||
color: @WarningIconColor;
|
||||
color: #db7500;
|
||||
}
|
||||
|
||||
.freeTierInfoBanner {
|
||||
|
||||
5027
package-lock.json
generated
5027
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@@ -7,14 +7,13 @@
|
||||
"@azure/arm-cosmosdb": "9.1.0",
|
||||
"@azure/cosmos": "3.9.0",
|
||||
"@azure/cosmos-language-service": "0.0.5",
|
||||
"@azure/identity": "1.2.1",
|
||||
"@azure/ms-rest-nodeauth": "3.0.7",
|
||||
"@azure/identity": "1.1.0",
|
||||
"@babel/plugin-proposal-class-properties": "7.12.1",
|
||||
"@babel/plugin-proposal-decorators": "7.12.12",
|
||||
"@jupyterlab/services": "6.0.2",
|
||||
"@jupyterlab/terminal": "3.0.3",
|
||||
"@jupyterlab/services": "6.0.0-rc.2",
|
||||
"@jupyterlab/terminal": "3.0.0-rc.2",
|
||||
"@microsoft/applicationinsights-web": "2.5.9",
|
||||
"@nteract/commutable": "7.4.2",
|
||||
"@nteract/commutable": "7.3.2",
|
||||
"@nteract/connected-components": "6.8.2",
|
||||
"@nteract/core": "15.1.0",
|
||||
"@nteract/data-explorer": "8.0.3",
|
||||
@@ -39,7 +38,6 @@
|
||||
"@nteract/transform-vega": "7.0.6",
|
||||
"@octokit/rest": "17.9.2",
|
||||
"@phosphor/widgets": "1.9.3",
|
||||
"@testing-library/jest-dom": "5.11.9",
|
||||
"@types/mkdirp": "1.0.1",
|
||||
"@types/node-fetch": "2.5.7",
|
||||
"@uifabric/react-cards": "0.109.110",
|
||||
@@ -50,7 +48,6 @@
|
||||
"bootstrap": "3.4.1",
|
||||
"canvas": "file:./canvas",
|
||||
"clean-webpack-plugin": "0.1.19",
|
||||
"clipboard-copy": "4.0.1",
|
||||
"copy-webpack-plugin": "6.0.2",
|
||||
"crossroads": "0.12.2",
|
||||
"css-element-queries": "1.1.1",
|
||||
@@ -66,9 +63,6 @@
|
||||
"eslint-plugin-react": "7.20.0",
|
||||
"hasher": "1.2.0",
|
||||
"html2canvas": "1.0.0-rc.5",
|
||||
"i18next": "19.8.4",
|
||||
"i18next-browser-languagedetector": "6.0.1",
|
||||
"i18next-http-backend": "1.0.23",
|
||||
"immutable": "4.0.0-rc.12",
|
||||
"is-ci": "2.0.0",
|
||||
"jquery": "3.5.1",
|
||||
@@ -77,8 +71,6 @@
|
||||
"knockout": "3.5.1",
|
||||
"mkdirp": "1.0.4",
|
||||
"monaco-editor": "0.18.1",
|
||||
"ms": "2.1.3",
|
||||
"msal": "1.4.4",
|
||||
"object.entries": "1.1.0",
|
||||
"office-ui-fabric-react": "7.134.1",
|
||||
"p-retry": "4.2.0",
|
||||
@@ -90,9 +82,8 @@
|
||||
"react-animate-height": "2.0.8",
|
||||
"react-dnd": "9.4.0",
|
||||
"react-dnd-html5-backend": "9.4.0",
|
||||
"react-dom": "16.13.1",
|
||||
"react-dom": "16.9.0",
|
||||
"react-hotkeys": "2.0.0",
|
||||
"react-i18next": "11.8.5",
|
||||
"react-notification-system": "0.2.17",
|
||||
"react-redux": "7.1.3",
|
||||
"redux": "4.0.4",
|
||||
@@ -100,7 +91,6 @@
|
||||
"rx-jupyter": "5.5.12",
|
||||
"rxjs": "6.6.3",
|
||||
"styled-components": "4.3.2",
|
||||
"swr": "0.4.0",
|
||||
"text-encoding": "0.7.0",
|
||||
"underscore": "1.9.1",
|
||||
"url-polyfill": "1.1.7",
|
||||
@@ -114,7 +104,6 @@
|
||||
"@babel/preset-env": "7.9.0",
|
||||
"@babel/preset-react": "7.9.4",
|
||||
"@babel/preset-typescript": "7.9.0",
|
||||
"@testing-library/react": "11.2.3",
|
||||
"@types/applicationinsights-js": "1.0.7",
|
||||
"@types/codemirror": "0.0.56",
|
||||
"@types/crossroads": "0.0.30",
|
||||
@@ -123,7 +112,7 @@
|
||||
"@types/enzyme-adapter-react-16": "1.0.6",
|
||||
"@types/expect-puppeteer": "4.4.3",
|
||||
"@types/hasher": "0.0.31",
|
||||
"@types/jest": "26.0.20",
|
||||
"@types/jest": "23.3.10",
|
||||
"@types/jest-environment-puppeteer": "4.3.2",
|
||||
"@types/memoize-one": "4.1.1",
|
||||
"@types/node": "12.11.1",
|
||||
@@ -131,8 +120,8 @@
|
||||
"@types/prop-types": "15.5.8",
|
||||
"@types/puppeteer": "3.0.1",
|
||||
"@types/q": "1.5.1",
|
||||
"@types/react": "17.0.0",
|
||||
"@types/react-dom": "17.0.0",
|
||||
"@types/react": "16.9.56",
|
||||
"@types/react-dom": "16.0.7",
|
||||
"@types/react-notification-system": "0.2.39",
|
||||
"@types/react-redux": "7.1.7",
|
||||
"@types/sinon": "2.3.3",
|
||||
@@ -142,6 +131,7 @@
|
||||
"@types/webfontloader": "1.6.29",
|
||||
"@typescript-eslint/eslint-plugin": "4.0.1",
|
||||
"@typescript-eslint/parser": "4.0.1",
|
||||
"adal-angular": "1.0.15",
|
||||
"axe-puppeteer": "1.1.0",
|
||||
"babel-jest": "24.9.0",
|
||||
"babel-loader": "8.1.0",
|
||||
@@ -156,9 +146,7 @@
|
||||
"eslint-cli": "1.1.1",
|
||||
"eslint-plugin-no-null": "1.0.2",
|
||||
"eslint-plugin-prefer-arrow": "1.2.2",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"expose-loader": "0.7.5",
|
||||
"fast-glob": "3.2.5",
|
||||
"file-loader": "2.0.0",
|
||||
"fs-extra": "7.0.0",
|
||||
"html-loader": "0.5.5",
|
||||
@@ -175,7 +163,7 @@
|
||||
"mini-css-extract-plugin": "0.4.3",
|
||||
"monaco-editor-webpack-plugin": "1.7.0",
|
||||
"node-fetch": "2.6.1",
|
||||
"prettier": "2.2.1",
|
||||
"prettier": "1.19.1",
|
||||
"puppeteer": "4.0.0",
|
||||
"raw-loader": "0.5.1",
|
||||
"rimraf": "3.0.0",
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"offerThroughput": 400,
|
||||
"databaseLevelThroughput": false,
|
||||
"collectionId": "Persons",
|
||||
"createNewDatabase": true,
|
||||
"partitionKey": { "kind": "Hash", "paths": ["/name"] },
|
||||
"data": [
|
||||
"g.addV('person').property(id, '1').property('name', 'Eva').property('age', 44)",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export enum AuthType {
|
||||
AAD = "aad",
|
||||
EncryptedToken = "encryptedtoken",
|
||||
MasterKey = "masterkey",
|
||||
ResourceToken = "resourcetoken",
|
||||
ConnectionString = "connectionstring",
|
||||
}
|
||||
export enum AuthType {
|
||||
AAD = "aad",
|
||||
EncryptedToken = "encryptedtoken",
|
||||
MasterKey = "masterkey",
|
||||
ResourceToken = "resourcetoken"
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import * as ko from "knockout";
|
||||
import * as ReactBindingHandler from "./ReactBindingHandler";
|
||||
import "../Explorer/Tables/DataTable/DataTableBindingManager";
|
||||
|
||||
export class BindingHandlersRegisterer {
|
||||
public static registerBindingHandlers() {
|
||||
ko.bindingHandlers.setTemplateReady = {
|
||||
init(
|
||||
element: any,
|
||||
wrappedValueAccessor: () => any,
|
||||
allBindings?: ko.AllBindings,
|
||||
viewModel?: any,
|
||||
bindingContext?: ko.BindingContext
|
||||
) {
|
||||
const value = ko.unwrap(wrappedValueAccessor());
|
||||
bindingContext?.$data.isTemplateReady(value);
|
||||
},
|
||||
} as ko.BindingHandler;
|
||||
|
||||
ReactBindingHandler.Registerer.register();
|
||||
}
|
||||
}
|
||||
import * as ko from "knockout";
|
||||
import * as ReactBindingHandler from "./ReactBindingHandler";
|
||||
|
||||
export class BindingHandlersRegisterer {
|
||||
public static registerBindingHandlers() {
|
||||
ko.bindingHandlers.setTemplateReady = {
|
||||
init(
|
||||
element: any,
|
||||
wrappedValueAccessor: () => any,
|
||||
allBindings?: ko.AllBindings,
|
||||
viewModel?: any,
|
||||
bindingContext?: ko.BindingContext
|
||||
) {
|
||||
const value = ko.unwrap(wrappedValueAccessor());
|
||||
bindingContext?.$data.isTemplateReady(value);
|
||||
}
|
||||
} as ko.BindingHandler;
|
||||
|
||||
ReactBindingHandler.Registerer.register();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export class Registerer {
|
||||
|
||||
// Initial rendering at mount point
|
||||
ReactDOM.render(adapter.renderComponent(), element);
|
||||
},
|
||||
}
|
||||
} as ko.BindingHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export class ArrayHashMap<T> {
|
||||
public forEach(key: string, iteratorFct: (value: T) => void) {
|
||||
const values = this.store.get(key);
|
||||
if (values) {
|
||||
values.forEach((value) => iteratorFct(value));
|
||||
values.forEach(value => iteratorFct(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import { HashMap } from "./HashMap";
|
||||
|
||||
export class AuthorizationEndpoints {
|
||||
public static arm: string = "https://management.core.windows.net/";
|
||||
public static common: string = "https://login.windows.net/";
|
||||
}
|
||||
|
||||
export class CodeOfConductEndpoints {
|
||||
public static privacyStatement: string = "https://aka.ms/ms-privacy-policy";
|
||||
public static codeOfConduct: string = "https://aka.ms/cosmos-code-of-conduct";
|
||||
@@ -7,7 +14,7 @@ export class CodeOfConductEndpoints {
|
||||
export class EndpointsRegex {
|
||||
public static readonly cassandra = [
|
||||
"AccountEndpoint=(.*).cassandra.cosmosdb.azure.com",
|
||||
"HostName=(.*).cassandra.cosmos.azure.com",
|
||||
"HostName=(.*).cassandra.cosmos.azure.com"
|
||||
];
|
||||
public static readonly mongo = "mongodb://.*:(.*)@(.*).documents.azure.com";
|
||||
public static readonly mongoCompute = "mongodb://.*:(.*)@(.*).mongo.cosmos.azure.com";
|
||||
@@ -105,6 +112,8 @@ export class Features {
|
||||
public static readonly hostedDataExplorer = "hosteddataexplorerenabled";
|
||||
public static readonly enableTtl = "enablettl";
|
||||
public static readonly enableNotebooks = "enablenotebooks";
|
||||
public static readonly enableGalleryPublish = "enablegallerypublish";
|
||||
public static readonly enableLinkInjection = "enablelinkinjection";
|
||||
public static readonly enableSpark = "enablespark";
|
||||
public static readonly livyEndpoint = "livyendpoint";
|
||||
public static readonly notebookServerUrl = "notebookserverurl";
|
||||
@@ -117,17 +126,15 @@ export class Features {
|
||||
public static readonly enableSchema = "enableschema";
|
||||
public static readonly enableSDKoperations = "enablesdkoperations";
|
||||
public static readonly showMinRUSurvey = "showminrusurvey";
|
||||
public static readonly enableDatabaseSettingsTabV1 = "enabledbsettingsv1";
|
||||
public static readonly selfServeType = "selfservetype";
|
||||
public static readonly enableKOPanel = "enablekopanel";
|
||||
}
|
||||
|
||||
// flight names returned from the portal are always lowercase
|
||||
export class Flights {
|
||||
public static readonly SettingsV2 = "settingsv2";
|
||||
public static readonly MongoIndexEditor = "mongoindexeditor";
|
||||
public static readonly MongoIndexing = "mongoindexing";
|
||||
public static readonly AutoscaleTest = "autoscaletest";
|
||||
public static readonly MongoIndexing = "mongoindexing";
|
||||
}
|
||||
|
||||
export class AfecFeatures {
|
||||
@@ -136,6 +143,19 @@ export class AfecFeatures {
|
||||
public static readonly StorageAnalytics = "storageanalytics-public-preview";
|
||||
}
|
||||
|
||||
export class Spark {
|
||||
public static readonly MaxWorkerCount = 10;
|
||||
public static readonly SKUs: HashMap<string> = new HashMap({
|
||||
"Cosmos.Spark.D1s": "D1s / 1 core / 4GB RAM",
|
||||
"Cosmos.Spark.D2s": "D2s / 2 cores / 8GB RAM",
|
||||
"Cosmos.Spark.D4s": "D4s / 4 cores / 16GB RAM",
|
||||
"Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM",
|
||||
"Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM",
|
||||
"Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM",
|
||||
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM"
|
||||
});
|
||||
}
|
||||
|
||||
export class TagNames {
|
||||
public static defaultExperience: string = "defaultExperience";
|
||||
}
|
||||
@@ -147,7 +167,7 @@ export class MongoDBAccounts {
|
||||
|
||||
export enum MongoBackendEndpointType {
|
||||
local,
|
||||
remote,
|
||||
remote
|
||||
}
|
||||
|
||||
// TODO: 435619 Add default endpoints per cloud and use regional only when available
|
||||
@@ -274,7 +294,7 @@ export class HttpStatusCodes {
|
||||
HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
|
||||
HttpStatusCodes.BadGateway,
|
||||
HttpStatusCodes.ServiceUnavailable,
|
||||
HttpStatusCodes.GatewayTimeout,
|
||||
HttpStatusCodes.GatewayTimeout
|
||||
];
|
||||
}
|
||||
|
||||
@@ -330,7 +350,10 @@ export class HashRoutePrefixes {
|
||||
public static docsWithIds(databaseId: string, collectionId: string, docId: string) {
|
||||
const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId);
|
||||
|
||||
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("{doc_id}", docId).replace("/", ""); // strip the first slash since hasher adds it
|
||||
return transformedDatabasePrefix
|
||||
.replace("{coll_id}", collectionId)
|
||||
.replace("{doc_id}", docId)
|
||||
.replace("/", ""); // strip the first slash since hasher adds it
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +399,7 @@ export class OfferVersions {
|
||||
export enum ConflictOperationType {
|
||||
Replace = "replace",
|
||||
Create = "create",
|
||||
Delete = "delete",
|
||||
Delete = "delete"
|
||||
}
|
||||
|
||||
export const EmulatorMasterKey =
|
||||
|
||||
@@ -10,17 +10,17 @@ describe("tokenProvider", () => {
|
||||
resourceId: "",
|
||||
resourceType: "dbs" as ResourceType,
|
||||
headers: {},
|
||||
getAuthorizationTokenUsingMasterKey: () => "",
|
||||
getAuthorizationTokenUsingMasterKey: () => ""
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
|
||||
});
|
||||
window.fetch = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
json: () => "{}",
|
||||
headers: new Map(),
|
||||
headers: new Map()
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -36,7 +36,7 @@ describe("tokenProvider", () => {
|
||||
|
||||
it("does not call the auth service if a master key is set", async () => {
|
||||
updateUserContext({
|
||||
masterKey: "foo",
|
||||
masterKey: "foo"
|
||||
});
|
||||
await tokenProvider(options);
|
||||
expect((window.fetch as any).mock.calls.length).toBe(0);
|
||||
@@ -50,7 +50,7 @@ describe("getTokenFromAuthService", () => {
|
||||
window.fetch = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
json: () => "{}",
|
||||
headers: new Map(),
|
||||
headers: new Map()
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -61,7 +61,7 @@ describe("getTokenFromAuthService", () => {
|
||||
|
||||
it("builds the correct URL in production", () => {
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
|
||||
});
|
||||
getTokenFromAuthService("GET", "dbs", "foo");
|
||||
expect(window.fetch).toHaveBeenCalledWith(
|
||||
@@ -72,7 +72,7 @@ describe("getTokenFromAuthService", () => {
|
||||
|
||||
it("builds the correct URL in dev", () => {
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://localhost:1234",
|
||||
BACKEND_ENDPOINT: "https://localhost:1234"
|
||||
});
|
||||
getTokenFromAuthService("GET", "dbs", "foo");
|
||||
expect(window.fetch).toHaveBeenCalledWith(
|
||||
@@ -96,15 +96,15 @@ describe("endpoint", () => {
|
||||
documentEndpoint: "bar",
|
||||
gremlinEndpoint: "foo",
|
||||
tableEndpoint: "foo",
|
||||
cassandraEndpoint: "foo",
|
||||
},
|
||||
},
|
||||
cassandraEndpoint: "foo"
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(endpoint()).toEqual("bar");
|
||||
});
|
||||
it("uses _endpoint if set", () => {
|
||||
updateUserContext({
|
||||
endpoint: "baz",
|
||||
endpoint: "baz"
|
||||
});
|
||||
expect(endpoint()).toEqual("baz");
|
||||
});
|
||||
@@ -121,7 +121,7 @@ describe("requestPlugin", () => {
|
||||
updateConfigContext({
|
||||
platform: Platform.Hosted,
|
||||
BACKEND_ENDPOINT: "https://localhost:1234",
|
||||
PROXY_PATH: "/proxy",
|
||||
PROXY_PATH: "/proxy"
|
||||
});
|
||||
const headers = {};
|
||||
const endpoint = "https://docs.azure.com";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as Cosmos from "@azure/cosmos";
|
||||
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
|
||||
import { configContext, Platform } from "../ConfigContext";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getErrorMessage } from "./ErrorHandlingUtils";
|
||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
||||
import { getErrorMessage } from "./ErrorHandlingUtils";
|
||||
import { userContext } from "../UserContext";
|
||||
|
||||
const _global = typeof self === "undefined" ? window : self;
|
||||
|
||||
@@ -58,13 +58,13 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-ms-encrypted-auth-token": userContext.accessToken,
|
||||
"x-ms-encrypted-auth-token": userContext.accessToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
verb,
|
||||
resourceType,
|
||||
resourceId,
|
||||
}),
|
||||
resourceId
|
||||
})
|
||||
});
|
||||
//TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json()
|
||||
const result = JSON.parse(await response.json());
|
||||
@@ -77,13 +77,13 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
|
||||
|
||||
export function client(): Cosmos.CosmosClient {
|
||||
const options: Cosmos.CosmosClientOptions = {
|
||||
endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called
|
||||
endpoint: endpoint() || " ", // CosmosClient gets upset if we pass a falsy value here
|
||||
key: userContext.masterKey,
|
||||
tokenProvider,
|
||||
connectionPolicy: {
|
||||
enableEndpointDiscovery: false,
|
||||
enableEndpointDiscovery: false
|
||||
},
|
||||
userAgentSuffix: "Azure Portal",
|
||||
userAgentSuffix: "Azure Portal"
|
||||
};
|
||||
|
||||
if (configContext.PROXY_PATH !== undefined) {
|
||||
|
||||
@@ -1,94 +1,94 @@
|
||||
import * as ko from "knockout";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
|
||||
export default class EditableUtility {
|
||||
public static observable<T>(initialValue?: T): ViewModels.Editable<T> {
|
||||
var observable: ViewModels.Editable<T> = <ViewModels.Editable<T>>ko.observable<T>(initialValue);
|
||||
|
||||
observable.edits = ko.observableArray<T>([initialValue]);
|
||||
observable.validations = ko.observableArray<(value: T) => boolean>([]);
|
||||
|
||||
observable.setBaseline = (baseline: T) => {
|
||||
observable(baseline);
|
||||
observable.edits([baseline]);
|
||||
};
|
||||
|
||||
observable.getEditableCurrentValue = ko.computed<T>(() => {
|
||||
const edits = (observable.edits && observable.edits()) || [];
|
||||
if (edits.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return edits[edits.length - 1];
|
||||
});
|
||||
|
||||
observable.getEditableOriginalValue = ko.computed<T>(() => {
|
||||
const edits = (observable.edits && observable.edits()) || [];
|
||||
if (edits.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return edits[0];
|
||||
});
|
||||
|
||||
observable.editableIsDirty = ko.computed<boolean>(() => {
|
||||
const edits = (observable.edits && observable.edits()) || [];
|
||||
if (edits.length <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let current: any = observable.getEditableCurrentValue();
|
||||
let original: any = observable.getEditableOriginalValue();
|
||||
|
||||
switch (typeof current) {
|
||||
case "string":
|
||||
case "undefined":
|
||||
case "number":
|
||||
case "boolean":
|
||||
current = current && current.toString();
|
||||
break;
|
||||
|
||||
default:
|
||||
current = JSON.stringify(current);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (typeof original) {
|
||||
case "string":
|
||||
case "undefined":
|
||||
case "number":
|
||||
case "boolean":
|
||||
original = original && original.toString();
|
||||
break;
|
||||
|
||||
default:
|
||||
original = JSON.stringify(original);
|
||||
break;
|
||||
}
|
||||
|
||||
if (current !== original) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
observable.subscribe((edit) => {
|
||||
var edits = observable.edits && observable.edits();
|
||||
if (!edits) {
|
||||
return;
|
||||
}
|
||||
edits.push(edit);
|
||||
observable.edits(edits);
|
||||
});
|
||||
|
||||
observable.editableIsValid = ko.observable<boolean>(true);
|
||||
observable.subscribe((value) => {
|
||||
const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || [];
|
||||
const isValid = validations.every((validate) => validate(value));
|
||||
observable.editableIsValid(isValid);
|
||||
});
|
||||
|
||||
return observable;
|
||||
}
|
||||
}
|
||||
import * as ko from "knockout";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
|
||||
export default class EditableUtility {
|
||||
public static observable<T>(initialValue?: T): ViewModels.Editable<T> {
|
||||
var observable: ViewModels.Editable<T> = <ViewModels.Editable<T>>ko.observable<T>(initialValue);
|
||||
|
||||
observable.edits = ko.observableArray<T>([initialValue]);
|
||||
observable.validations = ko.observableArray<(value: T) => boolean>([]);
|
||||
|
||||
observable.setBaseline = (baseline: T) => {
|
||||
observable(baseline);
|
||||
observable.edits([baseline]);
|
||||
};
|
||||
|
||||
observable.getEditableCurrentValue = ko.computed<T>(() => {
|
||||
const edits = (observable.edits && observable.edits()) || [];
|
||||
if (edits.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return edits[edits.length - 1];
|
||||
});
|
||||
|
||||
observable.getEditableOriginalValue = ko.computed<T>(() => {
|
||||
const edits = (observable.edits && observable.edits()) || [];
|
||||
if (edits.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return edits[0];
|
||||
});
|
||||
|
||||
observable.editableIsDirty = ko.computed<boolean>(() => {
|
||||
const edits = (observable.edits && observable.edits()) || [];
|
||||
if (edits.length <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let current: any = observable.getEditableCurrentValue();
|
||||
let original: any = observable.getEditableOriginalValue();
|
||||
|
||||
switch (typeof current) {
|
||||
case "string":
|
||||
case "undefined":
|
||||
case "number":
|
||||
case "boolean":
|
||||
current = current && current.toString();
|
||||
break;
|
||||
|
||||
default:
|
||||
current = JSON.stringify(current);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (typeof original) {
|
||||
case "string":
|
||||
case "undefined":
|
||||
case "number":
|
||||
case "boolean":
|
||||
original = original && original.toString();
|
||||
break;
|
||||
|
||||
default:
|
||||
original = JSON.stringify(original);
|
||||
break;
|
||||
}
|
||||
|
||||
if (current !== original) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
observable.subscribe(edit => {
|
||||
var edits = observable.edits && observable.edits();
|
||||
if (!edits) {
|
||||
return;
|
||||
}
|
||||
edits.push(edit);
|
||||
observable.edits(edits);
|
||||
});
|
||||
|
||||
observable.editableIsValid = ko.observable<boolean>(true);
|
||||
observable.subscribe(value => {
|
||||
const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || [];
|
||||
const isValid = validations.every(validate => validate(value));
|
||||
observable.editableIsValid(isValid);
|
||||
});
|
||||
|
||||
return observable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export function normalizeArmEndpoint(uri: string): string {
|
||||
if (uri && uri.slice(-1) !== "/") {
|
||||
return `${uri}/`;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
export function normalizeArmEndpoint(uri: string): string {
|
||||
if (uri && uri.slice(-1) !== "/") {
|
||||
return `${uri}/`;
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ARMError } from "../Utils/arm/request";
|
||||
import { HttpStatusCodes } from "./Constants";
|
||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||
import { SubscriptionType } from "../Contracts/SubscriptionType";
|
||||
import { userContext } from "../UserContext";
|
||||
import { ARMError } from "../Utils/arm/request";
|
||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||
import { HttpStatusCodes } from "./Constants";
|
||||
import { logError } from "./Logger";
|
||||
import { sendMessage } from "./MessageHandler";
|
||||
|
||||
@@ -38,14 +37,14 @@ const sendNotificationForError = (errorMessage: string, errorCode: number | stri
|
||||
}
|
||||
sendMessage({
|
||||
type: MessageTypes.ForbiddenError,
|
||||
reason: errorMessage,
|
||||
reason: errorMessage
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const replaceKnownError = (errorMessage: string): string => {
|
||||
if (
|
||||
userContext.subscriptionType === SubscriptionType.Internal &&
|
||||
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal &&
|
||||
errorMessage?.indexOf("SharedOffer is Disabled for your account") >= 0
|
||||
) {
|
||||
return "Database throughput is not supported for internal subscriptions.";
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import * as HeadersUtility from "./HeadersUtility";
|
||||
import { ExplorerSettings } from "../Shared/ExplorerSettings";
|
||||
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||
|
||||
describe("Headers Utility", () => {
|
||||
describe("shouldEnableCrossPartitionKeyForResourceWithPartitionKey()", () => {
|
||||
beforeEach(() => {
|
||||
ExplorerSettings.createDefaultSettings();
|
||||
});
|
||||
|
||||
it("should return true by default", () => {
|
||||
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false if the enable cross partition key feed option is false", () => {
|
||||
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "false");
|
||||
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true if the enable cross partition key feed option is true", () => {
|
||||
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "true");
|
||||
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
import * as HeadersUtility from "./HeadersUtility";
|
||||
import { ExplorerSettings } from "../Shared/ExplorerSettings";
|
||||
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||
|
||||
describe("Headers Utility", () => {
|
||||
describe("shouldEnableCrossPartitionKeyForResourceWithPartitionKey()", () => {
|
||||
beforeEach(() => {
|
||||
ExplorerSettings.createDefaultSettings();
|
||||
});
|
||||
|
||||
it("should return true by default", () => {
|
||||
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false if the enable cross partition key feed option is false", () => {
|
||||
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "false");
|
||||
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true if the enable cross partition key feed option is true", () => {
|
||||
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "true");
|
||||
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||
|
||||
export function shouldEnableCrossPartitionKey(): boolean {
|
||||
return LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true";
|
||||
}
|
||||
import * as Constants from "./Constants";
|
||||
|
||||
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||
|
||||
// x-ms-resource-quota: databases = 100; collections = 5000; users = 500000; permissions = 2000000;
|
||||
export function getQuota(responseHeaders: any): any {
|
||||
return responseHeaders && responseHeaders[Constants.HttpHeaders.resourceQuota]
|
||||
? parseStringIntoObject(responseHeaders[Constants.HttpHeaders.resourceQuota])
|
||||
: null;
|
||||
}
|
||||
|
||||
export function shouldEnableCrossPartitionKey(): boolean {
|
||||
return LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true";
|
||||
}
|
||||
|
||||
function parseStringIntoObject(resourceString: string) {
|
||||
var entityObject: any = {};
|
||||
|
||||
if (resourceString) {
|
||||
var entitiesArray: string[] = resourceString.split(";");
|
||||
for (var i: any = 0; i < entitiesArray.length; i++) {
|
||||
var entity: string[] = entitiesArray[i].split("=");
|
||||
entityObject[entity[0]] = entity[1];
|
||||
}
|
||||
}
|
||||
|
||||
return entityObject;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ describe("nextPage", () => {
|
||||
queryMetrics: {},
|
||||
requestCharge: 1,
|
||||
headers: {},
|
||||
activityId: "foo",
|
||||
}),
|
||||
activityId: "foo"
|
||||
})
|
||||
};
|
||||
|
||||
expect(await nextPage(fakeIterator, 10)).toMatchSnapshot();
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { QueryResults } from "../Contracts/ViewModels";
|
||||
|
||||
interface QueryResponse {
|
||||
// [Todo] remove any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
resources: any[];
|
||||
hasMoreResults: boolean;
|
||||
activityId: string;
|
||||
@@ -16,9 +14,8 @@ export interface MinimalQueryIterator {
|
||||
// Pick<QueryIterator<any>, "fetchNext">;
|
||||
|
||||
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
|
||||
return documentsIterator.fetchNext().then((response) => {
|
||||
return documentsIterator.fetchNext().then(response => {
|
||||
const documents = response.resources;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
|
||||
const itemCount = (documents && documents.length) || 0;
|
||||
return {
|
||||
@@ -29,7 +26,7 @@ export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex
|
||||
lastItemIndex: Number(firstItemIndex) + Number(itemCount),
|
||||
headers,
|
||||
activityId: response.activityId,
|
||||
requestCharge: response.requestCharge,
|
||||
requestCharge: response.requestCharge
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
jest.mock("./MessageHandler");
|
||||
import { LogEntryLevel } from "../Contracts/Diagnostics";
|
||||
import * as Logger from "./Logger";
|
||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||
import { sendMessage } from "./MessageHandler";
|
||||
|
||||
describe("Logger", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should log info messages", () => {
|
||||
Logger.logInfo("Test info", "DocDB");
|
||||
expect(sendMessage).toBeCalled();
|
||||
});
|
||||
|
||||
it("should log error messages", () => {
|
||||
Logger.logError("Test error", "DocDB");
|
||||
expect(sendMessage).toBeCalled();
|
||||
});
|
||||
|
||||
it("should log warnings", () => {
|
||||
Logger.logWarning("Test warning", "DocDB");
|
||||
expect(sendMessage).toBeCalled();
|
||||
});
|
||||
});
|
||||
jest.mock("./MessageHandler");
|
||||
import { LogEntryLevel } from "../Contracts/Diagnostics";
|
||||
import * as Logger from "./Logger";
|
||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||
import { sendMessage } from "./MessageHandler";
|
||||
|
||||
describe("Logger", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should log info messages", () => {
|
||||
Logger.logInfo("Test info", "DocDB");
|
||||
expect(sendMessage).toBeCalled();
|
||||
});
|
||||
|
||||
it("should log error messages", () => {
|
||||
Logger.logError("Test error", "DocDB");
|
||||
expect(sendMessage).toBeCalled();
|
||||
});
|
||||
|
||||
it("should log warnings", () => {
|
||||
Logger.logWarning("Test warning", "DocDB");
|
||||
expect(sendMessage).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ export function logError(errorMessage: string, area: string, code?: number | str
|
||||
function _logEntry(entry: Diagnostics.LogEntry): void {
|
||||
sendMessage({
|
||||
type: MessageTypes.LogInfo,
|
||||
data: JSON.stringify(entry),
|
||||
data: JSON.stringify(entry)
|
||||
});
|
||||
|
||||
const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => {
|
||||
@@ -60,6 +60,6 @@ function _generateLogEntry(
|
||||
level,
|
||||
message,
|
||||
area,
|
||||
code,
|
||||
code
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import Q from "q";
|
||||
import * as MessageHandler from "./MessageHandler";
|
||||
|
||||
describe("Message Handler", () => {
|
||||
it("should handle cached message", async () => {
|
||||
let mockPromise = {
|
||||
id: "123",
|
||||
startTime: new Date(),
|
||||
deferred: Q.defer<any>(),
|
||||
};
|
||||
let mockMessage = { message: { id: "123", data: "{}" } };
|
||||
MessageHandler.RequestMap[mockPromise.id] = mockPromise;
|
||||
MessageHandler.handleCachedDataMessage(mockMessage);
|
||||
expect(mockPromise.deferred.promise.isFulfilled()).toBe(true);
|
||||
});
|
||||
|
||||
it("should delete fulfilled promises on running the garbage collector", async () => {
|
||||
let message = {
|
||||
id: "123",
|
||||
startTime: new Date(),
|
||||
deferred: Q.defer<any>(),
|
||||
};
|
||||
|
||||
MessageHandler.handleCachedDataMessage(message);
|
||||
MessageHandler.runGarbageCollector();
|
||||
expect(MessageHandler.RequestMap["123"]).toBeUndefined();
|
||||
});
|
||||
});
|
||||
import Q from "q";
|
||||
import * as MessageHandler from "./MessageHandler";
|
||||
|
||||
describe("Message Handler", () => {
|
||||
it("should handle cached message", async () => {
|
||||
let mockPromise = {
|
||||
id: "123",
|
||||
startTime: new Date(),
|
||||
deferred: Q.defer<any>()
|
||||
};
|
||||
let mockMessage = { message: { id: "123", data: "{}" } };
|
||||
MessageHandler.RequestMap[mockPromise.id] = mockPromise;
|
||||
MessageHandler.handleCachedDataMessage(mockMessage);
|
||||
expect(mockPromise.deferred.promise.isFulfilled()).toBe(true);
|
||||
});
|
||||
|
||||
it("should delete fulfilled promises on running the garbage collector", async () => {
|
||||
let message = {
|
||||
id: "123",
|
||||
startTime: new Date(),
|
||||
deferred: Q.defer<any>()
|
||||
};
|
||||
|
||||
MessageHandler.handleCachedDataMessage(message);
|
||||
MessageHandler.runGarbageCollector();
|
||||
expect(MessageHandler.RequestMap["123"]).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ export function sendCachedDataMessage<TResponseDataModel>(
|
||||
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
|
||||
deferred: Q.defer<TResponseDataModel>(),
|
||||
startTime: new Date(),
|
||||
id: _.uniqueId(),
|
||||
id: _.uniqueId()
|
||||
};
|
||||
RequestMap[cachedDataPromise.id] = cachedDataPromise;
|
||||
sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
|
||||
@@ -54,7 +54,7 @@ export function sendMessage(data: any): void {
|
||||
portalChildWindow.parent.postMessage(
|
||||
{
|
||||
signature: "pcIframe",
|
||||
data: data,
|
||||
data: data
|
||||
},
|
||||
portalChildWindow.document.referrer
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ const fetchMock = () => {
|
||||
ok: true,
|
||||
text: () => "{}",
|
||||
json: () => "{}",
|
||||
headers: new Map(),
|
||||
headers: new Map()
|
||||
});
|
||||
};
|
||||
|
||||
@@ -27,8 +27,8 @@ const collection = {
|
||||
partitionKey: {
|
||||
paths: ["/pk"],
|
||||
kind: "Hash",
|
||||
version: 1,
|
||||
},
|
||||
version: 1
|
||||
}
|
||||
} as Collection;
|
||||
|
||||
const documentId = ({
|
||||
@@ -38,8 +38,8 @@ const documentId = ({
|
||||
partitionKey: {
|
||||
paths: ["/pk"],
|
||||
kind: "Hash",
|
||||
version: 1,
|
||||
},
|
||||
version: 1
|
||||
}
|
||||
} as unknown) as DocumentId;
|
||||
|
||||
const databaseAccount = {
|
||||
@@ -52,8 +52,8 @@ const databaseAccount = {
|
||||
documentEndpoint: "bar",
|
||||
gremlinEndpoint: "foo",
|
||||
tableEndpoint: "foo",
|
||||
cassandraEndpoint: "foo",
|
||||
},
|
||||
cassandraEndpoint: "foo"
|
||||
}
|
||||
} as DatabaseAccount;
|
||||
|
||||
describe("MongoProxyClient", () => {
|
||||
@@ -61,10 +61,10 @@ describe("MongoProxyClient", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigContext();
|
||||
updateUserContext({
|
||||
databaseAccount,
|
||||
databaseAccount
|
||||
});
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
|
||||
});
|
||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||
});
|
||||
@@ -93,10 +93,10 @@ describe("MongoProxyClient", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigContext();
|
||||
updateUserContext({
|
||||
databaseAccount,
|
||||
databaseAccount
|
||||
});
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
|
||||
});
|
||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||
});
|
||||
@@ -125,10 +125,10 @@ describe("MongoProxyClient", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigContext();
|
||||
updateUserContext({
|
||||
databaseAccount,
|
||||
databaseAccount
|
||||
});
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
|
||||
});
|
||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||
});
|
||||
@@ -157,10 +157,10 @@ describe("MongoProxyClient", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigContext();
|
||||
updateUserContext({
|
||||
databaseAccount,
|
||||
databaseAccount
|
||||
});
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
|
||||
});
|
||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||
});
|
||||
@@ -189,10 +189,10 @@ describe("MongoProxyClient", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigContext();
|
||||
updateUserContext({
|
||||
databaseAccount,
|
||||
databaseAccount
|
||||
});
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
|
||||
});
|
||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||
});
|
||||
@@ -220,11 +220,12 @@ describe("MongoProxyClient", () => {
|
||||
describe("getEndpoint", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigContext();
|
||||
delete window.authType;
|
||||
updateUserContext({
|
||||
databaseAccount,
|
||||
databaseAccount
|
||||
});
|
||||
updateConfigContext({
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
|
||||
});
|
||||
});
|
||||
|
||||
@@ -240,9 +241,7 @@ describe("MongoProxyClient", () => {
|
||||
});
|
||||
|
||||
it("returns a guest endpoint", () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.EncryptedToken,
|
||||
});
|
||||
window.authType = AuthType.EncryptedToken;
|
||||
const endpoint = getEndpoint();
|
||||
expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/guest/mongo/explorer");
|
||||
});
|
||||
|
||||
@@ -16,11 +16,11 @@ import { sendMessage } from "./MessageHandler";
|
||||
const defaultHeaders = {
|
||||
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
|
||||
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
|
||||
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15",
|
||||
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15"
|
||||
};
|
||||
|
||||
function authHeaders() {
|
||||
if (userContext.authType === AuthType.EncryptedToken) {
|
||||
if (window.authType === AuthType.EncryptedToken) {
|
||||
return { [HttpHeaders.guestAccessToken]: userContext.accessToken };
|
||||
} else {
|
||||
return { [HttpHeaders.authorization]: userContext.authorizationToken };
|
||||
@@ -31,7 +31,7 @@ export function queryIterator(databaseId: string, collection: Collection, query:
|
||||
let continuationToken: string;
|
||||
return {
|
||||
fetchNext: () => {
|
||||
return queryDocuments(databaseId, collection, false, query).then((response) => {
|
||||
return queryDocuments(databaseId, collection, false, query).then(response => {
|
||||
continuationToken = response.continuationToken;
|
||||
const headers: { [key: string]: string | number } = {};
|
||||
response.headers.forEach((value, key) => {
|
||||
@@ -42,10 +42,10 @@ export function queryIterator(databaseId: string, collection: Collection, query:
|
||||
headers,
|
||||
requestCharge: Number(headers[CosmosSDKConstants.HttpHeaders.RequestCharge]),
|
||||
activityId: String(headers[CosmosSDKConstants.HttpHeaders.ActivityId]),
|
||||
hasMoreResults: !!continuationToken,
|
||||
hasMoreResults: !!continuationToken
|
||||
};
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -74,9 +74,7 @@ export function queryDocuments(
|
||||
rg: userContext.resourceGroup,
|
||||
dba: databaseAccount.name,
|
||||
pk:
|
||||
collection && collection.partitionKey && !collection.partitionKey.systemKey
|
||||
? collection.partitionKeyProperty
|
||||
: "",
|
||||
collection && collection.partitionKey && !collection.partitionKey.systemKey ? collection.partitionKeyProperty : ""
|
||||
};
|
||||
|
||||
const endpoint = getEndpoint() || "";
|
||||
@@ -89,7 +87,7 @@ export function queryDocuments(
|
||||
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
|
||||
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
|
||||
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
|
||||
[HttpHeaders.contentType]: "application/query+json",
|
||||
[HttpHeaders.contentType]: "application/query+json"
|
||||
};
|
||||
|
||||
if (continuationToken) {
|
||||
@@ -102,14 +100,14 @@ export function queryDocuments(
|
||||
.fetch(`${endpoint}${path}?${queryString.stringify(params)}`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ query }),
|
||||
headers,
|
||||
headers
|
||||
})
|
||||
.then(async (response) => {
|
||||
.then(async response => {
|
||||
if (response.ok) {
|
||||
return {
|
||||
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
|
||||
documents: (await response.json()).Documents as DataModels.DocumentId[],
|
||||
headers: response.headers,
|
||||
headers: response.headers
|
||||
};
|
||||
}
|
||||
errorHandling(response, "querying documents", params);
|
||||
@@ -137,9 +135,7 @@ export function readDocument(
|
||||
rg: userContext.resourceGroup,
|
||||
dba: databaseAccount.name,
|
||||
pk:
|
||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||
? documentId.partitionKeyProperty
|
||||
: "",
|
||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
||||
};
|
||||
|
||||
const endpoint = getEndpoint();
|
||||
@@ -151,10 +147,10 @@ export function readDocument(
|
||||
...authHeaders(),
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent(
|
||||
JSON.stringify(documentId.partitionKeyHeader())
|
||||
),
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
@@ -179,7 +175,7 @@ export function createDocument(
|
||||
sid: userContext.subscriptionId,
|
||||
rg: userContext.resourceGroup,
|
||||
dba: databaseAccount.name,
|
||||
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
|
||||
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : ""
|
||||
};
|
||||
|
||||
const endpoint = getEndpoint();
|
||||
@@ -190,10 +186,10 @@ export function createDocument(
|
||||
body: JSON.stringify(documentContent),
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
},
|
||||
...authHeaders()
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
@@ -222,9 +218,7 @@ export function updateDocument(
|
||||
rg: userContext.resourceGroup,
|
||||
dba: databaseAccount.name,
|
||||
pk:
|
||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||
? documentId.partitionKeyProperty
|
||||
: "",
|
||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
||||
};
|
||||
const endpoint = getEndpoint();
|
||||
|
||||
@@ -236,10 +230,10 @@ export function updateDocument(
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: "application/json",
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||
},
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
@@ -263,9 +257,7 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
|
||||
rg: userContext.resourceGroup,
|
||||
dba: databaseAccount.name,
|
||||
pk:
|
||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||
? documentId.partitionKeyProperty
|
||||
: "",
|
||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
||||
};
|
||||
const endpoint = getEndpoint();
|
||||
|
||||
@@ -276,10 +268,10 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: "application/json",
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||
},
|
||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -307,7 +299,7 @@ export function createMongoCollectionWithProxy(
|
||||
rg: userContext.resourceGroup,
|
||||
dba: databaseAccount.name,
|
||||
isAutoPilot: !!params.autoPilotMaxThroughput,
|
||||
autoPilotThroughput: params.autoPilotMaxThroughput?.toString(),
|
||||
autoPilotThroughput: params.autoPilotMaxThroughput?.toString()
|
||||
};
|
||||
|
||||
const endpoint = getEndpoint();
|
||||
@@ -322,11 +314,11 @@ export function createMongoCollectionWithProxy(
|
||||
headers: {
|
||||
...defaultHeaders,
|
||||
...authHeaders(),
|
||||
[HttpHeaders.contentType]: "application/json",
|
||||
},
|
||||
[HttpHeaders.contentType]: "application/json"
|
||||
}
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
@@ -337,7 +329,7 @@ export function createMongoCollectionWithProxy(
|
||||
export function getEndpoint(): string {
|
||||
let url = (configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT) + "/api/mongo/explorer";
|
||||
|
||||
if (userContext.authType === AuthType.EncryptedToken) {
|
||||
if (window.authType === AuthType.EncryptedToken) {
|
||||
url = url.replace("api/mongo", "api/guest/mongo");
|
||||
}
|
||||
return url;
|
||||
|
||||
@@ -1,168 +1,168 @@
|
||||
/* Copyright 2013 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export default class MongoUtility {
|
||||
public static tojson = function (x: any, indent: string, nolint: boolean) {
|
||||
if (x === null || x === undefined) {
|
||||
return String(x);
|
||||
}
|
||||
indent = indent || "";
|
||||
|
||||
switch (typeof x) {
|
||||
case "string":
|
||||
var out = new Array(x.length + 1);
|
||||
out[0] = '"';
|
||||
for (var i = 0; i < x.length; i++) {
|
||||
if (x[i] === '"') {
|
||||
out[out.length] = '\\"';
|
||||
} else if (x[i] === "\\") {
|
||||
out[out.length] = "\\\\";
|
||||
} else if (x[i] === "\b") {
|
||||
out[out.length] = "\\b";
|
||||
} else if (x[i] === "\f") {
|
||||
out[out.length] = "\\f";
|
||||
} else if (x[i] === "\n") {
|
||||
out[out.length] = "\\n";
|
||||
} else if (x[i] === "\r") {
|
||||
out[out.length] = "\\r";
|
||||
} else if (x[i] === "\t") {
|
||||
out[out.length] = "\\t";
|
||||
} else {
|
||||
var code = x.charCodeAt(i);
|
||||
if (code < 0x20) {
|
||||
out[out.length] = (code < 0x10 ? "\\u000" : "\\u00") + code.toString(16);
|
||||
} else {
|
||||
out[out.length] = x[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.join("") + '"';
|
||||
case "number":
|
||||
/* falls through */
|
||||
case "boolean":
|
||||
return "" + x;
|
||||
case "object":
|
||||
var func = $.isArray(x) ? MongoUtility.tojsonArray : MongoUtility.tojsonObject;
|
||||
var s = func(x, indent, nolint);
|
||||
if (
|
||||
(nolint === null || nolint === undefined || nolint === true) &&
|
||||
s.length < 80 &&
|
||||
(indent === null || indent.length === 0)
|
||||
) {
|
||||
s = s.replace(/[\t\r\n]+/gm, " ");
|
||||
}
|
||||
return s;
|
||||
case "function":
|
||||
return x.toString();
|
||||
default:
|
||||
throw new Error("tojson can't handle type " + typeof x);
|
||||
}
|
||||
};
|
||||
|
||||
private static tojsonObject = function (x: any, indent: string, nolint: boolean) {
|
||||
var lineEnding = nolint ? " " : "\n";
|
||||
var tabSpace = nolint ? "" : "\t";
|
||||
indent = indent || "";
|
||||
|
||||
if (typeof x.tojson === "function" && x.tojson !== MongoUtility.tojson) {
|
||||
return x.tojson(indent, nolint);
|
||||
}
|
||||
|
||||
if (x.constructor && typeof x.constructor.tojson === "function" && x.constructor.tojson !== MongoUtility.tojson) {
|
||||
return x.constructor.tojson(x, indent, nolint);
|
||||
}
|
||||
|
||||
if (MongoUtility.hasDefinedProperty(x, "toString") && !$.isArray(x)) {
|
||||
return x.toString();
|
||||
}
|
||||
|
||||
if (x instanceof Error) {
|
||||
return x.toString();
|
||||
}
|
||||
|
||||
if (MongoUtility.isObjectId(x)) {
|
||||
return 'ObjectId("' + x.$oid + '")';
|
||||
}
|
||||
|
||||
// push one level of indent
|
||||
indent += tabSpace;
|
||||
var s = "{";
|
||||
|
||||
var pairs = [];
|
||||
for (var k in x) {
|
||||
if (x.hasOwnProperty(k)) {
|
||||
var val = x[k];
|
||||
var pair = '"' + k + '" : ' + MongoUtility.tojson(val, indent, nolint);
|
||||
|
||||
if (k === "_id") {
|
||||
pairs.unshift(pair);
|
||||
} else {
|
||||
pairs.push(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add proper line endings, indents, and commas to each line
|
||||
s += $.map(pairs, function (pair) {
|
||||
return lineEnding + indent + pair;
|
||||
}).join(",");
|
||||
s += lineEnding;
|
||||
|
||||
// pop one level of indent
|
||||
indent = indent.substring(1);
|
||||
return s + indent + "}";
|
||||
};
|
||||
|
||||
private static tojsonArray = function (a: any, indent: string, nolint: boolean) {
|
||||
if (a.length === 0) {
|
||||
return "[ ]";
|
||||
}
|
||||
|
||||
var lineEnding = nolint ? " " : "\n";
|
||||
if (!indent || nolint) {
|
||||
indent = "";
|
||||
}
|
||||
|
||||
var s = "[" + lineEnding;
|
||||
indent += "\t";
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
s += indent + MongoUtility.tojson(a[i], indent, nolint);
|
||||
if (i < a.length - 1) {
|
||||
s += "," + lineEnding;
|
||||
}
|
||||
}
|
||||
if (a.length === 0) {
|
||||
s += indent;
|
||||
}
|
||||
|
||||
indent = indent.substring(1);
|
||||
s += lineEnding + indent + "]";
|
||||
return s;
|
||||
};
|
||||
|
||||
private static hasDefinedProperty = function (obj: any, prop: string): boolean {
|
||||
if (Object.getPrototypeOf === undefined || Object.getPrototypeOf(obj) === null) {
|
||||
return false;
|
||||
} else if (obj.hasOwnProperty(prop)) {
|
||||
return true;
|
||||
} else {
|
||||
return MongoUtility.hasDefinedProperty(Object.getPrototypeOf(obj), prop);
|
||||
}
|
||||
};
|
||||
|
||||
private static isObjectId(obj: any): boolean {
|
||||
var keys = Object.keys(obj);
|
||||
return keys.length === 1 && keys[0] === "$oid" && typeof obj.$oid === "string" && /^[0-9a-f]{24}$/.test(obj.$oid);
|
||||
}
|
||||
}
|
||||
/* Copyright 2013 10gen Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export default class MongoUtility {
|
||||
public static tojson = function(x: any, indent: string, nolint: boolean) {
|
||||
if (x === null || x === undefined) {
|
||||
return String(x);
|
||||
}
|
||||
indent = indent || "";
|
||||
|
||||
switch (typeof x) {
|
||||
case "string":
|
||||
var out = new Array(x.length + 1);
|
||||
out[0] = '"';
|
||||
for (var i = 0; i < x.length; i++) {
|
||||
if (x[i] === '"') {
|
||||
out[out.length] = '\\"';
|
||||
} else if (x[i] === "\\") {
|
||||
out[out.length] = "\\\\";
|
||||
} else if (x[i] === "\b") {
|
||||
out[out.length] = "\\b";
|
||||
} else if (x[i] === "\f") {
|
||||
out[out.length] = "\\f";
|
||||
} else if (x[i] === "\n") {
|
||||
out[out.length] = "\\n";
|
||||
} else if (x[i] === "\r") {
|
||||
out[out.length] = "\\r";
|
||||
} else if (x[i] === "\t") {
|
||||
out[out.length] = "\\t";
|
||||
} else {
|
||||
var code = x.charCodeAt(i);
|
||||
if (code < 0x20) {
|
||||
out[out.length] = (code < 0x10 ? "\\u000" : "\\u00") + code.toString(16);
|
||||
} else {
|
||||
out[out.length] = x[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.join("") + '"';
|
||||
case "number":
|
||||
/* falls through */
|
||||
case "boolean":
|
||||
return "" + x;
|
||||
case "object":
|
||||
var func = $.isArray(x) ? MongoUtility.tojsonArray : MongoUtility.tojsonObject;
|
||||
var s = func(x, indent, nolint);
|
||||
if (
|
||||
(nolint === null || nolint === undefined || nolint === true) &&
|
||||
s.length < 80 &&
|
||||
(indent === null || indent.length === 0)
|
||||
) {
|
||||
s = s.replace(/[\t\r\n]+/gm, " ");
|
||||
}
|
||||
return s;
|
||||
case "function":
|
||||
return x.toString();
|
||||
default:
|
||||
throw new Error("tojson can't handle type " + typeof x);
|
||||
}
|
||||
};
|
||||
|
||||
private static tojsonObject = function(x: any, indent: string, nolint: boolean) {
|
||||
var lineEnding = nolint ? " " : "\n";
|
||||
var tabSpace = nolint ? "" : "\t";
|
||||
indent = indent || "";
|
||||
|
||||
if (typeof x.tojson === "function" && x.tojson !== MongoUtility.tojson) {
|
||||
return x.tojson(indent, nolint);
|
||||
}
|
||||
|
||||
if (x.constructor && typeof x.constructor.tojson === "function" && x.constructor.tojson !== MongoUtility.tojson) {
|
||||
return x.constructor.tojson(x, indent, nolint);
|
||||
}
|
||||
|
||||
if (MongoUtility.hasDefinedProperty(x, "toString") && !$.isArray(x)) {
|
||||
return x.toString();
|
||||
}
|
||||
|
||||
if (x instanceof Error) {
|
||||
return x.toString();
|
||||
}
|
||||
|
||||
if (MongoUtility.isObjectId(x)) {
|
||||
return 'ObjectId("' + x.$oid + '")';
|
||||
}
|
||||
|
||||
// push one level of indent
|
||||
indent += tabSpace;
|
||||
var s = "{";
|
||||
|
||||
var pairs = [];
|
||||
for (var k in x) {
|
||||
if (x.hasOwnProperty(k)) {
|
||||
var val = x[k];
|
||||
var pair = '"' + k + '" : ' + MongoUtility.tojson(val, indent, nolint);
|
||||
|
||||
if (k === "_id") {
|
||||
pairs.unshift(pair);
|
||||
} else {
|
||||
pairs.push(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add proper line endings, indents, and commas to each line
|
||||
s += $.map(pairs, function(pair) {
|
||||
return lineEnding + indent + pair;
|
||||
}).join(",");
|
||||
s += lineEnding;
|
||||
|
||||
// pop one level of indent
|
||||
indent = indent.substring(1);
|
||||
return s + indent + "}";
|
||||
};
|
||||
|
||||
private static tojsonArray = function(a: any, indent: string, nolint: boolean) {
|
||||
if (a.length === 0) {
|
||||
return "[ ]";
|
||||
}
|
||||
|
||||
var lineEnding = nolint ? " " : "\n";
|
||||
if (!indent || nolint) {
|
||||
indent = "";
|
||||
}
|
||||
|
||||
var s = "[" + lineEnding;
|
||||
indent += "\t";
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
s += indent + MongoUtility.tojson(a[i], indent, nolint);
|
||||
if (i < a.length - 1) {
|
||||
s += "," + lineEnding;
|
||||
}
|
||||
}
|
||||
if (a.length === 0) {
|
||||
s += indent;
|
||||
}
|
||||
|
||||
indent = indent.substring(1);
|
||||
s += lineEnding + indent + "]";
|
||||
return s;
|
||||
};
|
||||
|
||||
private static hasDefinedProperty = function(obj: any, prop: string): boolean {
|
||||
if (Object.getPrototypeOf === undefined || Object.getPrototypeOf(obj) === null) {
|
||||
return false;
|
||||
} else if (obj.hasOwnProperty(prop)) {
|
||||
return true;
|
||||
} else {
|
||||
return MongoUtility.hasDefinedProperty(Object.getPrototypeOf(obj), prop);
|
||||
}
|
||||
};
|
||||
|
||||
private static isObjectId(obj: any): boolean {
|
||||
var keys = Object.keys(obj);
|
||||
return keys.length === 1 && keys[0] === "$oid" && typeof obj.$oid === "string" && /^[0-9a-f]{24}$/.test(obj.$oid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ describe("parseSDKOfferResponse", () => {
|
||||
offerThroughput: 500,
|
||||
collectionThroughputInfo: {
|
||||
minimumRUForCollection: 400,
|
||||
numPhysicalPartitions: 1,
|
||||
},
|
||||
numPhysicalPartitions: 1
|
||||
}
|
||||
},
|
||||
id: "test",
|
||||
id: "test"
|
||||
} as SDKOfferDefinition;
|
||||
|
||||
const mockResponse = {
|
||||
resource: mockOfferDefinition,
|
||||
resource: mockOfferDefinition
|
||||
} as OfferResponse;
|
||||
|
||||
const expectedResult: Offer = {
|
||||
@@ -25,7 +25,7 @@ describe("parseSDKOfferResponse", () => {
|
||||
minimumThroughput: 400,
|
||||
id: "test",
|
||||
offerDefinition: mockOfferDefinition,
|
||||
offerReplacePending: false,
|
||||
offerReplacePending: false
|
||||
};
|
||||
|
||||
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);
|
||||
@@ -37,17 +37,17 @@ describe("parseSDKOfferResponse", () => {
|
||||
offerThroughput: 400,
|
||||
collectionThroughputInfo: {
|
||||
minimumRUForCollection: 400,
|
||||
numPhysicalPartitions: 1,
|
||||
numPhysicalPartitions: 1
|
||||
},
|
||||
offerAutopilotSettings: {
|
||||
maxThroughput: 5000,
|
||||
},
|
||||
maxThroughput: 5000
|
||||
}
|
||||
},
|
||||
id: "test",
|
||||
id: "test"
|
||||
} as SDKOfferDefinition;
|
||||
|
||||
const mockResponse = {
|
||||
resource: mockOfferDefinition,
|
||||
resource: mockOfferDefinition
|
||||
} as OfferResponse;
|
||||
|
||||
const expectedResult: Offer = {
|
||||
@@ -56,7 +56,7 @@ describe("parseSDKOfferResponse", () => {
|
||||
minimumThroughput: 400,
|
||||
id: "test",
|
||||
offerDefinition: mockOfferDefinition,
|
||||
offerReplacePending: false,
|
||||
offerReplacePending: false
|
||||
};
|
||||
|
||||
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);
|
||||
|
||||
@@ -22,7 +22,7 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer | und
|
||||
manualThroughput: undefined,
|
||||
minimumThroughput,
|
||||
offerDefinition,
|
||||
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true",
|
||||
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,6 +32,6 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer | und
|
||||
manualThroughput: offerContent.offerThroughput,
|
||||
minimumThroughput,
|
||||
offerDefinition,
|
||||
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true",
|
||||
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true"
|
||||
};
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ export const fetchPortalNotifications = async (): Promise<DataModels.Notificatio
|
||||
const headers = { [authorizationHeader.header]: authorizationHeader.token };
|
||||
|
||||
const response = await window.fetch(url, {
|
||||
headers,
|
||||
headers
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
@@ -1,219 +1,219 @@
|
||||
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import * as _ from "underscore";
|
||||
import * as DataModels from "../Contracts/DataModels";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import Explorer from "../Explorer/Explorer";
|
||||
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||
import { QueryUtils } from "../Utils/QueryUtils";
|
||||
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
||||
import { userContext } from "../UserContext";
|
||||
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
|
||||
import { createCollection } from "./dataAccess/createCollection";
|
||||
import { handleError } from "./ErrorHandlingUtils";
|
||||
import { createDocument } from "./dataAccess/createDocument";
|
||||
import { deleteDocument } from "./dataAccess/deleteDocument";
|
||||
import { queryDocuments } from "./dataAccess/queryDocuments";
|
||||
|
||||
export class QueriesClient {
|
||||
private static readonly PartitionKey: DataModels.PartitionKey = {
|
||||
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
||||
kind: BackendDefaults.partitionKeyKind,
|
||||
version: BackendDefaults.partitionKeyVersion,
|
||||
};
|
||||
private static readonly FetchQuery: string = "SELECT * FROM c";
|
||||
private static readonly FetchMongoQuery: string = "{}";
|
||||
|
||||
public constructor(private container: Explorer) {}
|
||||
|
||||
public async setupQueriesCollection(): Promise<DataModels.Collection> {
|
||||
const queriesCollection: ViewModels.Collection = this.findQueriesCollection();
|
||||
if (queriesCollection) {
|
||||
return Promise.resolve(queriesCollection.rawDataModel);
|
||||
}
|
||||
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Setting up account for saving queries");
|
||||
return createCollection({
|
||||
collectionId: SavedQueries.CollectionName,
|
||||
createNewDatabase: true,
|
||||
databaseId: SavedQueries.DatabaseName,
|
||||
partitionKey: QueriesClient.PartitionKey,
|
||||
offerThroughput: SavedQueries.OfferThroughput,
|
||||
databaseLevelThroughput: false,
|
||||
})
|
||||
.then(
|
||||
(collection: DataModels.Collection) => {
|
||||
NotificationConsoleUtils.logConsoleInfo("Successfully set up account for saving queries");
|
||||
return Promise.resolve(collection);
|
||||
},
|
||||
(error: any) => {
|
||||
handleError(error, "setupQueriesCollection", "Failed to set up account for saving queries");
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => clearMessage());
|
||||
}
|
||||
|
||||
public async saveQuery(query: DataModels.Query): Promise<void> {
|
||||
const queriesCollection = this.findQueriesCollection();
|
||||
if (!queriesCollection) {
|
||||
const errorMessage: string = "Account not set up to perform saved query operations";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
|
||||
return Promise.reject(errorMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
this.validateQuery(query);
|
||||
} catch (error) {
|
||||
const errorMessage: string = "Invalid query specified";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
|
||||
return Promise.reject(errorMessage);
|
||||
}
|
||||
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Saving query ${query.queryName}`);
|
||||
query.id = query.queryName;
|
||||
return createDocument(queriesCollection, query)
|
||||
.then(
|
||||
(savedQuery: DataModels.Query) => {
|
||||
NotificationConsoleUtils.logConsoleInfo(`Successfully saved query ${query.queryName}`);
|
||||
return Promise.resolve();
|
||||
},
|
||||
(error: any) => {
|
||||
if (error.code === HttpStatusCodes.Conflict.toString()) {
|
||||
error = `Query ${query.queryName} already exists`;
|
||||
}
|
||||
handleError(error, "saveQuery", `Failed to save query ${query.queryName}`);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => clearMessage());
|
||||
}
|
||||
|
||||
public async getQueries(): Promise<DataModels.Query[]> {
|
||||
const queriesCollection = this.findQueriesCollection();
|
||||
if (!queriesCollection) {
|
||||
const errorMessage: string = "Account not set up to perform saved query operations";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
|
||||
return Promise.reject(errorMessage);
|
||||
}
|
||||
|
||||
const options: any = { enableCrossPartitionQuery: true };
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Fetching saved queries");
|
||||
const queryIterator: QueryIterator<ItemDefinition & Resource> = queryDocuments(
|
||||
SavedQueries.DatabaseName,
|
||||
SavedQueries.CollectionName,
|
||||
this.fetchQueriesQuery(),
|
||||
options
|
||||
);
|
||||
const fetchQueries = async (firstItemIndex: number): Promise<ViewModels.QueryResults> =>
|
||||
await queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex);
|
||||
return QueryUtils.queryAllPages(fetchQueries)
|
||||
.then(
|
||||
(results: ViewModels.QueryResults) => {
|
||||
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
|
||||
if (!document) {
|
||||
return undefined;
|
||||
}
|
||||
const { id, resourceId, query, queryName } = document;
|
||||
const parsedQuery: DataModels.Query = {
|
||||
resourceId: resourceId,
|
||||
queryName: queryName,
|
||||
query: query,
|
||||
id: id,
|
||||
};
|
||||
try {
|
||||
this.validateQuery(parsedQuery);
|
||||
return parsedQuery;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
|
||||
NotificationConsoleUtils.logConsoleInfo("Successfully fetched saved queries");
|
||||
return Promise.resolve(queries);
|
||||
},
|
||||
(error: any) => {
|
||||
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => clearMessage());
|
||||
}
|
||||
|
||||
public async deleteQuery(query: DataModels.Query): Promise<void> {
|
||||
const queriesCollection = this.findQueriesCollection();
|
||||
if (!queriesCollection) {
|
||||
const errorMessage: string = "Account not set up to perform saved query operations";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
|
||||
return Promise.reject(errorMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
this.validateQuery(query);
|
||||
} catch (error) {
|
||||
const errorMessage: string = "Invalid query specified";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to delete query ${query.queryName}: ${errorMessage}`);
|
||||
}
|
||||
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Deleting query ${query.queryName}`);
|
||||
query.id = query.queryName;
|
||||
const documentId = new DocumentId(
|
||||
{
|
||||
partitionKey: QueriesClient.PartitionKey,
|
||||
partitionKeyProperty: "id",
|
||||
} as DocumentsTab,
|
||||
query,
|
||||
query.queryName
|
||||
); // TODO: Remove DocumentId's dependency on DocumentsTab
|
||||
const options: any = { partitionKey: query.resourceId };
|
||||
return deleteDocument(queriesCollection, documentId)
|
||||
.then(
|
||||
() => {
|
||||
NotificationConsoleUtils.logConsoleInfo(`Successfully deleted query ${query.queryName}`);
|
||||
return Promise.resolve();
|
||||
},
|
||||
(error: any) => {
|
||||
handleError(error, "deleteQuery", `Failed to delete query ${query.queryName}`);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => clearMessage());
|
||||
}
|
||||
|
||||
public getResourceId(): string {
|
||||
const databaseAccount = userContext.databaseAccount;
|
||||
const databaseAccountName = (databaseAccount && databaseAccount.name) || "";
|
||||
const subscriptionId = userContext.subscriptionId || "";
|
||||
const resourceGroup = userContext.resourceGroup || "";
|
||||
|
||||
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}`;
|
||||
}
|
||||
|
||||
private findQueriesCollection(): ViewModels.Collection {
|
||||
const queriesDatabase: ViewModels.Database = _.find(
|
||||
this.container.databases(),
|
||||
(database: ViewModels.Database) => database.id() === SavedQueries.DatabaseName
|
||||
);
|
||||
if (!queriesDatabase) {
|
||||
return undefined;
|
||||
}
|
||||
return _.find(
|
||||
queriesDatabase.collections(),
|
||||
(collection: ViewModels.Collection) => collection.id() === SavedQueries.CollectionName
|
||||
);
|
||||
}
|
||||
|
||||
private validateQuery(query: DataModels.Query): void {
|
||||
if (!query || query.queryName == null || query.query == null || query.resourceId == null) {
|
||||
throw new Error("Invalid query specified");
|
||||
}
|
||||
}
|
||||
|
||||
private fetchQueriesQuery(): string {
|
||||
if (this.container.isPreferredApiMongoDB()) {
|
||||
return QueriesClient.FetchMongoQuery;
|
||||
}
|
||||
return QueriesClient.FetchQuery;
|
||||
}
|
||||
}
|
||||
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import * as _ from "underscore";
|
||||
import * as DataModels from "../Contracts/DataModels";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import Explorer from "../Explorer/Explorer";
|
||||
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||
import { QueryUtils } from "../Utils/QueryUtils";
|
||||
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
||||
import { userContext } from "../UserContext";
|
||||
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
|
||||
import { createCollection } from "./dataAccess/createCollection";
|
||||
import { handleError } from "./ErrorHandlingUtils";
|
||||
import { createDocument } from "./dataAccess/createDocument";
|
||||
import { deleteDocument } from "./dataAccess/deleteDocument";
|
||||
import { queryDocuments } from "./dataAccess/queryDocuments";
|
||||
|
||||
export class QueriesClient {
|
||||
private static readonly PartitionKey: DataModels.PartitionKey = {
|
||||
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
||||
kind: BackendDefaults.partitionKeyKind,
|
||||
version: BackendDefaults.partitionKeyVersion
|
||||
};
|
||||
private static readonly FetchQuery: string = "SELECT * FROM c";
|
||||
private static readonly FetchMongoQuery: string = "{}";
|
||||
|
||||
public constructor(private container: Explorer) {}
|
||||
|
||||
public async setupQueriesCollection(): Promise<DataModels.Collection> {
|
||||
const queriesCollection: ViewModels.Collection = this.findQueriesCollection();
|
||||
if (queriesCollection) {
|
||||
return Promise.resolve(queriesCollection.rawDataModel);
|
||||
}
|
||||
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Setting up account for saving queries");
|
||||
return createCollection({
|
||||
collectionId: SavedQueries.CollectionName,
|
||||
createNewDatabase: true,
|
||||
databaseId: SavedQueries.DatabaseName,
|
||||
partitionKey: QueriesClient.PartitionKey,
|
||||
offerThroughput: SavedQueries.OfferThroughput,
|
||||
databaseLevelThroughput: false
|
||||
})
|
||||
.then(
|
||||
(collection: DataModels.Collection) => {
|
||||
NotificationConsoleUtils.logConsoleInfo("Successfully set up account for saving queries");
|
||||
return Promise.resolve(collection);
|
||||
},
|
||||
(error: any) => {
|
||||
handleError(error, "setupQueriesCollection", "Failed to set up account for saving queries");
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => clearMessage());
|
||||
}
|
||||
|
||||
public async saveQuery(query: DataModels.Query): Promise<void> {
|
||||
const queriesCollection = this.findQueriesCollection();
|
||||
if (!queriesCollection) {
|
||||
const errorMessage: string = "Account not set up to perform saved query operations";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
|
||||
return Promise.reject(errorMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
this.validateQuery(query);
|
||||
} catch (error) {
|
||||
const errorMessage: string = "Invalid query specified";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
|
||||
return Promise.reject(errorMessage);
|
||||
}
|
||||
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Saving query ${query.queryName}`);
|
||||
query.id = query.queryName;
|
||||
return createDocument(queriesCollection, query)
|
||||
.then(
|
||||
(savedQuery: DataModels.Query) => {
|
||||
NotificationConsoleUtils.logConsoleInfo(`Successfully saved query ${query.queryName}`);
|
||||
return Promise.resolve();
|
||||
},
|
||||
(error: any) => {
|
||||
if (error.code === HttpStatusCodes.Conflict.toString()) {
|
||||
error = `Query ${query.queryName} already exists`;
|
||||
}
|
||||
handleError(error, "saveQuery", `Failed to save query ${query.queryName}`);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => clearMessage());
|
||||
}
|
||||
|
||||
public async getQueries(): Promise<DataModels.Query[]> {
|
||||
const queriesCollection = this.findQueriesCollection();
|
||||
if (!queriesCollection) {
|
||||
const errorMessage: string = "Account not set up to perform saved query operations";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
|
||||
return Promise.reject(errorMessage);
|
||||
}
|
||||
|
||||
const options: any = { enableCrossPartitionQuery: true };
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Fetching saved queries");
|
||||
const queryIterator: QueryIterator<ItemDefinition & Resource> = queryDocuments(
|
||||
SavedQueries.DatabaseName,
|
||||
SavedQueries.CollectionName,
|
||||
this.fetchQueriesQuery(),
|
||||
options
|
||||
);
|
||||
const fetchQueries = async (firstItemIndex: number): Promise<ViewModels.QueryResults> =>
|
||||
await queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex);
|
||||
return QueryUtils.queryAllPages(fetchQueries)
|
||||
.then(
|
||||
(results: ViewModels.QueryResults) => {
|
||||
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
|
||||
if (!document) {
|
||||
return undefined;
|
||||
}
|
||||
const { id, resourceId, query, queryName } = document;
|
||||
const parsedQuery: DataModels.Query = {
|
||||
resourceId: resourceId,
|
||||
queryName: queryName,
|
||||
query: query,
|
||||
id: id
|
||||
};
|
||||
try {
|
||||
this.validateQuery(parsedQuery);
|
||||
return parsedQuery;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
|
||||
NotificationConsoleUtils.logConsoleInfo("Successfully fetched saved queries");
|
||||
return Promise.resolve(queries);
|
||||
},
|
||||
(error: any) => {
|
||||
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => clearMessage());
|
||||
}
|
||||
|
||||
public async deleteQuery(query: DataModels.Query): Promise<void> {
|
||||
const queriesCollection = this.findQueriesCollection();
|
||||
if (!queriesCollection) {
|
||||
const errorMessage: string = "Account not set up to perform saved query operations";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
|
||||
return Promise.reject(errorMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
this.validateQuery(query);
|
||||
} catch (error) {
|
||||
const errorMessage: string = "Invalid query specified";
|
||||
NotificationConsoleUtils.logConsoleError(`Failed to delete query ${query.queryName}: ${errorMessage}`);
|
||||
}
|
||||
|
||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Deleting query ${query.queryName}`);
|
||||
query.id = query.queryName;
|
||||
const documentId = new DocumentId(
|
||||
{
|
||||
partitionKey: QueriesClient.PartitionKey,
|
||||
partitionKeyProperty: "id"
|
||||
} as DocumentsTab,
|
||||
query,
|
||||
query.queryName
|
||||
); // TODO: Remove DocumentId's dependency on DocumentsTab
|
||||
const options: any = { partitionKey: query.resourceId };
|
||||
return deleteDocument(queriesCollection, documentId)
|
||||
.then(
|
||||
() => {
|
||||
NotificationConsoleUtils.logConsoleInfo(`Successfully deleted query ${query.queryName}`);
|
||||
return Promise.resolve();
|
||||
},
|
||||
(error: any) => {
|
||||
handleError(error, "deleteQuery", `Failed to delete query ${query.queryName}`);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
)
|
||||
.finally(() => clearMessage());
|
||||
}
|
||||
|
||||
public getResourceId(): string {
|
||||
const databaseAccount = userContext.databaseAccount;
|
||||
const databaseAccountName = (databaseAccount && databaseAccount.name) || "";
|
||||
const subscriptionId = userContext.subscriptionId || "";
|
||||
const resourceGroup = userContext.resourceGroup || "";
|
||||
|
||||
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}`;
|
||||
}
|
||||
|
||||
private findQueriesCollection(): ViewModels.Collection {
|
||||
const queriesDatabase: ViewModels.Database = _.find(
|
||||
this.container.databases(),
|
||||
(database: ViewModels.Database) => database.id() === SavedQueries.DatabaseName
|
||||
);
|
||||
if (!queriesDatabase) {
|
||||
return undefined;
|
||||
}
|
||||
return _.find(
|
||||
queriesDatabase.collections(),
|
||||
(collection: ViewModels.Collection) => collection.id() === SavedQueries.CollectionName
|
||||
);
|
||||
}
|
||||
|
||||
private validateQuery(query: DataModels.Query): void {
|
||||
if (!query || query.queryName == null || query.query == null || query.resourceId == null) {
|
||||
throw new Error("Invalid query specified");
|
||||
}
|
||||
}
|
||||
|
||||
private fetchQueriesQuery(): string {
|
||||
if (this.container.isPreferredApiMongoDB()) {
|
||||
return QueriesClient.FetchMongoQuery;
|
||||
}
|
||||
return QueriesClient.FetchQuery;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,107 +1,109 @@
|
||||
import * as ko from "knockout";
|
||||
|
||||
import { SplitterMetrics } from "./Constants";
|
||||
|
||||
export enum SplitterDirection {
|
||||
Horizontal = "horizontal",
|
||||
Vertical = "vertical",
|
||||
}
|
||||
|
||||
export interface SplitterBounds {
|
||||
max: number;
|
||||
min: number;
|
||||
}
|
||||
|
||||
export interface SplitterOptions {
|
||||
splitterId: string;
|
||||
leftId: string;
|
||||
bounds: SplitterBounds;
|
||||
direction: SplitterDirection;
|
||||
}
|
||||
|
||||
export class Splitter {
|
||||
public splitterId: string;
|
||||
public leftSideId: string;
|
||||
|
||||
public splitter!: HTMLElement;
|
||||
public leftSide!: HTMLElement;
|
||||
public lastX!: number;
|
||||
public lastWidth!: number;
|
||||
|
||||
private isCollapsed: ko.Observable<boolean>;
|
||||
private bounds: SplitterBounds;
|
||||
private direction: SplitterDirection;
|
||||
|
||||
constructor(options: SplitterOptions) {
|
||||
this.splitterId = options.splitterId;
|
||||
this.leftSideId = options.leftId;
|
||||
this.isCollapsed = ko.observable<boolean>(false);
|
||||
this.bounds = options.bounds;
|
||||
this.direction = options.direction;
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
public initialize() {
|
||||
if (document.getElementById(this.splitterId) !== null && document.getElementById(this.leftSideId) != null) {
|
||||
this.splitter = <HTMLElement>document.getElementById(this.splitterId);
|
||||
this.leftSide = <HTMLElement>document.getElementById(this.leftSideId);
|
||||
}
|
||||
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
|
||||
const splitterOptions: JQueryUI.ResizableOptions = {
|
||||
animate: true,
|
||||
animateDuration: "fast",
|
||||
start: this.onResizeStart,
|
||||
stop: this.onResizeStop,
|
||||
};
|
||||
|
||||
if (isVerticalSplitter) {
|
||||
$(this.leftSide).css("width", this.bounds.min);
|
||||
$(this.splitter).css("height", "100%");
|
||||
|
||||
splitterOptions.maxWidth = this.bounds.max;
|
||||
splitterOptions.minWidth = this.bounds.min;
|
||||
splitterOptions.handles = { e: "#" + this.splitterId };
|
||||
} else {
|
||||
$(this.leftSide).css("height", this.bounds.min);
|
||||
$(this.splitter).css("width", "100%");
|
||||
|
||||
splitterOptions.maxHeight = this.bounds.max;
|
||||
splitterOptions.minHeight = this.bounds.min;
|
||||
splitterOptions.handles = { s: "#" + this.splitterId };
|
||||
}
|
||||
|
||||
$(this.leftSide).resizable(splitterOptions);
|
||||
}
|
||||
|
||||
private onResizeStart: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
||||
if (this.direction === SplitterDirection.Vertical) {
|
||||
$(".ui-resizable-helper").height("100%");
|
||||
} else {
|
||||
$(".ui-resizable-helper").width("100%");
|
||||
}
|
||||
$("iframe").css("pointer-events", "none");
|
||||
};
|
||||
|
||||
private onResizeStop: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
||||
$("iframe").css("pointer-events", "auto");
|
||||
};
|
||||
|
||||
public collapseLeft() {
|
||||
this.lastX = $(this.splitter).position().left;
|
||||
this.lastWidth = $(this.leftSide).width();
|
||||
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
|
||||
$(this.leftSide).css("width", "");
|
||||
$(this.leftSide).resizable("option", "disabled", true).removeClass("ui-resizable-disabled"); // remove class so splitter is visible
|
||||
$(this.splitter).removeClass("ui-resizable-e");
|
||||
this.isCollapsed(true);
|
||||
}
|
||||
|
||||
public expandLeft() {
|
||||
$(this.splitter).addClass("ui-resizable-e");
|
||||
$(this.leftSide).css("width", this.lastWidth);
|
||||
$(this.splitter).css("left", this.lastX);
|
||||
$(this.splitter).css("left", ""); // this ensures the splitter's position is not fixed and enables movement during resizing
|
||||
$(this.leftSide).resizable("enable");
|
||||
this.isCollapsed(false);
|
||||
}
|
||||
}
|
||||
import * as ko from "knockout";
|
||||
|
||||
import { SplitterMetrics } from "./Constants";
|
||||
|
||||
export enum SplitterDirection {
|
||||
Horizontal = "horizontal",
|
||||
Vertical = "vertical"
|
||||
}
|
||||
|
||||
export interface SplitterBounds {
|
||||
max: number;
|
||||
min: number;
|
||||
}
|
||||
|
||||
export interface SplitterOptions {
|
||||
splitterId: string;
|
||||
leftId: string;
|
||||
bounds: SplitterBounds;
|
||||
direction: SplitterDirection;
|
||||
}
|
||||
|
||||
export class Splitter {
|
||||
public splitterId: string;
|
||||
public leftSideId: string;
|
||||
|
||||
public splitter!: HTMLElement;
|
||||
public leftSide!: HTMLElement;
|
||||
public lastX!: number;
|
||||
public lastWidth!: number;
|
||||
|
||||
private isCollapsed: ko.Observable<boolean>;
|
||||
private bounds: SplitterBounds;
|
||||
private direction: SplitterDirection;
|
||||
|
||||
constructor(options: SplitterOptions) {
|
||||
this.splitterId = options.splitterId;
|
||||
this.leftSideId = options.leftId;
|
||||
this.isCollapsed = ko.observable<boolean>(false);
|
||||
this.bounds = options.bounds;
|
||||
this.direction = options.direction;
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
public initialize() {
|
||||
if (document.getElementById(this.splitterId) !== null && document.getElementById(this.leftSideId) != null) {
|
||||
this.splitter = <HTMLElement>document.getElementById(this.splitterId);
|
||||
this.leftSide = <HTMLElement>document.getElementById(this.leftSideId);
|
||||
}
|
||||
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
|
||||
const splitterOptions: JQueryUI.ResizableOptions = {
|
||||
animate: true,
|
||||
animateDuration: "fast",
|
||||
start: this.onResizeStart,
|
||||
stop: this.onResizeStop
|
||||
};
|
||||
|
||||
if (isVerticalSplitter) {
|
||||
$(this.leftSide).css("width", this.bounds.min);
|
||||
$(this.splitter).css("height", "100%");
|
||||
|
||||
splitterOptions.maxWidth = this.bounds.max;
|
||||
splitterOptions.minWidth = this.bounds.min;
|
||||
splitterOptions.handles = { e: "#" + this.splitterId };
|
||||
} else {
|
||||
$(this.leftSide).css("height", this.bounds.min);
|
||||
$(this.splitter).css("width", "100%");
|
||||
|
||||
splitterOptions.maxHeight = this.bounds.max;
|
||||
splitterOptions.minHeight = this.bounds.min;
|
||||
splitterOptions.handles = { s: "#" + this.splitterId };
|
||||
}
|
||||
|
||||
$(this.leftSide).resizable(splitterOptions);
|
||||
}
|
||||
|
||||
private onResizeStart: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
||||
if (this.direction === SplitterDirection.Vertical) {
|
||||
$(".ui-resizable-helper").height("100%");
|
||||
} else {
|
||||
$(".ui-resizable-helper").width("100%");
|
||||
}
|
||||
$("iframe").css("pointer-events", "none");
|
||||
};
|
||||
|
||||
private onResizeStop: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
||||
$("iframe").css("pointer-events", "auto");
|
||||
};
|
||||
|
||||
public collapseLeft() {
|
||||
this.lastX = $(this.splitter).position().left;
|
||||
this.lastWidth = $(this.leftSide).width();
|
||||
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
|
||||
$(this.leftSide).css("width", "");
|
||||
$(this.leftSide)
|
||||
.resizable("option", "disabled", true)
|
||||
.removeClass("ui-resizable-disabled"); // remove class so splitter is visible
|
||||
$(this.splitter).removeClass("ui-resizable-e");
|
||||
this.isCollapsed(true);
|
||||
}
|
||||
|
||||
public expandLeft() {
|
||||
$(this.splitter).addClass("ui-resizable-e");
|
||||
$(this.leftSide).css("width", this.lastWidth);
|
||||
$(this.splitter).css("left", this.lastX);
|
||||
$(this.splitter).css("left", ""); // this ensures the splitter's position is not fixed and enables movement during resizing
|
||||
$(this.leftSide).resizable("enable");
|
||||
this.isCollapsed(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,18 @@
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*----------------------------------------------------------*/
|
||||
|
||||
export function getMonacoTheme(theme: string): string {
|
||||
switch (theme) {
|
||||
case "default":
|
||||
case "hc-white":
|
||||
return "vs";
|
||||
case "dark":
|
||||
return "vs-dark";
|
||||
case "hc-black":
|
||||
return "hc-black";
|
||||
default:
|
||||
return "vs";
|
||||
export default class ThemeUtility {
|
||||
public static getMonacoTheme(theme: string): string {
|
||||
switch (theme) {
|
||||
case "default":
|
||||
case "hc-white":
|
||||
return "vs";
|
||||
case "dark":
|
||||
return "vs-dark";
|
||||
case "hc-black":
|
||||
return "hc-black";
|
||||
default:
|
||||
return "vs";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ export default class UrlUtility {
|
||||
type: type,
|
||||
objectBody: {
|
||||
id: id,
|
||||
self: resourcePath,
|
||||
},
|
||||
self: resourcePath
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
|
||||
@@ -14,42 +14,38 @@ describe("createCollection", () => {
|
||||
collectionId: "testContainer",
|
||||
databaseId: "testDatabase",
|
||||
databaseLevelThroughput: true,
|
||||
offerThroughput: 400,
|
||||
offerThroughput: 400
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
name: "test"
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
||||
});
|
||||
});
|
||||
|
||||
it("should call ARM if logged in with AAD", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.AAD,
|
||||
});
|
||||
window.authType = AuthType.AAD;
|
||||
await createCollection(createCollectionParams);
|
||||
expect(armRequest).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call SDK if not logged in with non-AAD method", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.MasterKey,
|
||||
});
|
||||
window.authType = AuthType.MasterKey;
|
||||
(client as jest.Mock).mockReturnValue({
|
||||
databases: {
|
||||
createIfNotExists: () => {
|
||||
return {
|
||||
database: {
|
||||
containers: {
|
||||
create: () => ({}),
|
||||
},
|
||||
},
|
||||
create: () => ({})
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
await createCollection(createCollectionParams);
|
||||
expect(client).toHaveBeenCalled();
|
||||
@@ -63,7 +59,7 @@ describe("createCollection", () => {
|
||||
collectionId: "testContainer",
|
||||
databaseId: "testDatabase",
|
||||
databaseLevelThroughput: false,
|
||||
offerThroughput: 400,
|
||||
offerThroughput: 400
|
||||
};
|
||||
expect(constructRpOptions(manualThroughputParams)).toEqual({ throughput: 400 });
|
||||
|
||||
@@ -73,12 +69,12 @@ describe("createCollection", () => {
|
||||
databaseId: "testDatabase",
|
||||
databaseLevelThroughput: false,
|
||||
offerThroughput: 400,
|
||||
autoPilotMaxThroughput: 4000,
|
||||
autoPilotMaxThroughput: 4000
|
||||
};
|
||||
expect(constructRpOptions(autoPilotThroughputParams)).toEqual({
|
||||
autoscaleSettings: {
|
||||
maxThroughput: 4000,
|
||||
},
|
||||
maxThroughput: 4000
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,15 +11,15 @@ import { createMongoCollectionWithProxy } from "../MongoProxyClient";
|
||||
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import {
|
||||
createUpdateCassandraTable,
|
||||
getCassandraTable,
|
||||
getCassandraTable
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import {
|
||||
createUpdateMongoDBCollection,
|
||||
getMongoDBCollection,
|
||||
getMongoDBCollection
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import {
|
||||
createUpdateGremlinGraph,
|
||||
getGremlinGraph,
|
||||
getGremlinGraph
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
||||
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||
@@ -35,13 +35,13 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
|
||||
);
|
||||
try {
|
||||
let collection: DataModels.Collection;
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
if (params.createNewDatabase) {
|
||||
const createDatabaseParams: DataModels.CreateDatabaseParams = {
|
||||
autoPilotMaxThroughput: params.autoPilotMaxThroughput,
|
||||
databaseId: params.databaseId,
|
||||
databaseLevelThroughput: params.databaseLevelThroughput,
|
||||
offerThroughput: params.offerThroughput,
|
||||
offerThroughput: params.offerThroughput
|
||||
};
|
||||
await createDatabase(createDatabaseParams);
|
||||
}
|
||||
@@ -100,7 +100,7 @@ const createSqlContainer = async (params: DataModels.CreateCollectionParams): Pr
|
||||
|
||||
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||
const resource: ARMTypes.SqlContainerResource = {
|
||||
id: params.collectionId,
|
||||
id: params.collectionId
|
||||
};
|
||||
if (params.analyticalStorageTtl) {
|
||||
resource.analyticalStorageTtl = params.analyticalStorageTtl;
|
||||
@@ -118,8 +118,8 @@ const createSqlContainer = async (params: DataModels.CreateCollectionParams): Pr
|
||||
const rpPayload: ARMTypes.SqlDatabaseCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource,
|
||||
options,
|
||||
},
|
||||
options
|
||||
}
|
||||
};
|
||||
|
||||
const createResponse = await createUpdateSqlContainer(
|
||||
@@ -154,7 +154,7 @@ const createMongoCollection = async (params: DataModels.CreateCollectionParams):
|
||||
|
||||
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||
const resource: ARMTypes.MongoDBCollectionResource = {
|
||||
id: params.collectionId,
|
||||
id: params.collectionId
|
||||
};
|
||||
if (params.analyticalStorageTtl) {
|
||||
resource.analyticalStorageTtl = params.analyticalStorageTtl;
|
||||
@@ -170,8 +170,8 @@ const createMongoCollection = async (params: DataModels.CreateCollectionParams):
|
||||
const rpPayload: ARMTypes.MongoDBCollectionCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource,
|
||||
options,
|
||||
},
|
||||
options
|
||||
}
|
||||
};
|
||||
|
||||
const createResponse = await createUpdateMongoDBCollection(
|
||||
@@ -185,7 +185,7 @@ const createMongoCollection = async (params: DataModels.CreateCollectionParams):
|
||||
|
||||
if (params.createMongoWildcardIndex) {
|
||||
TelemetryProcessor.trace(Action.CreateMongoCollectionWithWildcardIndex, ActionModifiers.Mark, {
|
||||
message: "Mongo Collection created with wildcard index on all fields.",
|
||||
message: "Mongo Collection created with wildcard index on all fields."
|
||||
});
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ const createCassandraTable = async (params: DataModels.CreateCollectionParams):
|
||||
|
||||
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||
const resource: ARMTypes.CassandraTableResource = {
|
||||
id: params.collectionId,
|
||||
id: params.collectionId
|
||||
};
|
||||
if (params.analyticalStorageTtl) {
|
||||
resource.analyticalStorageTtl = params.analyticalStorageTtl;
|
||||
@@ -221,8 +221,8 @@ const createCassandraTable = async (params: DataModels.CreateCollectionParams):
|
||||
const rpPayload: ARMTypes.CassandraTableCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource,
|
||||
options,
|
||||
},
|
||||
options
|
||||
}
|
||||
};
|
||||
|
||||
const createResponse = await createUpdateCassandraTable(
|
||||
@@ -256,7 +256,7 @@ const createGraph = async (params: DataModels.CreateCollectionParams): Promise<D
|
||||
|
||||
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||
const resource: ARMTypes.GremlinGraphResource = {
|
||||
id: params.collectionId,
|
||||
id: params.collectionId
|
||||
};
|
||||
|
||||
if (params.indexingPolicy) {
|
||||
@@ -272,8 +272,8 @@ const createGraph = async (params: DataModels.CreateCollectionParams): Promise<D
|
||||
const rpPayload: ARMTypes.GremlinGraphCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource,
|
||||
options,
|
||||
},
|
||||
options
|
||||
}
|
||||
};
|
||||
|
||||
const createResponse = await createUpdateGremlinGraph(
|
||||
@@ -306,14 +306,14 @@ const createTable = async (params: DataModels.CreateCollectionParams): Promise<D
|
||||
|
||||
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
|
||||
const resource: ARMTypes.TableResource = {
|
||||
id: params.collectionId,
|
||||
id: params.collectionId
|
||||
};
|
||||
|
||||
const rpPayload: ARMTypes.TableCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource,
|
||||
options,
|
||||
},
|
||||
options
|
||||
}
|
||||
};
|
||||
|
||||
const createResponse = await createUpdateTable(
|
||||
@@ -334,13 +334,13 @@ export const constructRpOptions = (params: DataModels.CreateDatabaseParams): ARM
|
||||
if (params.autoPilotMaxThroughput) {
|
||||
return {
|
||||
autoscaleSettings: {
|
||||
maxThroughput: params.autoPilotMaxThroughput,
|
||||
},
|
||||
maxThroughput: params.autoPilotMaxThroughput
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
throughput: params.offerThroughput,
|
||||
throughput: params.offerThroughput
|
||||
};
|
||||
};
|
||||
|
||||
@@ -350,7 +350,7 @@ const createCollectionWithSDK = async (params: DataModels.CreateCollectionParams
|
||||
partitionKey: params.partitionKey || undefined,
|
||||
indexingPolicy: params.indexingPolicy || undefined,
|
||||
uniqueKeyPolicy: params.uniqueKeyPolicy || undefined,
|
||||
analyticalStorageTtl: params.analyticalStorageTtl,
|
||||
analyticalStorageTtl: params.analyticalStorageTtl
|
||||
} as ContainerRequest; // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
|
||||
const collectionOptions: RequestOptions = {};
|
||||
const createDatabaseBody: DatabaseRequest = { id: params.databaseId };
|
||||
|
||||
@@ -8,21 +8,21 @@ import {
|
||||
GremlinDatabaseCreateUpdateParameters,
|
||||
MongoDBDatabaseCreateUpdateParameters,
|
||||
SqlDatabaseCreateUpdateParameters,
|
||||
CreateUpdateOptions,
|
||||
CreateUpdateOptions
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import {
|
||||
createUpdateCassandraKeyspace,
|
||||
getCassandraKeyspace,
|
||||
getCassandraKeyspace
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import {
|
||||
createUpdateMongoDBDatabase,
|
||||
getMongoDBDatabase,
|
||||
getMongoDBDatabase
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import {
|
||||
createUpdateGremlinDatabase,
|
||||
getGremlinDatabase,
|
||||
getGremlinDatabase
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||
@@ -34,7 +34,7 @@ export async function createDatabase(params: DataModels.CreateDatabaseParams): P
|
||||
if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
|
||||
throw new Error("Creating database resources is not allowed for tables accounts");
|
||||
}
|
||||
const database: DataModels.Database = await (userContext.authType === AuthType.AAD && !userContext.useSDKOperations
|
||||
const database: DataModels.Database = await (window.authType === AuthType.AAD && !userContext.useSDKOperations
|
||||
? createDatabaseWithARM(params)
|
||||
: createDatabaseWithSDK(params));
|
||||
|
||||
@@ -85,10 +85,10 @@ async function createSqlDatabase(params: DataModels.CreateDatabaseParams): Promi
|
||||
const rpPayload: SqlDatabaseCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: {
|
||||
id: params.databaseId,
|
||||
id: params.databaseId
|
||||
},
|
||||
options,
|
||||
},
|
||||
options
|
||||
}
|
||||
};
|
||||
const createResponse = await createUpdateSqlDatabase(
|
||||
userContext.subscriptionId,
|
||||
@@ -121,10 +121,10 @@ async function createMongoDatabase(params: DataModels.CreateDatabaseParams): Pro
|
||||
const rpPayload: MongoDBDatabaseCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: {
|
||||
id: params.databaseId,
|
||||
id: params.databaseId
|
||||
},
|
||||
options,
|
||||
},
|
||||
options
|
||||
}
|
||||
};
|
||||
const createResponse = await createUpdateMongoDBDatabase(
|
||||
userContext.subscriptionId,
|
||||
@@ -157,10 +157,10 @@ async function createCassandraKeyspace(params: DataModels.CreateDatabaseParams):
|
||||
const rpPayload: CassandraKeyspaceCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: {
|
||||
id: params.databaseId,
|
||||
id: params.databaseId
|
||||
},
|
||||
options,
|
||||
},
|
||||
options
|
||||
}
|
||||
};
|
||||
const createResponse = await createUpdateCassandraKeyspace(
|
||||
userContext.subscriptionId,
|
||||
@@ -193,10 +193,10 @@ async function createGremlineDatabase(params: DataModels.CreateDatabaseParams):
|
||||
const rpPayload: GremlinDatabaseCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: {
|
||||
id: params.databaseId,
|
||||
id: params.databaseId
|
||||
},
|
||||
options,
|
||||
},
|
||||
options
|
||||
}
|
||||
};
|
||||
const createResponse = await createUpdateGremlinDatabase(
|
||||
userContext.subscriptionId,
|
||||
@@ -231,12 +231,12 @@ function constructRpOptions(params: DataModels.CreateDatabaseParams): CreateUpda
|
||||
if (params.autoPilotMaxThroughput) {
|
||||
return {
|
||||
autoscaleSettings: {
|
||||
maxThroughput: params.autoPilotMaxThroughput,
|
||||
},
|
||||
maxThroughput: params.autoPilotMaxThroughput
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
throughput: params.offerThroughput,
|
||||
throughput: params.offerThroughput
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
|
||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||
import {
|
||||
SqlStoredProcedureCreateUpdateParameters,
|
||||
SqlStoredProcedureResource,
|
||||
SqlStoredProcedureResource
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import {
|
||||
createUpdateSqlStoredProcedure,
|
||||
getSqlStoredProcedure,
|
||||
getSqlStoredProcedure
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
@@ -22,7 +22,7 @@ export async function createStoredProcedure(
|
||||
const clearMessage = logConsoleProgress(`Creating stored procedure ${storedProcedure.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -49,8 +49,8 @@ export async function createStoredProcedure(
|
||||
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: storedProcedure as SqlStoredProcedureResource,
|
||||
options: {},
|
||||
},
|
||||
options: {}
|
||||
}
|
||||
};
|
||||
const rpResponse = await createUpdateSqlStoredProcedure(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
|
||||
import { Resource, TriggerDefinition } from "@azure/cosmos";
|
||||
import {
|
||||
SqlTriggerCreateUpdateParameters,
|
||||
SqlTriggerResource,
|
||||
SqlTriggerResource
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
@@ -19,7 +19,7 @@ export async function createTrigger(
|
||||
const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -44,8 +44,8 @@ export async function createTrigger(
|
||||
const createTriggerParams: SqlTriggerCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: trigger as SqlTriggerResource,
|
||||
options: {},
|
||||
},
|
||||
options: {}
|
||||
}
|
||||
};
|
||||
const rpResponse = await createUpdateSqlTrigger(
|
||||
userContext.subscriptionId,
|
||||
@@ -59,7 +59,10 @@ export async function createTrigger(
|
||||
return rpResponse && (rpResponse.properties?.resource as TriggerDefinition & Resource);
|
||||
}
|
||||
|
||||
const response = await client().database(databaseId).container(collectionId).scripts.triggers.create(trigger);
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.triggers.create(trigger);
|
||||
return response.resource;
|
||||
} catch (error) {
|
||||
handleError(error, "CreateTrigger", `Error while creating trigger ${trigger.id}`);
|
||||
|
||||
@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
|
||||
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||
import {
|
||||
SqlUserDefinedFunctionCreateUpdateParameters,
|
||||
SqlUserDefinedFunctionResource,
|
||||
SqlUserDefinedFunctionResource
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import {
|
||||
createUpdateSqlUserDefinedFunction,
|
||||
getSqlUserDefinedFunction,
|
||||
getSqlUserDefinedFunction
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
@@ -22,7 +22,7 @@ export async function createUserDefinedFunction(
|
||||
const clearMessage = logConsoleProgress(`Creating user defined function ${userDefinedFunction.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -49,8 +49,8 @@ export async function createUserDefinedFunction(
|
||||
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: userDefinedFunction as SqlUserDefinedFunctionResource,
|
||||
options: {},
|
||||
},
|
||||
options: {}
|
||||
}
|
||||
};
|
||||
const rpResponse = await createUpdateSqlUserDefinedFunction(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -13,34 +13,30 @@ describe("deleteCollection", () => {
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
name: "test"
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
||||
});
|
||||
});
|
||||
|
||||
it("should call ARM if logged in with AAD", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.AAD,
|
||||
});
|
||||
window.authType = AuthType.AAD;
|
||||
await deleteCollection("database", "collection");
|
||||
expect(armRequest).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call SDK if not logged in with non-AAD method", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.MasterKey,
|
||||
});
|
||||
window.authType = AuthType.MasterKey;
|
||||
(client as jest.Mock).mockReturnValue({
|
||||
database: () => {
|
||||
return {
|
||||
container: () => {
|
||||
return {
|
||||
delete: (): unknown => undefined,
|
||||
delete: (): unknown => undefined
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
await deleteCollection("database", "collection");
|
||||
expect(client).toHaveBeenCalled();
|
||||
|
||||
@@ -13,10 +13,13 @@ import { client } from "../CosmosClient";
|
||||
export async function deleteCollection(databaseId: string, collectionId: string): Promise<void> {
|
||||
const clearMessage = logConsoleProgress(`Deleting container ${collectionId}`);
|
||||
try {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
await deleteCollectionWithARM(databaseId, collectionId);
|
||||
} else {
|
||||
await client().database(databaseId).container(collectionId).delete();
|
||||
await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.delete();
|
||||
}
|
||||
logConsoleInfo(`Successfully deleted container ${collectionId}`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -10,7 +10,7 @@ export const deleteConflict = async (collection: CollectionBase, conflictId: Con
|
||||
|
||||
try {
|
||||
const options = {
|
||||
partitionKey: getPartitionKeyHeaderForConflict(conflictId),
|
||||
partitionKey: getPartitionKeyHeaderForConflict(conflictId)
|
||||
};
|
||||
|
||||
await client()
|
||||
|
||||
@@ -13,30 +13,26 @@ describe("deleteDatabase", () => {
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
name: "test"
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
||||
});
|
||||
});
|
||||
|
||||
it("should call ARM if logged in with AAD", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.AAD,
|
||||
});
|
||||
window.authType = AuthType.AAD;
|
||||
await deleteDatabase("database");
|
||||
expect(armRequest).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call SDK if not logged in with non-AAD method", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.MasterKey,
|
||||
});
|
||||
window.authType = AuthType.MasterKey;
|
||||
(client as jest.Mock).mockReturnValue({
|
||||
database: () => {
|
||||
return {
|
||||
delete: (): unknown => undefined,
|
||||
delete: (): unknown => undefined
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
await deleteDatabase("database");
|
||||
expect(client).toHaveBeenCalled();
|
||||
|
||||
@@ -16,10 +16,12 @@ export async function deleteDatabase(databaseId: string): Promise<void> {
|
||||
if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
|
||||
throw new Error("Deleting database resources is not allowed for tables accounts");
|
||||
}
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
await deleteDatabaseWithARM(databaseId);
|
||||
} else {
|
||||
await client().database(databaseId).delete();
|
||||
await client()
|
||||
.database(databaseId)
|
||||
.delete();
|
||||
}
|
||||
logConsoleInfo(`Successfully deleted database ${databaseId}`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function deleteStoredProcedure(
|
||||
const clearMessage = logConsoleProgress(`Deleting stored procedure ${storedProcedureId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -27,7 +27,11 @@ export async function deleteStoredProcedure(
|
||||
storedProcedureId
|
||||
);
|
||||
} else {
|
||||
await client().database(databaseId).container(collectionId).scripts.storedProcedure(storedProcedureId).delete();
|
||||
await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.storedProcedure(storedProcedureId)
|
||||
.delete();
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, "DeleteStoredProcedure", `Error while deleting stored procedure ${storedProcedureId}`);
|
||||
|
||||
@@ -10,7 +10,7 @@ export async function deleteTrigger(databaseId: string, collectionId: string, tr
|
||||
const clearMessage = logConsoleProgress(`Deleting trigger ${triggerId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -23,7 +23,11 @@ export async function deleteTrigger(databaseId: string, collectionId: string, tr
|
||||
triggerId
|
||||
);
|
||||
} else {
|
||||
await client().database(databaseId).container(collectionId).scripts.trigger(triggerId).delete();
|
||||
await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.trigger(triggerId)
|
||||
.delete();
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, "DeleteTrigger", `Error while deleting trigger ${triggerId}`);
|
||||
|
||||
@@ -10,7 +10,7 @@ export async function deleteUserDefinedFunction(databaseId: string, collectionId
|
||||
const clearMessage = logConsoleProgress(`Deleting user defined function ${id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -23,7 +23,11 @@ export async function deleteUserDefinedFunction(databaseId: string, collectionId
|
||||
id
|
||||
);
|
||||
} else {
|
||||
await client().database(databaseId).container(collectionId).scripts.userDefinedFunction(id).delete();
|
||||
await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.userDefinedFunction(id)
|
||||
.delete();
|
||||
}
|
||||
} catch (error) {
|
||||
handleError(error, "DeleteUserDefinedFunction", `Error while deleting user defined function ${id}`);
|
||||
|
||||
@@ -33,7 +33,7 @@ export const executeStoredProcedure = async (
|
||||
);
|
||||
return {
|
||||
result: response.resource,
|
||||
scriptLogs: response.headers[HttpHeaders.scriptLogResults] as string,
|
||||
scriptLogs: response.headers[HttpHeaders.scriptLogResults] as string
|
||||
};
|
||||
} catch (error) {
|
||||
handleError(
|
||||
|
||||
@@ -41,7 +41,7 @@ interface MetricsResponse {
|
||||
}
|
||||
|
||||
export const getCollectionUsageSizeInKB = async (databaseName: string, containerName: string): Promise<number> => {
|
||||
if (userContext.authType !== AuthType.AAD) {
|
||||
if (window.authType !== AuthType.AAD) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ export const getCollectionUsageSizeInKB = async (databaseName: string, container
|
||||
apiVersion: "2018-01-01",
|
||||
queryParams: {
|
||||
filter,
|
||||
metricNames,
|
||||
},
|
||||
metricNames
|
||||
}
|
||||
});
|
||||
|
||||
if (metricsResponse?.value?.length !== 2) {
|
||||
@@ -76,7 +76,7 @@ export const getCollectionUsageSizeInKB = async (databaseName: string, container
|
||||
return dataUsageSizeInKb + indexUsageSizeInKb;
|
||||
} catch (error) {
|
||||
handleError(error, "getCollectionUsageSize");
|
||||
return undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,16 +3,18 @@ import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import * as Constants from "../Constants";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
|
||||
export async function getIndexTransformationProgress(databaseId: string, collectionId: string): Promise<number> {
|
||||
if (userContext.authType !== AuthType.AAD) {
|
||||
if (window.authType !== AuthType.AAD) {
|
||||
return undefined;
|
||||
}
|
||||
let indexTransformationPercentage: number;
|
||||
const clearMessage = logConsoleProgress(`Reading container ${collectionId}`);
|
||||
try {
|
||||
const response = await client().database(databaseId).container(collectionId).read({ populateQuotaInfo: true });
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.read({ populateQuotaInfo: true });
|
||||
|
||||
indexTransformationPercentage = parseInt(
|
||||
response.headers[Constants.HttpHeaders.collectionIndexTransformationProgress] as string
|
||||
|
||||
@@ -7,5 +7,8 @@ export const queryConflicts = (
|
||||
query: string,
|
||||
options: FeedOptions
|
||||
): QueryIterator<ConflictDefinition & Resource> => {
|
||||
return client().database(databaseId).container(containerId).conflicts.query(query, options);
|
||||
return client()
|
||||
.database(databaseId)
|
||||
.container(containerId)
|
||||
.conflicts.query(query, options);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,10 @@ export const queryDocuments = (
|
||||
options: FeedOptions
|
||||
): QueryIterator<ItemDefinition & Resource> => {
|
||||
options = getCommonQueryOptions(options);
|
||||
return client().database(databaseId).container(containerId).items.query(query, options);
|
||||
return client()
|
||||
.database(databaseId)
|
||||
.container(containerId)
|
||||
.items.query(query, options);
|
||||
};
|
||||
|
||||
export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
|
||||
|
||||
@@ -9,25 +9,25 @@ import { updateUserContext } from "../../UserContext";
|
||||
describe("readCollection", () => {
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
authType: AuthType.ResourceToken,
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
name: "test"
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
||||
});
|
||||
});
|
||||
|
||||
it("should call SDK if logged in with resource token", async () => {
|
||||
window.authType = AuthType.ResourceToken;
|
||||
(client as jest.Mock).mockReturnValue({
|
||||
database: () => {
|
||||
return {
|
||||
container: () => {
|
||||
return {
|
||||
read: (): unknown => ({}),
|
||||
read: (): unknown => ({})
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
await readCollection("database", "collection");
|
||||
expect(client).toHaveBeenCalled();
|
||||
|
||||
@@ -7,7 +7,10 @@ export async function readCollection(databaseId: string, collectionId: string):
|
||||
let collection: DataModels.Collection;
|
||||
const clearMessage = logConsoleProgress(`Querying container ${collectionId}`);
|
||||
try {
|
||||
const response = await client().database(databaseId).container(collectionId).read();
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.read();
|
||||
collection = response.resource as DataModels.Collection;
|
||||
} catch (error) {
|
||||
handleError(error, "ReadCollection", `Error while querying container ${collectionId}`);
|
||||
|
||||
@@ -16,7 +16,7 @@ export const readCollectionOffer = async (params: ReadCollectionOfferParams): Pr
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
) {
|
||||
@@ -106,7 +106,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
autoscaleMaxThroughput: autoscaleSettings.maxThroughput,
|
||||
manualThroughput: undefined,
|
||||
minimumThroughput,
|
||||
offerReplacePending: resource.offerReplacePending === "true",
|
||||
offerReplacePending: resource.offerReplacePending === "true"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
autoscaleMaxThroughput: undefined,
|
||||
manualThroughput: resource.throughput,
|
||||
minimumThroughput,
|
||||
offerReplacePending: resource.offerReplacePending === "true",
|
||||
offerReplacePending: resource.offerReplacePending === "true"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,36 +12,32 @@ describe("readCollections", () => {
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
name: "test"
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
||||
});
|
||||
});
|
||||
|
||||
it("should call ARM if logged in with AAD", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.AAD,
|
||||
});
|
||||
window.authType = AuthType.AAD;
|
||||
await readCollections("database");
|
||||
expect(armRequest).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call SDK if not logged in with non-AAD method", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.MasterKey,
|
||||
});
|
||||
window.authType = AuthType.MasterKey;
|
||||
(client as jest.Mock).mockReturnValue({
|
||||
database: () => {
|
||||
return {
|
||||
containers: {
|
||||
readAll: () => {
|
||||
return {
|
||||
fetchAll: (): unknown => [],
|
||||
fetchAll: (): unknown => []
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
await readCollections("database");
|
||||
expect(client).toHaveBeenCalled();
|
||||
|
||||
@@ -15,7 +15,7 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
|
||||
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
@@ -23,7 +23,10 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
|
||||
return await readCollectionsWithARM(databaseId);
|
||||
}
|
||||
|
||||
const sdkResponse = await client().database(databaseId).containers.readAll().fetchAll();
|
||||
const sdkResponse = await client()
|
||||
.database(databaseId)
|
||||
.containers.readAll()
|
||||
.fetchAll();
|
||||
return sdkResponse.resources as DataModels.Collection[];
|
||||
} catch (error) {
|
||||
handleError(error, "ReadCollections", `Error while querying containers for database ${databaseId}`);
|
||||
@@ -60,5 +63,5 @@ async function readCollectionsWithARM(databaseId: string): Promise<DataModels.Co
|
||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
||||
}
|
||||
|
||||
return rpResponse?.value?.map((collection) => collection.properties?.resource as DataModels.Collection);
|
||||
return rpResponse?.value?.map(collection => collection.properties?.resource as DataModels.Collection);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promis
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
) {
|
||||
@@ -78,7 +78,7 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
|
||||
autoscaleMaxThroughput: autoscaleSettings.maxThroughput,
|
||||
manualThroughput: undefined,
|
||||
minimumThroughput,
|
||||
offerReplacePending: resource.offerReplacePending === "true",
|
||||
offerReplacePending: resource.offerReplacePending === "true"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
|
||||
autoscaleMaxThroughput: undefined,
|
||||
manualThroughput: resource.throughput,
|
||||
minimumThroughput,
|
||||
offerReplacePending: resource.offerReplacePending === "true",
|
||||
offerReplacePending: resource.offerReplacePending === "true"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,32 +12,28 @@ describe("readDatabases", () => {
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
name: "test",
|
||||
name: "test"
|
||||
} as DatabaseAccount,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB,
|
||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
||||
});
|
||||
});
|
||||
|
||||
it("should call ARM if logged in with AAD", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.AAD,
|
||||
});
|
||||
window.authType = AuthType.AAD;
|
||||
await readDatabases();
|
||||
expect(armRequest).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call SDK if not logged in with non-AAD method", async () => {
|
||||
updateUserContext({
|
||||
authType: AuthType.MasterKey,
|
||||
});
|
||||
window.authType = AuthType.MasterKey;
|
||||
(client as jest.Mock).mockReturnValue({
|
||||
databases: {
|
||||
readAll: () => {
|
||||
return {
|
||||
fetchAll: (): unknown => [],
|
||||
fetchAll: (): unknown => []
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
await readDatabases();
|
||||
expect(client).toHaveBeenCalled();
|
||||
|
||||
@@ -15,13 +15,15 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
|
||||
const clearMessage = logConsoleProgress(`Querying databases`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
) {
|
||||
databases = await readDatabasesWithARM();
|
||||
} else {
|
||||
const sdkResponse = await client().databases.readAll().fetchAll();
|
||||
const sdkResponse = await client()
|
||||
.databases.readAll()
|
||||
.fetchAll();
|
||||
databases = sdkResponse.resources as DataModels.Database[];
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -56,5 +58,5 @@ async function readDatabasesWithARM(): Promise<DataModels.Database[]> {
|
||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
||||
}
|
||||
|
||||
return rpResponse?.value?.map((database) => database.properties?.resource as DataModels.Database);
|
||||
return rpResponse?.value?.map(database => database.properties?.resource as DataModels.Database);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function readMongoDBCollectionThroughRP(
|
||||
databaseId: string,
|
||||
collectionId: string
|
||||
): Promise<MongoDBCollectionResource> {
|
||||
if (userContext.authType !== AuthType.AAD) {
|
||||
if (window.authType !== AuthType.AAD) {
|
||||
return undefined;
|
||||
}
|
||||
let collection: MongoDBCollectionResource;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { readOffers } from "./readOffers";
|
||||
export const readOfferWithSDK = async (offerId: string, resourceId: string): Promise<Offer> => {
|
||||
if (!offerId) {
|
||||
const offers = await readOffers();
|
||||
const offer = offers.find((offer) => offer.resource === resourceId);
|
||||
const offer = offers.find(offer => offer.resource === resourceId);
|
||||
|
||||
if (!offer) {
|
||||
return undefined;
|
||||
@@ -18,10 +18,12 @@ export const readOfferWithSDK = async (offerId: string, resourceId: string): Pro
|
||||
|
||||
const options: RequestOptions = {
|
||||
initialHeaders: {
|
||||
[HttpHeaders.populateCollectionThroughputInfo]: true,
|
||||
},
|
||||
[HttpHeaders.populateCollectionThroughputInfo]: true
|
||||
}
|
||||
};
|
||||
const response = await client().offer(offerId).read(options);
|
||||
const response = await client()
|
||||
.offer(offerId)
|
||||
.read(options);
|
||||
|
||||
return parseSDKOfferResponse(response);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,9 @@ export const readOffers = async (): Promise<SDKOfferDefinition[]> => {
|
||||
const clearMessage = logConsoleProgress(`Querying offers`);
|
||||
|
||||
try {
|
||||
const response = await client().offers.readAll().fetchAll();
|
||||
const response = await client()
|
||||
.offers.readAll()
|
||||
.fetchAll();
|
||||
return response?.resources;
|
||||
} catch (error) {
|
||||
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too.
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function readStoredProcedures(
|
||||
const clearMessage = logConsoleProgress(`Querying stored procedures for container ${collectionId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -25,7 +25,7 @@ export async function readStoredProcedures(
|
||||
databaseId,
|
||||
collectionId
|
||||
);
|
||||
return rpResponse?.value?.map((sproc) => sproc.properties?.resource as StoredProcedureDefinition & Resource);
|
||||
return rpResponse?.value?.map(sproc => sproc.properties?.resource as StoredProcedureDefinition & Resource);
|
||||
}
|
||||
|
||||
const response = await client()
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function readTriggers(
|
||||
const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -25,10 +25,14 @@ export async function readTriggers(
|
||||
databaseId,
|
||||
collectionId
|
||||
);
|
||||
return rpResponse?.value?.map((trigger) => trigger.properties?.resource as TriggerDefinition & Resource);
|
||||
return rpResponse?.value?.map(trigger => trigger.properties?.resource as TriggerDefinition & Resource);
|
||||
}
|
||||
|
||||
const response = await client().database(databaseId).container(collectionId).scripts.triggers.readAll().fetchAll();
|
||||
const response = await client()
|
||||
.database(databaseId)
|
||||
.container(collectionId)
|
||||
.scripts.triggers.readAll()
|
||||
.fetchAll();
|
||||
return response?.resources;
|
||||
} catch (error) {
|
||||
handleError(error, "ReadTriggers", `Failed to query triggers for container ${collectionId}`);
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function readUserDefinedFunctions(
|
||||
const clearMessage = logConsoleProgress(`Querying user defined functions for container ${collectionId}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -25,7 +25,7 @@ export async function readUserDefinedFunctions(
|
||||
databaseId,
|
||||
collectionId
|
||||
);
|
||||
return rpResponse?.value?.map((udf) => udf.properties?.resource as UserDefinedFunctionDefinition & Resource);
|
||||
return rpResponse?.value?.map(udf => udf.properties?.resource as UserDefinedFunctionDefinition & Resource);
|
||||
}
|
||||
|
||||
const response = await client()
|
||||
|
||||
@@ -8,22 +8,22 @@ import {
|
||||
MongoDBCollectionCreateUpdateParameters,
|
||||
MongoDBCollectionResource,
|
||||
SqlContainerCreateUpdateParameters,
|
||||
SqlContainerResource,
|
||||
SqlContainerResource
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||
import { client } from "../CosmosClient";
|
||||
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import {
|
||||
createUpdateCassandraTable,
|
||||
getCassandraTable,
|
||||
getCassandraTable
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import {
|
||||
createUpdateMongoDBCollection,
|
||||
getMongoDBCollection,
|
||||
getMongoDBCollection
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import {
|
||||
createUpdateGremlinGraph,
|
||||
getGremlinGraph,
|
||||
getGremlinGraph
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
@@ -41,7 +41,7 @@ export async function updateCollection(
|
||||
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
|
||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
||||
@@ -130,8 +130,8 @@ export async function updateMongoDBCollectionThroughRP(
|
||||
const updateParams: MongoDBCollectionCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: newCollection,
|
||||
options: updateOptions,
|
||||
},
|
||||
options: updateOptions
|
||||
}
|
||||
};
|
||||
|
||||
const updateResponse = await createUpdateMongoDBCollection(
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
migrateSqlDatabaseToManualThroughput,
|
||||
migrateSqlContainerToAutoscale,
|
||||
migrateSqlContainerToManualThroughput,
|
||||
updateSqlContainerThroughput,
|
||||
updateSqlContainerThroughput
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import {
|
||||
updateCassandraKeyspaceThroughput,
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
migrateCassandraKeyspaceToManualThroughput,
|
||||
migrateCassandraTableToAutoscale,
|
||||
migrateCassandraTableToManualThroughput,
|
||||
updateCassandraTableThroughput,
|
||||
updateCassandraTableThroughput
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
||||
import {
|
||||
updateMongoDBDatabaseThroughput,
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
migrateMongoDBDatabaseToManualThroughput,
|
||||
migrateMongoDBCollectionToAutoscale,
|
||||
migrateMongoDBCollectionToManualThroughput,
|
||||
updateMongoDBCollectionThroughput,
|
||||
updateMongoDBCollectionThroughput
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||
import {
|
||||
updateGremlinDatabaseThroughput,
|
||||
@@ -41,13 +41,13 @@ import {
|
||||
migrateGremlinDatabaseToManualThroughput,
|
||||
migrateGremlinGraphToAutoscale,
|
||||
migrateGremlinGraphToManualThroughput,
|
||||
updateGremlinGraphThroughput,
|
||||
updateGremlinGraphThroughput
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
||||
import { userContext } from "../../UserContext";
|
||||
import {
|
||||
migrateTableToAutoscale,
|
||||
migrateTableToManualThroughput,
|
||||
updateTableThroughput,
|
||||
updateTableThroughput
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
||||
|
||||
export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> => {
|
||||
@@ -58,7 +58,7 @@ export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> =>
|
||||
const clearMessage = logConsoleProgress(`Updating offer for ${offerResourceText}`);
|
||||
|
||||
try {
|
||||
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
||||
if (params.collectionId) {
|
||||
updatedOffer = await updateCollectionOfferWithARM(params);
|
||||
} else if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
|
||||
@@ -110,7 +110,7 @@ const updateCollectionOfferWithARM = async (params: UpdateOfferParams): Promise<
|
||||
return await readCollectionOffer({
|
||||
collectionId: params.collectionId,
|
||||
databaseId: params.databaseId,
|
||||
offerId: params.currentOffer.id,
|
||||
offerId: params.currentOffer.id
|
||||
});
|
||||
};
|
||||
|
||||
@@ -140,7 +140,7 @@ const updateDatabaseOfferWithARM = async (params: UpdateOfferParams): Promise<Of
|
||||
|
||||
return await readDatabaseOffer({
|
||||
databaseId: params.databaseId,
|
||||
offerId: params.currentOffer.id,
|
||||
offerId: params.currentOffer.id
|
||||
});
|
||||
};
|
||||
|
||||
@@ -358,13 +358,13 @@ const updateGremlinDatabaseOffer = async (params: UpdateOfferParams): Promise<vo
|
||||
const createUpdateOfferBody = (params: UpdateOfferParams): ThroughputSettingsUpdateParameters => {
|
||||
const body: ThroughputSettingsUpdateParameters = {
|
||||
properties: {
|
||||
resource: {},
|
||||
},
|
||||
resource: {}
|
||||
}
|
||||
};
|
||||
|
||||
if (params.autopilotThroughput) {
|
||||
body.properties.resource.autoscaleSettings = {
|
||||
maxThroughput: params.autopilotThroughput,
|
||||
maxThroughput: params.autopilotThroughput
|
||||
};
|
||||
} else {
|
||||
body.properties.resource.throughput = params.manualThroughput;
|
||||
@@ -378,7 +378,7 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
|
||||
const newOffer: SDKOfferDefinition = {
|
||||
content: {
|
||||
offerThroughput: undefined,
|
||||
offerIsRUPerMinuteThroughputEnabled: false,
|
||||
offerIsRUPerMinuteThroughputEnabled: false
|
||||
},
|
||||
_etag: undefined,
|
||||
_ts: undefined,
|
||||
@@ -388,12 +388,12 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
|
||||
offerResourceId: sdkOfferDefinition.offerResourceId,
|
||||
offerVersion: sdkOfferDefinition.offerVersion,
|
||||
offerType: sdkOfferDefinition.offerType,
|
||||
resource: sdkOfferDefinition.resource,
|
||||
resource: sdkOfferDefinition.resource
|
||||
};
|
||||
|
||||
if (params.autopilotThroughput) {
|
||||
newOffer.content.offerAutopilotSettings = {
|
||||
maxThroughput: params.autopilotThroughput,
|
||||
maxThroughput: params.autopilotThroughput
|
||||
};
|
||||
} else {
|
||||
newOffer.content.offerThroughput = params.manualThroughput;
|
||||
@@ -402,12 +402,12 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
|
||||
const options: RequestOptions = {};
|
||||
if (params.migrateToAutoPilot) {
|
||||
options.initialHeaders = {
|
||||
[HttpHeaders.migrateOfferToAutopilot]: "true",
|
||||
[HttpHeaders.migrateOfferToAutopilot]: "true"
|
||||
};
|
||||
delete newOffer.content.offerAutopilotSettings;
|
||||
} else if (params.migrateToManual) {
|
||||
options.initialHeaders = {
|
||||
[HttpHeaders.migrateOfferToManualThroughput]: "true",
|
||||
[HttpHeaders.migrateOfferToManualThroughput]: "true"
|
||||
};
|
||||
newOffer.content.offerAutopilotSettings = { maxThroughput: 0 };
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
|
||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||
import {
|
||||
SqlStoredProcedureCreateUpdateParameters,
|
||||
SqlStoredProcedureResource,
|
||||
SqlStoredProcedureResource
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import {
|
||||
createUpdateSqlStoredProcedure,
|
||||
getSqlStoredProcedure,
|
||||
getSqlStoredProcedure
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
@@ -22,7 +22,7 @@ export async function updateStoredProcedure(
|
||||
const clearMessage = logConsoleProgress(`Updating stored procedure ${storedProcedure.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -39,8 +39,8 @@ export async function updateStoredProcedure(
|
||||
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: storedProcedure as SqlStoredProcedureResource,
|
||||
options: {},
|
||||
},
|
||||
options: {}
|
||||
}
|
||||
};
|
||||
const rpResponse = await createUpdateSqlStoredProcedure(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AuthType } from "../../AuthType";
|
||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
||||
import {
|
||||
SqlTriggerCreateUpdateParameters,
|
||||
SqlTriggerResource,
|
||||
SqlTriggerResource
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { TriggerDefinition } from "@azure/cosmos";
|
||||
import { client } from "../CosmosClient";
|
||||
@@ -19,7 +19,7 @@ export async function updateTrigger(
|
||||
const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -36,8 +36,8 @@ export async function updateTrigger(
|
||||
const createTriggerParams: SqlTriggerCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: trigger as SqlTriggerResource,
|
||||
options: {},
|
||||
},
|
||||
options: {}
|
||||
}
|
||||
};
|
||||
const rpResponse = await createUpdateSqlTrigger(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
|
||||
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||
import {
|
||||
SqlUserDefinedFunctionCreateUpdateParameters,
|
||||
SqlUserDefinedFunctionResource,
|
||||
SqlUserDefinedFunctionResource
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||
import { client } from "../CosmosClient";
|
||||
import {
|
||||
createUpdateSqlUserDefinedFunction,
|
||||
getSqlUserDefinedFunction,
|
||||
getSqlUserDefinedFunction
|
||||
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
@@ -22,7 +22,7 @@ export async function updateUserDefinedFunction(
|
||||
const clearMessage = logConsoleProgress(`Updating user defined function ${userDefinedFunction.id}`);
|
||||
try {
|
||||
if (
|
||||
userContext.authType === AuthType.AAD &&
|
||||
window.authType === AuthType.AAD &&
|
||||
!userContext.useSDKOperations &&
|
||||
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
|
||||
) {
|
||||
@@ -39,8 +39,8 @@ export async function updateUserDefinedFunction(
|
||||
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
|
||||
properties: {
|
||||
resource: userDefinedFunction as SqlUserDefinedFunctionResource,
|
||||
options: {},
|
||||
},
|
||||
options: {}
|
||||
}
|
||||
};
|
||||
const rpResponse = await createUpdateSqlUserDefinedFunction(
|
||||
userContext.subscriptionId,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export enum Platform {
|
||||
Portal = "Portal",
|
||||
Hosted = "Hosted",
|
||||
Emulator = "Emulator",
|
||||
Emulator = "Emulator"
|
||||
}
|
||||
|
||||
export interface ConfigContext {
|
||||
interface ConfigContext {
|
||||
platform: Platform;
|
||||
allowedParentFrameOrigins: string[];
|
||||
gitSha?: string;
|
||||
@@ -37,7 +37,7 @@ let configContext: Readonly<ConfigContext> = {
|
||||
`^https:\\/\\/[\\.\\w]*portal\\.microsoftazure.de$`,
|
||||
`^https:\\/\\/[\\.\\w]*ext\\.azure\\.(com|cn|us)$`,
|
||||
`^https:\\/\\/[\\.\\w]*\\.ext\\.microsoftazure\\.de$`,
|
||||
`^https://cosmos-db-dataexplorer-germanycentral.azurewebsites.de$`,
|
||||
`^https://cosmos-db-dataexplorer-germanycentral.azurewebsites.de$`
|
||||
],
|
||||
// Webpack injects this at build time
|
||||
gitSha: process.env.GIT_SHA,
|
||||
@@ -52,7 +52,7 @@ let configContext: Readonly<ConfigContext> = {
|
||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
||||
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
|
||||
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
|
||||
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
|
||||
};
|
||||
|
||||
export function resetConfigContext(): void {
|
||||
@@ -73,24 +73,20 @@ if (process.env.NODE_ENV === "development") {
|
||||
BACKEND_ENDPOINT: "https://localhost:" + port,
|
||||
MONGO_BACKEND_ENDPOINT: "https://localhost:" + port,
|
||||
PROXY_PATH: "/proxy",
|
||||
EMULATOR_ENDPOINT: "https://localhost:8081",
|
||||
EMULATOR_ENDPOINT: "https://localhost:8081"
|
||||
});
|
||||
}
|
||||
|
||||
export async function initializeConfiguration(): Promise<ConfigContext> {
|
||||
try {
|
||||
const response = await fetch("./config.json", {
|
||||
headers: {
|
||||
"If-None-Match": "", // disable client side cache
|
||||
},
|
||||
});
|
||||
const response = await fetch("./config.json");
|
||||
if (response.status === 200) {
|
||||
try {
|
||||
const { allowedParentFrameOrigins, ...externalConfig } = await response.json();
|
||||
Object.assign(configContext, externalConfig);
|
||||
if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) {
|
||||
updateConfigContext({
|
||||
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins],
|
||||
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins]
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -108,7 +104,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
|
||||
const platform = params.get("platform");
|
||||
switch (platform) {
|
||||
default:
|
||||
console.error(`Invalid platform query parameter: ${platform}`);
|
||||
console.log("Invalid platform query parameter given, ignoring");
|
||||
break;
|
||||
case Platform.Portal:
|
||||
case Platform.Hosted:
|
||||
@@ -117,7 +113,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("No configuration file found using defaults");
|
||||
console.log("No configuration file found using defaults");
|
||||
}
|
||||
return configContext;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export enum TabKind {
|
||||
TableEntities,
|
||||
Graph,
|
||||
SQLQuery,
|
||||
ScaleSettings,
|
||||
ScaleSettings
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20,7 +20,7 @@ export enum PaneKind {
|
||||
DeleteDatabase,
|
||||
GlobalSettings,
|
||||
AdHocAccess,
|
||||
SwitchDirectory,
|
||||
SwitchDirectory
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,5 +79,5 @@ export enum ActionType {
|
||||
OpenCollectionTab,
|
||||
OpenPane,
|
||||
TransmitCachedData,
|
||||
OpenSampleNotebook,
|
||||
OpenSampleNotebook
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ export enum LogEntryLevel {
|
||||
/**
|
||||
* Error level.
|
||||
*/
|
||||
Error = 2,
|
||||
Error = 2
|
||||
}
|
||||
/**
|
||||
* Schema of a log entry.
|
||||
|
||||
@@ -1,38 +1,39 @@
|
||||
import * as Versions from "./Versions";
|
||||
import * as ActionContracts from "./ActionContracts";
|
||||
import * as Diagnostics from "./Diagnostics";
|
||||
|
||||
/**
|
||||
* Messaging types used with Data Explorer <-> Portal communication
|
||||
* and Hosted <-> Explorer communication
|
||||
*/
|
||||
export enum MessageTypes {
|
||||
TelemetryInfo,
|
||||
LogInfo,
|
||||
RefreshResources,
|
||||
AllDatabases,
|
||||
CollectionsForDatabase,
|
||||
RefreshOffers,
|
||||
AllOffers,
|
||||
UpdateLocationHash,
|
||||
SingleOffer,
|
||||
RefreshOffer,
|
||||
UpdateAccountName,
|
||||
ForbiddenError,
|
||||
AadSignIn,
|
||||
GetAccessAadRequest,
|
||||
GetAccessAadResponse,
|
||||
UpdateAccountSwitch,
|
||||
UpdateDirectoryControl,
|
||||
SwitchAccount,
|
||||
SendNotification,
|
||||
ClearNotification,
|
||||
ExplorerClickEvent,
|
||||
LoadingStatus,
|
||||
GetArcadiaToken,
|
||||
CreateWorkspace,
|
||||
CreateSparkPool,
|
||||
RefreshDatabaseAccount,
|
||||
}
|
||||
|
||||
export { Versions, ActionContracts, Diagnostics };
|
||||
import * as Versions from "./Versions";
|
||||
import * as ActionContracts from "./ActionContracts";
|
||||
import * as Diagnostics from "./Diagnostics";
|
||||
|
||||
/**
|
||||
* Messaging types used with Data Explorer <-> Portal communication
|
||||
* and Hosted <-> Explorer communication
|
||||
*/
|
||||
export enum MessageTypes {
|
||||
TelemetryInfo,
|
||||
LogInfo,
|
||||
RefreshResources,
|
||||
AllDatabases,
|
||||
CollectionsForDatabase,
|
||||
RefreshOffers,
|
||||
AllOffers,
|
||||
UpdateLocationHash,
|
||||
SingleOffer,
|
||||
RefreshOffer,
|
||||
UpdateAccountName,
|
||||
ForbiddenError,
|
||||
AadSignIn,
|
||||
GetAccessAadRequest,
|
||||
GetAccessAadResponse,
|
||||
UpdateAccountSwitch,
|
||||
UpdateDirectoryControl,
|
||||
SwitchAccount,
|
||||
SendNotification,
|
||||
ClearNotification,
|
||||
ExplorerClickEvent,
|
||||
LoadingStatus,
|
||||
GetArcadiaToken,
|
||||
CreateWorkspace,
|
||||
CreateSparkPool,
|
||||
RefreshDatabaseAccount,
|
||||
InitTestExplorer
|
||||
}
|
||||
|
||||
export { Versions, ActionContracts, Diagnostics };
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* Messaging types used with SelfServe Component <-> Portal communication
|
||||
* and Hosted <-> SelfServe Component communication
|
||||
*/
|
||||
|
||||
export enum SelfServeMessageTypes {
|
||||
TelemetryInfo = "TelemetryInfo",
|
||||
Notification = "Notification",
|
||||
}
|
||||
@@ -3,5 +3,5 @@ export enum SubscriptionType {
|
||||
EA,
|
||||
Free,
|
||||
Internal,
|
||||
PAYG,
|
||||
PAYG
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
* Data Explorer version {major.minor.patch}
|
||||
*/
|
||||
export const DataExplorer: string = "1.0.1";
|
||||
/**
|
||||
* Data Explorer version {major.minor.patch}
|
||||
*/
|
||||
export const DataExplorer: string = "1.0.1";
|
||||
|
||||
@@ -1,446 +1,440 @@
|
||||
import {
|
||||
QueryMetrics,
|
||||
Resource,
|
||||
StoredProcedureDefinition,
|
||||
TriggerDefinition,
|
||||
UserDefinedFunctionDefinition,
|
||||
} from "@azure/cosmos";
|
||||
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../Explorer/Explorer";
|
||||
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient";
|
||||
import ConflictId from "../Explorer/Tree/ConflictId";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
||||
import Trigger from "../Explorer/Tree/Trigger";
|
||||
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
|
||||
import { SelfServeType } from "../SelfServe/SelfServeUtils";
|
||||
import { UploadDetails } from "../workers/upload/definitions";
|
||||
import * as DataModels from "./DataModels";
|
||||
import { SubscriptionType } from "./SubscriptionType";
|
||||
|
||||
export interface TokenProvider {
|
||||
getAuthHeader(): Promise<Headers>;
|
||||
}
|
||||
|
||||
export interface QueryResultsMetadata {
|
||||
hasMoreResults: boolean;
|
||||
firstItemIndex: number;
|
||||
lastItemIndex: number;
|
||||
itemCount: number;
|
||||
}
|
||||
|
||||
export interface QueryResults extends QueryResultsMetadata {
|
||||
documents: any[];
|
||||
activityId: string;
|
||||
requestCharge: number;
|
||||
roundTrips?: number;
|
||||
headers?: any;
|
||||
queryMetrics?: QueryMetrics;
|
||||
}
|
||||
|
||||
export interface Button {
|
||||
visible: ko.Computed<boolean>;
|
||||
enabled: ko.Computed<boolean>;
|
||||
isSelected?: ko.Computed<boolean>;
|
||||
}
|
||||
|
||||
export interface NotificationConsole {
|
||||
filteredConsoleData: ko.ObservableArray<ConsoleData>;
|
||||
isConsoleExpanded: ko.Observable<boolean>;
|
||||
|
||||
expandConsole(source: any, evt: MouseEvent): void;
|
||||
collapseConsole(source: any, evt: MouseEvent): void;
|
||||
}
|
||||
|
||||
export interface WaitsForTemplate {
|
||||
isTemplateReady: ko.Observable<boolean>;
|
||||
}
|
||||
|
||||
export interface TreeNode {
|
||||
nodeKind: string;
|
||||
rid: string;
|
||||
id: ko.Observable<string>;
|
||||
database?: Database;
|
||||
collection?: Collection;
|
||||
|
||||
onNewQueryClick?(source: any, event: MouseEvent): void;
|
||||
onNewStoredProcedureClick?(source: Collection, event: MouseEvent): void;
|
||||
onNewUserDefinedFunctionClick?(source: Collection, event: MouseEvent): void;
|
||||
onNewTriggerClick?(source: Collection, event: MouseEvent): void;
|
||||
}
|
||||
|
||||
export interface Database extends TreeNode {
|
||||
container: Explorer;
|
||||
self: string;
|
||||
id: ko.Observable<string>;
|
||||
collections: ko.ObservableArray<Collection>;
|
||||
offer: ko.Observable<DataModels.Offer>;
|
||||
isDatabaseExpanded: ko.Observable<boolean>;
|
||||
isDatabaseShared: ko.Computed<boolean>;
|
||||
|
||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||
|
||||
selectDatabase(): void;
|
||||
expandDatabase(): Promise<void>;
|
||||
collapseDatabase(): void;
|
||||
|
||||
loadCollections(): Promise<void>;
|
||||
findCollectionWithId(collectionId: string): Collection;
|
||||
openAddCollection(database: Database, event: MouseEvent): void;
|
||||
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
|
||||
onSettingsClick: () => void;
|
||||
loadOffer(): Promise<void>;
|
||||
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
|
||||
}
|
||||
|
||||
export interface CollectionBase extends TreeNode {
|
||||
container: Explorer;
|
||||
databaseId: string;
|
||||
self: string;
|
||||
rawDataModel: DataModels.Collection;
|
||||
partitionKey: DataModels.PartitionKey;
|
||||
partitionKeyProperty: string;
|
||||
partitionKeyPropertyHeader: string;
|
||||
id: ko.Observable<string>;
|
||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||
children: ko.ObservableArray<TreeNode>;
|
||||
isCollectionExpanded: ko.Observable<boolean>;
|
||||
|
||||
onDocumentDBDocumentsClick(): void;
|
||||
onNewQueryClick(source: any, event?: MouseEvent, queryText?: string): void;
|
||||
expandCollection(): void;
|
||||
collapseCollection(): void;
|
||||
getDatabase(): Database;
|
||||
}
|
||||
|
||||
export interface Collection extends CollectionBase {
|
||||
defaultTtl: ko.Observable<number>;
|
||||
analyticalStorageTtl: ko.Observable<number>;
|
||||
schema?: DataModels.ISchema;
|
||||
requestSchema?: () => void;
|
||||
indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
|
||||
uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
|
||||
usageSizeInKB: ko.Observable<number>;
|
||||
offer: ko.Observable<DataModels.Offer>;
|
||||
conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
|
||||
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
||||
geospatialConfig: ko.Observable<DataModels.GeospatialConfig>;
|
||||
documentIds: ko.ObservableArray<DocumentId>;
|
||||
|
||||
cassandraKeys: CassandraTableKeys;
|
||||
cassandraSchema: CassandraTableKey[];
|
||||
|
||||
onConflictsClick(): void;
|
||||
onTableEntitiesClick(): void;
|
||||
onGraphDocumentsClick(): void;
|
||||
onMongoDBDocumentsClick(): void;
|
||||
openTab(): void;
|
||||
|
||||
onSettingsClick: () => Promise<void>;
|
||||
|
||||
onNewGraphClick(): void;
|
||||
onNewMongoQueryClick(source: any, event?: MouseEvent, queryText?: string): void;
|
||||
onNewMongoShellClick(): void;
|
||||
onNewStoredProcedureClick(source: Collection, event?: MouseEvent): void;
|
||||
onNewUserDefinedFunctionClick(source: Collection, event?: MouseEvent): void;
|
||||
onNewTriggerClick(source: Collection, event?: MouseEvent): void;
|
||||
storedProcedures: ko.Computed<StoredProcedure[]>;
|
||||
userDefinedFunctions: ko.Computed<UserDefinedFunction[]>;
|
||||
triggers: ko.Computed<Trigger[]>;
|
||||
|
||||
isStoredProceduresExpanded: ko.Observable<boolean>;
|
||||
isTriggersExpanded: ko.Observable<boolean>;
|
||||
isUserDefinedFunctionsExpanded: ko.Observable<boolean>;
|
||||
|
||||
expandStoredProcedures(): void;
|
||||
expandUserDefinedFunctions(): void;
|
||||
expandTriggers(): void;
|
||||
|
||||
collapseStoredProcedures(): void;
|
||||
collapseUserDefinedFunctions(): void;
|
||||
collapseTriggers(): void;
|
||||
|
||||
loadUserDefinedFunctions(): Promise<any>;
|
||||
loadStoredProcedures(): Promise<any>;
|
||||
loadTriggers(): Promise<any>;
|
||||
loadOffer(): Promise<void>;
|
||||
|
||||
createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure;
|
||||
createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction;
|
||||
createTriggerNode(data: TriggerDefinition & Resource): Trigger;
|
||||
findStoredProcedureWithId(sprocRid: string): StoredProcedure;
|
||||
findTriggerWithId(triggerRid: string): Trigger;
|
||||
findUserDefinedFunctionWithId(udfRid: string): UserDefinedFunction;
|
||||
|
||||
onDragOver(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||
uploadFiles(fileList: FileList): Promise<UploadDetails>;
|
||||
|
||||
getLabel(): string;
|
||||
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options used to initialize pane
|
||||
*/
|
||||
export interface PaneOptions {
|
||||
id: string;
|
||||
visible: ko.Observable<boolean>;
|
||||
container?: Explorer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Graph configuration
|
||||
*/
|
||||
export enum NeighborType {
|
||||
SOURCES_ONLY,
|
||||
TARGETS_ONLY,
|
||||
BOTH,
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of observable related to graph configuration by user
|
||||
*/
|
||||
export interface GraphConfigUiData {
|
||||
showNeighborType: ko.Observable<NeighborType>;
|
||||
nodeProperties: ko.ObservableArray<string>;
|
||||
nodePropertiesWithNone: ko.ObservableArray<string>;
|
||||
nodeCaptionChoice: ko.Observable<string>;
|
||||
nodeColorKeyChoice: ko.Observable<string>;
|
||||
nodeIconChoice: ko.Observable<string>;
|
||||
nodeIconSet: ko.Observable<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* User input for creating new vertex
|
||||
*/
|
||||
export interface NewVertexData {
|
||||
label: string;
|
||||
properties: InputProperty[];
|
||||
}
|
||||
|
||||
export type GremlinPropertyValueType = string | boolean | number | null | undefined;
|
||||
export type InputPropertyValueTypeString = "string" | "number" | "boolean" | "null";
|
||||
export interface InputPropertyValue {
|
||||
value: GremlinPropertyValueType;
|
||||
type: InputPropertyValueTypeString;
|
||||
}
|
||||
/**
|
||||
* Property input by user
|
||||
*/
|
||||
export interface InputProperty {
|
||||
key: string;
|
||||
values: InputPropertyValue[];
|
||||
}
|
||||
|
||||
export interface Editable<T> extends ko.Observable<T> {
|
||||
setBaseline(baseline: T): void;
|
||||
|
||||
editableIsDirty: ko.Computed<boolean>;
|
||||
editableIsValid: ko.Observable<boolean>;
|
||||
getEditableCurrentValue?: ko.Computed<T>;
|
||||
getEditableOriginalValue?: ko.Computed<T>;
|
||||
edits?: ko.ObservableArray<T>;
|
||||
validations?: ko.ObservableArray<(value: T) => boolean>;
|
||||
}
|
||||
|
||||
export interface QueryError {
|
||||
message: string;
|
||||
start: string;
|
||||
end: string;
|
||||
code: string;
|
||||
severity: string;
|
||||
}
|
||||
|
||||
export interface DocumentRequestContainer {
|
||||
self: string;
|
||||
rid?: string;
|
||||
resourceName?: string;
|
||||
}
|
||||
|
||||
export interface DocumentClientOption {
|
||||
endpoint?: string;
|
||||
masterKey?: string;
|
||||
requestTimeoutMs?: number;
|
||||
}
|
||||
|
||||
// Tab options
|
||||
export interface TabOptions {
|
||||
tabKind: CollectionTabKind;
|
||||
title: string;
|
||||
tabPath: string;
|
||||
isActive: ko.Observable<boolean>;
|
||||
hashLocation: string;
|
||||
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]) => void;
|
||||
isTabsContentExpanded?: ko.Observable<boolean>;
|
||||
onLoadStartKey?: number;
|
||||
|
||||
// TODO Remove the flag and use a context to handle this
|
||||
// TODO: 145357 Remove dependency on collection/database and add abstraction
|
||||
collection?: CollectionBase;
|
||||
database?: Database;
|
||||
rid?: string;
|
||||
node?: TreeNode;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
export interface DocumentsTabOptions extends TabOptions {
|
||||
partitionKey: DataModels.PartitionKey;
|
||||
documentIds: ko.ObservableArray<DocumentId>;
|
||||
container?: Explorer;
|
||||
isPreferredApiMongoDB?: boolean;
|
||||
resourceTokenPartitionKey?: string;
|
||||
}
|
||||
|
||||
export interface ConflictsTabOptions extends TabOptions {
|
||||
partitionKey: DataModels.PartitionKey;
|
||||
conflictIds: ko.ObservableArray<ConflictId>;
|
||||
container?: Explorer;
|
||||
}
|
||||
|
||||
export interface QueryTabOptions extends TabOptions {
|
||||
partitionKey?: DataModels.PartitionKey;
|
||||
queryText?: string;
|
||||
resourceTokenPartitionKey?: string;
|
||||
}
|
||||
|
||||
export interface ScriptTabOption extends TabOptions {
|
||||
resource: any;
|
||||
isNew: boolean;
|
||||
partitionKey?: DataModels.PartitionKey;
|
||||
}
|
||||
|
||||
export interface EditorPosition {
|
||||
line: number;
|
||||
column: number;
|
||||
}
|
||||
|
||||
export enum DocumentExplorerState {
|
||||
noDocumentSelected,
|
||||
newDocumentValid,
|
||||
newDocumentInvalid,
|
||||
exisitingDocumentNoEdits,
|
||||
exisitingDocumentDirtyValid,
|
||||
exisitingDocumentDirtyInvalid,
|
||||
}
|
||||
|
||||
export enum IndexingPolicyEditorState {
|
||||
noCollectionSelected,
|
||||
noEdits,
|
||||
dirtyValid,
|
||||
dirtyInvalid,
|
||||
}
|
||||
|
||||
export enum ScriptEditorState {
|
||||
newInvalid,
|
||||
newValid,
|
||||
exisitingNoEdits,
|
||||
exisitingDirtyValid,
|
||||
exisitingDirtyInvalid,
|
||||
}
|
||||
|
||||
export enum CollectionTabKind {
|
||||
Documents = 0,
|
||||
Settings = 1,
|
||||
StoredProcedures = 2,
|
||||
UserDefinedFunctions = 3,
|
||||
Triggers = 4,
|
||||
Query = 5,
|
||||
Graph = 6,
|
||||
QueryTables = 9,
|
||||
MongoShell = 10,
|
||||
DatabaseSettings = 11,
|
||||
Conflicts = 12,
|
||||
Notebook = 13 /* Deprecated */,
|
||||
Terminal = 14,
|
||||
NotebookV2 = 15,
|
||||
SparkMasterTab = 16 /* Deprecated */,
|
||||
Gallery = 17,
|
||||
NotebookViewer = 18,
|
||||
Schema = 19,
|
||||
CollectionSettingsV2 = 20,
|
||||
DatabaseSettingsV2 = 21,
|
||||
}
|
||||
|
||||
export enum TerminalKind {
|
||||
Default = 0,
|
||||
Mongo = 1,
|
||||
Cassandra = 2,
|
||||
}
|
||||
|
||||
export interface DataExplorerInputsFrame {
|
||||
databaseAccount: any;
|
||||
subscriptionId?: string;
|
||||
resourceGroup?: string;
|
||||
masterKey?: string;
|
||||
hasWriteAccess?: boolean;
|
||||
authorizationToken?: string;
|
||||
features: { [key: string]: string };
|
||||
csmEndpoint?: string;
|
||||
dnsSuffix?: string;
|
||||
serverId?: string;
|
||||
extensionEndpoint?: string;
|
||||
subscriptionType?: SubscriptionType;
|
||||
quotaId?: string;
|
||||
addCollectionDefaultFlight?: string;
|
||||
isTryCosmosDBSubscription?: boolean;
|
||||
loadDatabaseAccountTimestamp?: number;
|
||||
sharedThroughputMinimum?: number;
|
||||
sharedThroughputMaximum?: number;
|
||||
sharedThroughputDefault?: number;
|
||||
dataExplorerVersion?: string;
|
||||
isAuthWithresourceToken?: boolean;
|
||||
defaultCollectionThroughput?: CollectionCreationDefaults;
|
||||
flights?: readonly string[];
|
||||
}
|
||||
|
||||
export interface SelfServeFrameInputs {
|
||||
selfServeType: SelfServeType;
|
||||
databaseAccount: any;
|
||||
subscriptionId: string;
|
||||
resourceGroup: string;
|
||||
authorizationToken: string;
|
||||
csmEndpoint: string;
|
||||
flights?: readonly string[];
|
||||
}
|
||||
|
||||
export interface CollectionCreationDefaults {
|
||||
storage: string;
|
||||
throughput: ThroughputDefaults;
|
||||
}
|
||||
|
||||
export interface ThroughputDefaults {
|
||||
fixed: number;
|
||||
unlimited:
|
||||
| number
|
||||
| {
|
||||
collectionThreshold: number;
|
||||
lessThanOrEqualToThreshold: number;
|
||||
greatThanThreshold: number;
|
||||
};
|
||||
unlimitedmax: number;
|
||||
unlimitedmin: number;
|
||||
shared: number;
|
||||
}
|
||||
|
||||
export class MonacoEditorSettings {
|
||||
public readonly language: string;
|
||||
public readonly readOnly: boolean;
|
||||
|
||||
constructor(supportedLanguage: string, isReadOnly: boolean) {
|
||||
this.language = supportedLanguage;
|
||||
this.readOnly = isReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
export interface AuthorizationTokenHeaderMetadata {
|
||||
header: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface DropdownOption<T> {
|
||||
text: string;
|
||||
value: T;
|
||||
disable?: boolean;
|
||||
}
|
||||
import {
|
||||
QueryMetrics,
|
||||
Resource,
|
||||
StoredProcedureDefinition,
|
||||
TriggerDefinition,
|
||||
UserDefinedFunctionDefinition
|
||||
} from "@azure/cosmos";
|
||||
import Q from "q";
|
||||
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../Explorer/Explorer";
|
||||
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient";
|
||||
import ConflictId from "../Explorer/Tree/ConflictId";
|
||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
||||
import Trigger from "../Explorer/Tree/Trigger";
|
||||
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
|
||||
import { SelfServeType } from "../SelfServe/SelfServeUtils";
|
||||
import { UploadDetails } from "../workers/upload/definitions";
|
||||
import * as DataModels from "./DataModels";
|
||||
import { SubscriptionType } from "./SubscriptionType";
|
||||
|
||||
export interface TokenProvider {
|
||||
getAuthHeader(): Promise<Headers>;
|
||||
}
|
||||
|
||||
export interface QueryResultsMetadata {
|
||||
hasMoreResults: boolean;
|
||||
firstItemIndex: number;
|
||||
lastItemIndex: number;
|
||||
itemCount: number;
|
||||
}
|
||||
|
||||
export interface QueryResults extends QueryResultsMetadata {
|
||||
documents: any[];
|
||||
activityId: string;
|
||||
requestCharge: number;
|
||||
roundTrips?: number;
|
||||
headers?: any;
|
||||
queryMetrics?: QueryMetrics;
|
||||
}
|
||||
|
||||
export interface Button {
|
||||
visible: ko.Computed<boolean>;
|
||||
enabled: ko.Computed<boolean>;
|
||||
isSelected?: ko.Computed<boolean>;
|
||||
}
|
||||
|
||||
export interface NotificationConsole {
|
||||
filteredConsoleData: ko.ObservableArray<ConsoleData>;
|
||||
isConsoleExpanded: ko.Observable<boolean>;
|
||||
|
||||
expandConsole(source: any, evt: MouseEvent): void;
|
||||
collapseConsole(source: any, evt: MouseEvent): void;
|
||||
}
|
||||
|
||||
export interface WaitsForTemplate {
|
||||
isTemplateReady: ko.Observable<boolean>;
|
||||
}
|
||||
|
||||
export interface TreeNode {
|
||||
nodeKind: string;
|
||||
rid: string;
|
||||
id: ko.Observable<string>;
|
||||
database?: Database;
|
||||
collection?: Collection;
|
||||
|
||||
onNewQueryClick?(source: any, event: MouseEvent): void;
|
||||
onNewStoredProcedureClick?(source: Collection, event: MouseEvent): void;
|
||||
onNewUserDefinedFunctionClick?(source: Collection, event: MouseEvent): void;
|
||||
onNewTriggerClick?(source: Collection, event: MouseEvent): void;
|
||||
}
|
||||
|
||||
export interface Database extends TreeNode {
|
||||
container: Explorer;
|
||||
self: string;
|
||||
id: ko.Observable<string>;
|
||||
collections: ko.ObservableArray<Collection>;
|
||||
offer: ko.Observable<DataModels.Offer>;
|
||||
isDatabaseExpanded: ko.Observable<boolean>;
|
||||
isDatabaseShared: ko.Computed<boolean>;
|
||||
|
||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||
|
||||
selectDatabase(): void;
|
||||
expandDatabase(): Promise<void>;
|
||||
collapseDatabase(): void;
|
||||
|
||||
loadCollections(): Promise<void>;
|
||||
findCollectionWithId(collectionId: string): Collection;
|
||||
openAddCollection(database: Database, event: MouseEvent): void;
|
||||
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
|
||||
onSettingsClick: () => void;
|
||||
loadOffer(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface CollectionBase extends TreeNode {
|
||||
container: Explorer;
|
||||
databaseId: string;
|
||||
self: string;
|
||||
rawDataModel: DataModels.Collection;
|
||||
partitionKey: DataModels.PartitionKey;
|
||||
partitionKeyProperty: string;
|
||||
partitionKeyPropertyHeader: string;
|
||||
id: ko.Observable<string>;
|
||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||
children: ko.ObservableArray<TreeNode>;
|
||||
isCollectionExpanded: ko.Observable<boolean>;
|
||||
|
||||
onDocumentDBDocumentsClick(): void;
|
||||
onNewQueryClick(source: any, event: MouseEvent, queryText?: string): void;
|
||||
expandCollection(): Q.Promise<any>;
|
||||
collapseCollection(): void;
|
||||
getDatabase(): Database;
|
||||
}
|
||||
|
||||
export interface Collection extends CollectionBase {
|
||||
defaultTtl: ko.Observable<number>;
|
||||
analyticalStorageTtl: ko.Observable<number>;
|
||||
schema?: DataModels.ISchema;
|
||||
requestSchema?: () => void;
|
||||
indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
|
||||
uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
|
||||
usageSizeInKB: ko.Observable<number>;
|
||||
offer: ko.Observable<DataModels.Offer>;
|
||||
conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
|
||||
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
||||
geospatialConfig: ko.Observable<DataModels.GeospatialConfig>;
|
||||
documentIds: ko.ObservableArray<DocumentId>;
|
||||
|
||||
cassandraKeys: CassandraTableKeys;
|
||||
cassandraSchema: CassandraTableKey[];
|
||||
|
||||
onConflictsClick(): void;
|
||||
onTableEntitiesClick(): void;
|
||||
onGraphDocumentsClick(): void;
|
||||
onMongoDBDocumentsClick(): void;
|
||||
openTab(): void;
|
||||
|
||||
onSettingsClick: () => Promise<void>;
|
||||
onDeleteCollectionContextMenuClick(source: Collection, event: MouseEvent): void;
|
||||
|
||||
onNewGraphClick(): void;
|
||||
onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string): void;
|
||||
onNewMongoShellClick(): void;
|
||||
onNewStoredProcedureClick(source: Collection, event: MouseEvent): void;
|
||||
onNewUserDefinedFunctionClick(source: Collection, event: MouseEvent): void;
|
||||
onNewTriggerClick(source: Collection, event: MouseEvent): void;
|
||||
storedProcedures: ko.Computed<StoredProcedure[]>;
|
||||
userDefinedFunctions: ko.Computed<UserDefinedFunction[]>;
|
||||
triggers: ko.Computed<Trigger[]>;
|
||||
|
||||
isStoredProceduresExpanded: ko.Observable<boolean>;
|
||||
isTriggersExpanded: ko.Observable<boolean>;
|
||||
isUserDefinedFunctionsExpanded: ko.Observable<boolean>;
|
||||
|
||||
expandStoredProcedures(): void;
|
||||
expandUserDefinedFunctions(): void;
|
||||
expandTriggers(): void;
|
||||
|
||||
collapseStoredProcedures(): void;
|
||||
collapseUserDefinedFunctions(): void;
|
||||
collapseTriggers(): void;
|
||||
|
||||
loadUserDefinedFunctions(): Promise<any>;
|
||||
loadStoredProcedures(): Promise<any>;
|
||||
loadTriggers(): Promise<any>;
|
||||
loadOffer(): Promise<void>;
|
||||
|
||||
createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure;
|
||||
createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction;
|
||||
createTriggerNode(data: TriggerDefinition & Resource): Trigger;
|
||||
findStoredProcedureWithId(sprocRid: string): StoredProcedure;
|
||||
findTriggerWithId(triggerRid: string): Trigger;
|
||||
findUserDefinedFunctionWithId(udfRid: string): UserDefinedFunction;
|
||||
|
||||
onDragOver(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||
uploadFiles(fileList: FileList): Q.Promise<UploadDetails>;
|
||||
|
||||
getLabel(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options used to initialize pane
|
||||
*/
|
||||
export interface PaneOptions {
|
||||
id: string;
|
||||
visible: ko.Observable<boolean>;
|
||||
container?: Explorer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Graph configuration
|
||||
*/
|
||||
export enum NeighborType {
|
||||
SOURCES_ONLY,
|
||||
TARGETS_ONLY,
|
||||
BOTH
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of observable related to graph configuration by user
|
||||
*/
|
||||
export interface GraphConfigUiData {
|
||||
showNeighborType: ko.Observable<NeighborType>;
|
||||
nodeProperties: ko.ObservableArray<string>;
|
||||
nodePropertiesWithNone: ko.ObservableArray<string>;
|
||||
nodeCaptionChoice: ko.Observable<string>;
|
||||
nodeColorKeyChoice: ko.Observable<string>;
|
||||
nodeIconChoice: ko.Observable<string>;
|
||||
nodeIconSet: ko.Observable<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* User input for creating new vertex
|
||||
*/
|
||||
export interface NewVertexData {
|
||||
label: string;
|
||||
properties: InputProperty[];
|
||||
}
|
||||
|
||||
export type GremlinPropertyValueType = string | boolean | number | null | undefined;
|
||||
export type InputPropertyValueTypeString = "string" | "number" | "boolean" | "null";
|
||||
export interface InputPropertyValue {
|
||||
value: GremlinPropertyValueType;
|
||||
type: InputPropertyValueTypeString;
|
||||
}
|
||||
/**
|
||||
* Property input by user
|
||||
*/
|
||||
export interface InputProperty {
|
||||
key: string;
|
||||
values: InputPropertyValue[];
|
||||
}
|
||||
|
||||
export interface Editable<T> extends ko.Observable<T> {
|
||||
setBaseline(baseline: T): void;
|
||||
|
||||
editableIsDirty: ko.Computed<boolean>;
|
||||
editableIsValid: ko.Observable<boolean>;
|
||||
getEditableCurrentValue?: ko.Computed<T>;
|
||||
getEditableOriginalValue?: ko.Computed<T>;
|
||||
edits?: ko.ObservableArray<T>;
|
||||
validations?: ko.ObservableArray<(value: T) => boolean>;
|
||||
}
|
||||
|
||||
export interface QueryError {
|
||||
message: string;
|
||||
start: string;
|
||||
end: string;
|
||||
code: string;
|
||||
severity: string;
|
||||
}
|
||||
|
||||
export interface DocumentRequestContainer {
|
||||
self: string;
|
||||
rid?: string;
|
||||
resourceName?: string;
|
||||
}
|
||||
|
||||
export interface DocumentClientOption {
|
||||
endpoint?: string;
|
||||
masterKey?: string;
|
||||
requestTimeoutMs?: number;
|
||||
}
|
||||
|
||||
// Tab options
|
||||
export interface TabOptions {
|
||||
tabKind: CollectionTabKind;
|
||||
title: string;
|
||||
tabPath: string;
|
||||
isActive: ko.Observable<boolean>;
|
||||
hashLocation: string;
|
||||
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]) => void;
|
||||
isTabsContentExpanded?: ko.Observable<boolean>;
|
||||
onLoadStartKey?: number;
|
||||
|
||||
// TODO Remove the flag and use a context to handle this
|
||||
// TODO: 145357 Remove dependency on collection/database and add abstraction
|
||||
collection?: CollectionBase;
|
||||
database?: Database;
|
||||
rid?: string;
|
||||
node?: TreeNode;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
export interface DocumentsTabOptions extends TabOptions {
|
||||
partitionKey: DataModels.PartitionKey;
|
||||
documentIds: ko.ObservableArray<DocumentId>;
|
||||
container?: Explorer;
|
||||
isPreferredApiMongoDB?: boolean;
|
||||
resourceTokenPartitionKey?: string;
|
||||
}
|
||||
|
||||
export interface SettingsTabV2Options extends TabOptions {
|
||||
getPendingNotification: Q.Promise<DataModels.Notification>;
|
||||
}
|
||||
|
||||
export interface ConflictsTabOptions extends TabOptions {
|
||||
partitionKey: DataModels.PartitionKey;
|
||||
conflictIds: ko.ObservableArray<ConflictId>;
|
||||
container?: Explorer;
|
||||
}
|
||||
|
||||
export interface QueryTabOptions extends TabOptions {
|
||||
partitionKey?: DataModels.PartitionKey;
|
||||
queryText?: string;
|
||||
resourceTokenPartitionKey?: string;
|
||||
}
|
||||
|
||||
export interface ScriptTabOption extends TabOptions {
|
||||
resource: any;
|
||||
isNew: boolean;
|
||||
partitionKey?: DataModels.PartitionKey;
|
||||
}
|
||||
|
||||
export interface EditorPosition {
|
||||
line: number;
|
||||
column: number;
|
||||
}
|
||||
|
||||
export enum DocumentExplorerState {
|
||||
noDocumentSelected,
|
||||
newDocumentValid,
|
||||
newDocumentInvalid,
|
||||
exisitingDocumentNoEdits,
|
||||
exisitingDocumentDirtyValid,
|
||||
exisitingDocumentDirtyInvalid
|
||||
}
|
||||
|
||||
export enum IndexingPolicyEditorState {
|
||||
noCollectionSelected,
|
||||
noEdits,
|
||||
dirtyValid,
|
||||
dirtyInvalid
|
||||
}
|
||||
|
||||
export enum ScriptEditorState {
|
||||
newInvalid,
|
||||
newValid,
|
||||
exisitingNoEdits,
|
||||
exisitingDirtyValid,
|
||||
exisitingDirtyInvalid
|
||||
}
|
||||
|
||||
export enum CollectionTabKind {
|
||||
Documents = 0,
|
||||
Settings = 1,
|
||||
StoredProcedures = 2,
|
||||
UserDefinedFunctions = 3,
|
||||
Triggers = 4,
|
||||
Query = 5,
|
||||
Graph = 6,
|
||||
QueryTables = 9,
|
||||
MongoShell = 10,
|
||||
DatabaseSettings = 11,
|
||||
Conflicts = 12,
|
||||
Notebook = 13 /* Deprecated */,
|
||||
Terminal = 14,
|
||||
NotebookV2 = 15,
|
||||
SparkMasterTab = 16,
|
||||
Gallery = 17,
|
||||
NotebookViewer = 18,
|
||||
Schema = 19,
|
||||
SettingsV2 = 20
|
||||
}
|
||||
|
||||
export enum TerminalKind {
|
||||
Default = 0,
|
||||
Mongo = 1,
|
||||
Cassandra = 2
|
||||
}
|
||||
|
||||
export interface DataExplorerInputsFrame {
|
||||
databaseAccount: any;
|
||||
subscriptionId: string;
|
||||
resourceGroup: string;
|
||||
masterKey: string;
|
||||
hasWriteAccess: boolean;
|
||||
authorizationToken: string;
|
||||
features: any;
|
||||
csmEndpoint: string;
|
||||
dnsSuffix: string;
|
||||
serverId: string;
|
||||
extensionEndpoint: string;
|
||||
subscriptionType: SubscriptionType;
|
||||
quotaId: string;
|
||||
addCollectionDefaultFlight: string;
|
||||
isTryCosmosDBSubscription: boolean;
|
||||
loadDatabaseAccountTimestamp?: number;
|
||||
sharedThroughputMinimum?: number;
|
||||
sharedThroughputMaximum?: number;
|
||||
sharedThroughputDefault?: number;
|
||||
dataExplorerVersion?: string;
|
||||
isAuthWithresourceToken?: boolean;
|
||||
defaultCollectionThroughput?: CollectionCreationDefaults;
|
||||
flights?: readonly string[];
|
||||
selfServeType?: SelfServeType;
|
||||
}
|
||||
|
||||
export interface CollectionCreationDefaults {
|
||||
storage: string;
|
||||
throughput: ThroughputDefaults;
|
||||
}
|
||||
|
||||
export interface ThroughputDefaults {
|
||||
fixed: number;
|
||||
unlimited:
|
||||
| number
|
||||
| {
|
||||
collectionThreshold: number;
|
||||
lessThanOrEqualToThreshold: number;
|
||||
greatThanThreshold: number;
|
||||
};
|
||||
unlimitedmax: number;
|
||||
unlimitedmin: number;
|
||||
shared: number;
|
||||
}
|
||||
|
||||
export class MonacoEditorSettings {
|
||||
public readonly language: string;
|
||||
public readonly readOnly: boolean;
|
||||
|
||||
constructor(supportedLanguage: string, isReadOnly: boolean) {
|
||||
this.language = supportedLanguage;
|
||||
this.readOnly = isReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
export interface AuthorizationTokenHeaderMetadata {
|
||||
header: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface DropdownOption<T> {
|
||||
text: string;
|
||||
value: T;
|
||||
disable?: boolean;
|
||||
}
|
||||
|
||||
@@ -6,19 +6,19 @@ describe("The Heatmap Control", () => {
|
||||
const dataPoints = {
|
||||
"1": {
|
||||
"2019-06-19T00:59:10Z": {
|
||||
"Normalized Throughput": 0.35,
|
||||
"Normalized Throughput": 0.35
|
||||
},
|
||||
"2019-06-19T00:48:10Z": {
|
||||
"Normalized Throughput": 0.25,
|
||||
},
|
||||
},
|
||||
"Normalized Throughput": 0.25
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const chartCaptions = {
|
||||
chartTitle: "chart title",
|
||||
yAxisTitle: "YAxisTitle",
|
||||
tooltipText: "Tooltip text",
|
||||
timeWindow: 123456789,
|
||||
timeWindow: 123456789
|
||||
};
|
||||
|
||||
let heatmap: Heatmap;
|
||||
@@ -75,12 +75,12 @@ describe("The Heatmap Control", () => {
|
||||
if (dayjs().utcOffset()) {
|
||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
|
||||
"2019-06-19T00:48:10Z",
|
||||
"2019-06-19T00:59:10Z",
|
||||
"2019-06-19T00:59:10Z"
|
||||
]);
|
||||
} else {
|
||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
|
||||
"2019-06-19T00:48:10Z",
|
||||
"2019-06-19T00:59:10Z",
|
||||
"2019-06-19T00:59:10Z"
|
||||
]);
|
||||
}
|
||||
});
|
||||
@@ -106,9 +106,9 @@ describe("iframe rendering when there is no data", () => {
|
||||
data: {
|
||||
chartData: {},
|
||||
chartSettings: {},
|
||||
theme: 4,
|
||||
},
|
||||
},
|
||||
theme: 4
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
||||
@@ -126,9 +126,9 @@ describe("iframe rendering when there is no data", () => {
|
||||
data: {
|
||||
chartData: {},
|
||||
chartSettings: {},
|
||||
theme: 2,
|
||||
},
|
||||
},
|
||||
theme: 2
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
HeatmapData,
|
||||
LayoutSettings,
|
||||
PartitionTimeStampToData,
|
||||
PortalTheme,
|
||||
PortalTheme
|
||||
} from "./HeatmapDatatypes";
|
||||
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
||||
import { sendCachedDataMessage, sendMessage } from "../../Common/MessageHandler";
|
||||
@@ -43,7 +43,7 @@ export class Heatmap {
|
||||
return {
|
||||
family: StyleConstants.DataExplorerFont,
|
||||
size,
|
||||
color,
|
||||
color
|
||||
};
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export class Heatmap {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
};
|
||||
// go thru all rows and create 2d matrix for heatmap...
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
@@ -115,7 +115,7 @@ export class Heatmap {
|
||||
[0.7, "#E46612"],
|
||||
[0.8, "#E64914"],
|
||||
[0.9, "#B80016"],
|
||||
[1.0, "#B80016"],
|
||||
[1.0, "#B80016"]
|
||||
],
|
||||
name: "",
|
||||
hovertemplate: this._heatmapCaptions.tooltipText,
|
||||
@@ -123,11 +123,11 @@ export class Heatmap {
|
||||
thickness: 15,
|
||||
outlinewidth: 0,
|
||||
tickcolor: StyleConstants.BaseDark,
|
||||
tickfont: this._getFontStyles(10, this._defaultFontColor),
|
||||
tickfont: this._getFontStyles(10, this._defaultFontColor)
|
||||
},
|
||||
y: this._chartData.yAxisPoints,
|
||||
x: this._chartData.xAxisPoints,
|
||||
},
|
||||
x: this._chartData.xAxisPoints
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ export class Heatmap {
|
||||
r: 10,
|
||||
b: 35,
|
||||
t: 30,
|
||||
pad: 0,
|
||||
pad: 0
|
||||
},
|
||||
paper_bgcolor: "transparent",
|
||||
plot_bgcolor: "transparent",
|
||||
@@ -154,7 +154,7 @@ export class Heatmap {
|
||||
autotick: true,
|
||||
fixedrange: true,
|
||||
ticks: "",
|
||||
showticklabels: false,
|
||||
showticklabels: false
|
||||
},
|
||||
xaxis: {
|
||||
fixedrange: true,
|
||||
@@ -167,13 +167,13 @@ export class Heatmap {
|
||||
autotick: true,
|
||||
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
|
||||
showticklabels: true,
|
||||
tickfont: this._getFontStyles(10),
|
||||
tickfont: this._getFontStyles(10)
|
||||
},
|
||||
title: {
|
||||
text: this._heatmapCaptions.chartTitle,
|
||||
x: 0.01,
|
||||
font: this._getFontStyles(13, this._defaultFontColor),
|
||||
},
|
||||
font: this._getFontStyles(13, this._defaultFontColor)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ export class Heatmap {
|
||||
return {
|
||||
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
|
||||
responsive: true,*/
|
||||
displayModeBar: false,
|
||||
displayModeBar: false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export enum PortalTheme {
|
||||
blue = 1,
|
||||
azure,
|
||||
light,
|
||||
dark,
|
||||
dark
|
||||
}
|
||||
|
||||
export interface HeatmapData {
|
||||
|
||||
@@ -4,5 +4,5 @@ export enum DefaultAccountExperienceType {
|
||||
MongoDB = "MongoDB",
|
||||
Table = "Table",
|
||||
Cassandra = "Cassandra",
|
||||
ApiForMongoDB = "Azure Cosmos DB for MongoDB API",
|
||||
ApiForMongoDB = "Azure Cosmos DB for MongoDB API"
|
||||
}
|
||||
|
||||
383
src/Definitions/adal.d.ts
vendored
Normal file
383
src/Definitions/adal.d.ts
vendored
Normal file
@@ -0,0 +1,383 @@
|
||||
// Type definitions for adal-angular 1.0.1.1
|
||||
// Project: https://github.com/AzureAD/azure-activedirectory-library-for-js#readme
|
||||
// Definitions by: Daniel Perez Alvarez <https://github.com/unindented>
|
||||
// Anthony Ciccarello <https://github.com/aciccarello>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
//This is a customized version of adal on top of version 1.0.1 which does not support multi tenant
|
||||
// Customized version add tenantId to stored tokens so when tenant change, adal will refetch instead of read from sessionStorage
|
||||
|
||||
// In module contexts the class constructor function is the exported object
|
||||
// export = AuthenticationContext;
|
||||
|
||||
// This class is defined globally in not in a module context
|
||||
declare class AuthenticationContext {
|
||||
instance: string;
|
||||
config: AuthenticationContext.Options;
|
||||
callback: AuthenticationContext.TokenCallback;
|
||||
popUp: boolean;
|
||||
isAngular: boolean;
|
||||
|
||||
/**
|
||||
* Enum for request type
|
||||
*/
|
||||
REQUEST_TYPE: AuthenticationContext.RequestType;
|
||||
RESPONSE_TYPE: AuthenticationContext.ResponseType;
|
||||
CONSTANTS: AuthenticationContext.Constants;
|
||||
|
||||
constructor(options: AuthenticationContext.Options);
|
||||
/**
|
||||
* Initiates the login process by redirecting the user to Azure AD authorization endpoint.
|
||||
*/
|
||||
login(): void;
|
||||
/**
|
||||
* Returns whether a login is in progress.
|
||||
*/
|
||||
loginInProgress(): boolean;
|
||||
/**
|
||||
* Gets token for the specified resource from the cache.
|
||||
* @param resource A URI that identifies the resource for which the token is requested.
|
||||
* @param tenantId tenant Id.
|
||||
*/
|
||||
getCachedToken(resource: string, tenantId: string): string;
|
||||
/**
|
||||
* If user object exists, returns it. Else creates a new user object by decoding `id_token` from the cache.
|
||||
*/
|
||||
getCachedUser(): AuthenticationContext.UserInfo;
|
||||
/**
|
||||
* Adds the passed callback to the array of callbacks for the specified resource.
|
||||
* @param resource A URI that identifies the resource for which the token is requested.
|
||||
* @param expectedState A unique identifier (guid).
|
||||
* @param callback The callback provided by the caller. It will be called with token or error.
|
||||
*/
|
||||
registerCallback(
|
||||
expectedState: string,
|
||||
resource: string,
|
||||
callback: AuthenticationContext.TokenCallback,
|
||||
tenantId: string
|
||||
): void;
|
||||
/**
|
||||
* Acquires token from the cache if it is not expired. Otherwise sends request to AAD to obtain a new token.
|
||||
* @param resource Resource URI identifying the target resource.
|
||||
* @param callback The callback provided by the caller. It will be called with token or error.
|
||||
*/
|
||||
acquireToken(resource: string, tenantId: string, callback: AuthenticationContext.TokenCallback): void;
|
||||
/**
|
||||
* Acquires token (interactive flow using a popup window) by sending request to AAD to obtain a new token.
|
||||
* @param resource Resource URI identifying the target resource.
|
||||
* @param extraQueryParameters Query parameters to add to the authentication request.
|
||||
* @param claims Claims to add to the authentication request.
|
||||
* @param callback The callback provided by the caller. It will be called with token or error.
|
||||
*/
|
||||
acquireTokenPopup(
|
||||
resource: string,
|
||||
tenantId: string,
|
||||
extraQueryParameters: string | null | undefined,
|
||||
claims: string | null | undefined,
|
||||
callback: AuthenticationContext.TokenCallback
|
||||
): void;
|
||||
/**
|
||||
* Acquires token (interactive flow using a redirect) by sending request to AAD to obtain a new token. In this case the callback passed in the authentication request constructor will be called.
|
||||
* @param resource Resource URI identifying the target resource.
|
||||
* @param extraQueryParameters Query parameters to add to the authentication request.
|
||||
* @param claims Claims to add to the authentication request.
|
||||
*/
|
||||
acquireTokenRedirect(
|
||||
resource: string,
|
||||
tenantId: string,
|
||||
extraQueryParameters?: string | null,
|
||||
claims?: string | null
|
||||
): void;
|
||||
/**
|
||||
* Redirects the browser to Azure AD authorization endpoint.
|
||||
* @param urlNavigate URL of the authorization endpoint.
|
||||
*/
|
||||
promptUser(urlNavigate: string): void;
|
||||
/**
|
||||
* Clears cache items.
|
||||
*/
|
||||
clearCache(): void;
|
||||
/**
|
||||
* Clears cache items for a given resource.
|
||||
* @param resource Resource URI identifying the target resource.
|
||||
*/
|
||||
clearCacheForResource(resource: string): void;
|
||||
/**
|
||||
* Redirects user to logout endpoint. After logout, it will redirect to `postLogoutRedirectUri` if added as a property on the config object.
|
||||
*/
|
||||
logOut(): void;
|
||||
/**
|
||||
* Calls the passed in callback with the user object or error message related to the user.
|
||||
* @param callback The callback provided by the caller. It will be called with user or error.
|
||||
*/
|
||||
getUser(callback: AuthenticationContext.UserCallback): void;
|
||||
/**
|
||||
* Checks if the URL fragment contains access token, id token or error description.
|
||||
* @param hash Hash passed from redirect page.
|
||||
*/
|
||||
isCallback(hash: string): boolean;
|
||||
/**
|
||||
* Gets login error.
|
||||
*/
|
||||
getLoginError(): string;
|
||||
/**
|
||||
* Creates a request info object from the URL fragment and returns it.
|
||||
*/
|
||||
getRequestInfo(hash: string): AuthenticationContext.RequestInfo;
|
||||
/**
|
||||
* Saves token or error received in the response from AAD in the cache. In case of `id_token`, it also creates the user object.
|
||||
*/
|
||||
saveTokenFromHash(requestInfo: AuthenticationContext.RequestInfo): void;
|
||||
/**
|
||||
* Gets resource for given endpoint if mapping is provided with config.
|
||||
* @param endpoint Resource URI identifying the target resource.
|
||||
*/
|
||||
getResourceForEndpoint(resource: string): string;
|
||||
/**
|
||||
* This method must be called for processing the response received from AAD. It extracts the hash, processes the token or error, saves it in the cache and calls the callbacks with the result.
|
||||
* @param hash Hash fragment of URL. Defaults to `window.location.hash`.
|
||||
*/
|
||||
handleWindowCallback(hash?: string): void;
|
||||
|
||||
/**
|
||||
* Checks the logging Level, constructs the log message and logs it. Users need to implement/override this method to turn on logging.
|
||||
* @param level Level can be set 0, 1, 2 and 3 which turns on 'error', 'warning', 'info' or 'verbose' level logging respectively.
|
||||
* @param message Message to log.
|
||||
* @param error Error to log.
|
||||
*/
|
||||
log(level: AuthenticationContext.LoggingLevel, message: string, error: any): void;
|
||||
/**
|
||||
* Logs messages when logging level is set to 0.
|
||||
* @param message Message to log.
|
||||
* @param error Error to log.
|
||||
*/
|
||||
error(message: string, error: any): void;
|
||||
/**
|
||||
* Logs messages when logging level is set to 1.
|
||||
* @param message Message to log.
|
||||
*/
|
||||
warn(message: string): void;
|
||||
/**
|
||||
* Logs messages when logging level is set to 2.
|
||||
* @param message Message to log.
|
||||
*/
|
||||
info(message: string): void;
|
||||
/**
|
||||
* Logs messages when logging level is set to 3.
|
||||
* @param message Message to log.
|
||||
*/
|
||||
verbose(message: string): void;
|
||||
|
||||
/**
|
||||
* Logs Pii messages when Logging Level is set to 0 and window.piiLoggingEnabled is set to true.
|
||||
* @param message Message to log.
|
||||
* @param error Error to log.
|
||||
*/
|
||||
errorPii(message: string, error: any): void;
|
||||
|
||||
/**
|
||||
* Logs Pii messages when Logging Level is set to 1 and window.piiLoggingEnabled is set to true.
|
||||
* @param message Message to log.
|
||||
*/
|
||||
warnPii(message: string): void;
|
||||
|
||||
/**
|
||||
* Logs messages when Logging Level is set to 2 and window.piiLoggingEnabled is set to true.
|
||||
* @param message Message to log.
|
||||
*/
|
||||
infoPii(message: string): void;
|
||||
|
||||
/**
|
||||
* Logs messages when Logging Level is set to 3 and window.piiLoggingEnabled is set to true.
|
||||
* @param message Message to log.
|
||||
*/
|
||||
verbosePii(message: string): void;
|
||||
}
|
||||
|
||||
declare namespace AuthenticationContext {
|
||||
function inject(config: Options): AuthenticationContext;
|
||||
|
||||
type LoggingLevel = 0 | 1 | 2 | 3;
|
||||
|
||||
type RequestType = "LOGIN" | "RENEW_TOKEN" | "UNKNOWN";
|
||||
|
||||
type ResponseType = "id_token token" | "token";
|
||||
|
||||
interface RequestInfo {
|
||||
/**
|
||||
* Object comprising of fields such as id_token/error, session_state, state, e.t.c.
|
||||
*/
|
||||
parameters: any;
|
||||
/**
|
||||
* Request type.
|
||||
*/
|
||||
requestType: RequestType;
|
||||
/**
|
||||
* Whether state is valid.
|
||||
*/
|
||||
stateMatch: boolean;
|
||||
/**
|
||||
* Unique guid used to match the response with the request.
|
||||
*/
|
||||
stateResponse: string;
|
||||
/**
|
||||
* Whether `requestType` contains `id_token`, `access_token` or error.
|
||||
*/
|
||||
valid: boolean;
|
||||
}
|
||||
|
||||
interface UserInfo {
|
||||
/**
|
||||
* Username assigned from UPN or email.
|
||||
*/
|
||||
userName: string;
|
||||
/**
|
||||
* Properties parsed from `id_token`.
|
||||
*/
|
||||
profile: any;
|
||||
}
|
||||
|
||||
type TokenCallback = (errorDesc: string | null, token: string | null, error: any) => void;
|
||||
|
||||
type UserCallback = (errorDesc: string | null, user: UserInfo | null) => void;
|
||||
|
||||
/**
|
||||
* Configuration options for Authentication Context
|
||||
*/
|
||||
interface Options {
|
||||
/**
|
||||
* Client ID assigned to your app by Azure Active Directory.
|
||||
*/
|
||||
clientId: string;
|
||||
/**
|
||||
* Endpoint at which you expect to receive tokens.Defaults to `window.location.href`.
|
||||
*/
|
||||
redirectUri?: string;
|
||||
/**
|
||||
* Azure Active Directory instance. Defaults to `https://login.microsoftonline.com/`.
|
||||
*/
|
||||
instance?: string;
|
||||
/**
|
||||
* Your target tenant. Defaults to `common`.
|
||||
*/
|
||||
tenant?: string;
|
||||
/**
|
||||
* Query parameters to add to the authentication request.
|
||||
*/
|
||||
extraQueryParameter?: string;
|
||||
/**
|
||||
* Unique identifier used to map the request with the response. Defaults to RFC4122 version 4 guid (128 bits).
|
||||
*/
|
||||
correlationId?: string;
|
||||
/**
|
||||
* User defined function of handling the navigation to Azure AD authorization endpoint in case of login.
|
||||
*/
|
||||
displayCall?: (url: string) => void;
|
||||
/**
|
||||
* Set this to true to enable login in a popup winodow instead of a full redirect. Defaults to `false`.
|
||||
*/
|
||||
popUp?: boolean;
|
||||
/**
|
||||
* Set this to the resource to request on login. Defaults to `clientId`.
|
||||
*/
|
||||
loginResource?: string;
|
||||
/**
|
||||
* Set this to redirect the user to a custom login page.
|
||||
*/
|
||||
localLoginUrl?: string;
|
||||
/**
|
||||
* Redirects to start page after login. Defaults to `true`.
|
||||
*/
|
||||
navigateToLoginRequestUrl?: boolean;
|
||||
/**
|
||||
* Set this to redirect the user to a custom logout page.
|
||||
*/
|
||||
logOutUri?: string;
|
||||
/**
|
||||
* Redirects the user to postLogoutRedirectUri after logout. Defaults to `redirectUri`.
|
||||
*/
|
||||
postLogoutRedirectUri?: string;
|
||||
/**
|
||||
* Sets browser storage to either 'localStorage' or sessionStorage'. Defaults to `sessionStorage`.
|
||||
*/
|
||||
cacheLocation?: "localStorage" | "sessionStorage";
|
||||
/**
|
||||
* Array of keywords or URIs. Adal will attach a token to outgoing requests that have these keywords or URIs.
|
||||
*/
|
||||
endpoints?: { [resource: string]: string };
|
||||
/**
|
||||
* Array of keywords or URIs. Adal will not attach a token to outgoing requests that have these keywords or URIs.
|
||||
*/
|
||||
anonymousEndpoints?: string[];
|
||||
/**
|
||||
* If the cached token is about to be expired in the expireOffsetSeconds (in seconds), Adal will renew the token instead of using the cached token. Defaults to 300 seconds.
|
||||
*/
|
||||
expireOffsetSeconds?: number;
|
||||
/**
|
||||
* The number of milliseconds of inactivity before a token renewal response from AAD should be considered timed out. Defaults to 6 seconds.
|
||||
*/
|
||||
loadFrameTimeout?: number;
|
||||
/**
|
||||
* Callback to be invoked when a token is acquired.
|
||||
*/
|
||||
callback?: TokenCallback;
|
||||
}
|
||||
|
||||
interface LoggingConfig {
|
||||
level: LoggingLevel;
|
||||
log: (message: string) => void;
|
||||
piiLoggingEnabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum for storage constants
|
||||
*/
|
||||
interface Constants {
|
||||
ACCESS_TOKEN: "access_token";
|
||||
EXPIRES_IN: "expires_in";
|
||||
ID_TOKEN: "id_token";
|
||||
ERROR_DESCRIPTION: "error_description";
|
||||
SESSION_STATE: "session_state";
|
||||
STORAGE: {
|
||||
TOKEN_KEYS: "adal.token.keys";
|
||||
ACCESS_TOKEN_KEY: "adal.access.token.key";
|
||||
EXPIRATION_KEY: "adal.expiration.key";
|
||||
STATE_LOGIN: "adal.state.login";
|
||||
STATE_RENEW: "adal.state.renew";
|
||||
NONCE_IDTOKEN: "adal.nonce.idtoken";
|
||||
SESSION_STATE: "adal.session.state";
|
||||
USERNAME: "adal.username";
|
||||
IDTOKEN: "adal.idtoken";
|
||||
ERROR: "adal.error";
|
||||
ERROR_DESCRIPTION: "adal.error.description";
|
||||
LOGIN_REQUEST: "adal.login.request";
|
||||
LOGIN_ERROR: "adal.login.error";
|
||||
RENEW_STATUS: "adal.token.renew.status";
|
||||
};
|
||||
RESOURCE_DELIMETER: "|";
|
||||
LOADFRAME_TIMEOUT: "6000";
|
||||
TOKEN_RENEW_STATUS_CANCELED: "Canceled";
|
||||
TOKEN_RENEW_STATUS_COMPLETED: "Completed";
|
||||
TOKEN_RENEW_STATUS_IN_PROGRESS: "In Progress";
|
||||
LOGGING_LEVEL: {
|
||||
ERROR: 0;
|
||||
WARN: 1;
|
||||
INFO: 2;
|
||||
VERBOSE: 3;
|
||||
};
|
||||
LEVEL_STRING_MAP: {
|
||||
0: "ERROR:";
|
||||
1: "WARNING:";
|
||||
2: "INFO:";
|
||||
3: "VERBOSE:";
|
||||
};
|
||||
POPUP_WIDTH: 483;
|
||||
POPUP_HEIGHT: 600;
|
||||
}
|
||||
}
|
||||
|
||||
// declare global {
|
||||
// interface Window {
|
||||
// Logging: AuthenticationContext.LoggingConfig;
|
||||
// }
|
||||
// }
|
||||
3908
src/Definitions/datatables.d.ts
vendored
3908
src/Definitions/datatables.d.ts
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user