Compare commits

..

71 Commits

Author SHA1 Message Date
Steve Faulkner
c857b9aab9 Remove any 2021-01-19 10:05:48 -06:00
Srinath Narayanan
cd45f2943d Merge branch 'master' into users/srnara/selfserve 2021-01-18 16:37:45 -08:00
Srinath Narayanan
385c9f216f Addressed PR comments 2021-01-18 16:21:31 -08:00
Deborah Chen
8c40df0fa1 Adding in experimentation for autoscale test (#345)
* Adding autoscale flight info

* Add flight info to cassandra collection pane

* Add telemetry for autoscale toggle on/off in create resource blade and scale/settings

* Run formatting and add expected properties to test file

* removing empty line

* Updating to pass unit tests

Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2021-01-15 17:15:15 -06:00
Srinath Narayanan
d17508cc27 Added SelfServeComponent 2021-01-15 12:34:08 -08:00
Srinath Narayanan
2ec2a891b4 renamed dropdown -> choice 2021-01-15 08:27:26 -08:00
Steve Faulkner
fcbc9474ea Remove Preview for Synapse Link (#389) 2021-01-15 09:51:14 -06:00
Srinath Narayanan
b34628e9fc Merge branch 'master' into users/srnara/selfserve 2021-01-15 07:39:50 -08:00
Srinath Narayanan
3fb4af53c8 Removed reactbinding changes 2021-01-15 07:09:14 -08:00
Steve Faulkner
81f861af39 Empty commit to refresh nuget after transient failures 2021-01-14 17:37:24 -06:00
victor-meng
9afa29cdb6 Properly construct the query to delete Cassandra row (#388) 2021-01-14 16:59:31 -06:00
Chris-MS-896
9a1e8b2d87 Add rest of three utils files to Matser (#370)
* 'minor change'
2021-01-13 17:49:06 -06:00
Srinath Narayanan
41f37055ef Merge branch 'master' into users/srnara/selfserve 2021-01-13 06:21:29 -08:00
Srinath Narayanan
aa925d8d54 fixed compilation errors 2021-01-13 06:12:37 -08:00
Srinath Narayanan
c9cea86225 fixed lint errors 2021-01-13 05:26:47 -08:00
Srinath Narayanan
318842624f Resolved PR comments
Added tests
Moved onSubmt and initialize inside base class
Moved testExplorer to separate folder
made fields of SelfServe Class non static
2021-01-12 22:02:45 -08:00
Tim Sander
babda4d9cb fix issue where Mongo indexing checkbox stops adding wildcard index (#384) 2021-01-12 18:38:16 -06:00
Steve Faulkner
9d20a13dd4 Warn on SubQuery (#378) 2021-01-12 13:53:15 -06:00
Chris-MS-896
3effbe6991 no message (#372) 2021-01-12 13:09:20 -06:00
Chris-MS-896
af53697ff4 Add file of Terminal to Master (#371)
* "minor changes"
2021-01-12 12:55:47 -06:00
Chris-MS-896
b1ad80480e Add two files of notebook component in Matser (#363)
* “minor changes”
2021-01-12 12:55:21 -06:00
Armando Trejo Oliver
9247a6c4a2 A11y fixes - Add a skip link and remove duplicate ids (#381)
* Add a skip link to allow people who navigate sequentially through content more direct access to the primary content of the Data Explorer

Co-authored-by: Chris Cao (Zen3 Infosolutions America Inc) <v-yiqcao@microsoft.com>

* Rename id of partition key field in  Add Collection Pane to ensure no  elements contain duplicate attributes.

Co-authored-by: Chris Cao (Zen3 Infosolutions America Inc) <v-yiqcao@microsoft.com>
2021-01-12 09:55:04 -08:00
Steve Faulkner
767d46480e Revert TablesEntitiyListViewModel changes (#382) 2021-01-11 16:16:40 -06:00
Chris-MS-896
2d98c5d269 add ArraysByKeyCache.ts (#366)
* 'add ArraysByKeyCache'
* "minor change"
2021-01-08 22:51:50 -06:00
Steve Faulkner
6627172a52 Add Architecture Diagram to README (#380) 2021-01-08 22:20:40 -06:00
Steve Faulkner
19fa5e17a5 Fix JSONEditor bug with undefined value (#379) 2021-01-08 22:20:06 -06:00
Chris-MS-896
a4a367a212 Add all arm request related files to Matser (#373)
* “minor changes”
* 'changes for unit test'
2021-01-08 21:56:29 -06:00
Chris-MS-896
983c9201bb Add two files of GraphExplorer component in Master (#365) 2021-01-08 21:14:53 -06:00
Chris-MS-896
76d7f00a90 Add two files of Table to master (#364) 2021-01-08 20:56:59 -06:00
Chris-MS-896
6490597736 add CollapsiblePanel/CollapsiblePanelComponent.ts and /ErrorDisplayComponent to Master (#357) 2021-01-08 20:29:15 -06:00
Chris-MS-896
229119e697 add file offerUtility to tsconfig (#356) 2021-01-08 20:14:12 -06:00
Steve Faulkner
ceefd7c615 Fix Conflict Resolution path setting (#377)
* Fix Conflict Resolution path setting

* Fix test
2021-01-08 12:36:44 -06:00
Laurent Nguyen
6e619175c6 Fix missing scrollbar in left pane when too many collections/notebooks (#375)
Constrain left pane container to height: 100% so that scrollbar show up when content wants to overflow.
The `main` classname seems too generic, but I left it alone (so I don't break anything), since this part will eventually be ported to React.
2021-01-08 14:00:26 +00:00
victor-meng
08e8bf4bcf Fix two settings tab issues (#374) 2021-01-07 15:38:13 -06:00
Chris-MS-896
89dc0f394b Add Spliter file to Master (#358) 2021-01-06 12:51:42 -06:00
Chris-MS-896
30e0001b7f no message (#359) 2021-01-05 16:45:13 -06:00
Steve Faulkner
4a8f408112 Add UX for Mongo indexing experiment (#368)
Co-authored-by: Tim Sander <tisande@microsoft.com>
2021-01-05 16:04:55 -06:00
Armando Trejo Oliver
e801364800 Remove stale .main class from tree.less (#362)
.main CSS class has a naming conflict with Moncao editor CSS classes and this is causing  A11y issues with Moncao editor.

This class should no longer be used since we moved to the new tree component in REACT, so I am removing it. From my testing, this is not affecting anything.

If we find any styling issue later, we should fix without adding back this class.
2021-01-05 10:53:55 -08:00
Srinath Narayanan
373327dc88 removed unnecessary changes 2021-01-04 18:34:24 -08:00
Srinath Narayanan
8333ee7ec4 Merge branch 'master' into users/srnara/selfserve 2021-01-04 18:29:33 -08:00
Srinath Narayanan
97116175ab Added comments for Example 2021-01-04 18:15:09 -08:00
victor-meng
a55f2d0de9 Free tier improvements in DE (#348)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2021-01-04 12:56:55 -08:00
Steve Faulkner
d40b1aa9b5 Remove Empty Query Logging (#361) 2021-01-04 13:58:01 -06:00
Steve Faulkner
cc63cdc1fd Remove dependency on canvas (#354) 2020-12-26 21:56:37 -06:00
Srinath Narayanan
f770bb193e renamed sqlx->example and added invalid type 2020-12-22 22:25:52 -08:00
Srinath Narayanan
8cb8d10bc3 added feature for self serve type 2020-12-22 21:10:27 -08:00
Srinath Narayanan
b298caf9ff removed landingpage 2020-12-21 03:44:46 -08:00
Srinath Narayanan
a2022fbbac added inital open from data explorer 2020-12-21 03:40:48 -08:00
Steve Faulkner
c3058ee5a9 Check for undefined query results (#350) 2020-12-18 19:55:32 -06:00
Steve Faulkner
b000631a0c Revert web.config changes (#349) 2020-12-18 19:26:10 -06:00
vchske
e8f4c8f93c Cost Estimate Changes (#342)
* Initial change of estimated cost to table format

* Converted cost estimate to table format and added different data for current vs updated cost estimates.

* lint fixes

* Changed the names of some interfaces

* Refactored a unit call to use an argument interface to avoid future confusion.

* Changed the severity of the save warning

* Format fix

* Fixed test due to styling change

Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2020-12-18 16:15:55 -08:00
Steve Faulkner
16bde97e47 Rewrite URL for IE users (#340) 2020-12-18 16:08:40 -06:00
Steve Faulkner
6da43ee27b Publish IE specific Nuget package (#347)
* Publish IE specific Nuget package

* Require ally tests to pass
2020-12-17 17:41:38 -06:00
Gahl Levy
ebae484b8f Fix duplicate settings tabs (#343)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2020-12-17 13:01:36 -08:00
Steve Faulkner
dfb1b50621 Explorer.ts Cleanup (#341)
Co-authored-by: victor-meng <56978073+victor-meng@users.noreply.github.com>
2020-12-16 20:00:39 -06:00
victor-meng
f54e8eb692 Move queryDocuments out of DataAccessUtility (#334) 2020-12-16 15:27:17 -08:00
Steve Faulkner
ea39c1d092 Fix offer update notification for AAD users (#338) 2020-12-11 13:38:57 -06:00
vchske
c21f42159f Updated cost messaging for new db/container pane (#333)
* Adds information text further explaining estimated cost.

* Minor tweak to cost messaging

* Updated unit tests

* Added text and link for capacity planner when choosing manual RUs
2020-12-11 10:06:43 -08:00
Srinath Narayanan
b3b57462ef Added overall defaults 2020-12-10 16:59:16 -08:00
victor-meng
31e4b49f11 Only call getCollectionDataUsageSize for AAD users (#337) 2020-12-10 14:13:08 -08:00
Tanuj Mittal
40491ec9c5 Gallery related fixes (#312)
* AVERT fixes

* Remove enableCodeOfConduct feature flag

* Fix reporting abuse

* Add empty screen for Liked and Published tabs in Gallery

* fix build

* Remove unused code

* Fix standalone public gallery
2020-12-10 13:09:18 -08:00
Srinath Narayanan
95fc75cb23 Added custom renderer as async type 2020-12-10 12:50:04 -08:00
Tanuj Mittal
e133df18dd Record baseUrl for OpenTerminal success/failure telemetry (#335)
This is useful to know which terminal is opening.
2020-12-10 19:54:21 +00:00
Srinath Narayanan
c97eb6018b Made selfServe standalone page 2020-12-08 03:00:12 -08:00
Srinath Narayanan
90fb7e7d8f added custom element and base class 2020-12-07 02:23:20 -08:00
Srinath Narayanan
2dbde9c31a proper resolution of promises 2020-12-03 01:11:07 -08:00
Srinath Narayanan
4381ea447c removed type requirement 2020-12-02 01:45:59 -08:00
Srinath Narayanan
69b17f1a00 Added Recursive add 2020-12-01 03:57:54 -08:00
Srinath Narayanan
8cf160d818 added todo comment and removed console.log 2020-11-24 07:37:52 -08:00
Srinath Narayanan
88d71d7070 working version 2020-11-23 17:46:59 -08:00
Srinath Narayanan
84017660c1 added recursion and inition decorators 2020-11-23 14:21:52 -08:00
533 changed files with 48925 additions and 46667 deletions

View File

@@ -14,7 +14,6 @@ src/Common/DataAccessUtilityBase.ts
src/Common/DeleteFeedback.ts
src/Common/DocumentClientUtilityBase.ts
src/Common/EditableUtility.ts
src/Common/EnvironmentUtility.ts
src/Common/HashMap.test.ts
src/Common/HashMap.ts
src/Common/HeadersUtility.test.ts

View File

@@ -1,39 +1,39 @@
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"],
env: {
jest: true,
jest: true
},
extends: ["plugin:react/recommended"],
plugins: ["react"],
plugins: ["react"]
},
{
files: ["**/*.{test,spec}.{ts,tsx}"],
env: {
jest: true,
jest: true
},
extends: ["plugin:jest/recommended"],
plugins: ["jest"],
},
plugins: ["jest"]
}
],
rules: {
curly: "error",
@@ -47,8 +47,8 @@ module.exports = {
"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 '{}'"
}
]
}
};

View File

@@ -101,6 +101,7 @@ jobs:
PLATFORM: "Emulator"
NODE_TLS_REJECT_UNAUTHORIZED: 0
- uses: actions/upload-artifact@v2
if: failure()
with:
name: screenshots
path: failed-*
@@ -159,13 +160,14 @@ jobs:
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-*
nuget:
name: Publish Nuget
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted]
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted, accessibility]
runs-on: ubuntu-latest
env:
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
@@ -189,7 +191,7 @@ jobs:
nugetmpac:
name: Publish Nuget MPAC
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted]
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted, accessibility]
runs-on: ubuntu-latest
env:
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
@@ -211,3 +213,28 @@ jobs:
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:
path: "*.nupkg"

BIN
.vs/slnx.sqlite Normal file

Binary file not shown.

View File

@@ -13,29 +13,18 @@ UI for Azure Cosmos DB. Powers the [Azure Portal](https://portal.azure.com/), ht
### Watch mode
Run `npm run watch` to start the development server and automatically rebuild on changes
Run `npm start` to start the development server and automatically rebuild on changes
### Specifying Development Platform
### Hosted Development (https://cosmos.azure.com)
Setting the environment variable `PLATFORM` during the build process will force the explorer to load the specified platform. By default in development it will run in `Hosted` mode. Valid options:
- Hosted
- Emulator
- Portal
`PLATFORM=Emulator npm run watch`
### Hosted Development
The default webpack dev server configuration will proxy requests to the production portal backend: `https://main.documentdb.ext.azure.com`. This will allow you to use production connection strings on your local machine.
To run pure hosted mode, in `webpack.config.js` change index HtmlWebpackPlugin to use hostedExplorer.html and change entry for index to use HostedExplorer.ts.
- Visit: `https://localhost:1234/hostedExplorer.html`
- Local sign in via AAD will NOT work. Connection string only in dev mode. Use the Portal if you need AAD auth.
- The default webpack dev server configuration will proxy requests to the production portal backend: `https://main.documentdb.ext.azure.com`. This will allow you to use production connection strings on your local machine.
### Emulator Development
In a window environment, running `npm run build` will automatically copy the built files from `/dist` over to the default emulator install paths. In a non-windows environment you can specify an alternate endpoint using `EMULATOR_ENDPOINT` and webpack dev server will proxy requests for you.
`PLATFORM=Emulator EMULATOR_ENDPOINT=https://my-vm.azure.com:8081 npm run watch`
- Start the Cosmos Emulator
- Visit: https://localhost:1234/index.html
#### Setting up a Remote Emulator
@@ -55,16 +44,8 @@ The Cosmos emulator currently only runs in Windows environments. You can still d
### Portal Development
The Cosmos Portal that consumes this repo is not currently open source. If you have access to this project, `npm run build` will copy the built files over to the portal where they will be loaded by the portal development environment
You can however load a local running instance of data explorer in the production portal.
1. Turn off browser SSL validation for localhost: chrome://flags/#allow-insecure-localhost OR Install valid SSL certs for localhost (on IE, follow these [instructions](https://www.technipages.com/ie-bypass-problem-with-this-websites-security-certificate) to install the localhost certificate in the right place)
2. Allowlist `https://localhost:1234` domain for CORS in the Azure Cosmos DB portal
3. Start the project in portal mode: `PLATFORM=Portal npm run watch`
4. Load the portal using the following link: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html
Live reload will occur, but data explorer will not properly integrate again with the parent iframe. You will have to manually reload the page.
- Visit: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html
- You may have to manually visit https://localhost:1234/explorer.html first and click through any SSL certificate warnings
### Testing
@@ -88,6 +69,10 @@ Jest and Puppeteer are used for end to end browser based tests and are contained
We generally adhere to the release strategy [documented by the Azure SDK Guidelines](https://azure.github.io/azure-sdk/policies_repobranching.html#release-branches). Most releases should happen from the master branch. If master contains commits that cannot be released, you may create a release from a `release/` or `hotfix/` branch. See linked documentation for more details.
### Architechture
[![](https://mermaid.ink/img/eyJjb2RlIjoiZ3JhcGggTFJcbiAgaG9zdGVkKGh0dHBzOi8vY29zbW9zLmF6dXJlLmNvbSlcbiAgcG9ydGFsKFBvcnRhbClcbiAgZW11bGF0b3IoRW11bGF0b3IpXG4gIGFhZFtBQURdXG4gIHJlc291cmNlVG9rZW5bUmVzb3VyY2UgVG9rZW5dXG4gIGNvbm5lY3Rpb25TdHJpbmdbQ29ubmVjdGlvbiBTdHJpbmddXG4gIHBvcnRhbFRva2VuW0VuY3J5cHRlZCBQb3J0YWwgVG9rZW5dXG4gIG1hc3RlcktleVtNYXN0ZXIgS2V5XVxuICBhcm1bQVJNIFJlc291cmNlIFByb3ZpZGVyXVxuICBkYXRhcGxhbmVbRGF0YSBQbGFuZV1cbiAgcHJveHlbUG9ydGFsIEFQSSBQcm94eV1cbiAgc3FsW1NRTF1cbiAgbW9uZ29bTW9uZ29dXG4gIHRhYmxlc1tUYWJsZXNdXG4gIGNhc3NhbmRyYVtDYXNzYW5kcmFdXG4gIGdyYWZbR3JhcGhdXG5cblxuICBlbXVsYXRvciAtLT4gbWFzdGVyS2V5IC0tLS0-IGRhdGFwbGFuZVxuICBwb3J0YWwgLS0-IGFhZFxuICBob3N0ZWQgLS0-IHBvcnRhbFRva2VuICYgcmVzb3VyY2VUb2tlbiAmIGNvbm5lY3Rpb25TdHJpbmcgJiBhYWRcbiAgYWFkIC0tLT4gYXJtXG4gIGFhZCAtLS0-IGRhdGFwbGFuZVxuICBhYWQgLS0tPiBwcm94eVxuICByZXNvdXJjZVRva2VuIC0tLT4gc3FsIC0tPiBkYXRhcGxhbmVcbiAgcG9ydGFsVG9rZW4gLS0tPiBwcm94eVxuICBwcm94eSAtLT4gZGF0YXBsYW5lXG4gIGNvbm5lY3Rpb25TdHJpbmcgLS0-IHNxbCAmIG1vbmdvICYgY2Fzc2FuZHJhICYgZ3JhZiAmIHRhYmxlc1xuICBzcWwgLS0-IGRhdGFwbGFuZVxuICB0YWJsZXMgLS0-IGRhdGFwbGFuZVxuICBtb25nbyAtLT4gcHJveHlcbiAgY2Fzc2FuZHJhIC0tPiBwcm94eVxuICBncmFmIC0tPiBwcm94eVxuXG5cdFx0IiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifSwidXBkYXRlRWRpdG9yIjpmYWxzZX0)](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggTFJcbiAgaG9zdGVkKGh0dHBzOi8vY29zbW9zLmF6dXJlLmNvbSlcbiAgcG9ydGFsKFBvcnRhbClcbiAgZW11bGF0b3IoRW11bGF0b3IpXG4gIGFhZFtBQURdXG4gIHJlc291cmNlVG9rZW5bUmVzb3VyY2UgVG9rZW5dXG4gIGNvbm5lY3Rpb25TdHJpbmdbQ29ubmVjdGlvbiBTdHJpbmddXG4gIHBvcnRhbFRva2VuW0VuY3J5cHRlZCBQb3J0YWwgVG9rZW5dXG4gIG1hc3RlcktleVtNYXN0ZXIgS2V5XVxuICBhcm1bQVJNIFJlc291cmNlIFByb3ZpZGVyXVxuICBkYXRhcGxhbmVbRGF0YSBQbGFuZV1cbiAgcHJveHlbUG9ydGFsIEFQSSBQcm94eV1cbiAgc3FsW1NRTF1cbiAgbW9uZ29bTW9uZ29dXG4gIHRhYmxlc1tUYWJsZXNdXG4gIGNhc3NhbmRyYVtDYXNzYW5kcmFdXG4gIGdyYWZbR3JhcGhdXG5cblxuICBlbXVsYXRvciAtLT4gbWFzdGVyS2V5IC0tLS0-IGRhdGFwbGFuZVxuICBwb3J0YWwgLS0-IGFhZFxuICBob3N0ZWQgLS0-IHBvcnRhbFRva2VuICYgcmVzb3VyY2VUb2tlbiAmIGNvbm5lY3Rpb25TdHJpbmcgJiBhYWRcbiAgYWFkIC0tLT4gYXJtXG4gIGFhZCAtLS0-IGRhdGFwbGFuZVxuICBhYWQgLS0tPiBwcm94eVxuICByZXNvdXJjZVRva2VuIC0tLT4gc3FsIC0tPiBkYXRhcGxhbmVcbiAgcG9ydGFsVG9rZW4gLS0tPiBwcm94eVxuICBwcm94eSAtLT4gZGF0YXBsYW5lXG4gIGNvbm5lY3Rpb25TdHJpbmcgLS0-IHNxbCAmIG1vbmdvICYgY2Fzc2FuZHJhICYgZ3JhZiAmIHRhYmxlc1xuICBzcWwgLS0-IGRhdGFwbGFuZVxuICB0YWJsZXMgLS0-IGRhdGFwbGFuZVxuICBtb25nbyAtLT4gcHJveHlcbiAgY2Fzc2FuZHJhIC0tPiBwcm94eVxuICBncmFmIC0tPiBwcm94eVxuXG5cdFx0IiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifSwidXBkYXRlRWRpdG9yIjpmYWxzZX0)
# Contributing
Please read the [contribution guidelines](./CONTRIBUTING.md).

View File

@@ -1,3 +1,4 @@
module.exports = {
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"],
plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]]
};

7
canvas/README.md Normal file
View File

@@ -0,0 +1,7 @@
# Why?
This adds a mock module for `canvas`. Nteract has a ignored require and undeclared dependency on this module. `cavnas` is a server side node module and is not used in browser side code for nteract.
Installing it locally (`npm install canvas`) will resolve the problem, but it is a native module so it is flaky depending on the system, node version, processor arch, etc. This module provides a simpler, more robust solution.
Remove this workaround if [this bug](https://github.com/nteract/any-vega/issues/2) ever gets resolved

1
canvas/index.js Normal file
View File

@@ -0,0 +1 @@
module.exports = {}

11
canvas/package.json Normal file
View File

@@ -0,0 +1,11 @@
{
"name": "canvas",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

View File

@@ -6,6 +6,6 @@ module.exports = {
slowMo: 55,
defaultViewport: null,
ignoreHTTPSErrors: true,
args: ["--disable-web-security"],
},
args: ["--disable-web-security"]
}
};

View File

@@ -1,5 +1,5 @@
module.exports = {
preset: "jest-puppeteer",
testMatch: ["<rootDir>/test/**/*.spec.[jt]s?(x)"],
setupFiles: ["dotenv/config"],
setupFiles: ["dotenv/config"]
};

View File

@@ -42,8 +42,8 @@ module.exports = {
branches: 20,
functions: 24,
lines: 30,
statements: 29.0,
},
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,

View File

@@ -3,8 +3,8 @@
/******************************************************************************/
@font-face {
font-family: wf_segoe-ui_normal;
src: url('../../fonts/segoe-ui/west-european/normal/latest.woff');
font-family: wf_segoe-ui_normal;
src: url("../../fonts/segoe-ui/west-european/normal/latest.woff");
}
@DataExplorerFont: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
@@ -20,26 +20,26 @@
COLORS
/******************************************************************************/
@AccentMediumHigh: #0058AD;
@AccentMedium: #004E87;
@AccentHigh: #1EBAED;
@AccentExtraHigh: #55B3FF;
@AccentLow: #EDF6FF;
@AccentMediumLow: #DDEEFE;
@AccentLight: #EEF7FF;
@AccentExtra: #DDF0FF;
@AccentMediumHigh: #0058ad;
@AccentMedium: #004e87;
@AccentHigh: #1ebaed;
@AccentExtraHigh: #55b3ff;
@AccentLow: #edf6ff;
@AccentMediumLow: #ddeefe;
@AccentLight: #eef7ff;
@AccentExtra: #ddf0ff;
@SelectionHigh: #B91F26;
@BaseLight: #FFFFFF;
@SelectionHigh: #b91f26;
@BaseLight: #ffffff;
@BaseDark: #000000;
@NotificationLow: #FFF4CE;
@NotificationHigh: #F9E9B0;
@Purple1: #8A2DA5;
@NotificationLow: #fff4ce;
@NotificationHigh: #f9e9b0;
@Purple1: #8a2da5;
@Dirty: #9b4f96;
@BaseLow: #F2F2F2;
@BaseMediumLow: #E6E6E6;
@BaseMedium: #CCCCCC;
@BaseLow: #f2f2f2;
@BaseMediumLow: #e6e6e6;
@BaseMedium: #cccccc;
@BaseMediumHigh: #767676;
@BaseHigh: #393939;
@@ -53,7 +53,7 @@
@ErrorColor: @SelectionHigh;
@SelectionColor: #3074B0;
@SelectionColor: #3074b0;
@FocusColor: #605e5c;
@@ -80,7 +80,7 @@
@ImgWidth: 14px;
@ImgHeight: 14px;
@toggleFontWeight:700;
@toggleFontWeight: 700;
//Resource Tree
@TreeLineHeight: 17px;
@@ -144,16 +144,16 @@
/**********************************************************************************/
.flex-display(@display: flex) {
display: ~"-webkit-@{display}";
display: ~"-ms-@{display}box"; // IE10 uses -ms-flexbox
display: ~"-ms-@{display}"; // IE11
display: @display;
display: ~"-webkit-@{display}";
display: ~"-ms-@{display}box"; // IE10 uses -ms-flexbox
display: ~"-ms-@{display}"; // IE11
display: @display;
}
.flex-direction(@direction: column) {
-webkit-flex-direction: @direction;
-ms-flex-direction: @direction;
flex-direction: @direction;
-ms-flex-direction: @direction;
flex-direction: @direction;
}
/*************************************************************************************
@@ -161,32 +161,31 @@
**************************************************************************************/
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.selectedRadio,
.selectedRadio:hover,
.selectedRadio:active,
.selectedRadio.dirty,
.tab [type=radio]:checked ~ label,
.tab [type=radio]:checked ~ label:hover {
-ms-high-contrast-adjust: none;
-webkit-text-fill-color: HighlightText;
color: HighlightText;
border-color: HighlightText;
background-color: Highlight;
}
.queryMetricsSummaryTuple {
th, td {
&:nth-child(2) {
width: @IETableDataWidth;
}
&:nth-child(3) {
width: 50%;
}
}
.selectedRadio,
.selectedRadio:hover,
.selectedRadio:active,
.selectedRadio.dirty,
.tab [type="radio"]:checked ~ label,
.tab [type="radio"]:checked ~ label:hover {
-ms-high-contrast-adjust: none;
-webkit-text-fill-color: HighlightText;
color: HighlightText;
border-color: HighlightText;
background-color: Highlight;
}
.queryMetricsSummaryTuple {
th,
td {
&:nth-child(2) {
width: @IETableDataWidth;
}
&:nth-child(3) {
width: 50%;
}
}
}
}
/********************************************************************************************
@@ -194,15 +193,15 @@
*********************************************************************************************/
.hover() {
background-color: @AccentLight;
background-color: @AccentLight;
}
.active() {
background-color: @AccentExtra;
background-color: @AccentExtra;
}
.focus() {
outline: 1px dashed @FocusColor;
outline: 1px dashed @FocusColor;
}
/************************************************************************************************
@@ -212,63 +211,87 @@
@ToggleWidth: 180px;
.toggleSwitch() {
max-width: 100%;
margin-bottom: @SmallSpace;
padding: @SmallSpace;
cursor: pointer;
color: @BaseHigh;
font-weight: 400;
font-size: @mediumFontSize;
font-family: @DataExplorerFont;
max-width: 100%;
margin-bottom: @SmallSpace;
padding: @SmallSpace;
cursor: pointer;
color: @BaseHigh;
font-weight: 400;
font-size: @mediumFontSize;
font-family: @DataExplorerFont;
}
.selectedToggle() {
border-bottom: 2px solid @BaseHigh;
border-bottom: 2px solid @BaseHigh;
}
.unselectedToggle() {
color: @AccentMediumHigh;
color: @AccentMediumHigh;
}
/********************************************************************************************************
Common Data Explorer Icons
*********************************************************************************************************/
.dataExplorerIcons() {
cursor: pointer;
width: @ImgWidth;
height: @ImgHeight;
cursor: pointer;
width: @ImgWidth;
height: @ImgHeight;
}
/*********************************************************************************************************
Info Tooltip
**********************************************************************************************************/
.infoTooltip() {
position: relative;
display: inline-block;
position: relative;
display: inline-block;
}
.tooltipText(@textColor: @BaseLight, @backgroundColor: @BaseHigh) {
visibility: hidden;
background-color: @backgroundColor;
color: @textColor;
position: absolute;
z-index: 1;
left: @MediumSpace;
padding: @MediumSpace;
visibility: hidden;
background-color: @backgroundColor;
color: @textColor;
position: absolute;
z-index: 1;
left: @MediumSpace;
padding: @MediumSpace;
}
.tooltipTextAfter(@color: @BaseDark) {
content: "";
position: absolute;
right: 100%;
border-style: solid;
border-color: transparent @color transparent transparent;
left: 0px;
width: 0;
height: 0;
border-color: @InfoPointerColor transparent;
content: "";
position: absolute;
right: 100%;
border-style: solid;
border-color: transparent @color transparent transparent;
left: 0px;
width: 0;
height: 0;
border-color: @InfoPointerColor transparent;
}
.tooltipVisible() {
visibility: visible;
visibility: visible;
}
.inputTooltip() {
position: relative;
}
.inputTooltipText(@textColor: @BaseLight, @backgroundColor: @BaseHigh) {
background-color: @backgroundColor;
color: @textColor;
position: absolute;
z-index: 1;
padding: @MediumSpace;
}
.inputTooltipTextAfter(@color: @BaseDark) {
content: "";
position: absolute;
right: 100%;
border-style: solid;
border-color: transparent @color transparent transparent;
left: 10px;
width: 0;
height: 0;
border-color: @InfoPointerColor transparent;
}

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,11 @@
@NavMediumSpace: 10px;
@NavLargeSpace: 15px;
.skip-link {
position: fixed;
top: -200px;
}
html {
font-family: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
padding: 0px;

View File

@@ -1,20 +1,12 @@
@import "./Common/Constants";
.main {
width: 100%;
float: left;
transition: all .0s ease-in-out;
-ms-transition: all 0s ease-in-out;
-webkit-transition: all 0s ease-in-out;
-moz-transition: all .0s ease-in-out;
height: 100%;
background-color: white;
border-left: 0px solid white;
}
.resourceTree {
height: 100%;
flex: 0 0 auto;
.main {
height: 100%;
}
}
.resourceTreeScroll {

265
package-lock.json generated
View File

@@ -393,7 +393,6 @@
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz",
"integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==",
"dev": true,
"requires": {
"@babel/helper-function-name": "^7.10.4",
"@babel/helper-member-expression-to-functions": "^7.12.1",
@@ -620,6 +619,25 @@
"@babel/plugin-syntax-async-generators": "^7.8.0"
}
},
"@babel/plugin-proposal-class-properties": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz",
"integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==",
"requires": {
"@babel/helper-create-class-features-plugin": "^7.12.1",
"@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-proposal-decorators": {
"version": "7.12.12",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.12.tgz",
"integrity": "sha512-fhkE9lJYpw2mjHelBpM2zCbaA11aov2GJs7q4cFaXNrWx0H3bW58H9Esy2rdtYOghFBEYUDRIpvlgi+ZD+AvvQ==",
"requires": {
"@babel/helper-create-class-features-plugin": "^7.12.1",
"@babel/helper-plugin-utils": "^7.10.4",
"@babel/plugin-syntax-decorators": "^7.12.1"
}
},
"@babel/plugin-proposal-dynamic-import": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz",
@@ -729,6 +747,14 @@
"@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-syntax-decorators": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz",
"integrity": "sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w==",
"requires": {
"@babel/helper-plugin-utils": "^7.10.4"
}
},
"@babel/plugin-syntax-dynamic-import": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
@@ -5393,11 +5419,6 @@
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@@ -5641,6 +5662,7 @@
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"optional": true,
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
@@ -6883,14 +6905,7 @@
"dev": true
},
"canvas": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.1.tgz",
"integrity": "sha512-S98rKsPcuhfTcYbtF53UIJhcbgIAK533d1kJKMwsMwAIFgfd58MOyxRud3kktlzWiEkFliaJtvyZCBtud/XVEA==",
"requires": {
"nan": "^2.14.0",
"node-pre-gyp": "^0.11.0",
"simple-get": "^3.0.3"
}
"version": "file:canvas"
},
"capture-exit": {
"version": "2.0.0",
@@ -7454,7 +7469,8 @@
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"optional": true
},
"constants-browserify": {
"version": "1.0.0",
@@ -8435,6 +8451,7 @@
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"optional": true,
"requires": {
"mimic-response": "^2.0.0"
}
@@ -8460,7 +8477,8 @@
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"optional": true
},
"deep-is": {
"version": "0.1.3",
@@ -8652,7 +8670,8 @@
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"optional": true
},
"depd": {
"version": "1.1.2",
@@ -8688,7 +8707,8 @@
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"optional": true
},
"detect-newline": {
"version": "2.1.0",
@@ -10674,14 +10694,6 @@
"universalify": "^0.1.0"
}
},
"fs-minipass": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
"requires": {
"minipass": "^2.6.0"
}
},
"fs-observable": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/fs-observable/-/fs-observable-4.1.14.tgz",
@@ -10823,6 +10835,7 @@
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"optional": true,
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
@@ -10837,12 +10850,14 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -10851,6 +10866,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -10861,6 +10877,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -11350,7 +11367,8 @@
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"optional": true
},
"has-value": {
"version": "1.0.0",
@@ -11832,14 +11850,6 @@
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw=="
},
"ignore-walk": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
"requires": {
"minimatch": "^3.0.4"
}
},
"image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
@@ -15544,7 +15554,8 @@
"mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"optional": true
},
"min-document": {
"version": "2.19.0",
@@ -15601,15 +15612,6 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"minipass": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
},
"minipass-collect": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
@@ -15679,14 +15681,6 @@
}
}
},
"minizlib": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
"requires": {
"minipass": "^2.9.0"
}
},
"mississippi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
@@ -15861,7 +15855,8 @@
"nan": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
"optional": true
},
"nanomatch": {
"version": "1.2.13",
@@ -15911,26 +15906,6 @@
"semver": "^5.4.1"
}
},
"needle": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz",
"integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==",
"requires": {
"debug": "^3.2.6",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
}
}
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@@ -16099,41 +16074,6 @@
"which": "^1.3.0"
}
},
"node-pre-gyp": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
"integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4"
},
"dependencies": {
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"requires": {
"glob": "^7.1.3"
}
}
}
},
"node-releases": {
"version": "1.1.66",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz",
@@ -16146,15 +16086,6 @@
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=",
"optional": true
},
"nopt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
}
},
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@@ -16179,29 +16110,6 @@
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
},
"npm-bundled": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
"requires": {
"npm-normalize-package-bin": "^1.0.1"
}
},
"npm-normalize-package-bin": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
},
"npm-packlist": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1",
"npm-normalize-package-bin": "^1.0.1"
}
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -16214,6 +16122,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"optional": true,
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
@@ -16605,7 +16514,8 @@
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true
},
"os-locale": {
"version": "1.4.0",
@@ -16624,20 +16534,6 @@
"windows-release": "^3.1.0"
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"osenv": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
}
},
"p-defer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
@@ -17265,9 +17161,9 @@
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
},
"prettier": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"dev": true
},
"pretty-error": {
@@ -17690,6 +17586,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"optional": true,
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
@@ -18101,6 +17998,11 @@
"resolved": "https://registry.npmjs.org/redux-observable/-/redux-observable-2.0.0-alpha.0.tgz",
"integrity": "sha512-w0RsVGprIFiYi1AhFCOATiv3ld2AtuobvbcVsLvX19p8eAwLowWl2OrKYcCq/QEeEpmSHTXutXfVfcBnzaWmdw=="
},
"reflect-metadata": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
},
"reflect.ownkeys": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
@@ -19116,12 +19018,14 @@
"simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"optional": true
},
"simple-get": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
"optional": true,
"requires": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
@@ -20113,30 +20017,6 @@
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
"dev": true
},
"tar": {
"version": "4.4.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.8.6",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
},
"dependencies": {
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"requires": {
"minimist": "^1.2.5"
}
}
}
},
"tar-fs": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
@@ -20728,9 +20608,9 @@
}
},
"typescript": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz",
"integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz",
"integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==",
"dev": true
},
"typestyle": {
@@ -22192,6 +22072,7 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"optional": true,
"requires": {
"string-width": "^1.0.2 || 2"
},
@@ -22199,12 +22080,14 @@
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"optional": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"optional": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
@@ -22214,6 +22097,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"optional": true,
"requires": {
"ansi-regex": "^3.0.0"
}
@@ -22383,7 +22267,8 @@
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
"yargs": {
"version": "13.3.2",

View File

@@ -6,8 +6,10 @@
"dependencies": {
"@azure/arm-cosmosdb": "9.1.0",
"@azure/cosmos": "3.9.0",
"@azure/identity": "1.1.0",
"@azure/cosmos-language-service": "0.0.5",
"@azure/identity": "1.1.0",
"@babel/plugin-proposal-class-properties": "7.12.1",
"@babel/plugin-proposal-decorators": "7.12.12",
"@jupyterlab/services": "6.0.0-rc.2",
"@jupyterlab/terminal": "3.0.0-rc.2",
"@microsoft/applicationinsights-web": "2.5.9",
@@ -44,7 +46,7 @@
"applicationinsights": "1.8.0",
"babel-polyfill": "6.26.0",
"bootstrap": "3.4.1",
"canvas": "2.6.1",
"canvas": "file:./canvas",
"clean-webpack-plugin": "0.1.19",
"copy-webpack-plugin": "6.0.2",
"crossroads": "0.12.2",
@@ -85,6 +87,7 @@
"react-notification-system": "0.2.17",
"react-redux": "7.1.3",
"redux": "4.0.4",
"reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12",
"rxjs": "6.6.3",
"styled-components": "4.3.2",
@@ -160,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",
@@ -170,7 +173,7 @@
"ts-loader": "6.2.2",
"tslint": "5.11.0",
"tslint-microsoft-contrib": "6.0.0",
"typescript": "4.1.2",
"typescript": "4.0.2",
"url-loader": "1.1.1",
"wait-on": "4.0.2",
"webpack": "4.43.0",

View File

@@ -1,6 +1,6 @@
export enum AuthType {
AAD = "aad",
EncryptedToken = "encryptedtoken",
MasterKey = "masterkey",
ResourceToken = "resourcetoken",
}
export enum AuthType {
AAD = "aad",
EncryptedToken = "encryptedtoken",
MasterKey = "masterkey",
ResourceToken = "resourcetoken"
}

View File

@@ -1,21 +1,21 @@
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();
}
}
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();
}
}

View File

@@ -42,7 +42,7 @@ export class Registerer {
// Initial rendering at mount point
ReactDOM.render(adapter.renderComponent(), element);
},
}
} as ko.BindingHandler;
}
}

View File

@@ -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));
}
}

View File

@@ -14,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";
@@ -113,7 +113,6 @@ export class Features {
public static readonly enableTtl = "enablettl";
public static readonly enableNotebooks = "enablenotebooks";
public static readonly enableGalleryPublish = "enablegallerypublish";
public static readonly enableCodeOfConduct = "enablecodeofconduct";
public static readonly enableLinkInjection = "enablelinkinjection";
public static readonly enableSpark = "enablespark";
public static readonly livyEndpoint = "livyendpoint";
@@ -127,12 +126,15 @@ export class Features {
public static readonly enableSchema = "enableschema";
public static readonly enableSDKoperations = "enablesdkoperations";
public static readonly showMinRUSurvey = "showminrusurvey";
public static readonly selfServeType = "selfservetype";
}
// 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 AutoscaleTest = "autoscaletest";
public static readonly MongoIndexing = "mongoindexing";
}
export class AfecFeatures {
@@ -150,7 +152,7 @@ export class Spark {
"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",
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM"
});
}
@@ -165,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
@@ -292,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
];
}
@@ -348,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
}
}
@@ -394,7 +399,7 @@ export class OfferVersions {
export enum ConflictOperationType {
Replace = "replace",
Create = "create",
Delete = "delete",
Delete = "delete"
}
export const EmulatorMasterKey =

View File

@@ -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";

View File

@@ -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());
@@ -81,9 +81,9 @@ export function client(): Cosmos.CosmosClient {
key: userContext.masterKey,
tokenProvider,
connectionPolicy: {
enableEndpointDiscovery: false,
enableEndpointDiscovery: false
},
userAgentSuffix: "Azure Portal",
userAgentSuffix: "Azure Portal"
};
if (configContext.PROXY_PATH !== undefined) {

View File

@@ -1,155 +0,0 @@
import { ConflictDefinition, FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import Q from "q";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import ConflictId from "../Explorer/Tree/ConflictId";
import DocumentId from "../Explorer/Tree/DocumentId";
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
import * as Constants from "./Constants";
import { client } from "./CosmosClient";
export function getCommonQueryOptions(options: FeedOptions): any {
const storedItemPerPageSetting: number = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage);
options = options || {};
options.populateQueryMetrics = true;
options.enableScanInQuery = options.enableScanInQuery || true;
if (!options.partitionKey) {
options.forceQueryPlan = true;
}
options.maxItemCount =
options.maxItemCount ||
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
Constants.Queries.itemsPerPage;
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
return options;
}
export function queryDocuments(
databaseId: string,
containerId: string,
query: string,
options: any
): Q.Promise<QueryIterator<ItemDefinition & Resource>> {
options = getCommonQueryOptions(options);
const documentsIterator = client().database(databaseId).container(containerId).items.query(query, options);
return Q(documentsIterator);
}
export function getPartitionKeyHeaderForConflict(conflictId: ConflictId): Object {
const partitionKeyDefinition: DataModels.PartitionKey = conflictId.partitionKey;
const partitionKeyValue: any = conflictId.partitionKeyValue;
return getPartitionKeyHeader(partitionKeyDefinition, partitionKeyValue);
}
export function getPartitionKeyHeader(partitionKeyDefinition: DataModels.PartitionKey, partitionKeyValue: any): Object {
if (!partitionKeyDefinition) {
return undefined;
}
if (partitionKeyValue === undefined) {
return [{}];
}
return [partitionKeyValue];
}
export function updateDocument(
collection: ViewModels.CollectionBase,
documentId: DocumentId,
newDocument: any
): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.replace(newDocument)
.then((response) => response.resource)
);
}
export function executeStoredProcedure(
collection: ViewModels.Collection,
storedProcedure: StoredProcedure,
partitionKeyValue: any,
params: any[]
): Q.Promise<any> {
// TODO remove this deferred. Kept it because of timeout code at bottom of function
const deferred = Q.defer<any>();
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedure(storedProcedure.id())
.execute(partitionKeyValue, params, { enableScriptLogging: true })
.then((response) =>
deferred.resolve({
result: response.resource,
scriptLogs: response.headers[Constants.HttpHeaders.scriptLogResults],
})
)
.catch((error) => deferred.reject(error));
return deferred.promise.timeout(
Constants.ClientDefaults.requestTimeoutMs,
`Request timed out while executing stored procedure ${storedProcedure.id()}`
);
}
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.items.create(newDocument)
.then((response) => response.resource)
);
}
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.read()
.then((response) => response.resource)
);
}
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return Q(
client().database(collection.databaseId).container(collection.id()).item(documentId.id(), partitionKey).delete()
);
}
export function deleteConflict(
collection: ViewModels.CollectionBase,
conflictId: ConflictId,
options: any = {}
): Q.Promise<any> {
options.partitionKey = options.partitionKey || getPartitionKeyHeaderForConflict(conflictId);
return Q(
client().database(collection.databaseId).container(collection.id()).conflict(conflictId.id()).delete(options)
);
}
export function queryConflicts(
databaseId: string,
containerId: string,
query: string,
options: any
): Q.Promise<QueryIterator<ConflictDefinition & Resource>> {
const documentsIterator = client().database(databaseId).container(containerId).conflicts.query(query, options);
return Q(documentsIterator);
}

View File

@@ -1,217 +0,0 @@
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import Q from "q";
import * as ViewModels from "../Contracts/ViewModels";
import ConflictId from "../Explorer/Tree/ConflictId";
import DocumentId from "../Explorer/Tree/DocumentId";
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import { logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
import * as Constants from "./Constants";
import * as DataAccessUtilityBase from "./DataAccessUtilityBase";
import { MinimalQueryIterator, nextPage } from "./IteratorUtilities";
import { handleError } from "./ErrorHandlingUtils";
// TODO: Log all promise resolutions and errors with verbosity levels
export function queryDocuments(
databaseId: string,
containerId: string,
query: string,
options: any
): Q.Promise<QueryIterator<ItemDefinition & Resource>> {
return DataAccessUtilityBase.queryDocuments(databaseId, containerId, query, options);
}
export function queryConflicts(
databaseId: string,
containerId: string,
query: string,
options: any
): Q.Promise<QueryIterator<ConflictDefinition & Resource>> {
return DataAccessUtilityBase.queryConflicts(databaseId, containerId, query, options);
}
export function getEntityName() {
const defaultExperience =
window.dataExplorer && window.dataExplorer.defaultExperience && window.dataExplorer.defaultExperience();
if (defaultExperience === Constants.DefaultAccountExperience.MongoDB) {
return "document";
}
return "item";
}
export function executeStoredProcedure(
collection: ViewModels.Collection,
storedProcedure: StoredProcedure,
partitionKeyValue: any,
params: any[]
): Q.Promise<any> {
var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Executing stored procedure ${storedProcedure.id()}`);
DataAccessUtilityBase.executeStoredProcedure(collection, storedProcedure, partitionKeyValue, params)
.then(
(response: any) => {
deferred.resolve(response);
logConsoleInfo(
`Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
},
(error: any) => {
handleError(
error,
"ExecuteStoredProcedure",
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function queryDocumentsPage(
resourceName: string,
documentsIterator: MinimalQueryIterator,
firstItemIndex: number,
options: any
): Q.Promise<ViewModels.QueryResults> {
var deferred = Q.defer<ViewModels.QueryResults>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
Q(nextPage(documentsIterator, firstItemIndex))
.then(
(result: ViewModels.QueryResults) => {
const itemCount = (result.documents && result.documents.length) || 0;
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
deferred.resolve(result);
},
(error: any) => {
handleError(error, "QueryDocumentsPage", `Failed to query ${entityName} for container ${resourceName}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Reading ${entityName} ${documentId.id()}`);
DataAccessUtilityBase.readDocument(collection, documentId)
.then(
(document: any) => {
deferred.resolve(document);
},
(error: any) => {
handleError(error, "ReadDocument", `Failed to read ${entityName} ${documentId.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function updateDocument(
collection: ViewModels.CollectionBase,
documentId: DocumentId,
newDocument: any
): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Updating ${entityName} ${documentId.id()}`);
DataAccessUtilityBase.updateDocument(collection, documentId, newDocument)
.then(
(updatedDocument: any) => {
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
deferred.resolve(updatedDocument);
},
(error: any) => {
handleError(error, "UpdateDocument", `Failed to update ${entityName} ${documentId.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Creating new ${entityName} for container ${collection.id()}`);
DataAccessUtilityBase.createDocument(collection, newDocument)
.then(
(savedDocument: any) => {
logConsoleInfo(`Successfully created new ${entityName} for container ${collection.id()}`);
deferred.resolve(savedDocument);
},
(error: any) => {
handleError(error, "CreateDocument", `Error while creating new ${entityName} for container ${collection.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`);
DataAccessUtilityBase.deleteDocument(collection, documentId)
.then(
(response: any) => {
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
deferred.resolve(response);
},
(error: any) => {
handleError(error, "DeleteDocument", `Error while deleting ${entityName} ${documentId.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function deleteConflict(
collection: ViewModels.CollectionBase,
conflictId: ConflictId,
options?: any
): Q.Promise<any> {
var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Deleting conflict ${conflictId.id()}`);
DataAccessUtilityBase.deleteConflict(collection, conflictId, options)
.then(
(response: any) => {
logConsoleInfo(`Successfully deleted conflict ${conflictId.id()}`);
deferred.resolve(response);
},
(error: any) => {
handleError(error, "DeleteConflict", `Error while deleting conflict ${conflictId.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}

View File

@@ -0,0 +1,10 @@
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
import { userContext } from "../UserContext";
export const getEntityName = (): string => {
if (userContext.defaultExperience === DefaultAccountExperienceType.MongoDB) {
return "document";
}
return "item";
};

View File

@@ -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;
}
}

View File

@@ -1,8 +1,6 @@
export default class EnvironmentUtility {
public static normalizeArmEndpointUri(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;
}

View File

@@ -21,7 +21,7 @@ export const handleError = (error: string | ARMError | Error, area: string, cons
sendNotificationForError(errorMessage, errorCode);
};
export const getErrorMessage = (error: string | Error): string => {
export const getErrorMessage = (error: string | Error = ""): string => {
const errorMessage = typeof error === "string" ? error : error.message;
return replaceKnownError(errorMessage);
};
@@ -37,7 +37,7 @@ const sendNotificationForError = (errorMessage: string, errorCode: number | stri
}
sendMessage({
type: MessageTypes.ForbiddenError,
reason: errorMessage,
reason: errorMessage
});
}
};
@@ -45,10 +45,10 @@ const sendNotificationForError = (errorMessage: string, errorCode: number | stri
const replaceKnownError = (errorMessage: string): string => {
if (
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal &&
errorMessage.indexOf("SharedOffer is Disabled for your account") >= 0
errorMessage?.indexOf("SharedOffer is Disabled for your account") >= 0
) {
return "Database throughput is not supported for internal subscriptions.";
} else if (errorMessage.indexOf("Partition key paths must contain only valid") >= 0) {
} else if (errorMessage?.indexOf("Partition key paths must contain only valid") >= 0) {
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
}

View File

@@ -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);
});
});
});

View File

@@ -1,28 +1,28 @@
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;
}
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;
}

View File

@@ -11,8 +11,8 @@ describe("nextPage", () => {
queryMetrics: {},
requestCharge: 1,
headers: {},
activityId: "foo",
}),
activityId: "foo"
})
};
expect(await nextPage(fakeIterator, 10)).toMatchSnapshot();

View File

@@ -14,7 +14,7 @@ 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;
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
const itemCount = (documents && documents.length) || 0;
@@ -26,7 +26,7 @@ export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex
lastItemIndex: Number(firstItemIndex) + Number(itemCount),
headers,
activityId: response.activityId,
requestCharge: response.requestCharge,
requestCharge: response.requestCharge
};
});
}

View File

@@ -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();
});
});

View File

@@ -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
};
}

View File

@@ -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();
});
});

View File

@@ -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
);

View File

@@ -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);
});
@@ -222,10 +222,10 @@ describe("MongoProxyClient", () => {
resetConfigContext();
delete window.authType;
updateUserContext({
databaseAccount,
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
});

View File

@@ -16,7 +16,7 @@ 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() {
@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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,6 +25,7 @@ describe("parseSDKOfferResponse", () => {
minimumThroughput: 400,
id: "test",
offerDefinition: mockOfferDefinition,
offerReplacePending: false
};
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);
@@ -36,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 = {
@@ -55,6 +56,7 @@ describe("parseSDKOfferResponse", () => {
minimumThroughput: 400,
id: "test",
offerDefinition: mockOfferDefinition,
offerReplacePending: false
};
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);

View File

@@ -1,8 +1,12 @@
import { Offer, SDKOfferDefinition } from "../Contracts/DataModels";
import { OfferResponse } from "@azure/cosmos";
import { HttpHeaders } from "./Constants";
export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
const offerDefinition: SDKOfferDefinition = offerResponse?.resource;
export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer | undefined => {
const offerDefinition: SDKOfferDefinition | undefined = offerResponse?.resource;
if (!offerDefinition) {
return undefined;
}
const offerContent = offerDefinition.content;
if (!offerContent) {
return undefined;
@@ -11,14 +15,14 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
const minimumThroughput = offerContent.collectionThroughputInfo?.minimumRUForCollection;
const autopilotSettings = offerContent.offerAutopilotSettings;
if (autopilotSettings) {
if (autopilotSettings && autopilotSettings.maxThroughput && minimumThroughput) {
return {
id: offerDefinition.id,
autoscaleMaxThroughput: autopilotSettings.maxThroughput,
manualThroughput: undefined,
minimumThroughput,
offerDefinition,
headers: offerResponse.headers,
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true"
};
}
@@ -28,6 +32,6 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
manualThroughput: offerContent.offerThroughput,
minimumThroughput,
offerDefinition,
headers: offerResponse.headers,
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true"
};
};

View File

@@ -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) {

View File

@@ -1,253 +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 { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
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 { createDocument, deleteDocument, queryDocuments, queryDocumentsPage } from "./DocumentClientUtilityBase";
import { createCollection } from "./dataAccess/createCollection";
import { handleError } from "./ErrorHandlingUtils";
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 id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
"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.logConsoleMessage(
ConsoleDataType.Info,
"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(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
}
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.logConsoleMessage(
ConsoleDataType.Error,
`Failed to save query ${query.queryName}: ${errorMessage}`
);
return Promise.reject(errorMessage);
}
try {
this.validateQuery(query);
} catch (error) {
const errorMessage: string = "Invalid query specified";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to save query ${query.queryName}: ${errorMessage}`
);
return Promise.reject(errorMessage);
}
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Saving query ${query.queryName}`
);
query.id = query.queryName;
return createDocument(queriesCollection, query)
.then(
(savedQuery: DataModels.Query) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`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(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
}
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.logConsoleMessage(
ConsoleDataType.Error,
`Failed to fetch saved queries: ${errorMessage}`
);
return Promise.reject(errorMessage);
}
const options: any = { enableCrossPartitionQuery: true };
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
return queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
.then(
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex, options);
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.logConsoleMessage(ConsoleDataType.Info, "Successfully fetched saved queries");
return Promise.resolve(queries);
},
(error: any) => {
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
return Promise.reject(error);
}
);
},
(error: any) => {
// should never get into this state but we handle this regardless
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
}
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.logConsoleMessage(
ConsoleDataType.Error,
`Failed to fetch saved queries: ${errorMessage}`
);
return Promise.reject(errorMessage);
}
try {
this.validateQuery(query);
} catch (error) {
const errorMessage: string = "Invalid query specified";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to delete query ${query.queryName}: ${errorMessage}`
);
}
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`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.logConsoleMessage(
ConsoleDataType.Info,
`Successfully deleted query ${query.queryName}`
);
return Promise.resolve();
},
(error: any) => {
handleError(error, "deleteQuery", `Failed to delete query ${query.queryName}`);
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
}
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;
}
}

View File

@@ -1,106 +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() {
this.splitter = document.getElementById(this.splitterId);
this.leftSide = 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);
}
}

View File

@@ -32,8 +32,8 @@ export default class UrlUtility {
type: type,
objectBody: {
id: id,
self: resourcePath,
},
self: resourcePath
}
};
return result;

View File

@@ -1,6 +1,5 @@
jest.mock("../../Utils/arm/request");
jest.mock("../CosmosClient");
jest.mock("../DataAccessUtilityBase");
import { AuthType } from "../../AuthType";
import { CreateCollectionParams, DatabaseAccount } from "../../Contracts/DataModels";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
@@ -15,15 +14,15 @@ 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
});
});
@@ -41,12 +40,12 @@ describe("createCollection", () => {
return {
database: {
containers: {
create: () => ({}),
},
},
create: () => ({})
}
}
};
},
},
}
}
});
await createCollection(createCollectionParams);
expect(client).toHaveBeenCalled();
@@ -60,7 +59,7 @@ describe("createCollection", () => {
collectionId: "testContainer",
databaseId: "testDatabase",
databaseLevelThroughput: false,
offerThroughput: 400,
offerThroughput: 400
};
expect(constructRpOptions(manualThroughputParams)).toEqual({ throughput: 400 });
@@ -70,12 +69,12 @@ describe("createCollection", () => {
databaseId: "testDatabase",
databaseLevelThroughput: false,
offerThroughput: 400,
autoPilotMaxThroughput: 4000,
autoPilotMaxThroughput: 4000
};
expect(constructRpOptions(autoPilotThroughputParams)).toEqual({
autoscaleSettings: {
maxThroughput: 4000,
},
maxThroughput: 4000
}
});
});
});

View File

@@ -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";
@@ -41,7 +41,7 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
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 };

View File

@@ -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";
@@ -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
};
}

View File

@@ -0,0 +1,25 @@
import { CollectionBase } from "../../Contracts/ViewModels";
import { client } from "../CosmosClient";
import { getEntityName } from "../DocumentUtility";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
export const createDocument = async (collection: CollectionBase, newDocument: unknown): Promise<unknown> => {
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Creating new ${entityName} for container ${collection.id()}`);
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.items.create(newDocument);
logConsoleInfo(`Successfully created new ${entityName} for container ${collection.id()}`);
return response?.resource;
} catch (error) {
handleError(error, "CreateDocument", `Error while creating new ${entityName} for container ${collection.id()}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -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";
@@ -49,8 +49,8 @@ export async function createStoredProcedure(
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
properties: {
resource: storedProcedure as SqlStoredProcedureResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlStoredProcedure(
userContext.subscriptionId,

View File

@@ -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";
@@ -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}`);

View File

@@ -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";
@@ -49,8 +49,8 @@ export async function createUserDefinedFunction(
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
properties: {
resource: userDefinedFunction as SqlUserDefinedFunctionResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlUserDefinedFunction(
userContext.subscriptionId,

View File

@@ -13,9 +13,9 @@ describe("deleteCollection", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -32,11 +32,11 @@ describe("deleteCollection", () => {
return {
container: () => {
return {
delete: (): unknown => undefined,
delete: (): unknown => undefined
};
},
}
};
},
}
});
await deleteCollection("database", "collection");
expect(client).toHaveBeenCalled();

View File

@@ -16,7 +16,10 @@ export async function deleteCollection(databaseId: string, collectionId: string)
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) {

View File

@@ -0,0 +1,36 @@
import ConflictId from "../../Explorer/Tree/ConflictId";
import { CollectionBase } from "../../Contracts/ViewModels";
import { RequestOptions } from "@azure/cosmos";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
export const deleteConflict = async (collection: CollectionBase, conflictId: ConflictId): Promise<void> => {
const clearMessage = logConsoleProgress(`Deleting conflict ${conflictId.id()}`);
try {
const options = {
partitionKey: getPartitionKeyHeaderForConflict(conflictId)
};
await client()
.database(collection.databaseId)
.container(collection.id())
.conflict(conflictId.id())
.delete(options as RequestOptions);
logConsoleInfo(`Successfully deleted conflict ${conflictId.id()}`);
} catch (error) {
handleError(error, "DeleteConflict", `Error while deleting conflict ${conflictId.id()}`);
throw error;
} finally {
clearMessage();
}
};
const getPartitionKeyHeaderForConflict = (conflictId: ConflictId): unknown => {
if (!conflictId.partitionKey) {
return undefined;
}
return conflictId.partitionKeyValue === undefined ? [{}] : [conflictId.partitionKeyValue];
};

View File

@@ -13,9 +13,9 @@ describe("deleteDatabase", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -30,9 +30,9 @@ describe("deleteDatabase", () => {
(client as jest.Mock).mockReturnValue({
database: () => {
return {
delete: (): unknown => undefined,
delete: (): unknown => undefined
};
},
}
});
await deleteDatabase("database");
expect(client).toHaveBeenCalled();

View File

@@ -19,7 +19,9 @@ export async function deleteDatabase(databaseId: string): Promise<void> {
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) {

View File

@@ -0,0 +1,25 @@
import { CollectionBase } from "../../Contracts/ViewModels";
import { client } from "../CosmosClient";
import { getEntityName } from "../DocumentUtility";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import DocumentId from "../../Explorer/Tree/DocumentId";
export const deleteDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<void> => {
const entityName: string = getEntityName();
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`);
try {
await client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), documentId.partitionKeyValue)
.delete();
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
} catch (error) {
handleError(error, "DeleteDocument", `Error while deleting ${entityName} ${documentId.id()}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -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}`);

View File

@@ -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}`);

View File

@@ -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}`);

View File

@@ -0,0 +1,48 @@
import { Collection } from "../../Contracts/ViewModels";
import { ClientDefaults, HttpHeaders } from "../Constants";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
import StoredProcedure from "../../Explorer/Tree/StoredProcedure";
export interface ExecuteSprocResult {
result: StoredProcedure;
scriptLogs: string;
}
export const executeStoredProcedure = async (
collection: Collection,
storedProcedure: StoredProcedure,
partitionKeyValue: string,
params: string[]
): Promise<ExecuteSprocResult> => {
const clearMessage = logConsoleProgress(`Executing stored procedure ${storedProcedure.id()}`);
const timeout = setTimeout(() => {
throw Error(`Request timed out while executing stored procedure ${storedProcedure.id()}`);
}, ClientDefaults.requestTimeoutMs);
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedure(storedProcedure.id())
.execute(partitionKeyValue, params, { enableScriptLogging: true });
clearTimeout(timeout);
logConsoleInfo(
`Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
return {
result: response.resource,
scriptLogs: response.headers[HttpHeaders.scriptLogResults] as string
};
} catch (error) {
handleError(
error,
"ExecuteStoredProcedure",
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -1,3 +1,4 @@
import { AuthType } from "../../AuthType";
import { armRequest } from "../../Utils/arm/request";
import { configContext } from "../../ConfigContext";
import { handleError } from "../ErrorHandlingUtils";
@@ -40,6 +41,10 @@ interface MetricsResponse {
}
export const getCollectionUsageSizeInKB = async (databaseName: string, containerName: string): Promise<number> => {
if (window.authType !== AuthType.AAD) {
return undefined;
}
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
@@ -55,8 +60,8 @@ export const getCollectionUsageSizeInKB = async (databaseName: string, container
apiVersion: "2018-01-01",
queryParams: {
filter,
metricNames,
},
metricNames
}
});
if (metricsResponse?.value?.length !== 2) {

View File

@@ -11,7 +11,10 @@ export async function getIndexTransformationProgress(databaseId: string, collect
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

View File

@@ -0,0 +1,14 @@
import { ConflictDefinition, FeedOptions, QueryIterator, Resource } from "@azure/cosmos";
import { client } from "../CosmosClient";
export const queryConflicts = (
databaseId: string,
containerId: string,
query: string,
options: FeedOptions
): QueryIterator<ConflictDefinition & Resource> => {
return client()
.database(databaseId)
.container(containerId)
.conflicts.query(query, options);
};

View File

@@ -1,5 +1,5 @@
import { getCommonQueryOptions } from "./DataAccessUtilityBase";
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
import { getCommonQueryOptions } from "./queryDocuments";
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
describe("getCommonQueryOptions", () => {
it("builds the correct default options objects", () => {

View File

@@ -0,0 +1,34 @@
import { Queries } from "../Constants";
import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
import { client } from "../CosmosClient";
export const queryDocuments = (
databaseId: string,
containerId: string,
query: string,
options: FeedOptions
): QueryIterator<ItemDefinition & Resource> => {
options = getCommonQueryOptions(options);
return client()
.database(databaseId)
.container(containerId)
.items.query(query, options);
};
export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
const storedItemPerPageSetting: number = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage);
options = options || {};
options.populateQueryMetrics = true;
options.enableScanInQuery = options.enableScanInQuery || true;
if (!options.partitionKey) {
options.forceQueryPlan = true;
}
options.maxItemCount =
options.maxItemCount ||
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
Queries.itemsPerPage;
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
return options;
};

View File

@@ -0,0 +1,26 @@
import { QueryResults } from "../../Contracts/ViewModels";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { MinimalQueryIterator, nextPage } from "../IteratorUtilities";
import { handleError } from "../ErrorHandlingUtils";
import { getEntityName } from "../DocumentUtility";
export const queryDocumentsPage = async (
resourceName: string,
documentsIterator: MinimalQueryIterator,
firstItemIndex: number
): Promise<QueryResults> => {
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
try {
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex);
const itemCount = (result.documents && result.documents.length) || 0;
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
return result;
} catch (error) {
handleError(error, "QueryDocumentsPage", `Failed to query ${entityName} for container ${resourceName}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -10,9 +10,9 @@ describe("readCollection", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -23,11 +23,11 @@ describe("readCollection", () => {
return {
container: () => {
return {
read: (): unknown => ({}),
read: (): unknown => ({})
};
},
}
};
},
}
});
await readCollection("database", "collection");
expect(client).toHaveBeenCalled();

View File

@@ -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}`);

View File

@@ -106,6 +106,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
autoscaleMaxThroughput: autoscaleSettings.maxThroughput,
manualThroughput: undefined,
minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true"
};
}
@@ -114,6 +115,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
autoscaleMaxThroughput: undefined,
manualThroughput: resource.throughput,
minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true"
};
}

View File

@@ -12,9 +12,9 @@ describe("readCollections", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -32,12 +32,12 @@ describe("readCollections", () => {
containers: {
readAll: () => {
return {
fetchAll: (): unknown => [],
fetchAll: (): unknown => []
};
},
},
}
}
};
},
}
});
await readCollections("database");
expect(client).toHaveBeenCalled();

View File

@@ -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);
}

View File

@@ -78,6 +78,7 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
autoscaleMaxThroughput: autoscaleSettings.maxThroughput,
manualThroughput: undefined,
minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true"
};
}
@@ -86,6 +87,7 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
autoscaleMaxThroughput: undefined,
manualThroughput: resource.throughput,
minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true"
};
}

View File

@@ -12,9 +12,9 @@ describe("readDatabases", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -30,10 +30,10 @@ describe("readDatabases", () => {
databases: {
readAll: () => {
return {
fetchAll: (): unknown => [],
fetchAll: (): unknown => []
};
},
},
}
}
});
await readDatabases();
expect(client).toHaveBeenCalled();

View File

@@ -21,7 +21,9 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
) {
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);
}

View File

@@ -0,0 +1,27 @@
import { Item } from "@azure/cosmos";
import { CollectionBase } from "../../Contracts/ViewModels";
import { client } from "../CosmosClient";
import { getEntityName } from "../DocumentUtility";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import DocumentId from "../../Explorer/Tree/DocumentId";
export const readDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<Item> => {
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Reading ${entityName} ${documentId.id()}`);
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), documentId.partitionKeyValue)
.read();
return response?.resource;
} catch (error) {
handleError(error, "ReadDocument", `Failed to read ${entityName} ${documentId.id()}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -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);
};

View File

@@ -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.

View File

@@ -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()

View File

@@ -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}`);

View File

@@ -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()

View File

@@ -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";
@@ -130,8 +130,8 @@ export async function updateMongoDBCollectionThroughRP(
const updateParams: MongoDBCollectionCreateUpdateParameters = {
properties: {
resource: newCollection,
options: updateOptions,
},
options: updateOptions
}
};
const updateResponse = await createUpdateMongoDBCollection(

View File

@@ -0,0 +1,32 @@
import { CollectionBase } from "../../Contracts/ViewModels";
import { Item } from "@azure/cosmos";
import { client } from "../CosmosClient";
import { getEntityName } from "../DocumentUtility";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import DocumentId from "../../Explorer/Tree/DocumentId";
export const updateDocument = async (
collection: CollectionBase,
documentId: DocumentId,
newDocument: Item
): Promise<Item> => {
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Updating ${entityName} ${documentId.id()}`);
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), documentId.partitionKeyValue)
.replace(newDocument);
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
return response?.resource;
} catch (error) {
handleError(error, "UpdateDocument", `Failed to update ${entityName} ${documentId.id()}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -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> => {
@@ -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 };
}

View File

@@ -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";
@@ -39,8 +39,8 @@ export async function updateStoredProcedure(
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
properties: {
resource: storedProcedure as SqlStoredProcedureResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlStoredProcedure(
userContext.subscriptionId,

View File

@@ -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";
@@ -36,8 +36,8 @@ export async function updateTrigger(
const createTriggerParams: SqlTriggerCreateUpdateParameters = {
properties: {
resource: trigger as SqlTriggerResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlTrigger(
userContext.subscriptionId,

View File

@@ -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";
@@ -39,8 +39,8 @@ export async function updateUserDefinedFunction(
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
properties: {
resource: userDefinedFunction as SqlUserDefinedFunctionResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlUserDefinedFunction(
userContext.subscriptionId,

View File

@@ -1,7 +1,7 @@
export enum Platform {
Portal = "Portal",
Hosted = "Hosted",
Emulator = "Emulator",
Emulator = "Emulator"
}
interface ConfigContext {
@@ -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,7 +73,7 @@ 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"
});
}
@@ -86,7 +86,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
Object.assign(configContext, externalConfig);
if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) {
updateConfigContext({
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins],
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins]
});
}
} catch (error) {

View File

@@ -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

View File

@@ -21,7 +21,7 @@ export enum LogEntryLevel {
/**
* Error level.
*/
Error = 2,
Error = 2
}
/**
* Schema of a log entry.

View File

@@ -1,39 +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,
InitTestExplorer,
}
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 };

View File

@@ -3,5 +3,5 @@ export enum SubscriptionType {
EA,
Free,
Internal,
PAYG,
PAYG
}

View File

@@ -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";

View File

@@ -1,438 +1,440 @@
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 { 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 = 19,
}
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[];
}
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;
}

Some files were not shown because too many files have changed in this diff Show More