Compare commits

..

141 Commits

Author SHA1 Message Date
Laurent Nguyen
a17d71d76e Merge branch 'master' into languy-resource-tree-to-react 2021-03-16 16:27:30 +01:00
Steve Faulkner
732d7ce8fa Fix upload worker error display (#547) 2021-03-15 22:47:49 -05:00
Laurent Nguyen
faf2e3b559 Remove comment and test code 2021-03-15 17:35:35 +01:00
Steve Faulkner
254c551999 Test Explorer Improvements (#541) 2021-03-14 22:53:16 -05:00
Jordi Bunster
f86883de6c MostRecentActivity changes (#463)
This changes the public API a bit, so that recording activity (the most common use) is less involved.
2021-03-15 03:10:48 +00:00
Laurent Nguyen
7e992c2b17 Fix build issues and reformat 2021-03-12 14:01:35 +01:00
Laurent Nguyen
21b92ed4f8 Merge branch 'master' into languy-resource-tree-to-react 2021-03-12 11:21:01 +01:00
Laurent Nguyen
e5755dff39 Fix unit tests 2021-03-12 10:36:27 +01:00
Laurent Nguyen
e48a6a10cb Fix notebook updates issues 2021-03-11 16:22:59 +01:00
Steve Faulkner
62550f8d6a Deprecate Explorer Properties (#535) 2021-03-10 23:02:55 -06:00
hardiknai-techm
184910ee6c Resolve Lint errors in NotebookConfigurationUtils.ts (#490) 2021-03-10 21:47:08 -06:00
Steve Faulkner
9253ab1876 Run Emulator test only on master (#536) 2021-03-10 18:52:32 -06:00
Steve Faulkner
920c95b614 Fix cleanup databases task 2021-03-10 16:03:50 -06:00
Srinath Narayanan
1d98c83be5 Created selfServe landing page (#444)
* Portal changes for DedicatedGateway

Changes to support creation and deletion of DedicatedGateway resource.

Tested locally with various scenarios.

* Portal changes for DedicatedGateway. CR feedback

* Stylecop changes

* created selfServe.html landing page

* Removing TODO comments

* exposed baselineValues

* added getOnSaveNotification

* disable UI when onSave is taking place

* minro edits

* made polling optional

* added optional polling

* added default

* Added portal notifications

* merged more changes

* minor edits

* added label for description

* Added correlationids and polling of refresh

* Added correlationids and polling of refresh

* minor edit

* added label tooltip

* removed ClassInfo decorator

* Added dynamic decription

* added info and warninf types for description

* more changes to promise retry

* promise retry changes

* added spinner on selfserve load

* compile errors fixed

* New changes

* added operationstatus link

* merged sqlxEdits

* undid sqlx changes

* added completed notification

* passed retryInterval in notif options

* more retry changes

* more changes

* added polling on landing on the page

* edits for error display

* added keys blade link

* added link generation

* added link to blade

* Modified info and description

* fixed format errors

* added selfserve contract to output files

* addressed PR comments

Co-authored-by: Balaji Sridharan <fnbalaji@microsoft.com>
Co-authored-by: fnbalaji <75445927+fnbalaji@users.noreply.github.com>
2021-03-10 13:55:05 -08:00
Sunil Kumar Yadav
b85a20cbea Fix Lint errors in StringUtility.ts (#479) 2021-03-09 19:33:29 -06:00
hardiknai-techm
d0f6923d24 Configure onsave auto format eslint in vscode (#478) 2021-03-09 19:31:38 -06:00
Srinath Narayanan
ecdc41ada9 Added more selfserve changes (#443)
* exposed baselineValues

* added getOnSaveNotification

* disable UI when onSave is taking place

* added optional polling

* Added portal notifications

* minor edits

* added label for description

* Added correlationids and polling of refresh

* added label tooltip

* removed ClassInfo decorator

* Added dynamic decription

* added info and warninf types for description

* promise retry changes

* compile errors fixed

* merged sqlxEdits

* undid sqlx changes

* added completed notification

* passed retryInterval in notif options

* added polling on landing on the page

* edits for error display

* added link generation

* addressed PR comments

* modified test

* fixed compilation error
2021-03-09 16:07:23 -08:00
Tanuj Mittal
c1b74266eb Gallery fixes (#514)
- Fix COC overlay height
- Make standalone gallery usable on mobile devices

Before:
![image](https://user-images.githubusercontent.com/693092/110415215-81cd0680-80b7-11eb-8000-bd0b8536607a.png)

After:
![image](https://user-images.githubusercontent.com/693092/110415236-898cab00-80b7-11eb-8266-94a5718113fe.png)
2021-03-09 18:42:54 +00:00
hardiknai-techm
ef6ecf0a5f Resolve Lint errors in NavBar component (#528) 2021-03-09 12:34:01 -06:00
Sunil Kumar Yadav
b241771e69 fixed eslint of IteratorUtility (#469) 2021-03-09 12:27:24 -06:00
victor-meng
9d63a346e4 Fix issues in the add collection pane (#501) 2021-03-09 10:18:40 -08:00
hardiknai-techm
2e7c7440d3 Resolve Lint errors in CommandBarUtil.test.tsx (#529) 2021-03-09 10:20:14 -06:00
Sunil Kumar Yadav
641dae30a1 fix-eslint-NTeractUtil.ts (#493) 2021-03-09 10:17:02 -06:00
Sunil Kumar Yadav
f192310697 Fixed lint issue and remove unused code (#491) 2021-03-08 22:54:00 -06:00
Sunil Kumar Yadav
588c1d3ec3 fixed eslint GraphUtil and removed files from eslintignore (#482) 2021-03-08 22:53:08 -06:00
hardiknai-techm
7eb2817acc Resolve Lint errors in MessageValidation.ts (#489) 2021-03-08 22:45:56 -06:00
Sunil Kumar Yadav
9c28b7f9c5 Fixed lint issue of AuthorizationUtils (#496) 2021-03-08 19:10:26 -06:00
Sunil Kumar Yadav
4807169b0c Eslint/fix lint date time utility (#471)
* fixed lint issue of DateTimeUtility

* remove commented code

* Do change request.
2021-03-08 17:20:45 -06:00
Sunil Kumar Yadav
d85b6285ac fixed lint issue of StringUtils file (#480)
* fixed lint issue of StringUtils file

* Fixed null lint issue
2021-03-08 17:20:11 -06:00
hardiknai-techm
9617b80b56 Fix Lint errors in ThemeUtility (#477)
* Fix Lint errors in ThemeUtility

* format ThemeUtility.ts file

Co-authored-by: hardiknai <hardiknai92@gmail.com>
Co-authored-by: zen3-hardik <hardikkumar.n@zen3tech.com>
2021-03-08 17:19:22 -06:00
hardiknai-techm
1af44fb207 Fix Lint errors in JunoUtil (#484) 2021-03-08 17:10:24 -06:00
hardiknai-techm
9d30dd5d0a Resolve Lint errors in AutoPilotUtils.ts (#488) 2021-03-08 17:09:46 -06:00
hardiknai-techm
4eb0dedddb Resolve Lint errors in quickstart.ts (#492) 2021-03-08 17:08:24 -06:00
Steve Faulkner
c844986c34 Move resourceToken test to portal runner sub (#499)
Co-authored-by: Steve Faulkner <stfaul@microsoft.com>
2021-03-08 16:35:20 -06:00
Steve Faulkner
4e702716bd More Flaky Test Improvements (#498) 2021-03-08 14:41:12 -06:00
Jordi Bunster
1d2995ef32 Fix eslint warnings (#456) 2021-03-08 10:29:41 -08:00
hardiknai-techm
ee919a68a5 Resolve Lint errors in definitions.ts (#494) 2021-03-08 11:32:45 -06:00
Steve Faulkner
2ec9df52aa Increase cleanup window to 60 minutes 2021-03-08 11:16:40 -06:00
Steve Faulkner
0ed9fe029d Fix Test DB cleanup conditional 2021-03-08 10:45:26 -06:00
Steve Faulkner
45af1d7cf9 Fix Test Database Cleanup Script (#497) 2021-03-08 10:14:31 -06:00
Laurent Nguyen
4480a7250d Remove ResourceTreeAdapter 2021-03-08 14:20:27 +01:00
Srinath Narayanan
69975cd0e8 made public gallery the first tab (#483) 2021-03-05 09:03:48 -08:00
Steve Faulkner
c1141406ff End to End Test Improvements Round 2 (#475) 2021-03-04 22:49:52 -06:00
sunilyadav840
acb284eac7 Eslint/fix lint headers utility (#470) 2021-03-04 20:46:33 -06:00
Steve Faulkner
498c39c877 End to End Test Improvements (#474)
* End to End Test Improvements

* indenting

* Log completed

* Fix up some test

* Add delay

Co-authored-by: Steve Faulkner <stfaul@microsoft.com>
2021-03-04 18:12:31 -06:00
Steve Faulkner
87e016f03c Always publish nuget master build (#472) 2021-03-04 12:24:43 -06:00
Srinath Narayanan
3a1841ad3c Remove injected cell before download (#467)
* remove newcell on download

* addressed pr comments
2021-03-04 00:05:30 -08:00
Jordi Bunster
d314a20b81 Fix subscription leak (#465)
* Fix subscription leak

* Update src/Explorer/SplashScreen/SplashScreen.tsx

Co-authored-by: Laurent Nguyen <laurent.nguyen@microsoft.com>

* Array needs to exist in the first place

Co-authored-by: Laurent Nguyen <laurent.nguyen@microsoft.com>
2021-03-03 21:59:43 -08:00
Jordi Bunster
7188e8d8c2 Remove Explorer.mostRecentActivity (#455)
This also moves the UI concerns of MostRecentActivity over to SplashScreen

Co-authored-by: Steve Faulkner <stfaul@microsoft.com>
2021-03-03 01:24:08 -08:00
Srinath Narayanan
3cd2ec93f2 fixed notebook viewer bug (#461) 2021-03-02 14:19:10 -08:00
Srinath Narayanan
b8e9903287 Added spinner for notebook delete (#458)
* initial UI for delete published nb spinner

* added notebook delete spinner

* addressed PR comments
2021-03-02 13:59:10 -08:00
victor-meng
4127d0f522 Fix sample container is not populated with items (#459) 2021-03-02 12:41:50 -06:00
Srinath Narayanan
56b5a9861b Removed published, favourites tabs for hosted gallery (#457)
* added hosted explorer check

* added check for non null container
2021-03-01 05:24:11 -08:00
Steve Faulkner
10664162c7 Refactor Telemetry to include account name and experience (#452) 2021-02-28 15:56:09 -06:00
Srinath Narayanan
cf01ffa957 removed enableGalleryPublish, enableLinkInjection feature flags (#454)
* removed enableGalleryPublish, enableLinkInjection

* removed ENABLE_GALLERY_PUBLISH
2021-02-26 13:07:44 -08:00
victor-meng
3cc1945140 Fix serverless create issue (#453) 2021-02-25 19:29:29 -06:00
Garrett Ausfeldt
864d9393f2 Change schema endpoints (#447)
* fix resource bug (id is a method)

* change schema endpoints

* fix test

Co-authored-by: REDMOND\gaausfel <gaausfel@microsoft.com>
2021-02-25 11:36:01 -08:00
Steve Faulkner
8629bcbe2d Move "Open Full Screen" to React Panel (#449) 2021-02-24 19:04:28 -06:00
Steve Faulkner
6c90ef2e62 Remove Dialog Adapter (#446) 2021-02-24 18:41:28 -06:00
Steve Faulkner
2d2d8b6efe Remove Unused Tabs (#450) 2021-02-24 17:48:33 -06:00
victor-meng
7cbf7202b0 Fix start with samples in Gremlin API (#448) 2021-02-24 12:17:15 -06:00
Jordi Bunster
e8e5eb55cb Remove SplashScreenComponentAdapter (#440)
Merge SplashScreenComponentAdapter and SplashScreenComponent into one SplashScreen React component.
2021-02-23 11:15:57 -08:00
Steve Faulkner
f0c82a430b Remove AdHoc Access and Token Renewal Pane (#445) 2021-02-23 11:16:00 -06:00
Steve Faulkner
3777b6922e Revert "ci: add Azure Static Web Apps workflow file " This reverts commit aec951694a. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # Your branch is up to date with 'origin/master'. # # Changes to be committed: # deleted: .github/workflows/azure-static-web-apps-victorious-sea-0ea0a4710.yml # 2021-02-22 20:21:40 -06:00
Azure Static Web Apps
aec951694a ci: add Azure Static Web Apps workflow file
on-behalf-of: @Azure opensource@microsoft.com
2021-02-22 19:58:22 -06:00
Steve Faulkner
07474b8271 Remove window.authType (#437) 2021-02-22 14:43:58 -06:00
Deborah Chen
e092e5140f Fixing issue with autoscale A/B test (#442)
Currently, the first time a user opens the New Container pane, the autoscale setting is not being read from the flight info. This is because the flight into is being set in resetData(), which is not called the 1st time a user opens the pane. 

This change adds logic to set the flight info on the open call.
2021-02-22 18:54:55 +00:00
Steve Faulkner
1f4074f3e8 Update Coding Guidelines (#441)
Co-authored-by: Christopher Anderson <anderson.chris.john@gmail.com>
2021-02-18 13:18:50 -06:00
Chris-MS-896
1ec0d9a0be End to end testcase for mongoDb index policy (#426)
* 'update for index policy'

* input

* 'refactor for ci/cd'

* 'refactor for ci/cd'

* no message

* 'refractor'

* 'format change'

* added changes

* "refactor"

* ‘test lint’

* "format"

* “push setting back”

* ‘refractor’

* Update test/utils/shared.ts

Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>

Co-authored-by: Srinath Narayanan <srnara@microsoft.com>
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2021-02-18 05:40:58 -08:00
Srinath Narayanan
eddc334cb5 mongo index editor for AAD login + hostedExplorer (#438) 2021-02-17 12:52:47 -08:00
victor-meng
22d8a7a1be Move database settings tab to react (#386)
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
2021-02-10 16:06:14 -06:00
victor-meng
4210e0752b Move delete collection confirmation pane to react (#417) 2021-02-10 13:44:00 -08:00
Steve Faulkner
b217d4be1b Delete Cassandra tables/keyspaces via ARM (#436) 2021-02-08 18:52:53 -06:00
victor-meng
81fd442fad Make getCollectionDataUsageSize call fail gracefully (#434) 2021-02-08 16:02:02 -08:00
Steve Faulkner
87f7dd2230 Rename Feedback -> Report Issue (#425)
Co-authored-by: victor-meng <56978073+victor-meng@users.noreply.github.com>
2021-02-08 14:23:55 -06:00
Srinath Narayanan
9926fd97a2 Test explorer changes (#420)
* Changes to publish pane

* fixed format errors

* fixed failing test

* added test explorer changes for mongo accounts

* added log for test

* fixed lit errors

* added secrets to ci.yml file

* fixed failing self serve test
2021-02-08 09:42:16 -08:00
Tanuj Mittal
2a7546e0de Skip SelfServe e2e test (#432) 2021-02-06 02:30:46 +05:30
Tanuj Mittal
4b442dd869 Use GET instead of PATCH for some Juno endpoints (#431) 2021-02-06 02:01:34 +05:30
Tanuj Mittal
f0b4737313 Update gallery colors (#430)
* Update gallery colors

* fix lint error

* Fix test
2021-02-06 01:19:36 +05:30
Srinath Narayanan
8dc5ed590a Added Spinner for public gallery (#427)
* Added more publish changes

* addressed PR comments

* fixed lint errors

Co-authored-by: Tanuj Mittal <tamitta@microsoft.com>
2021-02-05 11:32:26 -08:00
Tanuj Mittal
afaa844d28 Telemetry updates (#429) 2021-02-06 00:26:20 +05:30
Tanuj Mittal
3e5a876ef2 Fix setting isGalleryPublishEnabled when flight is enabled (#428) 2021-02-05 20:55:08 +05:30
Tanuj Mittal
51abf1560a Disable dark overlay for Dialog (#423) 2021-02-05 19:28:57 +05:30
Srinath Narayanan
1c0fed88c0 Changes to cards in notebook gallery (#422)
* added gallery changes

* addressed PR comments
2021-02-05 02:32:55 -08:00
Tanuj Mittal
93cfd52e36 Update Code of Conduct Overlay and other minor changes (#424)
* Use light theme for coc-overlay

* Updates

* Fix vertical height for COC overlay
2021-02-05 14:56:50 +05:30
Tanuj Mittal
3fd014ddad Disable caching for config.json file (#421)
* Disable caching for config.json file

* Disable cache when fetching config.json
2021-02-04 15:25:13 +05:30
Srinath Narayanan
3b6fda4fa5 Changes to notebook publish pane (#419)
* Changes to publish pane

* fixed format errors

* fixed failing test

Co-authored-by: Tanuj Mittal <tamitta@microsoft.com>
2021-02-03 10:46:51 -08:00
Tanuj Mittal
db7c45c9b8 Enable gallery publishing in MPAC (#416)
* Enable gallery publishing in MPAC

* Address feedback

* Use ENABLE_GALLERY_PUBLISH config in standalone gallery

* Fix test
2021-02-03 23:42:11 +05:30
Tanuj Mittal
4f6b75fe79 More gallery updates (#418)
* More gallery updates

* Add PublishContent icon

* Address feedback
2021-02-03 23:24:27 +05:30
Tanuj Mittal
5038a01079 Add telemetry for Notebooks Gallery and other updates (#413)
* Add telemetry for Notebooks Gallery

* More changes

* Address feedback and fix lint error

* Fix margins for My published work
2021-02-03 14:48:50 +05:30
Tanuj Mittal
e0063c76d9 Add support for gallerypublish flight (#412) 2021-02-03 02:05:19 +05:30
Tanuj Mittal
9278654479 Public gallery improvements (#409)
- [x] Don't show extension in name field for publish
- [x] Open "Your published work" tab after publishing
- [x] Continue showing dialog for Report Abuse status
- [x] For showing COC in Public Gallery tab show backdrop of thumbnails
- [x] Liked -> My Favorites & Your published work -> My published work
2021-01-29 17:04:38 +00:00
Tanuj Mittal
59113d7bbf Update Juno endpoints (#405) 2021-01-29 20:28:20 +05:30
Tim Sander
88d8200c14 Check that customer is using Mongo 3.6 before applying index everything policy (#410) 2021-01-28 15:26:47 -06:00
Srinath Narayanan
6aaddd9c60 Added localization for the Self Serve Model (#406)
* added localization for selfserve model

* added comment

* addressed PR comments

* fixed format errors

* Addressed PR comments
2021-01-28 11:17:02 -08:00
Jordi Bunster
f8ede0cc1e Remove Q from ViewModels (#390)
I got cold feet at the thought of merging #324 in one go, so I'm going to split it into smaller chunks and keep rebasing the large one until there's no more Q.
2021-01-28 18:13:26 +00:00
Laurent Nguyen
bddb288a89 Update package versions and package-lock.json (#404)
The file `package-lock.json` is not in sync with `package.json` anymore. This causes build issues when upgrading a package.
This change sync's `package-lock.json` and fixes the build issues.
2021-01-28 08:50:24 +00:00
Steve Faulkner
a14d20a88e Fix applyExplorerBindings call in Portal (#408) 2021-01-27 20:37:14 -06:00
Steve Faulkner
f1db1ed978 Region Select Button (#407) 2021-01-27 15:32:53 -06:00
Laurent Nguyen
86a483c3a4 Fix notebook cell selection bug (#402)
This fixes a bug that prevents getting focus to a text cell (effectively preventing editing) when the window height is small after double-clicking on a neighboring code cell.
The issue is that selecting a text cell is broken likely because there's a behavior change in MonacoEditor that keeps the focus on the code cell. The selection issue will probably be resolved when migrating the text cell to Monaco (which will acquire and keep focus the same way), but for now, this will disable the faulty code which doesn't appear to work anymore (presumably auto-scrolling to the cell).
2021-01-27 09:09:54 +00:00
Tanuj Mittal
263262a040 Update Juno endpoints to pass subscriptionId (#339)
Corresponding [server side change](https://msdata.visualstudio.com/CosmosDB/_git/CosmosDB-portal/pullrequest/464443?_a=overview) has been deployed to Prod so now we can go ahead with DE side changes.
2021-01-27 08:08:58 +00:00
victor-meng
bd4d8da065 Move notification console to react (#400) 2021-01-26 15:32:37 -08:00
Steve Faulkner
59ec18cd9b Add basic static code metrics (#396) 2021-01-26 13:13:13 -06:00
Srinath Narayanan
49bf8c60db Added more Self Serve functionalities (#401)
* added recursion and inition decorators

* working version

* added todo comment and removed console.log

* Added Recursive add

* removed type requirement

* proper resolution of promises

* added custom element and base class

* Made selfServe standalone page

* Added custom renderer as async type

* Added overall defaults

* added inital open from data explorer

* removed landingpage

* added feature for self serve type

* renamed sqlx->example and added invalid type

* Added comments for Example

* removed unnecessary changes

* Resolved PR comments

Added tests
Moved onSubmt and initialize inside base class
Moved testExplorer to separate folder
made fields of SelfServe Class non static

* fixed lint errors

* fixed compilation errors

* Removed reactbinding changes

* renamed dropdown -> choice

* Added SelfServeComponent

* Addressed PR comments

* added toggle, visibility, text display,commandbar

* added sqlx example

* added onRefrssh

* formatting changes

* rmoved radioswitch display

* updated smartui tests

* Added more tests

* onSubmit -> onSave

* Resolved PR comments
2021-01-26 09:44:14 -08:00
Steve Faulkner
b0b973b21a Refactor explorer config into useKnockoutExplorer hook (#397)
Co-authored-by: Steve Faulkner <stfaul@microsoft.com>
2021-01-25 13:56:15 -06:00
Chris-MS-896
3529e80f0d no message (#398) 2021-01-22 10:02:35 -06:00
Srinath Narayanan
a298fd8389 Added message to indicate compound indexes are not supported in Mongo Index editor (#395)
* mongo message

* Added test and bug fix in Main.tsx

* format changes

* added new formatting

* added null check
2021-01-21 10:56:05 -08:00
Steve Faulkner
1ecc467f60 Remove IE nuget (#394) 2021-01-20 12:46:12 -06:00
Steve Faulkner
b3cafe3468 Add telemetry to Spark+Synapse Pools (#392) 2021-01-20 11:08:29 -06:00
Steve Faulkner
4be53284b5 Prettier 2.0 (#393) 2021-01-20 09:15:01 -06:00
Srinath Narayanan
c1937ca464 Added the Self Serve Data Model (#367)
* added recursion and inition decorators

* working version

* added todo comment and removed console.log

* Added Recursive add

* removed type requirement

* proper resolution of promises

* added custom element and base class

* Made selfServe standalone page

* Added custom renderer as async type

* Added overall defaults

* added inital open from data explorer

* removed landingpage

* added feature for self serve type

* renamed sqlx->example and added invalid type

* Added comments for Example

* removed unnecessary changes

* Resolved PR comments

Added tests
Moved onSubmt and initialize inside base class
Moved testExplorer to separate folder
made fields of SelfServe Class non static

* fixed lint errors

* fixed compilation errors

* Removed reactbinding changes

* renamed dropdown -> choice

* Added SelfServeComponent

* Addressed PR comments

* merged master

* added selfservetype.none for emulator and hosted experience

* fixed formatting errors

* Removed "any" type

* undid package.json changes
2021-01-19 22:42:45 -08:00
Steve Faulkner
2b2de7c645 Migrated Hosted Explorer to React (#360)
Co-authored-by: Victor Meng <vimeng@microsoft.com>
Co-authored-by: Steve Faulkner <stfaul@microsoft.com>
2021-01-19 16:31:55 -06: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
Steve Faulkner
fcbc9474ea Remove Preview for Synapse Link (#389) 2021-01-15 09:51:14 -06: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
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
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
595 changed files with 82248 additions and 49057 deletions

View File

@@ -4,6 +4,8 @@ PORTAL_RUNNER_SUBSCRIPTION=
PORTAL_RUNNER_RESOURCE_GROUP= PORTAL_RUNNER_RESOURCE_GROUP=
PORTAL_RUNNER_DATABASE_ACCOUNT= PORTAL_RUNNER_DATABASE_ACCOUNT=
PORTAL_RUNNER_DATABASE_ACCOUNT_KEY= PORTAL_RUNNER_DATABASE_ACCOUNT_KEY=
PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT=
PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT_KEY=
PORTAL_RUNNER_CONNECTION_STRING= PORTAL_RUNNER_CONNECTION_STRING=
NOTEBOOKS_TEST_RUNNER_TENANT_ID= NOTEBOOKS_TEST_RUNNER_TENANT_ID=
NOTEBOOKS_TEST_RUNNER_CLIENT_ID= NOTEBOOKS_TEST_RUNNER_CLIENT_ID=

View File

@@ -11,15 +11,9 @@ src/Common/CosmosClient.test.ts
src/Common/CosmosClient.ts src/Common/CosmosClient.ts
src/Common/DataAccessUtilityBase.test.ts src/Common/DataAccessUtilityBase.test.ts
src/Common/DataAccessUtilityBase.ts src/Common/DataAccessUtilityBase.ts
src/Common/DeleteFeedback.ts
src/Common/DocumentClientUtilityBase.ts
src/Common/EditableUtility.ts src/Common/EditableUtility.ts
src/Common/HashMap.test.ts src/Common/HashMap.test.ts
src/Common/HashMap.ts src/Common/HashMap.ts
src/Common/HeadersUtility.test.ts
src/Common/HeadersUtility.ts
src/Common/IteratorUtilities.test.ts
src/Common/IteratorUtilities.ts
src/Common/Logger.test.ts src/Common/Logger.test.ts
src/Common/MessageHandler.test.ts src/Common/MessageHandler.test.ts
src/Common/MessageHandler.ts src/Common/MessageHandler.ts
@@ -30,7 +24,6 @@ src/Common/ObjectCache.test.ts
src/Common/ObjectCache.ts src/Common/ObjectCache.ts
src/Common/QueriesClient.ts src/Common/QueriesClient.ts
src/Common/Splitter.ts src/Common/Splitter.ts
src/Common/ThemeUtility.ts
src/Common/UrlUtility.ts src/Common/UrlUtility.ts
src/Config.ts src/Config.ts
src/Contracts/ActionContracts.ts src/Contracts/ActionContracts.ts
@@ -58,8 +51,6 @@ src/Explorer/ComponentRegisterer.test.ts
src/Explorer/ComponentRegisterer.ts src/Explorer/ComponentRegisterer.ts
src/Explorer/ContextMenuButtonFactory.ts src/Explorer/ContextMenuButtonFactory.ts
src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts
src/Explorer/Controls/CommandButton/CommandButton.test.ts
src/Explorer/Controls/CommandButton/CommandButton.ts
src/Explorer/Controls/DiffEditor/DiffEditorComponent.ts src/Explorer/Controls/DiffEditor/DiffEditorComponent.ts
src/Explorer/Controls/DynamicList/DynamicList.test.ts src/Explorer/Controls/DynamicList/DynamicList.test.ts
src/Explorer/Controls/DynamicList/DynamicListComponent.ts src/Explorer/Controls/DynamicList/DynamicListComponent.ts
@@ -87,7 +78,7 @@ src/Explorer/DataSamples/ContainerSampleGenerator.test.ts
src/Explorer/DataSamples/ContainerSampleGenerator.ts src/Explorer/DataSamples/ContainerSampleGenerator.ts
src/Explorer/DataSamples/DataSamplesUtil.test.ts src/Explorer/DataSamples/DataSamplesUtil.test.ts
src/Explorer/DataSamples/DataSamplesUtil.ts src/Explorer/DataSamples/DataSamplesUtil.ts
src/Explorer/Explorer.ts src/Explorer/Explorer.tsx
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.test.ts src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.test.ts
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts
src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.test.ts src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.test.ts
@@ -95,8 +86,6 @@ src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.ts
src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts
src/Explorer/Graph/GraphExplorerComponent/GraphData.test.ts src/Explorer/Graph/GraphExplorerComponent/GraphData.test.ts
src/Explorer/Graph/GraphExplorerComponent/GraphData.ts src/Explorer/Graph/GraphExplorerComponent/GraphData.ts
src/Explorer/Graph/GraphExplorerComponent/GraphUtil.test.ts
src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts
src/Explorer/Graph/GraphExplorerComponent/GremlinClient.test.ts src/Explorer/Graph/GraphExplorerComponent/GremlinClient.test.ts
src/Explorer/Graph/GraphExplorerComponent/GremlinClient.ts src/Explorer/Graph/GraphExplorerComponent/GremlinClient.ts
src/Explorer/Graph/GraphExplorerComponent/GremlinSimpleClient.test.ts src/Explorer/Graph/GraphExplorerComponent/GremlinSimpleClient.test.ts
@@ -110,7 +99,6 @@ src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.ts
src/Explorer/Menus/ContextMenu.ts src/Explorer/Menus/ContextMenu.ts
src/Explorer/MostRecentActivity/MostRecentActivity.ts src/Explorer/MostRecentActivity/MostRecentActivity.ts
src/Explorer/Notebook/FileSystemUtil.ts src/Explorer/Notebook/FileSystemUtil.ts
src/Explorer/Notebook/NTeractUtil.ts
src/Explorer/Notebook/NotebookClientV2.ts src/Explorer/Notebook/NotebookClientV2.ts
src/Explorer/Notebook/NotebookComponent/NotebookContentProvider.ts src/Explorer/Notebook/NotebookComponent/NotebookContentProvider.ts
src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts
@@ -162,7 +150,7 @@ src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts
src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts
src/Explorer/Panes/UploadFilePane.ts src/Explorer/Panes/UploadFilePane.ts
src/Explorer/Panes/UploadItemsPane.ts src/Explorer/Panes/UploadItemsPane.ts
src/Explorer/SplashScreen/SplashScreenComponentAdapter.test.ts src/Explorer/SplashScreen/SplashScreen.test.ts
src/Explorer/Tables/Constants.ts src/Explorer/Tables/Constants.ts
src/Explorer/Tables/DataTable/CacheBase.ts src/Explorer/Tables/DataTable/CacheBase.ts
src/Explorer/Tables/DataTable/DataTableBindingManager.ts src/Explorer/Tables/DataTable/DataTableBindingManager.ts
@@ -170,7 +158,6 @@ src/Explorer/Tables/DataTable/DataTableBuilder.ts
src/Explorer/Tables/DataTable/DataTableContextMenu.ts src/Explorer/Tables/DataTable/DataTableContextMenu.ts
src/Explorer/Tables/DataTable/DataTableOperationManager.ts src/Explorer/Tables/DataTable/DataTableOperationManager.ts
src/Explorer/Tables/DataTable/DataTableOperations.ts src/Explorer/Tables/DataTable/DataTableOperations.ts
src/Explorer/Tables/DataTable/DataTableUtilities.ts
src/Explorer/Tables/DataTable/DataTableViewModel.ts src/Explorer/Tables/DataTable/DataTableViewModel.ts
src/Explorer/Tables/DataTable/TableCommands.ts src/Explorer/Tables/DataTable/TableCommands.ts
src/Explorer/Tables/DataTable/TableEntityCache.ts src/Explorer/Tables/DataTable/TableEntityCache.ts
@@ -179,8 +166,6 @@ src/Explorer/Tables/Entities.ts
src/Explorer/Tables/QueryBuilder/ClauseGroup.ts src/Explorer/Tables/QueryBuilder/ClauseGroup.ts
src/Explorer/Tables/QueryBuilder/ClauseGroupViewModel.ts src/Explorer/Tables/QueryBuilder/ClauseGroupViewModel.ts
src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts
src/Explorer/Tables/QueryBuilder/DateTimeUtilities.test.ts
src/Explorer/Tables/QueryBuilder/DateTimeUtilities.ts
src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts
src/Explorer/Tables/QueryBuilder/QueryClauseViewModel.ts src/Explorer/Tables/QueryBuilder/QueryClauseViewModel.ts
src/Explorer/Tables/QueryBuilder/QueryViewModel.ts src/Explorer/Tables/QueryBuilder/QueryViewModel.ts
@@ -200,7 +185,6 @@ src/Explorer/Tabs/QueryTab.test.ts
src/Explorer/Tabs/QueryTab.ts src/Explorer/Tabs/QueryTab.ts
src/Explorer/Tabs/QueryTablesTab.ts src/Explorer/Tabs/QueryTablesTab.ts
src/Explorer/Tabs/ScriptTabBase.ts src/Explorer/Tabs/ScriptTabBase.ts
src/Explorer/Tabs/SparkMasterTab.ts
src/Explorer/Tabs/StoredProcedureTab.ts src/Explorer/Tabs/StoredProcedureTab.ts
src/Explorer/Tabs/TabComponents.ts src/Explorer/Tabs/TabComponents.ts
src/Explorer/Tabs/TabsBase.ts src/Explorer/Tabs/TabsBase.ts
@@ -264,8 +248,6 @@ src/Shared/ExplorerSettings.ts
src/Shared/PriceEstimateCalculator.ts src/Shared/PriceEstimateCalculator.ts
src/Shared/StorageUtility.test.ts src/Shared/StorageUtility.test.ts
src/Shared/StorageUtility.ts src/Shared/StorageUtility.ts
src/Shared/StringUtility.test.ts
src/Shared/StringUtility.ts
src/Shared/appInsights.ts src/Shared/appInsights.ts
src/SparkClusterManager/ArcadiaResourceManager.ts src/SparkClusterManager/ArcadiaResourceManager.ts
src/SparkClusterManager/SparkClusterManager.ts src/SparkClusterManager/SparkClusterManager.ts
@@ -274,25 +256,14 @@ src/Terminal/NotebookAppContracts.d.ts
src/Terminal/index.ts src/Terminal/index.ts
src/TokenProviders/PortalTokenProvider.ts src/TokenProviders/PortalTokenProvider.ts
src/TokenProviders/TokenProviderFactory.ts src/TokenProviders/TokenProviderFactory.ts
src/Utils/AuthorizationUtils.test.ts
src/Utils/AuthorizationUtils.ts
src/Utils/AutoPilotUtils.test.ts
src/Utils/AutoPilotUtils.ts
src/Utils/DatabaseAccountUtils.test.ts src/Utils/DatabaseAccountUtils.test.ts
src/Utils/DatabaseAccountUtils.ts src/Utils/DatabaseAccountUtils.ts
src/Utils/JunoUtils.ts
src/Utils/MessageValidation.ts
src/Utils/NotebookConfigurationUtils.ts
src/Utils/PricingUtils.test.ts src/Utils/PricingUtils.test.ts
src/Utils/QueryUtils.test.ts src/Utils/QueryUtils.test.ts
src/Utils/QueryUtils.ts src/Utils/QueryUtils.ts
src/Utils/StringUtils.test.ts
src/Utils/StringUtils.ts
src/applyExplorerBindings.ts src/applyExplorerBindings.ts
src/global.d.ts src/global.d.ts
src/quickstart.ts
src/setupTests.ts src/setupTests.ts
src/workers/upload/definitions.ts
src/workers/upload/index.ts src/workers/upload/index.ts
src/Explorer/Controls/AccessibleElement/AccessibleElement.tsx src/Explorer/Controls/AccessibleElement/AccessibleElement.tsx
src/Explorer/Controls/Accordion/AccordionComponent.tsx src/Explorer/Controls/Accordion/AccordionComponent.tsx
@@ -339,15 +310,7 @@ src/Explorer/Graph/GraphExplorerComponent/QueryContainerComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNeighborsComponent.tsx src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNeighborsComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.tsx src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx
src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx
src/Explorer/Menus/CommandBar/CommandBarUtil.test.tsx
src/Explorer/Menus/CommandBar/CommandBarUtil.tsx src/Explorer/Menus/CommandBar/CommandBarUtil.tsx
src/Explorer/Menus/CommandBar/MemoryTrackerComponent.tsx
src/Explorer/Menus/NavBar/ControlBarComponent.tsx
src/Explorer/Menus/NavBar/ControlBarComponentAdapter.tsx
src/Explorer/Menus/NavBar/MeControlComponent.test.tsx
src/Explorer/Menus/NavBar/MeControlComponent.tsx
src/Explorer/Menus/NavBar/MeControlComponentAdapter.tsx
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.test.tsx src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.test.tsx
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.tsx src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.tsx
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponentAdapter.tsx src/Explorer/Menus/NotificationConsole/NotificationConsoleComponentAdapter.tsx
@@ -377,8 +340,7 @@ src/Explorer/Notebook/temp/inputs/editor.tsx
src/Explorer/Notebook/temp/markdown-cell.tsx src/Explorer/Notebook/temp/markdown-cell.tsx
src/Explorer/Notebook/temp/source.tsx src/Explorer/Notebook/temp/source.tsx
src/Explorer/Notebook/temp/syntax-highlighter/index.tsx src/Explorer/Notebook/temp/syntax-highlighter/index.tsx
src/Explorer/SplashScreen/SplashScreenComponent.tsx src/Explorer/SplashScreen/SplashScreen.tsx
src/Explorer/SplashScreen/SplashScreenComponentApdapter.tsx
src/Explorer/Tabs/GalleryTab.tsx src/Explorer/Tabs/GalleryTab.tsx
src/Explorer/Tabs/NotebookViewerTab.tsx src/Explorer/Tabs/NotebookViewerTab.tsx
src/Explorer/Tabs/TerminalTab.tsx src/Explorer/Tabs/TerminalTab.tsx

View File

@@ -1,41 +1,39 @@
module.exports = { module.exports = {
env: { env: {
browser: true, browser: true,
es6: true es6: true,
}, },
plugins: ["@typescript-eslint", "no-null", "prefer-arrow"], plugins: ["@typescript-eslint", "no-null", "prefer-arrow"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
globals: { globals: {
Atomics: "readonly", Atomics: "readonly",
SharedArrayBuffer: "readonly" SharedArrayBuffer: "readonly",
}, },
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
parserOptions: { parserOptions: {
ecmaFeatures: { ecmaFeatures: {
jsx: true jsx: true,
}, },
ecmaVersion: 2018, ecmaVersion: 2018,
sourceType: "module" sourceType: "module",
}, },
overrides: [ overrides: [
{ {
files: ["**/*.tsx"], files: ["**/*.tsx"],
env: { extends: ["plugin:react/recommended"], // TODO: Add react-hooks
jest: true plugins: ["react"],
},
extends: ["plugin:react/recommended"],
plugins: ["react"]
}, },
{ {
files: ["**/*.{test,spec}.{ts,tsx}"], files: ["**/*.{test,spec}.{ts,tsx}"],
env: { env: {
jest: true jest: true,
}, },
extends: ["plugin:jest/recommended"], extends: ["plugin:jest/recommended"],
plugins: ["jest"] plugins: ["jest"],
} },
], ],
rules: { rules: {
"no-console": ["error", { allow: ["error", "warn", "dir"] }],
curly: "error", curly: "error",
"@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-extraneous-class": "error", "@typescript-eslint/no-extraneous-class": "error",
@@ -48,8 +46,8 @@ module.exports = {
"error", "error",
{ {
selector: "CallExpression[callee.object.name='JSON'][callee.property.name='stringify'] Identifier[name=/$err/]", 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

@@ -9,15 +9,29 @@ on:
branches: branches:
- master - master
jobs: jobs:
codemetrics:
runs-on: ubuntu-latest
name: "Log Code Metrics"
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v2
- name: Use Node.js 14.x
uses: actions/setup-node@v1
with:
node-version: 14.x
- run: npm ci
- run: node utils/codeMetrics.js
env:
CODE_METRICS_APP_ID: ${{ secrets.CODE_METRICS_APP_ID }}
compile: compile:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: "Compile TypeScript" name: "Compile TypeScript"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 12.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run compile - run: npm run compile
- run: npm run compile:strict - run: npm run compile:strict
@@ -26,10 +40,10 @@ jobs:
name: "Check Format" name: "Check Format"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 12.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run format:check - run: npm run format:check
lint: lint:
@@ -37,10 +51,10 @@ jobs:
name: "Lint" name: "Lint"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 12.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run lint - run: npm run lint
unittest: unittest:
@@ -48,10 +62,10 @@ jobs:
name: "Unit Tests" name: "Unit Tests"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 12.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run test - run: npm run test
build: build:
@@ -60,10 +74,10 @@ jobs:
name: "Build" name: "Build"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 12.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 14.x
- run: npm ci - run: npm ci
- run: npm run build:contracts - run: npm run build:contracts
- name: Restore Build Cache - name: Restore Build Cache
@@ -80,21 +94,21 @@ jobs:
path: dist/ path: dist/
endtoendemulator: endtoendemulator:
name: "End To End Emulator Tests" name: "End To End Emulator Tests"
needs: [lint, format, compile, unittest] if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 12.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 14.x
- uses: southpolesteve/cosmos-emulator-github-action@v1 - uses: southpolesteve/cosmos-emulator-github-action@v1
- name: End to End Tests - name: End to End Tests
run: | run: |
npm ci npm ci
npm start & npm start &
npm run wait-for-server npm run wait-for-server
npx jest -c ./jest.config.e2e.js --detectOpenHandles sql npx jest -c ./jest.config.e2e.js --detectOpenHandles test/sql/container.spec.ts
shell: bash shell: bash
env: env:
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/explorer.html?platform=Emulator" DATA_EXPLORER_ENDPOINT: "https://localhost:1234/explorer.html?platform=Emulator"
@@ -111,10 +125,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 12.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 14.x
- name: Accessibility Check - name: Accessibility Check
run: | run: |
# Ubuntu gets mad when webpack runs too many files watchers # Ubuntu gets mad when webpack runs too many files watchers
@@ -129,45 +143,72 @@ jobs:
env: env:
NODE_TLS_REJECT_UNAUTHORIZED: 0 NODE_TLS_REJECT_UNAUTHORIZED: 0
endtoendhosted: endtoendhosted:
name: "End to End Hosted Tests" name: "End to End Tests"
needs: [lint, format, compile, unittest] needs: [cleanupaccounts]
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
PORTAL_RUNNER_SUBSCRIPTION: ${{ secrets.PORTAL_RUNNER_SUBSCRIPTION }}
PORTAL_RUNNER_RESOURCE_GROUP: ${{ secrets.PORTAL_RUNNER_RESOURCE_GROUP }}
PORTAL_RUNNER_DATABASE_ACCOUNT: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT }}
PORTAL_RUNNER_DATABASE_ACCOUNT_KEY: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT_KEY }}
PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT: ${{ secrets.PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT }}
PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT_KEY: ${{ secrets.PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT_KEY }}
NOTEBOOKS_TEST_RUNNER_TENANT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_TENANT_ID }}
NOTEBOOKS_TEST_RUNNER_CLIENT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_ID }}
NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }}
PORTAL_RUNNER_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
MONGO_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
CASSANDRA_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_CASSANDRA }}
TABLES_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_TABLE }}
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/hostedExplorer.html"
strategy:
fail-fast: false
matrix:
test-file:
- ./test/cassandra/container.spec.ts
- ./test/mongo/mongoIndexPolicy.spec.ts
- ./test/notebooks/uploadAndOpenNotebook.spec.ts
- ./test/selfServe/selfServeExample.spec.ts
- ./test/sql/container.spec.ts
- ./test/sql/resourceToken.spec.ts
- ./test/tables/container.spec.ts
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js 12.x - name: Use Node.js 14.x
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 12.x node-version: 14.x
- name: End to End Hosted Tests - run: npm ci
run: | - run: npm start &
npm ci - run: node utils/cleanupDBs.js
npm start & - run: npm run wait-for-server
npm run wait-for-server - name: ${{ matrix['test-file'] }}
npm run test:e2e run: npx jest -c ./jest.config.e2e.js --detectOpenHandles ${{ matrix['test-file'] }}
shell: bash shell: bash
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
PORTAL_RUNNER_SUBSCRIPTION: ${{ secrets.PORTAL_RUNNER_SUBSCRIPTION }}
PORTAL_RUNNER_RESOURCE_GROUP: ${{ secrets.PORTAL_RUNNER_RESOURCE_GROUP }}
PORTAL_RUNNER_DATABASE_ACCOUNT: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT }}
PORTAL_RUNNER_DATABASE_ACCOUNT_KEY: ${{ secrets.PORTAL_RUNNER_DATABASE_ACCOUNT_KEY }}
NOTEBOOKS_TEST_RUNNER_TENANT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_TENANT_ID }}
NOTEBOOKS_TEST_RUNNER_CLIENT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_ID }}
NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }}
PORTAL_RUNNER_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
MONGO_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
CASSANDRA_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_CASSANDRA }}
TABLES_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_TABLE }}
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/hostedExplorer.html"
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
if: failure() if: failure()
with: with:
name: screenshots name: screenshots
path: failed-* path: failed-*
cleanupaccounts:
name: "Cleanup Test Database Accounts"
runs-on: ubuntu-latest
env:
NOTEBOOKS_TEST_RUNNER_CLIENT_ID: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_ID }}
NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET: ${{ secrets.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET }}
steps:
- uses: actions/checkout@v2
- name: Use Node.js 14.x
uses: actions/setup-node@v1
with:
node-version: 14.x
- run: npm ci
- run: node utils/cleanupDBs.js
nuget: nuget:
name: Publish Nuget name: Publish Nuget
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/') if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted, accessibility] needs: [build]
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }} NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
@@ -183,7 +224,7 @@ jobs:
- run: cp ./configs/prod.json config.json - run: cp ./configs/prod.json config.json
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT" - 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 pack -Version "2.0.0-github-${GITHUB_SHA}"
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg - run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: packages name: packages
with: with:
@@ -191,7 +232,7 @@ jobs:
nugetmpac: nugetmpac:
name: Publish Nuget MPAC name: Publish Nuget MPAC
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/') if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted, accessibility] needs: [build]
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }} NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
@@ -208,32 +249,7 @@ jobs:
- run: sed -i 's/Azure.Cosmos.DB.Data.Explorer/Azure.Cosmos.DB.Data.Explorer.MPAC/g' DataExplorer.nuspec - run: sed -i 's/Azure.Cosmos.DB.Data.Explorer/Azure.Cosmos.DB.Data.Explorer.MPAC/g' DataExplorer.nuspec
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT" - run: nuget 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 pack -Version "2.0.0-github-${GITHUB_SHA}"
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg - run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
- uses: actions/upload-artifact@v2
name: packages
with:
path: "*.nupkg"
nugetie:
name: Publish Nuget IE
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendhosted, accessibility]
runs-on: ubuntu-latest
env:
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
AZURE_DEVOPS_PAT: ${{ secrets.AZURE_DEVOPS_PAT }}
steps:
- uses: nuget/setup-nuget@v1
with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
- name: Download Dist Folder
uses: actions/download-artifact@v2
with:
name: dist
- run: cp ./configs/prod.json config.json
- run: sed -i 's/Azure.Cosmos.DB.Data.Explorer/Azure.Cosmos.DB.Data.Explorer.IE/g' DataExplorer.nuspec
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: packages name: packages
with: with:

43
.vscode/settings.json vendored
View File

@@ -1,21 +1,26 @@
// Place your settings in this file to overwrite default and user settings. // Place your settings in this file to overwrite default and user settings.
{ {
"files.exclude": { "files.exclude": {
".vs": true, ".vs": true,
".vscode/**": true, ".vscode/**": true,
"*.trx": true, "*.trx": true,
"**/.DS_Store": true, "**/.DS_Store": true,
"**/.git": true, "**/.git": true,
"**/.hg": true, "**/.hg": true,
"**/.svn": true, "**/.svn": true,
"built/**": true, "built/**": true,
"coverage/**": true, "coverage/**": true,
"libs/**": true, "libs/**": true,
"node_modules/**": true, "node_modules/**": true,
"package-lock.json": true, "package-lock.json": true,
"quickstart/**": true, "quickstart/**": true,
"test/out/**": true, "test/out/**": true,
"workers/libs/**": true "workers/libs/**": true
}, },
"typescript.tsdk": "node_modules/typescript/lib" "typescript.tsdk": "node_modules/typescript/lib",
} "editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": true
}
}

194
CODING_GUIDELINES.md Normal file
View File

@@ -0,0 +1,194 @@
# Coding Guidelines and Recommendations
Cosmos Explorer has been under constant development for over 5 years. As a result, there are many different patterns and practices in the codebase. This document serves as a guide to how we write code and helps avoid propagating practices which are no longer preferred. Each requirement in this document is labeled and color-coded to show the relative importance. In order from highest to lowest importance:
✅ DO this. If you feel you need an exception, engage with the project owners _prior_ to implementation.
⛔️ DO NOT do this. If you feel you need an exception, engage with the project owners _prior_ to implementation.
☑️ YOU SHOULD strongly consider this but it is not a requirement. If not following this advice, please comment code with why and proactively begin a discussion as part of the PR process.
⚠️ YOU SHOULD NOT strongly consider not doing this. If not following this advice, please comment code with why and proactively begin a discussion as part of the PR process.
💭 YOU MAY consider this advice if appropriate to your situation. Other team members may comment on this as part of PR review, but there is no need to be proactive.
## Development Environment
☑️ YOU SHOULD
- Use VSCode and install the following extensions. This setup will catch most linting/formatting/type errors as you develop:
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
💭 YOU MAY
- Use the [GitHub CLI](https://cli.github.com/). It has helpful workflows for submitting PRs as well as for checking out other team member's PRs.
- Use Windows, Linux (including WSL), or OSX. We have team members developing on all three environments.
✅ DO
- Maintain cross-platform compatibility when modifying any engineering or build systems
## Code Formatting
✅ DO
- Use [Prettier](https://prettier.io/) to format your code
- This will occur automatically if using the recommended editor setup
- `npm run format` will also format code
## Linting
✅ DO
- Use [ESLint](https://eslint.org/) to check for code errors.
- This will occur automatically if using the recommended editor setup
- `npm run lint` will also check for linting errors
💭 YOU MAY
- Consider adding new lint rules.
- If you find yourself performing "nits" as part of PR review, consider adding a lint rule that will automatically catch the error in the future
⚠️ YOU SHOULD NOT
- Disable lint rules
- Lint rules exist as guidance and to catch common mistakes
- You will find places we disable specific lint rules however it should be exceptional.
- If a rule does need to be disabled, prefer disabling a specific line instead of the entire file.
⛔️ DO NOT
- Add [TSLint](https://palantir.github.io/tslint/) rules
- TSLint has been deprecated and is on track to be removed
- Always prefer ESLint rules
## UI Components
☑️ YOU SHOULD
- Write new components using [React](https://reactjs.org/). We are actively migrating Cosmos Explorer off of [Knockout](https://knockoutjs.com/).
- Use [Fluent](https://developer.microsoft.com/en-us/fluentui#/) components.
- Fluent components are designed to be highly accessible and composable
- Using Fluent allows us to build upon the work of the Fluent team and leads to a lower total cost of ownership for UI code
### React
☑️ YOU SHOULD
- Use pure functional components when no state is required
💭 YOU MAY
- Use functional (hooks) or class components
- The project contains examples of both
- Neither is strongly preferred at this time
⛔️ DO NOT
- Use inheritance for sharing component behavior.
- React documentation covers this topic in detail https://reactjs.org/docs/composition-vs-inheritance.html
- Suffix your file or component name with "Component"
- Even though the code has examples of it, we are ending the practice.
## Libraries
⚠️ YOU SHOULD NOT
- Add new libraries to package.json.
- Adding libraries may bring in code that explodes the bundled size or attempts to run NodeJS code in the browser
- Consult with project owners for help with library selection if one is needed
⛔️ DO NOT
- Use underscore.js
- Much of this library is now native to JS and will be automatically transpiled
- Use jQuery
- Much of this library is not native to the DOM.
- We are planning to remove it
## Testing
⛔️ DO NOT
- Decrease test coverage
- Unit/Functional test coverage is checked as part of the CI process
### Unit Tests
✅ DO
- Write unit tests for non-UI and utility code.
- Write your tests using [Jest](https://jestjs.io/)
☑️ YOU SHOULD
- Abstract non-UI and utility code so it can run either the NodeJS or Browser environment
### Functional(Component) Tests
✅ DO
- Write tests for UI components
- Write your tests using [Jest](https://jestjs.io/)
- Use either Enzyme or React Testing Library to perform component tests.
### Mocking
✅ DO
- Use Jest's built-in mocking helpers
☑️ YOU SHOULD
- Write code that does not require mocking
- Build components that do not require mocking extremely large or difficult to mock objects (like Explorer.ts). Pass _only_ what you need.
⛔️ DO NOT
- Use sinon.js for mocking
- Sinon has been deprecated and planned for removal
### End to End Tests
✅ DO
- Use [Puppeteer](https://developers.google.com/web/tools/puppeteer) and [Jest](https://jestjs.io/)
- Write or modify an existing E2E test that covers the primary use case of any major feature.
- Use caution. Do not try to cover every case. End to End tests can be slow and brittle.
☑️ YOU SHOULD
- Write tests that use accessible attributes to perform actions. Role, Title, Label, etc
- More information https://testing-library.com/docs/queries/about#priority
⚠️ YOU SHOULD NOT
- Add test specfic `data-*` attributes to dom elements
- This is a common current practice, but one we would like to avoid in the future
- End to end tests need to use semantic HTML and accesible attributes to be truely end to end
- No user or screen reader actually navigates an app using `data-*` attributes
- Add arbitrary time delays to wait for page to render or element to be ready.
- All the time delays add up and slow down testing.
- Prefer using the framework's "wait for..." functionality.
### Migrating Knockout to React
✅ DO
- Consult other team members before beginning migration work. There is a significant amount of flux in patterns we are using and it is important we do not propagate incorrect patterns.
- Start by converting HTML to JSX: https://magic.reactjs.net/htmltojsx.htm. Add functionality as a second step.
☑️ YOU SHOULD
- Write React components that require no dependency on Knockout or observables to trigger rendering.
## Browser Support
✅ DO
- Support all [browsers supported by the Azure Portal](https://docs.microsoft.com/en-us/azure/azure-portal/azure-portal-supported-browsers-devices)
- Support IE11
- In practice, this should not need to be considered as part of a normal development workflow
- Polyfills and transpilation are already provided by our engineering systems.
- This requirement will be removed on March 30th, 2021 when Azure drops IE11 support.

View File

@@ -1,6 +1,6 @@
# Contribution guidelines to Data Explorer # Contribution guidelines to Data Explorer
This project welcomes contributions and suggestions. Most contributions require you to agree to a This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com. the rights to use your contribution. For details, visit https://cla.microsoft.com.
@@ -13,6 +13,7 @@ For more information see the [Code of Conduct FAQ](https://opensource.microsoft.
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Microsoft Open Source Code of Conduct ## Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
Resources: Resources:
@@ -20,33 +21,3 @@ Resources:
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
## Browser support
Please make sure to support all modern browsers as well as Internet Explorer 11.
For IE support, polyfill is preferred over new usage of lodash or underscore. We already polyfill almost everything by importing babel-polyfill at the top of entry points.
## Coding guidelines, conventions and recommendations
### Typescript
* Follow this [typescript style guide](https://github.com/excelmicro/typescript) which is based on [airbnb's style guide](https://github.com/airbnb/javascript).
* Conventions speficic to this project:
- Use double-quotes for string
- Don't use `null`, use `undefined`
- Pascal case for private static readonly fields
- Camel case for classnames in markup
* Don't use class unless necessary
* Code related to notebooks should be dynamically imported so that it is loaded from a separate bundle only if the account is notebook-enabled. There are already top-level notebook components which are dynamically imported and their dependencies can be statically imported from these files.
* Prefer using [Fluent UI controls](https://developer.microsoft.com/en-us/fluentui#/controls/web) over creating your own, in order to maintain consistency and support a11y.
### React
* Prefer using React class components over function components and hooks unless you have a simple component and require no nested functions:
* Nested functions may be harder to test independently
* Switching from function component to class component later mayb be painful
## Testing
Any PR should not decrease testing coverage.
## Recommended Tools and VS Code extensions
* [Bookmarks](https://github.com/alefragnani/vscode-bookmarks)
* [Bracket pair colorizer](https://github.com/CoenraadS/Bracket-Pair-Colorizer-2)
* [GitHub Pull Requests and Issues](https://github.com/Microsoft/vscode-pull-request-github)

View File

@@ -69,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. 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 # Contributing
Please read the [contribution guidelines](./CONTRIBUTING.md). Please read the [contribution guidelines](./CONTRIBUTING.md).

View File

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

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.31449 2.01439L4.00103 5.31963L3.26105 4.57965L7.8407 0L12.4203 4.57965L11.6804 5.31963L8.36691 2.01439V12.8428H7.31449V2.01439ZM13.629 12.8428H14.6814V16H1V12.8428H2.05242V14.9476H13.629V12.8428Z" fill="#0078D4"/>
</svg>

After

Width:  |  Height:  |  Size: 329 B

View File

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

View File

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

View File

@@ -39,11 +39,11 @@ module.exports = {
// An object that configures minimum threshold enforcement for coverage results // An object that configures minimum threshold enforcement for coverage results
coverageThreshold: { coverageThreshold: {
global: { global: {
branches: 20, branches: 22,
functions: 24, functions: 28,
lines: 30, lines: 33,
statements: 29.0 statements: 31,
} },
}, },
// Make calling deprecated APIs throw helpful error messages // 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 "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", "^dnd-core$": "dnd-core/dist/cjs",
"^react-dnd$": "react-dnd/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 // 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 // A map from regular expressions to paths to transformers
transform: { transform: {
"^.+\\.html?$": "html-loader-jest", "^.+\\.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 // 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 // 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, // unmockedModulePathPatterns: undefined,

View File

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

View File

@@ -1523,6 +1523,21 @@ p {
.tooltipVisible(); .tooltipVisible();
} }
.inputTooltip {
.inputTooltip();
}
.inputTooltip .inputTooltipText {
top: -68px;
.inputTooltipText();
}
.inputTooltip .inputTooltipText::after {
border-width: @MediumSpace @MediumSpace 0 @MediumSpace;
top: 55px;
.inputTooltipTextAfter();
}
.infoTooltip a { .infoTooltip a {
color: @AccentHigh; color: @AccentHigh;
} }
@@ -1694,6 +1709,7 @@ input::-webkit-calendar-picker-indicator {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
max-height: 100vh;
} }
.contextual-pane .paneErrorDetailsContainer { .contextual-pane .paneErrorDetailsContainer {
@@ -2083,7 +2099,7 @@ a:link {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: auto;
height: 100%; height: 100%;
} }
@@ -3027,3 +3043,45 @@ settings-pane {
.collapsibleSection :hover { .collapsibleSection :hover {
cursor: pointer; cursor: pointer;
} }
.messageBarInfoIcon {
color: @InfoIconColor;
}
.messageBarWarningIcon {
color: @WarningIconColor;
}
.freeTierInfoBanner {
background-color: @BaseLow;
display: inline-flex;
padding: @DefaultSpace;
width: 100%;
.freeTierInfoIcon img {
height: 28px;
width: 28px;
margin-left: 4px;
}
.freeTierInfoMessage {
margin: auto 0;
padding-left: @MediumSpace;
}
}
.freeTierInlineWarning {
display: inline-flex;
padding: 8px 8px 8px 0;
width: 100%;
.freeTierWarningIcon img {
height: 20px;
width: 20px;
}
.freeTierWarningMessage {
margin: auto 0;
padding-left: @SmallSpace;
}
}

View File

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

View File

@@ -1,20 +1,13 @@
@import "./Common/Constants"; @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 { .resourceTree {
height: 100%; height: 100%;
flex: 0 0 auto; flex: 0 0 auto;
.main {
height: 100%;
}
border-right: 1px solid @BaseMedium;
} }
.resourceTreeScroll { .resourceTreeScroll {

31403
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,13 +7,14 @@
"@azure/arm-cosmosdb": "9.1.0", "@azure/arm-cosmosdb": "9.1.0",
"@azure/cosmos": "3.9.0", "@azure/cosmos": "3.9.0",
"@azure/cosmos-language-service": "0.0.5", "@azure/cosmos-language-service": "0.0.5",
"@azure/identity": "1.1.0", "@azure/identity": "1.2.1",
"@azure/msal-browser": "2.8.0", "@azure/ms-rest-nodeauth": "3.0.7",
"@azure/msal-react": "1.0.0-alpha.1", "@babel/plugin-proposal-class-properties": "7.12.1",
"@jupyterlab/services": "6.0.0-rc.2", "@babel/plugin-proposal-decorators": "7.12.12",
"@jupyterlab/terminal": "3.0.0-rc.2", "@jupyterlab/services": "6.0.2",
"@jupyterlab/terminal": "3.0.3",
"@microsoft/applicationinsights-web": "2.5.9", "@microsoft/applicationinsights-web": "2.5.9",
"@nteract/commutable": "7.3.2", "@nteract/commutable": "7.4.2",
"@nteract/connected-components": "6.8.2", "@nteract/connected-components": "6.8.2",
"@nteract/core": "15.1.0", "@nteract/core": "15.1.0",
"@nteract/data-explorer": "8.0.3", "@nteract/data-explorer": "8.0.3",
@@ -38,6 +39,7 @@
"@nteract/transform-vega": "7.0.6", "@nteract/transform-vega": "7.0.6",
"@octokit/rest": "17.9.2", "@octokit/rest": "17.9.2",
"@phosphor/widgets": "1.9.3", "@phosphor/widgets": "1.9.3",
"@testing-library/jest-dom": "5.11.9",
"@types/mkdirp": "1.0.1", "@types/mkdirp": "1.0.1",
"@types/node-fetch": "2.5.7", "@types/node-fetch": "2.5.7",
"@uifabric/react-cards": "0.109.110", "@uifabric/react-cards": "0.109.110",
@@ -48,6 +50,7 @@
"bootstrap": "3.4.1", "bootstrap": "3.4.1",
"canvas": "file:./canvas", "canvas": "file:./canvas",
"clean-webpack-plugin": "0.1.19", "clean-webpack-plugin": "0.1.19",
"clipboard-copy": "4.0.1",
"copy-webpack-plugin": "6.0.2", "copy-webpack-plugin": "6.0.2",
"crossroads": "0.12.2", "crossroads": "0.12.2",
"css-element-queries": "1.1.1", "css-element-queries": "1.1.1",
@@ -63,6 +66,9 @@
"eslint-plugin-react": "7.20.0", "eslint-plugin-react": "7.20.0",
"hasher": "1.2.0", "hasher": "1.2.0",
"html2canvas": "1.0.0-rc.5", "html2canvas": "1.0.0-rc.5",
"i18next": "19.8.4",
"i18next-browser-languagedetector": "6.0.1",
"i18next-http-backend": "1.0.23",
"immutable": "4.0.0-rc.12", "immutable": "4.0.0-rc.12",
"is-ci": "2.0.0", "is-ci": "2.0.0",
"jquery": "3.5.1", "jquery": "3.5.1",
@@ -71,6 +77,7 @@
"knockout": "3.5.1", "knockout": "3.5.1",
"mkdirp": "1.0.4", "mkdirp": "1.0.4",
"monaco-editor": "0.18.1", "monaco-editor": "0.18.1",
"ms": "2.1.3",
"msal": "1.4.4", "msal": "1.4.4",
"object.entries": "1.1.0", "object.entries": "1.1.0",
"office-ui-fabric-react": "7.134.1", "office-ui-fabric-react": "7.134.1",
@@ -83,15 +90,17 @@
"react-animate-height": "2.0.8", "react-animate-height": "2.0.8",
"react-dnd": "9.4.0", "react-dnd": "9.4.0",
"react-dnd-html5-backend": "9.4.0", "react-dnd-html5-backend": "9.4.0",
"react-dom": "16.9.0", "react-dom": "16.13.1",
"react-hotkeys": "2.0.0", "react-hotkeys": "2.0.0",
"react-i18next": "11.8.5",
"react-notification-system": "0.2.17", "react-notification-system": "0.2.17",
"react-query": "3.5.5",
"react-redux": "7.1.3", "react-redux": "7.1.3",
"redux": "4.0.4", "redux": "4.0.4",
"reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12", "rx-jupyter": "5.5.12",
"rxjs": "6.6.3", "rxjs": "6.6.3",
"styled-components": "4.3.2", "styled-components": "4.3.2",
"swr": "0.4.0",
"text-encoding": "0.7.0", "text-encoding": "0.7.0",
"underscore": "1.9.1", "underscore": "1.9.1",
"url-polyfill": "1.1.7", "url-polyfill": "1.1.7",
@@ -105,6 +114,7 @@
"@babel/preset-env": "7.9.0", "@babel/preset-env": "7.9.0",
"@babel/preset-react": "7.9.4", "@babel/preset-react": "7.9.4",
"@babel/preset-typescript": "7.9.0", "@babel/preset-typescript": "7.9.0",
"@testing-library/react": "11.2.3",
"@types/applicationinsights-js": "1.0.7", "@types/applicationinsights-js": "1.0.7",
"@types/codemirror": "0.0.56", "@types/codemirror": "0.0.56",
"@types/crossroads": "0.0.30", "@types/crossroads": "0.0.30",
@@ -113,7 +123,7 @@
"@types/enzyme-adapter-react-16": "1.0.6", "@types/enzyme-adapter-react-16": "1.0.6",
"@types/expect-puppeteer": "4.4.3", "@types/expect-puppeteer": "4.4.3",
"@types/hasher": "0.0.31", "@types/hasher": "0.0.31",
"@types/jest": "23.3.10", "@types/jest": "26.0.20",
"@types/jest-environment-puppeteer": "4.3.2", "@types/jest-environment-puppeteer": "4.3.2",
"@types/memoize-one": "4.1.1", "@types/memoize-one": "4.1.1",
"@types/node": "12.11.1", "@types/node": "12.11.1",
@@ -121,8 +131,8 @@
"@types/prop-types": "15.5.8", "@types/prop-types": "15.5.8",
"@types/puppeteer": "3.0.1", "@types/puppeteer": "3.0.1",
"@types/q": "1.5.1", "@types/q": "1.5.1",
"@types/react": "16.9.56", "@types/react": "17.0.0",
"@types/react-dom": "16.0.7", "@types/react-dom": "17.0.0",
"@types/react-notification-system": "0.2.39", "@types/react-notification-system": "0.2.39",
"@types/react-redux": "7.1.7", "@types/react-redux": "7.1.7",
"@types/sinon": "2.3.3", "@types/sinon": "2.3.3",
@@ -146,7 +156,9 @@
"eslint-cli": "1.1.1", "eslint-cli": "1.1.1",
"eslint-plugin-no-null": "1.0.2", "eslint-plugin-no-null": "1.0.2",
"eslint-plugin-prefer-arrow": "1.2.2", "eslint-plugin-prefer-arrow": "1.2.2",
"eslint-plugin-react-hooks": "4.2.0",
"expose-loader": "0.7.5", "expose-loader": "0.7.5",
"fast-glob": "3.2.5",
"file-loader": "2.0.0", "file-loader": "2.0.0",
"fs-extra": "7.0.0", "fs-extra": "7.0.0",
"html-loader": "0.5.5", "html-loader": "0.5.5",
@@ -163,7 +175,7 @@
"mini-css-extract-plugin": "0.4.3", "mini-css-extract-plugin": "0.4.3",
"monaco-editor-webpack-plugin": "1.7.0", "monaco-editor-webpack-plugin": "1.7.0",
"node-fetch": "2.6.1", "node-fetch": "2.6.1",
"prettier": "1.19.1", "prettier": "2.2.1",
"puppeteer": "4.0.0", "puppeteer": "4.0.0",
"raw-loader": "0.5.1", "raw-loader": "0.5.1",
"rimraf": "3.0.0", "rimraf": "3.0.0",

View File

@@ -3,6 +3,7 @@
"offerThroughput": 400, "offerThroughput": 400,
"databaseLevelThroughput": false, "databaseLevelThroughput": false,
"collectionId": "Persons", "collectionId": "Persons",
"createNewDatabase": true,
"partitionKey": { "kind": "Hash", "paths": ["/name"] }, "partitionKey": { "kind": "Hash", "paths": ["/name"] },
"data": [ "data": [
"g.addV('person').property(id, '1').property('name', 'Eva').property('age', 44)", "g.addV('person').property(id, '1').property('name', 'Eva').property('age', 44)",

View File

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

View File

@@ -1,21 +1,22 @@
import * as ko from "knockout"; import * as ko from "knockout";
import * as ReactBindingHandler from "./ReactBindingHandler"; import * as ReactBindingHandler from "./ReactBindingHandler";
import "../Explorer/Tables/DataTable/DataTableBindingManager";
export class BindingHandlersRegisterer {
public static registerBindingHandlers() { export class BindingHandlersRegisterer {
ko.bindingHandlers.setTemplateReady = { public static registerBindingHandlers() {
init( ko.bindingHandlers.setTemplateReady = {
element: any, init(
wrappedValueAccessor: () => any, element: any,
allBindings?: ko.AllBindings, wrappedValueAccessor: () => any,
viewModel?: any, allBindings?: ko.AllBindings,
bindingContext?: ko.BindingContext viewModel?: any,
) { bindingContext?: ko.BindingContext
const value = ko.unwrap(wrappedValueAccessor()); ) {
bindingContext?.$data.isTemplateReady(value); const value = ko.unwrap(wrappedValueAccessor());
} bindingContext?.$data.isTemplateReady(value);
} as ko.BindingHandler; },
} as ko.BindingHandler;
ReactBindingHandler.Registerer.register();
} ReactBindingHandler.Registerer.register();
} }
}

View File

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

View File

@@ -40,7 +40,7 @@ export class ArrayHashMap<T> {
public forEach(key: string, iteratorFct: (value: T) => void) { public forEach(key: string, iteratorFct: (value: T) => void) {
const values = this.store.get(key); const values = this.store.get(key);
if (values) { if (values) {
values.forEach(value => iteratorFct(value)); values.forEach((value) => iteratorFct(value));
} }
} }

View File

@@ -1,434 +1,417 @@
import { HashMap } from "./HashMap"; export class CodeOfConductEndpoints {
public static privacyStatement: string = "https://aka.ms/ms-privacy-policy";
export class AuthorizationEndpoints { public static codeOfConduct: string = "https://aka.ms/cosmos-code-of-conduct";
public static arm: string = "https://management.core.windows.net/"; public static termsOfUse: string = "https://aka.ms/ms-terms-of-use";
public static common: string = "https://login.windows.net/"; }
}
export class EndpointsRegex {
export class CodeOfConductEndpoints { public static readonly cassandra = [
public static privacyStatement: string = "https://aka.ms/ms-privacy-policy"; "AccountEndpoint=(.*).cassandra.cosmosdb.azure.com",
public static codeOfConduct: string = "https://aka.ms/cosmos-code-of-conduct"; "HostName=(.*).cassandra.cosmos.azure.com",
public static termsOfUse: string = "https://aka.ms/ms-terms-of-use"; ];
} public static readonly mongo = "mongodb://.*:(.*)@(.*).documents.azure.com";
public static readonly mongoCompute = "mongodb://.*:(.*)@(.*).mongo.cosmos.azure.com";
export class EndpointsRegex { public static readonly sql = "AccountEndpoint=https://(.*).documents.azure.com";
public static readonly cassandra = [ public static readonly table = "TableEndpoint=https://(.*).table.cosmosdb.azure.com";
"AccountEndpoint=(.*).cassandra.cosmosdb.azure.com", }
"HostName=(.*).cassandra.cosmos.azure.com"
]; export class ApiEndpoints {
public static readonly mongo = "mongodb://.*:(.*)@(.*).documents.azure.com"; public static runtimeProxy: string = "/api/RuntimeProxy";
public static readonly mongoCompute = "mongodb://.*:(.*)@(.*).mongo.cosmos.azure.com"; public static guestRuntimeProxy: string = "/api/guest/RuntimeProxy";
public static readonly sql = "AccountEndpoint=https://(.*).documents.azure.com"; }
public static readonly table = "TableEndpoint=https://(.*).table.cosmosdb.azure.com";
} export class ServerIds {
public static localhost: string = "localhost";
export class ApiEndpoints { public static blackforest: string = "blackforest";
public static runtimeProxy: string = "/api/RuntimeProxy"; public static fairfax: string = "fairfax";
public static guestRuntimeProxy: string = "/api/guest/RuntimeProxy"; public static mooncake: string = "mooncake";
} public static productionPortal: string = "prod";
public static dev: string = "dev";
export class ServerIds { }
public static localhost: string = "localhost";
public static blackforest: string = "blackforest"; export class ArmApiVersions {
public static fairfax: string = "fairfax"; public static readonly documentDB: string = "2015-11-06";
public static mooncake: string = "mooncake"; public static readonly arcadia: string = "2019-06-01-preview";
public static productionPortal: string = "prod"; public static readonly arcadiaLivy: string = "2019-11-01-preview";
public static dev: string = "dev"; public static readonly arm: string = "2015-11-01";
} public static readonly armFeatures: string = "2014-08-01-preview";
public static readonly publicVersion = "2020-04-01";
export class ArmApiVersions { }
public static readonly documentDB: string = "2015-11-06";
public static readonly arcadia: string = "2019-06-01-preview"; export class ArmResourceTypes {
public static readonly arcadiaLivy: string = "2019-11-01-preview"; public static readonly notebookWorkspaces = "Microsoft.DocumentDB/databaseAccounts/notebookWorkspaces";
public static readonly arm: string = "2015-11-01"; public static readonly synapseWorkspaces = "Microsoft.Synapse/workspaces";
public static readonly armFeatures: string = "2014-08-01-preview"; }
public static readonly publicVersion = "2020-04-01";
} export class BackendDefaults {
public static partitionKeyKind: string = "Hash";
export class ArmResourceTypes { public static singlePartitionStorageInGb: string = "10";
public static readonly notebookWorkspaces = "Microsoft.DocumentDB/databaseAccounts/notebookWorkspaces"; public static multiPartitionStorageInGb: string = "100";
public static readonly synapseWorkspaces = "Microsoft.Synapse/workspaces"; public static maxChangeFeedRetentionDuration: number = 10;
} public static partitionKeyVersion = 2;
}
export class BackendDefaults {
public static partitionKeyKind: string = "Hash"; export class ClientDefaults {
public static singlePartitionStorageInGb: string = "10"; public static requestTimeoutMs: number = 60000;
public static multiPartitionStorageInGb: string = "100"; public static portalCacheTimeoutMs: number = 10000;
public static maxChangeFeedRetentionDuration: number = 10; public static errorNotificationTimeoutMs: number = 5000;
public static partitionKeyVersion = 2; public static copyHelperTimeoutMs: number = 2000;
} public static waitForDOMElementMs: number = 500;
public static cacheBustingTimeoutMs: number =
export class ClientDefaults { 10 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/;
public static requestTimeoutMs: number = 60000; public static databaseThroughputIncreaseFactor: number = 100;
public static portalCacheTimeoutMs: number = 10000; public static readonly arcadiaTokenRefreshInterval: number =
public static errorNotificationTimeoutMs: number = 5000; 20 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/;
public static copyHelperTimeoutMs: number = 2000; public static readonly arcadiaTokenRefreshIntervalPaddingMs: number = 2000;
public static waitForDOMElementMs: number = 500; }
public static cacheBustingTimeoutMs: number =
10 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/; export class AccountKind {
public static databaseThroughputIncreaseFactor: number = 100; public static DocumentDB: string = "DocumentDB";
public static readonly arcadiaTokenRefreshInterval: number = public static MongoDB: string = "MongoDB";
20 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/; public static Parse: string = "Parse";
public static readonly arcadiaTokenRefreshIntervalPaddingMs: number = 2000; public static GlobalDocumentDB: string = "GlobalDocumentDB";
} public static Default: string = AccountKind.DocumentDB;
}
export class AccountKind {
public static DocumentDB: string = "DocumentDB"; export class CorrelationBackend {
public static MongoDB: string = "MongoDB"; public static Url: string = "https://aka.ms/cosmosdbanalytics";
public static Parse: string = "Parse"; }
public static GlobalDocumentDB: string = "GlobalDocumentDB";
public static Default: string = AccountKind.DocumentDB; export class DefaultAccountExperience {
} public static DocumentDB: string = "DocumentDB";
public static Graph: string = "Graph";
export class CorrelationBackend { public static MongoDB: string = "MongoDB";
public static Url: string = "https://aka.ms/cosmosdbanalytics"; public static ApiForMongoDB: string = "Azure Cosmos DB for MongoDB API";
} public static Table: string = "Table";
public static Cassandra: string = "Cassandra";
export class DefaultAccountExperience { public static Default: string = DefaultAccountExperience.DocumentDB;
public static DocumentDB: string = "DocumentDB"; }
public static Graph: string = "Graph";
public static MongoDB: string = "MongoDB"; export class CapabilityNames {
public static ApiForMongoDB: string = "Azure Cosmos DB for MongoDB API"; public static EnableTable: string = "EnableTable";
public static Table: string = "Table"; public static EnableGremlin: string = "EnableGremlin";
public static Cassandra: string = "Cassandra"; public static EnableCassandra: string = "EnableCassandra";
public static Default: string = DefaultAccountExperience.DocumentDB; public static EnableAutoScale: string = "EnableAutoScale";
} public static readonly EnableNotebooks: string = "EnableNotebooks";
public static readonly EnableStorageAnalytics: string = "EnableStorageAnalytics";
export class CapabilityNames { public static readonly EnableMongo: string = "EnableMongo";
public static EnableTable: string = "EnableTable"; public static readonly EnableServerless: string = "EnableServerless";
public static EnableGremlin: string = "EnableGremlin"; }
public static EnableCassandra: string = "EnableCassandra";
public static EnableAutoScale: string = "EnableAutoScale"; export class Features {
public static readonly EnableNotebooks: string = "EnableNotebooks"; public static readonly cosmosdb = "cosmosdb";
public static readonly EnableStorageAnalytics: string = "EnableStorageAnalytics"; public static readonly enableChangeFeedPolicy = "enablechangefeedpolicy";
public static readonly EnableMongo: string = "EnableMongo"; public static readonly executeSproc = "dataexplorerexecutesproc";
public static readonly EnableServerless: string = "EnableServerless"; public static readonly hostedDataExplorer = "hosteddataexplorerenabled";
} public static readonly enableTtl = "enablettl";
public static readonly enableNotebooks = "enablenotebooks";
export class Features { public static readonly enableSpark = "enablespark";
public static readonly cosmosdb = "cosmosdb"; public static readonly livyEndpoint = "livyendpoint";
public static readonly enableChangeFeedPolicy = "enablechangefeedpolicy"; public static readonly notebookServerUrl = "notebookserverurl";
public static readonly executeSproc = "dataexplorerexecutesproc"; public static readonly notebookServerToken = "notebookservertoken";
public static readonly hostedDataExplorer = "hosteddataexplorerenabled"; public static readonly notebookBasePath = "notebookbasepath";
public static readonly enableTtl = "enablettl"; public static readonly canExceedMaximumValue = "canexceedmaximumvalue";
public static readonly enableNotebooks = "enablenotebooks"; public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
public static readonly enableGalleryPublish = "enablegallerypublish"; public static readonly ttl90Days = "ttl90days";
public static readonly enableLinkInjection = "enablelinkinjection"; public static readonly enableRightPanelV2 = "enablerightpanelv2";
public static readonly enableSpark = "enablespark"; public static readonly enableSchema = "enableschema";
public static readonly livyEndpoint = "livyendpoint"; public static readonly enableSDKoperations = "enablesdkoperations";
public static readonly notebookServerUrl = "notebookserverurl"; public static readonly showMinRUSurvey = "showminrusurvey";
public static readonly notebookServerToken = "notebookservertoken"; public static readonly enableDatabaseSettingsTabV1 = "enabledbsettingsv1";
public static readonly notebookBasePath = "notebookbasepath"; public static readonly selfServeType = "selfservetype";
public static readonly canExceedMaximumValue = "canexceedmaximumvalue"; public static readonly enableKOPanel = "enablekopanel";
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput"; }
public static readonly ttl90Days = "ttl90days";
public static readonly enableRightPanelV2 = "enablerightpanelv2"; // flight names returned from the portal are always lowercase
public static readonly enableSchema = "enableschema"; export class Flights {
public static readonly enableSDKoperations = "enablesdkoperations"; public static readonly SettingsV2 = "settingsv2";
public static readonly showMinRUSurvey = "showminrusurvey"; public static readonly MongoIndexEditor = "mongoindexeditor";
} public static readonly MongoIndexing = "mongoindexing";
public static readonly AutoscaleTest = "autoscaletest";
// flight names returned from the portal are always lowercase }
export class Flights {
public static readonly SettingsV2 = "settingsv2"; export class AfecFeatures {
public static readonly MongoIndexEditor = "mongoindexeditor"; public static readonly Spark = "spark-public-preview";
} public static readonly Notebooks = "sparknotebooks-public-preview";
public static readonly StorageAnalytics = "storageanalytics-public-preview";
export class AfecFeatures { }
public static readonly Spark = "spark-public-preview";
public static readonly Notebooks = "sparknotebooks-public-preview"; export class TagNames {
public static readonly StorageAnalytics = "storageanalytics-public-preview"; public static defaultExperience: string = "defaultExperience";
} }
export class Spark { export class MongoDBAccounts {
public static readonly MaxWorkerCount = 10; public static protocol: string = "https";
public static readonly SKUs: HashMap<string> = new HashMap({ public static defaultPort: string = "10255";
"Cosmos.Spark.D1s": "D1s / 1 core / 4GB RAM", }
"Cosmos.Spark.D2s": "D2s / 2 cores / 8GB RAM",
"Cosmos.Spark.D4s": "D4s / 4 cores / 16GB RAM", export enum MongoBackendEndpointType {
"Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM", local,
"Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM", remote,
"Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM", }
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM"
}); // TODO: 435619 Add default endpoints per cloud and use regional only when available
} export class CassandraBackend {
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
export class TagNames { public static readonly guestCreateOrDeleteApi: string = "api/guest/cassandra/createordelete";
public static defaultExperience: string = "defaultExperience"; public static readonly queryApi: string = "api/cassandra";
} public static readonly guestQueryApi: string = "api/guest/cassandra";
public static readonly keysApi: string = "api/cassandra/keys";
export class MongoDBAccounts { public static readonly guestKeysApi: string = "api/guest/cassandra/keys";
public static protocol: string = "https"; public static readonly schemaApi: string = "api/cassandra/schema";
public static defaultPort: string = "10255"; public static readonly guestSchemaApi: string = "api/guest/cassandra/schema";
} }
export enum MongoBackendEndpointType { export class Queries {
local, public static CustomPageOption: string = "custom";
remote public static UnlimitedPageOption: string = "unlimited";
} public static itemsPerPage: number = 100;
public static unlimitedItemsPerPage: number = 100; // TODO: Figure out appropriate value so it works for accounts with a large number of partitions
// TODO: 435619 Add default endpoints per cloud and use regional only when available
export class CassandraBackend { public static QueryEditorMinHeightRatio: number = 0.1;
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete"; public static QueryEditorMaxHeightRatio: number = 0.4;
public static readonly guestCreateOrDeleteApi: string = "api/guest/cassandra/createordelete"; public static readonly DefaultMaxDegreeOfParallelism = 6;
public static readonly queryApi: string = "api/cassandra"; }
public static readonly guestQueryApi: string = "api/guest/cassandra";
public static readonly keysApi: string = "api/cassandra/keys"; export class SavedQueries {
public static readonly guestKeysApi: string = "api/guest/cassandra/keys"; public static readonly CollectionName: string = "___Query";
public static readonly schemaApi: string = "api/cassandra/schema"; public static readonly DatabaseName: string = "___Cosmos";
public static readonly guestSchemaApi: string = "api/guest/cassandra/schema"; public static readonly OfferThroughput: number = 400;
} public static readonly PartitionKeyProperty: string = "id";
}
export class Queries {
public static CustomPageOption: string = "custom"; export class DocumentsGridMetrics {
public static UnlimitedPageOption: string = "unlimited"; public static DocumentsPerPage: number = 100;
public static itemsPerPage: number = 100; public static IndividualRowHeight: number = 34;
public static unlimitedItemsPerPage: number = 100; // TODO: Figure out appropriate value so it works for accounts with a large number of partitions public static BufferHeight: number = 28;
public static SplitterMinWidth: number = 200;
public static QueryEditorMinHeightRatio: number = 0.1; public static SplitterMaxWidth: number = 360;
public static QueryEditorMaxHeightRatio: number = 0.4;
public static readonly DefaultMaxDegreeOfParallelism = 6; public static DocumentEditorMinWidthRatio: number = 0.2;
} public static DocumentEditorMaxWidthRatio: number = 0.4;
}
export class SavedQueries {
public static readonly CollectionName: string = "___Query"; export class ExplorerMetrics {
public static readonly DatabaseName: string = "___Cosmos"; public static SplitterMinWidth: number = 240;
public static readonly OfferThroughput: number = 400; public static SplitterMaxWidth: number = 400;
public static readonly PartitionKeyProperty: string = "id"; public static CollapsedResourceTreeWidth: number = 36;
} }
export class DocumentsGridMetrics { export class SplitterMetrics {
public static DocumentsPerPage: number = 100; public static CollapsedPositionLeft: number = ExplorerMetrics.CollapsedResourceTreeWidth;
public static IndividualRowHeight: number = 34; }
public static BufferHeight: number = 28;
public static SplitterMinWidth: number = 200; export class Areas {
public static SplitterMaxWidth: number = 360; public static ResourceTree: string = "Resource Tree";
public static ContextualPane: string = "Contextual Pane";
public static DocumentEditorMinWidthRatio: number = 0.2; public static Tab: string = "Tab";
public static DocumentEditorMaxWidthRatio: number = 0.4; public static ShareDialog: string = "Share Access Dialog";
} public static Notebook: string = "Notebook";
}
export class ExplorerMetrics {
public static SplitterMinWidth: number = 240; export class HttpHeaders {
public static SplitterMaxWidth: number = 400; public static activityId: string = "x-ms-activity-id";
public static CollapsedResourceTreeWidth: number = 36; public static apiType: string = "x-ms-cosmos-apitype";
} public static authorization: string = "authorization";
public static collectionIndexTransformationProgress: string =
export class SplitterMetrics { "x-ms-documentdb-collection-index-transformation-progress";
public static CollapsedPositionLeft: number = ExplorerMetrics.CollapsedResourceTreeWidth; public static continuation: string = "x-ms-continuation";
} public static correlationRequestId: string = "x-ms-correlation-request-id";
public static enableScriptLogging: string = "x-ms-documentdb-script-enable-logging";
export class Areas { public static guestAccessToken: string = "x-ms-encrypted-auth-token";
public static ResourceTree: string = "Resource Tree"; public static getReadOnlyKey: string = "x-ms-get-read-only-key";
public static ContextualPane: string = "Contextual Pane"; public static connectionString: string = "x-ms-connection-string";
public static Tab: string = "Tab"; public static msDate: string = "x-ms-date";
public static ShareDialog: string = "Share Access Dialog"; public static location: string = "Location";
public static Notebook: string = "Notebook"; public static contentType: string = "Content-Type";
} public static offerReplacePending: string = "x-ms-offer-replace-pending";
public static user: string = "x-ms-user";
export class HttpHeaders { public static populatePartitionStatistics: string = "x-ms-documentdb-populatepartitionstatistics";
public static activityId: string = "x-ms-activity-id"; public static queryMetrics: string = "x-ms-documentdb-query-metrics";
public static apiType: string = "x-ms-cosmos-apitype"; public static requestCharge: string = "x-ms-request-charge";
public static authorization: string = "authorization"; public static resourceQuota: string = "x-ms-resource-quota";
public static collectionIndexTransformationProgress: string = public static resourceUsage: string = "x-ms-resource-usage";
"x-ms-documentdb-collection-index-transformation-progress"; public static retryAfterMs: string = "x-ms-retry-after-ms";
public static continuation: string = "x-ms-continuation"; public static scriptLogResults: string = "x-ms-documentdb-script-log-results";
public static correlationRequestId: string = "x-ms-correlation-request-id"; public static populateCollectionThroughputInfo = "x-ms-documentdb-populatecollectionthroughputinfo";
public static enableScriptLogging: string = "x-ms-documentdb-script-enable-logging"; public static supportSpatialLegacyCoordinates = "x-ms-documentdb-supportspatiallegacycoordinates";
public static guestAccessToken: string = "x-ms-encrypted-auth-token"; public static usePolygonsSmallerThanAHemisphere = "x-ms-documentdb-usepolygonssmallerthanahemisphere";
public static getReadOnlyKey: string = "x-ms-get-read-only-key"; public static autoPilotThroughput = "autoscaleSettings";
public static connectionString: string = "x-ms-connection-string"; public static autoPilotThroughputSDK = "x-ms-cosmos-offer-autopilot-settings";
public static msDate: string = "x-ms-date"; public static partitionKey: string = "x-ms-documentdb-partitionkey";
public static location: string = "Location"; public static migrateOfferToManualThroughput: string = "x-ms-cosmos-migrate-offer-to-manual-throughput";
public static contentType: string = "Content-Type"; public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot";
public static offerReplacePending: string = "x-ms-offer-replace-pending"; }
public static user: string = "x-ms-user";
public static populatePartitionStatistics: string = "x-ms-documentdb-populatepartitionstatistics"; export class ApiType {
public static queryMetrics: string = "x-ms-documentdb-query-metrics"; // Mapped to hexadecimal values in the backend
public static requestCharge: string = "x-ms-request-charge"; public static readonly MongoDB: number = 1;
public static resourceQuota: string = "x-ms-resource-quota"; public static readonly Gremlin: number = 2;
public static resourceUsage: string = "x-ms-resource-usage"; public static readonly Cassandra: number = 4;
public static retryAfterMs: string = "x-ms-retry-after-ms"; public static readonly Table: number = 8;
public static scriptLogResults: string = "x-ms-documentdb-script-log-results"; public static readonly SQL: number = 16;
public static populateCollectionThroughputInfo = "x-ms-documentdb-populatecollectionthroughputinfo"; }
public static supportSpatialLegacyCoordinates = "x-ms-documentdb-supportspatiallegacycoordinates";
public static usePolygonsSmallerThanAHemisphere = "x-ms-documentdb-usepolygonssmallerthanahemisphere"; export class HttpStatusCodes {
public static autoPilotThroughput = "autoscaleSettings"; public static readonly OK: number = 200;
public static autoPilotThroughputSDK = "x-ms-cosmos-offer-autopilot-settings"; public static readonly Created: number = 201;
public static partitionKey: string = "x-ms-documentdb-partitionkey"; public static readonly Accepted: number = 202;
public static migrateOfferToManualThroughput: string = "x-ms-cosmos-migrate-offer-to-manual-throughput"; public static readonly NoContent: number = 204;
public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot"; public static readonly NotModified: number = 304;
} public static readonly Unauthorized: number = 401;
public static readonly Forbidden: number = 403;
export class ApiType { public static readonly NotFound: number = 404;
// Mapped to hexadecimal values in the backend public static readonly TooManyRequests: number = 429;
public static readonly MongoDB: number = 1; public static readonly Conflict: number = 409;
public static readonly Gremlin: number = 2;
public static readonly Cassandra: number = 4; public static readonly InternalServerError: number = 500;
public static readonly Table: number = 8; public static readonly BadGateway: number = 502;
public static readonly SQL: number = 16; public static readonly ServiceUnavailable: number = 503;
} public static readonly GatewayTimeout: number = 504;
export class HttpStatusCodes { public static readonly RetryableStatusCodes: number[] = [
public static readonly OK: number = 200; HttpStatusCodes.TooManyRequests,
public static readonly Created: number = 201; HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
public static readonly Accepted: number = 202; HttpStatusCodes.BadGateway,
public static readonly NoContent: number = 204; HttpStatusCodes.ServiceUnavailable,
public static readonly NotModified: number = 304; HttpStatusCodes.GatewayTimeout,
public static readonly Unauthorized: number = 401; ];
public static readonly Forbidden: number = 403; }
public static readonly NotFound: number = 404;
public static readonly TooManyRequests: number = 429; export class Urls {
public static readonly Conflict: number = 409; public static feedbackEmail = "https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Data%20Explorer%20Feedback";
public static autoscaleMigration = "https://aka.ms/cosmos-autoscale-migration";
public static readonly InternalServerError: number = 500; public static freeTierInformation = "https://aka.ms/cosmos-free-tier";
public static readonly BadGateway: number = 502; public static cosmosPricing = "https://aka.ms/azure-cosmos-db-pricing";
public static readonly ServiceUnavailable: number = 503; }
public static readonly GatewayTimeout: number = 504;
export class HashRoutePrefixes {
public static readonly RetryableStatusCodes: number[] = [ public static databases: string = "/dbs/{db_id}";
HttpStatusCodes.TooManyRequests, public static collections: string = "/dbs/{db_id}/colls/{coll_id}";
HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list public static sprocHash: string = "/sprocs/";
HttpStatusCodes.BadGateway, public static sprocs: string = HashRoutePrefixes.collections + HashRoutePrefixes.sprocHash + "{sproc_id}";
HttpStatusCodes.ServiceUnavailable, public static docs: string = HashRoutePrefixes.collections + "/docs/{doc_id}/";
HttpStatusCodes.GatewayTimeout public static conflicts: string = HashRoutePrefixes.collections + "/conflicts";
];
} public static databasesWithId(databaseId: string): string {
return this.databases.replace("{db_id}", databaseId).replace("/", ""); // strip the first slash since hasher adds it
export class Urls { }
public static feedbackEmail = "https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Data%20Explorer%20Feedback";
public static autoscaleMigration = "https://aka.ms/cosmos-autoscale-migration"; public static collectionsWithIds(databaseId: string, collectionId: string): string {
public static freeTierInformation = "https://aka.ms/cosmos-free-tier"; const transformedDatabasePrefix: string = this.collections.replace("{db_id}", databaseId);
public static cosmosPricing = "https://aka.ms/azure-cosmos-db-pricing";
} return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it
}
export class HashRoutePrefixes {
public static databases: string = "/dbs/{db_id}"; public static sprocWithIds(
public static collections: string = "/dbs/{db_id}/colls/{coll_id}"; databaseId: string,
public static sprocHash: string = "/sprocs/"; collectionId: string,
public static sprocs: string = HashRoutePrefixes.collections + HashRoutePrefixes.sprocHash + "{sproc_id}"; sprocId: string,
public static docs: string = HashRoutePrefixes.collections + "/docs/{doc_id}/"; stripFirstSlash: boolean = true
public static conflicts: string = HashRoutePrefixes.collections + "/conflicts"; ): string {
const transformedDatabasePrefix: string = this.sprocs.replace("{db_id}", databaseId);
public static databasesWithId(databaseId: string): string {
return this.databases.replace("{db_id}", databaseId).replace("/", ""); // strip the first slash since hasher adds it const transformedSprocRoute: string = transformedDatabasePrefix
} .replace("{coll_id}", collectionId)
.replace("{sproc_id}", sprocId);
public static collectionsWithIds(databaseId: string, collectionId: string): string { if (!!stripFirstSlash) {
const transformedDatabasePrefix: string = this.collections.replace("{db_id}", databaseId); return transformedSprocRoute.replace("/", ""); // strip the first slash since hasher adds it
}
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it
} return transformedSprocRoute;
}
public static sprocWithIds(
databaseId: string, public static conflictsWithIds(databaseId: string, collectionId: string) {
collectionId: string, const transformedDatabasePrefix: string = this.conflicts.replace("{db_id}", databaseId);
sprocId: string,
stripFirstSlash: boolean = true return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it;
): string { }
const transformedDatabasePrefix: string = this.sprocs.replace("{db_id}", databaseId);
public static docsWithIds(databaseId: string, collectionId: string, docId: string) {
const transformedSprocRoute: string = transformedDatabasePrefix const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId);
.replace("{coll_id}", collectionId)
.replace("{sproc_id}", sprocId); return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("{doc_id}", docId).replace("/", ""); // strip the first slash since hasher adds it
if (!!stripFirstSlash) { }
return transformedSprocRoute.replace("/", ""); // strip the first slash since hasher adds it }
}
export class ConfigurationOverridesValues {
return transformedSprocRoute; public static IsBsonSchemaV2: string = "true";
} }
public static conflictsWithIds(databaseId: string, collectionId: string) { export class KeyCodes {
const transformedDatabasePrefix: string = this.conflicts.replace("{db_id}", databaseId); public static Space: number = 32;
public static Enter: number = 13;
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it; public static Escape: number = 27;
} public static UpArrow: number = 38;
public static DownArrow: number = 40;
public static docsWithIds(databaseId: string, collectionId: string, docId: string) { public static LeftArrow: number = 37;
const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId); public static RightArrow: number = 39;
public static Tab: number = 9;
return transformedDatabasePrefix }
.replace("{coll_id}", collectionId)
.replace("{doc_id}", docId) // Normalized per: https://www.w3.org/TR/uievents-key/#named-key-attribute-values
.replace("/", ""); // strip the first slash since hasher adds it export class NormalizedEventKey {
} public static readonly Space = " ";
} public static readonly Enter = "Enter";
public static readonly Escape = "Escape";
export class ConfigurationOverridesValues { public static readonly UpArrow = "ArrowUp";
public static IsBsonSchemaV2: string = "true"; public static readonly DownArrow = "ArrowDown";
} public static readonly LeftArrow = "ArrowLeft";
public static readonly RightArrow = "ArrowRight";
export class KeyCodes { }
public static Space: number = 32;
public static Enter: number = 13; export class TryCosmosExperience {
public static Escape: number = 27; public static extendUrl: string = "https://trycosmosdb.azure.com/api/resource/extendportal?userId={0}";
public static UpArrow: number = 38; public static deleteUrl: string = "https://trycosmosdb.azure.com/api/resource/deleteportal?userId={0}";
public static DownArrow: number = 40; public static collectionsPerAccount: number = 3;
public static LeftArrow: number = 37; public static maxRU: number = 5000;
public static RightArrow: number = 39; public static defaultRU: number = 3000;
public static Tab: number = 9; }
}
export class OfferVersions {
// Normalized per: https://www.w3.org/TR/uievents-key/#named-key-attribute-values public static V1: string = "V1";
export class NormalizedEventKey { public static V2: string = "V2";
public static readonly Space = " "; }
public static readonly Enter = "Enter";
public static readonly Escape = "Escape"; export enum ConflictOperationType {
public static readonly UpArrow = "ArrowUp"; Replace = "replace",
public static readonly DownArrow = "ArrowDown"; Create = "create",
public static readonly LeftArrow = "ArrowLeft"; Delete = "delete",
public static readonly RightArrow = "ArrowRight"; }
}
export const EmulatorMasterKey =
export class TryCosmosExperience { //[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
public static extendUrl: string = "https://trycosmosdb.azure.com/api/resource/extendportal?userId={0}"; "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
public static deleteUrl: string = "https://trycosmosdb.azure.com/api/resource/deleteportal?userId={0}";
public static collectionsPerAccount: number = 3; // A variable @MyVariable defined in Constants.less is accessible as StyleConstants.MyVariable
public static maxRU: number = 5000; export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less");
public static defaultRU: number = 3000;
} export class Notebook {
public static readonly defaultBasePath = "./notebooks";
export class OfferVersions { public static readonly heartbeatDelayMs = 5000;
public static V1: string = "V1"; public static readonly kernelRestartInitialDelayMs = 1000;
public static V2: string = "V2"; public static readonly kernelRestartMaxDelayMs = 20000;
} public static readonly autoSaveIntervalMs = 120000;
export enum ConflictOperationType { public static readonly MyNotebooksTitle = "My Notebooks";
Replace = "replace", public static readonly GitHubReposTitle = "GitHub repos";
Create = "create", }
Delete = "delete"
} export class SparkLibrary {
public static readonly nameMinLength = 3;
export const EmulatorMasterKey = public static readonly nameMaxLength = 63;
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")] }
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
export class AnalyticalStorageTtl {
// A variable @MyVariable defined in Constants.less is accessible as StyleConstants.MyVariable public static readonly Days90: number = 7776000;
export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less"); public static readonly Infinite: number = -1;
public static readonly Disabled: number = 0;
export class Notebook { }
public static readonly defaultBasePath = "./notebooks";
public static readonly heartbeatDelayMs = 5000; export class TerminalQueryParams {
public static readonly kernelRestartInitialDelayMs = 1000; public static readonly Terminal = "terminal";
public static readonly kernelRestartMaxDelayMs = 20000; public static readonly Server = "server";
public static readonly autoSaveIntervalMs = 120000; public static readonly Token = "token";
} public static readonly SubscriptionId = "subscriptionId";
public static readonly TerminalEndpoint = "terminalEndpoint";
export class SparkLibrary { }
public static readonly nameMinLength = 3;
public static readonly nameMaxLength = 63;
}
export class AnalyticalStorageTtl {
public static readonly Days90: number = 7776000;
public static readonly Infinite: number = -1;
public static readonly Disabled: number = 0;
}
export class TerminalQueryParams {
public static readonly Terminal = "terminal";
public static readonly Server = "server";
public static readonly Token = "token";
public static readonly SubscriptionId = "subscriptionId";
public static readonly TerminalEndpoint = "terminalEndpoint";
}

View File

@@ -10,17 +10,17 @@ describe("tokenProvider", () => {
resourceId: "", resourceId: "",
resourceType: "dbs" as ResourceType, resourceType: "dbs" as ResourceType,
headers: {}, headers: {},
getAuthorizationTokenUsingMasterKey: () => "" getAuthorizationTokenUsingMasterKey: () => "",
}; };
beforeEach(() => { beforeEach(() => {
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com" BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
}); });
window.fetch = jest.fn().mockImplementation(() => { window.fetch = jest.fn().mockImplementation(() => {
return { return {
json: () => "{}", 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 () => { it("does not call the auth service if a master key is set", async () => {
updateUserContext({ updateUserContext({
masterKey: "foo" masterKey: "foo",
}); });
await tokenProvider(options); await tokenProvider(options);
expect((window.fetch as any).mock.calls.length).toBe(0); expect((window.fetch as any).mock.calls.length).toBe(0);
@@ -50,7 +50,7 @@ describe("getTokenFromAuthService", () => {
window.fetch = jest.fn().mockImplementation(() => { window.fetch = jest.fn().mockImplementation(() => {
return { return {
json: () => "{}", json: () => "{}",
headers: new Map() headers: new Map(),
}; };
}); });
}); });
@@ -61,7 +61,7 @@ describe("getTokenFromAuthService", () => {
it("builds the correct URL in production", () => { it("builds the correct URL in production", () => {
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com" BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
}); });
getTokenFromAuthService("GET", "dbs", "foo"); getTokenFromAuthService("GET", "dbs", "foo");
expect(window.fetch).toHaveBeenCalledWith( expect(window.fetch).toHaveBeenCalledWith(
@@ -72,7 +72,7 @@ describe("getTokenFromAuthService", () => {
it("builds the correct URL in dev", () => { it("builds the correct URL in dev", () => {
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://localhost:1234" BACKEND_ENDPOINT: "https://localhost:1234",
}); });
getTokenFromAuthService("GET", "dbs", "foo"); getTokenFromAuthService("GET", "dbs", "foo");
expect(window.fetch).toHaveBeenCalledWith( expect(window.fetch).toHaveBeenCalledWith(
@@ -96,15 +96,15 @@ describe("endpoint", () => {
documentEndpoint: "bar", documentEndpoint: "bar",
gremlinEndpoint: "foo", gremlinEndpoint: "foo",
tableEndpoint: "foo", tableEndpoint: "foo",
cassandraEndpoint: "foo" cassandraEndpoint: "foo",
} },
} },
}); });
expect(endpoint()).toEqual("bar"); expect(endpoint()).toEqual("bar");
}); });
it("uses _endpoint if set", () => { it("uses _endpoint if set", () => {
updateUserContext({ updateUserContext({
endpoint: "baz" endpoint: "baz",
}); });
expect(endpoint()).toEqual("baz"); expect(endpoint()).toEqual("baz");
}); });
@@ -121,7 +121,7 @@ describe("requestPlugin", () => {
updateConfigContext({ updateConfigContext({
platform: Platform.Hosted, platform: Platform.Hosted,
BACKEND_ENDPOINT: "https://localhost:1234", BACKEND_ENDPOINT: "https://localhost:1234",
PROXY_PATH: "/proxy" PROXY_PATH: "/proxy",
}); });
const headers = {}; const headers = {};
const endpoint = "https://docs.azure.com"; const endpoint = "https://docs.azure.com";

View File

@@ -1,10 +1,10 @@
import * as Cosmos from "@azure/cosmos"; import * as Cosmos from "@azure/cosmos";
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos"; import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
import { configContext, Platform } from "../ConfigContext"; import { configContext, Platform } from "../ConfigContext";
import { getErrorMessage } from "./ErrorHandlingUtils"; import { userContext } from "../UserContext";
import { logConsoleError } from "../Utils/NotificationConsoleUtils"; import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import { EmulatorMasterKey, HttpHeaders } from "./Constants"; import { EmulatorMasterKey, HttpHeaders } from "./Constants";
import { userContext } from "../UserContext"; import { getErrorMessage } from "./ErrorHandlingUtils";
const _global = typeof self === "undefined" ? window : self; const _global = typeof self === "undefined" ? window : self;
@@ -58,13 +58,13 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
method: "POST", method: "POST",
headers: { headers: {
"content-type": "application/json", "content-type": "application/json",
"x-ms-encrypted-auth-token": userContext.accessToken "x-ms-encrypted-auth-token": userContext.accessToken,
}, },
body: JSON.stringify({ body: JSON.stringify({
verb, verb,
resourceType, 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() //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()); const result = JSON.parse(await response.json());
@@ -77,13 +77,13 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
export function client(): Cosmos.CosmosClient { export function client(): Cosmos.CosmosClient {
const options: Cosmos.CosmosClientOptions = { const options: Cosmos.CosmosClientOptions = {
endpoint: endpoint() || " ", // CosmosClient gets upset if we pass a falsy value here endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called
key: userContext.masterKey, key: userContext.masterKey,
tokenProvider, tokenProvider,
connectionPolicy: { connectionPolicy: {
enableEndpointDiscovery: false enableEndpointDiscovery: false,
}, },
userAgentSuffix: "Azure Portal" userAgentSuffix: "Azure Portal",
}; };
if (configContext.PROXY_PATH !== undefined) { if (configContext.PROXY_PATH !== undefined) {

View File

@@ -1,94 +1,94 @@
import * as ko from "knockout"; import * as ko from "knockout";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
export default class EditableUtility { export default class EditableUtility {
public static observable<T>(initialValue?: T): ViewModels.Editable<T> { public static observable<T>(initialValue?: T): ViewModels.Editable<T> {
var observable: ViewModels.Editable<T> = <ViewModels.Editable<T>>ko.observable<T>(initialValue); var observable: ViewModels.Editable<T> = <ViewModels.Editable<T>>ko.observable<T>(initialValue);
observable.edits = ko.observableArray<T>([initialValue]); observable.edits = ko.observableArray<T>([initialValue]);
observable.validations = ko.observableArray<(value: T) => boolean>([]); observable.validations = ko.observableArray<(value: T) => boolean>([]);
observable.setBaseline = (baseline: T) => { observable.setBaseline = (baseline: T) => {
observable(baseline); observable(baseline);
observable.edits([baseline]); observable.edits([baseline]);
}; };
observable.getEditableCurrentValue = ko.computed<T>(() => { observable.getEditableCurrentValue = ko.computed<T>(() => {
const edits = (observable.edits && observable.edits()) || []; const edits = (observable.edits && observable.edits()) || [];
if (edits.length === 0) { if (edits.length === 0) {
return undefined; return undefined;
} }
return edits[edits.length - 1]; return edits[edits.length - 1];
}); });
observable.getEditableOriginalValue = ko.computed<T>(() => { observable.getEditableOriginalValue = ko.computed<T>(() => {
const edits = (observable.edits && observable.edits()) || []; const edits = (observable.edits && observable.edits()) || [];
if (edits.length === 0) { if (edits.length === 0) {
return undefined; return undefined;
} }
return edits[0]; return edits[0];
}); });
observable.editableIsDirty = ko.computed<boolean>(() => { observable.editableIsDirty = ko.computed<boolean>(() => {
const edits = (observable.edits && observable.edits()) || []; const edits = (observable.edits && observable.edits()) || [];
if (edits.length <= 1) { if (edits.length <= 1) {
return false; return false;
} }
let current: any = observable.getEditableCurrentValue(); let current: any = observable.getEditableCurrentValue();
let original: any = observable.getEditableOriginalValue(); let original: any = observable.getEditableOriginalValue();
switch (typeof current) { switch (typeof current) {
case "string": case "string":
case "undefined": case "undefined":
case "number": case "number":
case "boolean": case "boolean":
current = current && current.toString(); current = current && current.toString();
break; break;
default: default:
current = JSON.stringify(current); current = JSON.stringify(current);
break; break;
} }
switch (typeof original) { switch (typeof original) {
case "string": case "string":
case "undefined": case "undefined":
case "number": case "number":
case "boolean": case "boolean":
original = original && original.toString(); original = original && original.toString();
break; break;
default: default:
original = JSON.stringify(original); original = JSON.stringify(original);
break; break;
} }
if (current !== original) { if (current !== original) {
return true; return true;
} }
return false; return false;
}); });
observable.subscribe(edit => { observable.subscribe((edit) => {
var edits = observable.edits && observable.edits(); var edits = observable.edits && observable.edits();
if (!edits) { if (!edits) {
return; return;
} }
edits.push(edit); edits.push(edit);
observable.edits(edits); observable.edits(edits);
}); });
observable.editableIsValid = ko.observable<boolean>(true); observable.editableIsValid = ko.observable<boolean>(true);
observable.subscribe(value => { observable.subscribe((value) => {
const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || []; const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || [];
const isValid = validations.every(validate => validate(value)); const isValid = validations.every((validate) => validate(value));
observable.editableIsValid(isValid); observable.editableIsValid(isValid);
}); });
return observable; return observable;
} }
} }

View File

@@ -1,6 +1,6 @@
export function normalizeArmEndpoint(uri: string): string { export function normalizeArmEndpoint(uri: string): string {
if (uri && uri.slice(-1) !== "/") { if (uri && uri.slice(-1) !== "/") {
return `${uri}/`; return `${uri}/`;
} }
return uri; return uri;
} }

View File

@@ -1,8 +1,9 @@
import { ARMError } from "../Utils/arm/request";
import { HttpStatusCodes } from "./Constants";
import { MessageTypes } from "../Contracts/ExplorerContracts"; import { MessageTypes } from "../Contracts/ExplorerContracts";
import { SubscriptionType } from "../Contracts/SubscriptionType"; import { SubscriptionType } from "../Contracts/SubscriptionType";
import { userContext } from "../UserContext";
import { ARMError } from "../Utils/arm/request";
import { logConsoleError } from "../Utils/NotificationConsoleUtils"; import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import { HttpStatusCodes } from "./Constants";
import { logError } from "./Logger"; import { logError } from "./Logger";
import { sendMessage } from "./MessageHandler"; import { sendMessage } from "./MessageHandler";
@@ -21,7 +22,7 @@ export const handleError = (error: string | ARMError | Error, area: string, cons
sendNotificationForError(errorMessage, errorCode); 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; const errorMessage = typeof error === "string" ? error : error.message;
return replaceKnownError(errorMessage); return replaceKnownError(errorMessage);
}; };
@@ -37,18 +38,18 @@ const sendNotificationForError = (errorMessage: string, errorCode: number | stri
} }
sendMessage({ sendMessage({
type: MessageTypes.ForbiddenError, type: MessageTypes.ForbiddenError,
reason: errorMessage reason: errorMessage,
}); });
} }
}; };
const replaceKnownError = (errorMessage: string): string => { const replaceKnownError = (errorMessage: string): string => {
if ( if (
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal && userContext.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."; 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."; 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 * as HeadersUtility from "./HeadersUtility";
import { ExplorerSettings } from "../Shared/ExplorerSettings"; import { ExplorerSettings } from "../Shared/ExplorerSettings";
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility"; import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
describe("Headers Utility", () => { describe("Headers Utility", () => {
describe("shouldEnableCrossPartitionKeyForResourceWithPartitionKey()", () => { describe("shouldEnableCrossPartitionKeyForResourceWithPartitionKey()", () => {
beforeEach(() => { beforeEach(() => {
ExplorerSettings.createDefaultSettings(); ExplorerSettings.createDefaultSettings();
}); });
it("should return true by default", () => { it("should return true by default", () => {
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true); expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
}); });
it("should return false if the enable cross partition key feed option is false", () => { it("should return false if the enable cross partition key feed option is false", () => {
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "false"); LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "false");
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(false); expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(false);
}); });
it("should return true if the enable cross partition key feed option is true", () => { it("should return true if the enable cross partition key feed option is true", () => {
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "true"); LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "true");
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true); expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
}); });
}); });
}); });

View File

@@ -1,28 +1,5 @@
import * as Constants from "./Constants"; import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility"; export function shouldEnableCrossPartitionKey(): boolean {
return LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true";
// 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: {}, queryMetrics: {},
requestCharge: 1, requestCharge: 1,
headers: {}, headers: {},
activityId: "foo" activityId: "foo",
}) }),
}; };
expect(await nextPage(fakeIterator, 10)).toMatchSnapshot(); expect(await nextPage(fakeIterator, 10)).toMatchSnapshot();

View File

@@ -1,6 +1,8 @@
import { QueryResults } from "../Contracts/ViewModels"; import { QueryResults } from "../Contracts/ViewModels";
interface QueryResponse { interface QueryResponse {
// [Todo] remove any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
resources: any[]; resources: any[];
hasMoreResults: boolean; hasMoreResults: boolean;
activityId: string; activityId: string;
@@ -14,8 +16,9 @@ export interface MinimalQueryIterator {
// Pick<QueryIterator<any>, "fetchNext">; // Pick<QueryIterator<any>, "fetchNext">;
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> { export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
return documentsIterator.fetchNext().then(response => { return documentsIterator.fetchNext().then((response) => {
const documents = response.resources; const documents = response.resources;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
const itemCount = (documents && documents.length) || 0; const itemCount = (documents && documents.length) || 0;
return { return {
@@ -26,7 +29,7 @@ export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex
lastItemIndex: Number(firstItemIndex) + Number(itemCount), lastItemIndex: Number(firstItemIndex) + Number(itemCount),
headers, headers,
activityId: response.activityId, activityId: response.activityId,
requestCharge: response.requestCharge requestCharge: response.requestCharge,
}; };
}); });
} }

View File

@@ -1,26 +1,26 @@
jest.mock("./MessageHandler"); jest.mock("./MessageHandler");
import { LogEntryLevel } from "../Contracts/Diagnostics"; import { LogEntryLevel } from "../Contracts/Diagnostics";
import * as Logger from "./Logger"; import * as Logger from "./Logger";
import { MessageTypes } from "../Contracts/ExplorerContracts"; import { MessageTypes } from "../Contracts/ExplorerContracts";
import { sendMessage } from "./MessageHandler"; import { sendMessage } from "./MessageHandler";
describe("Logger", () => { describe("Logger", () => {
beforeEach(() => { beforeEach(() => {
jest.resetAllMocks(); jest.resetAllMocks();
}); });
it("should log info messages", () => { it("should log info messages", () => {
Logger.logInfo("Test info", "DocDB"); Logger.logInfo("Test info", "DocDB");
expect(sendMessage).toBeCalled(); expect(sendMessage).toBeCalled();
}); });
it("should log error messages", () => { it("should log error messages", () => {
Logger.logError("Test error", "DocDB"); Logger.logError("Test error", "DocDB");
expect(sendMessage).toBeCalled(); expect(sendMessage).toBeCalled();
}); });
it("should log warnings", () => { it("should log warnings", () => {
Logger.logWarning("Test warning", "DocDB"); Logger.logWarning("Test warning", "DocDB");
expect(sendMessage).toBeCalled(); 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 { function _logEntry(entry: Diagnostics.LogEntry): void {
sendMessage({ sendMessage({
type: MessageTypes.LogInfo, type: MessageTypes.LogInfo,
data: JSON.stringify(entry) data: JSON.stringify(entry),
}); });
const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => { const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => {
@@ -60,6 +60,6 @@ function _generateLogEntry(
level, level,
message, message,
area, area,
code code,
}; };
} }

View File

@@ -1,28 +1,28 @@
import Q from "q"; import Q from "q";
import * as MessageHandler from "./MessageHandler"; import * as MessageHandler from "./MessageHandler";
describe("Message Handler", () => { describe("Message Handler", () => {
it("should handle cached message", async () => { it("should handle cached message", async () => {
let mockPromise = { let mockPromise = {
id: "123", id: "123",
startTime: new Date(), startTime: new Date(),
deferred: Q.defer<any>() deferred: Q.defer<any>(),
}; };
let mockMessage = { message: { id: "123", data: "{}" } }; let mockMessage = { message: { id: "123", data: "{}" } };
MessageHandler.RequestMap[mockPromise.id] = mockPromise; MessageHandler.RequestMap[mockPromise.id] = mockPromise;
MessageHandler.handleCachedDataMessage(mockMessage); MessageHandler.handleCachedDataMessage(mockMessage);
expect(mockPromise.deferred.promise.isFulfilled()).toBe(true); expect(mockPromise.deferred.promise.isFulfilled()).toBe(true);
}); });
it("should delete fulfilled promises on running the garbage collector", async () => { it("should delete fulfilled promises on running the garbage collector", async () => {
let message = { let message = {
id: "123", id: "123",
startTime: new Date(), startTime: new Date(),
deferred: Q.defer<any>() deferred: Q.defer<any>(),
}; };
MessageHandler.handleCachedDataMessage(message); MessageHandler.handleCachedDataMessage(message);
MessageHandler.runGarbageCollector(); MessageHandler.runGarbageCollector();
expect(MessageHandler.RequestMap["123"]).toBeUndefined(); expect(MessageHandler.RequestMap["123"]).toBeUndefined();
}); });
}); });

View File

@@ -35,7 +35,7 @@ export function sendCachedDataMessage<TResponseDataModel>(
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = { let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
deferred: Q.defer<TResponseDataModel>(), deferred: Q.defer<TResponseDataModel>(),
startTime: new Date(), startTime: new Date(),
id: _.uniqueId() id: _.uniqueId(),
}; };
RequestMap[cachedDataPromise.id] = cachedDataPromise; RequestMap[cachedDataPromise.id] = cachedDataPromise;
sendMessage({ type: messageType, params: params, id: cachedDataPromise.id }); sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
@@ -54,7 +54,7 @@ export function sendMessage(data: any): void {
portalChildWindow.parent.postMessage( portalChildWindow.parent.postMessage(
{ {
signature: "pcIframe", signature: "pcIframe",
data: data data: data,
}, },
portalChildWindow.document.referrer portalChildWindow.document.referrer
); );

View File

@@ -14,7 +14,7 @@ const fetchMock = () => {
ok: true, ok: true,
text: () => "{}", text: () => "{}",
json: () => "{}", json: () => "{}",
headers: new Map() headers: new Map(),
}); });
}; };
@@ -27,8 +27,8 @@ const collection = {
partitionKey: { partitionKey: {
paths: ["/pk"], paths: ["/pk"],
kind: "Hash", kind: "Hash",
version: 1 version: 1,
} },
} as Collection; } as Collection;
const documentId = ({ const documentId = ({
@@ -38,8 +38,8 @@ const documentId = ({
partitionKey: { partitionKey: {
paths: ["/pk"], paths: ["/pk"],
kind: "Hash", kind: "Hash",
version: 1 version: 1,
} },
} as unknown) as DocumentId; } as unknown) as DocumentId;
const databaseAccount = { const databaseAccount = {
@@ -52,8 +52,8 @@ const databaseAccount = {
documentEndpoint: "bar", documentEndpoint: "bar",
gremlinEndpoint: "foo", gremlinEndpoint: "foo",
tableEndpoint: "foo", tableEndpoint: "foo",
cassandraEndpoint: "foo" cassandraEndpoint: "foo",
} },
} as DatabaseAccount; } as DatabaseAccount;
describe("MongoProxyClient", () => { describe("MongoProxyClient", () => {
@@ -61,10 +61,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => { beforeEach(() => {
resetConfigContext(); resetConfigContext();
updateUserContext({ updateUserContext({
databaseAccount databaseAccount,
}); });
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com" BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
}); });
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -93,10 +93,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => { beforeEach(() => {
resetConfigContext(); resetConfigContext();
updateUserContext({ updateUserContext({
databaseAccount databaseAccount,
}); });
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com" BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
}); });
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -125,10 +125,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => { beforeEach(() => {
resetConfigContext(); resetConfigContext();
updateUserContext({ updateUserContext({
databaseAccount databaseAccount,
}); });
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com" BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
}); });
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -157,10 +157,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => { beforeEach(() => {
resetConfigContext(); resetConfigContext();
updateUserContext({ updateUserContext({
databaseAccount databaseAccount,
}); });
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com" BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
}); });
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -189,10 +189,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => { beforeEach(() => {
resetConfigContext(); resetConfigContext();
updateUserContext({ updateUserContext({
databaseAccount databaseAccount,
}); });
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com" BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
}); });
window.fetch = jest.fn().mockImplementation(fetchMock); window.fetch = jest.fn().mockImplementation(fetchMock);
}); });
@@ -220,12 +220,11 @@ describe("MongoProxyClient", () => {
describe("getEndpoint", () => { describe("getEndpoint", () => {
beforeEach(() => { beforeEach(() => {
resetConfigContext(); resetConfigContext();
delete window.authType;
updateUserContext({ updateUserContext({
databaseAccount databaseAccount,
}); });
updateConfigContext({ updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com" BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
}); });
}); });
@@ -241,7 +240,9 @@ describe("MongoProxyClient", () => {
}); });
it("returns a guest endpoint", () => { it("returns a guest endpoint", () => {
window.authType = AuthType.EncryptedToken; updateUserContext({
authType: AuthType.EncryptedToken,
});
const endpoint = getEndpoint(); const endpoint = getEndpoint();
expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/guest/mongo/explorer"); expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/guest/mongo/explorer");
}); });

View File

@@ -16,11 +16,11 @@ import { sendMessage } from "./MessageHandler";
const defaultHeaders = { const defaultHeaders = {
[HttpHeaders.apiType]: ApiType.MongoDB.toString(), [HttpHeaders.apiType]: ApiType.MongoDB.toString(),
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100", [CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15" [CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15",
}; };
function authHeaders() { function authHeaders() {
if (window.authType === AuthType.EncryptedToken) { if (userContext.authType === AuthType.EncryptedToken) {
return { [HttpHeaders.guestAccessToken]: userContext.accessToken }; return { [HttpHeaders.guestAccessToken]: userContext.accessToken };
} else { } else {
return { [HttpHeaders.authorization]: userContext.authorizationToken }; return { [HttpHeaders.authorization]: userContext.authorizationToken };
@@ -31,7 +31,7 @@ export function queryIterator(databaseId: string, collection: Collection, query:
let continuationToken: string; let continuationToken: string;
return { return {
fetchNext: () => { fetchNext: () => {
return queryDocuments(databaseId, collection, false, query).then(response => { return queryDocuments(databaseId, collection, false, query).then((response) => {
continuationToken = response.continuationToken; continuationToken = response.continuationToken;
const headers: { [key: string]: string | number } = {}; const headers: { [key: string]: string | number } = {};
response.headers.forEach((value, key) => { response.headers.forEach((value, key) => {
@@ -42,10 +42,10 @@ export function queryIterator(databaseId: string, collection: Collection, query:
headers, headers,
requestCharge: Number(headers[CosmosSDKConstants.HttpHeaders.RequestCharge]), requestCharge: Number(headers[CosmosSDKConstants.HttpHeaders.RequestCharge]),
activityId: String(headers[CosmosSDKConstants.HttpHeaders.ActivityId]), activityId: String(headers[CosmosSDKConstants.HttpHeaders.ActivityId]),
hasMoreResults: !!continuationToken hasMoreResults: !!continuationToken,
}; };
}); });
} },
}; };
} }
@@ -74,7 +74,9 @@ export function queryDocuments(
rg: userContext.resourceGroup, rg: userContext.resourceGroup,
dba: databaseAccount.name, dba: databaseAccount.name,
pk: pk:
collection && collection.partitionKey && !collection.partitionKey.systemKey ? collection.partitionKeyProperty : "" collection && collection.partitionKey && !collection.partitionKey.systemKey
? collection.partitionKeyProperty
: "",
}; };
const endpoint = getEndpoint() || ""; const endpoint = getEndpoint() || "";
@@ -87,7 +89,7 @@ export function queryDocuments(
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true", [CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true", [CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true", [CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
[HttpHeaders.contentType]: "application/query+json" [HttpHeaders.contentType]: "application/query+json",
}; };
if (continuationToken) { if (continuationToken) {
@@ -100,14 +102,14 @@ export function queryDocuments(
.fetch(`${endpoint}${path}?${queryString.stringify(params)}`, { .fetch(`${endpoint}${path}?${queryString.stringify(params)}`, {
method: "POST", method: "POST",
body: JSON.stringify({ query }), body: JSON.stringify({ query }),
headers headers,
}) })
.then(async response => { .then(async (response) => {
if (response.ok) { if (response.ok) {
return { return {
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation), continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
documents: (await response.json()).Documents as DataModels.DocumentId[], documents: (await response.json()).Documents as DataModels.DocumentId[],
headers: response.headers headers: response.headers,
}; };
} }
errorHandling(response, "querying documents", params); errorHandling(response, "querying documents", params);
@@ -135,7 +137,9 @@ export function readDocument(
rg: userContext.resourceGroup, rg: userContext.resourceGroup,
dba: databaseAccount.name, dba: databaseAccount.name,
pk: pk:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : "" documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperty
: "",
}; };
const endpoint = getEndpoint(); const endpoint = getEndpoint();
@@ -147,10 +151,10 @@ export function readDocument(
...authHeaders(), ...authHeaders(),
[CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent( [CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent(
JSON.stringify(documentId.partitionKeyHeader()) JSON.stringify(documentId.partitionKeyHeader())
) ),
} },
}) })
.then(response => { .then((response) => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} }
@@ -175,7 +179,7 @@ export function createDocument(
sid: userContext.subscriptionId, sid: userContext.subscriptionId,
rg: userContext.resourceGroup, rg: userContext.resourceGroup,
dba: databaseAccount.name, dba: databaseAccount.name,
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "" pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
}; };
const endpoint = getEndpoint(); const endpoint = getEndpoint();
@@ -186,10 +190,10 @@ export function createDocument(
body: JSON.stringify(documentContent), body: JSON.stringify(documentContent),
headers: { headers: {
...defaultHeaders, ...defaultHeaders,
...authHeaders() ...authHeaders(),
} },
}) })
.then(response => { .then((response) => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} }
@@ -218,7 +222,9 @@ export function updateDocument(
rg: userContext.resourceGroup, rg: userContext.resourceGroup,
dba: databaseAccount.name, dba: databaseAccount.name,
pk: pk:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : "" documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperty
: "",
}; };
const endpoint = getEndpoint(); const endpoint = getEndpoint();
@@ -230,10 +236,10 @@ export function updateDocument(
...defaultHeaders, ...defaultHeaders,
...authHeaders(), ...authHeaders(),
[HttpHeaders.contentType]: "application/json", [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) { if (response.ok) {
return response.json(); return response.json();
} }
@@ -257,7 +263,9 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
rg: userContext.resourceGroup, rg: userContext.resourceGroup,
dba: databaseAccount.name, dba: databaseAccount.name,
pk: pk:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : "" documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperty
: "",
}; };
const endpoint = getEndpoint(); const endpoint = getEndpoint();
@@ -268,10 +276,10 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
...defaultHeaders, ...defaultHeaders,
...authHeaders(), ...authHeaders(),
[HttpHeaders.contentType]: "application/json", [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) { if (response.ok) {
return undefined; return undefined;
} }
@@ -299,7 +307,7 @@ export function createMongoCollectionWithProxy(
rg: userContext.resourceGroup, rg: userContext.resourceGroup,
dba: databaseAccount.name, dba: databaseAccount.name,
isAutoPilot: !!params.autoPilotMaxThroughput, isAutoPilot: !!params.autoPilotMaxThroughput,
autoPilotThroughput: params.autoPilotMaxThroughput?.toString() autoPilotThroughput: params.autoPilotMaxThroughput?.toString(),
}; };
const endpoint = getEndpoint(); const endpoint = getEndpoint();
@@ -314,11 +322,11 @@ export function createMongoCollectionWithProxy(
headers: { headers: {
...defaultHeaders, ...defaultHeaders,
...authHeaders(), ...authHeaders(),
[HttpHeaders.contentType]: "application/json" [HttpHeaders.contentType]: "application/json",
} },
} }
) )
.then(response => { .then((response) => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
} }
@@ -329,7 +337,7 @@ export function createMongoCollectionWithProxy(
export function getEndpoint(): string { export function getEndpoint(): string {
let url = (configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT) + "/api/mongo/explorer"; let url = (configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT) + "/api/mongo/explorer";
if (window.authType === AuthType.EncryptedToken) { if (userContext.authType === AuthType.EncryptedToken) {
url = url.replace("api/mongo", "api/guest/mongo"); url = url.replace("api/mongo", "api/guest/mongo");
} }
return url; return url;

View File

@@ -1,168 +1,168 @@
/* Copyright 2013 10gen Inc. /* Copyright 2013 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
export default class MongoUtility { export default class MongoUtility {
public static tojson = function(x: any, indent: string, nolint: boolean) { public static tojson = function (x: any, indent: string, nolint: boolean) {
if (x === null || x === undefined) { if (x === null || x === undefined) {
return String(x); return String(x);
} }
indent = indent || ""; indent = indent || "";
switch (typeof x) { switch (typeof x) {
case "string": case "string":
var out = new Array(x.length + 1); var out = new Array(x.length + 1);
out[0] = '"'; out[0] = '"';
for (var i = 0; i < x.length; i++) { for (var i = 0; i < x.length; i++) {
if (x[i] === '"') { if (x[i] === '"') {
out[out.length] = '\\"'; out[out.length] = '\\"';
} else if (x[i] === "\\") { } else if (x[i] === "\\") {
out[out.length] = "\\\\"; out[out.length] = "\\\\";
} else if (x[i] === "\b") { } else if (x[i] === "\b") {
out[out.length] = "\\b"; out[out.length] = "\\b";
} else if (x[i] === "\f") { } else if (x[i] === "\f") {
out[out.length] = "\\f"; out[out.length] = "\\f";
} else if (x[i] === "\n") { } else if (x[i] === "\n") {
out[out.length] = "\\n"; out[out.length] = "\\n";
} else if (x[i] === "\r") { } else if (x[i] === "\r") {
out[out.length] = "\\r"; out[out.length] = "\\r";
} else if (x[i] === "\t") { } else if (x[i] === "\t") {
out[out.length] = "\\t"; out[out.length] = "\\t";
} else { } else {
var code = x.charCodeAt(i); var code = x.charCodeAt(i);
if (code < 0x20) { if (code < 0x20) {
out[out.length] = (code < 0x10 ? "\\u000" : "\\u00") + code.toString(16); out[out.length] = (code < 0x10 ? "\\u000" : "\\u00") + code.toString(16);
} else { } else {
out[out.length] = x[i]; out[out.length] = x[i];
} }
} }
} }
return out.join("") + '"'; return out.join("") + '"';
case "number": case "number":
/* falls through */ /* falls through */
case "boolean": case "boolean":
return "" + x; return "" + x;
case "object": case "object":
var func = $.isArray(x) ? MongoUtility.tojsonArray : MongoUtility.tojsonObject; var func = $.isArray(x) ? MongoUtility.tojsonArray : MongoUtility.tojsonObject;
var s = func(x, indent, nolint); var s = func(x, indent, nolint);
if ( if (
(nolint === null || nolint === undefined || nolint === true) && (nolint === null || nolint === undefined || nolint === true) &&
s.length < 80 && s.length < 80 &&
(indent === null || indent.length === 0) (indent === null || indent.length === 0)
) { ) {
s = s.replace(/[\t\r\n]+/gm, " "); s = s.replace(/[\t\r\n]+/gm, " ");
} }
return s; return s;
case "function": case "function":
return x.toString(); return x.toString();
default: default:
throw new Error("tojson can't handle type " + typeof x); throw new Error("tojson can't handle type " + typeof x);
} }
}; };
private static tojsonObject = function(x: any, indent: string, nolint: boolean) { private static tojsonObject = function (x: any, indent: string, nolint: boolean) {
var lineEnding = nolint ? " " : "\n"; var lineEnding = nolint ? " " : "\n";
var tabSpace = nolint ? "" : "\t"; var tabSpace = nolint ? "" : "\t";
indent = indent || ""; indent = indent || "";
if (typeof x.tojson === "function" && x.tojson !== MongoUtility.tojson) { if (typeof x.tojson === "function" && x.tojson !== MongoUtility.tojson) {
return x.tojson(indent, nolint); return x.tojson(indent, nolint);
} }
if (x.constructor && typeof x.constructor.tojson === "function" && x.constructor.tojson !== MongoUtility.tojson) { if (x.constructor && typeof x.constructor.tojson === "function" && x.constructor.tojson !== MongoUtility.tojson) {
return x.constructor.tojson(x, indent, nolint); return x.constructor.tojson(x, indent, nolint);
} }
if (MongoUtility.hasDefinedProperty(x, "toString") && !$.isArray(x)) { if (MongoUtility.hasDefinedProperty(x, "toString") && !$.isArray(x)) {
return x.toString(); return x.toString();
} }
if (x instanceof Error) { if (x instanceof Error) {
return x.toString(); return x.toString();
} }
if (MongoUtility.isObjectId(x)) { if (MongoUtility.isObjectId(x)) {
return 'ObjectId("' + x.$oid + '")'; return 'ObjectId("' + x.$oid + '")';
} }
// push one level of indent // push one level of indent
indent += tabSpace; indent += tabSpace;
var s = "{"; var s = "{";
var pairs = []; var pairs = [];
for (var k in x) { for (var k in x) {
if (x.hasOwnProperty(k)) { if (x.hasOwnProperty(k)) {
var val = x[k]; var val = x[k];
var pair = '"' + k + '" : ' + MongoUtility.tojson(val, indent, nolint); var pair = '"' + k + '" : ' + MongoUtility.tojson(val, indent, nolint);
if (k === "_id") { if (k === "_id") {
pairs.unshift(pair); pairs.unshift(pair);
} else { } else {
pairs.push(pair); pairs.push(pair);
} }
} }
} }
// Add proper line endings, indents, and commas to each line // Add proper line endings, indents, and commas to each line
s += $.map(pairs, function(pair) { s += $.map(pairs, function (pair) {
return lineEnding + indent + pair; return lineEnding + indent + pair;
}).join(","); }).join(",");
s += lineEnding; s += lineEnding;
// pop one level of indent // pop one level of indent
indent = indent.substring(1); indent = indent.substring(1);
return s + indent + "}"; return s + indent + "}";
}; };
private static tojsonArray = function(a: any, indent: string, nolint: boolean) { private static tojsonArray = function (a: any, indent: string, nolint: boolean) {
if (a.length === 0) { if (a.length === 0) {
return "[ ]"; return "[ ]";
} }
var lineEnding = nolint ? " " : "\n"; var lineEnding = nolint ? " " : "\n";
if (!indent || nolint) { if (!indent || nolint) {
indent = ""; indent = "";
} }
var s = "[" + lineEnding; var s = "[" + lineEnding;
indent += "\t"; indent += "\t";
for (var i = 0; i < a.length; i++) { for (var i = 0; i < a.length; i++) {
s += indent + MongoUtility.tojson(a[i], indent, nolint); s += indent + MongoUtility.tojson(a[i], indent, nolint);
if (i < a.length - 1) { if (i < a.length - 1) {
s += "," + lineEnding; s += "," + lineEnding;
} }
} }
if (a.length === 0) { if (a.length === 0) {
s += indent; s += indent;
} }
indent = indent.substring(1); indent = indent.substring(1);
s += lineEnding + indent + "]"; s += lineEnding + indent + "]";
return s; return s;
}; };
private static hasDefinedProperty = function(obj: any, prop: string): boolean { private static hasDefinedProperty = function (obj: any, prop: string): boolean {
if (Object.getPrototypeOf === undefined || Object.getPrototypeOf(obj) === null) { if (Object.getPrototypeOf === undefined || Object.getPrototypeOf(obj) === null) {
return false; return false;
} else if (obj.hasOwnProperty(prop)) { } else if (obj.hasOwnProperty(prop)) {
return true; return true;
} else { } else {
return MongoUtility.hasDefinedProperty(Object.getPrototypeOf(obj), prop); return MongoUtility.hasDefinedProperty(Object.getPrototypeOf(obj), prop);
} }
}; };
private static isObjectId(obj: any): boolean { private static isObjectId(obj: any): boolean {
var keys = Object.keys(obj); var keys = Object.keys(obj);
return keys.length === 1 && keys[0] === "$oid" && typeof obj.$oid === "string" && /^[0-9a-f]{24}$/.test(obj.$oid); 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, offerThroughput: 500,
collectionThroughputInfo: { collectionThroughputInfo: {
minimumRUForCollection: 400, minimumRUForCollection: 400,
numPhysicalPartitions: 1 numPhysicalPartitions: 1,
} },
}, },
id: "test" id: "test",
} as SDKOfferDefinition; } as SDKOfferDefinition;
const mockResponse = { const mockResponse = {
resource: mockOfferDefinition resource: mockOfferDefinition,
} as OfferResponse; } as OfferResponse;
const expectedResult: Offer = { const expectedResult: Offer = {
@@ -25,7 +25,7 @@ describe("parseSDKOfferResponse", () => {
minimumThroughput: 400, minimumThroughput: 400,
id: "test", id: "test",
offerDefinition: mockOfferDefinition, offerDefinition: mockOfferDefinition,
offerReplacePending: false offerReplacePending: false,
}; };
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult); expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);
@@ -37,17 +37,17 @@ describe("parseSDKOfferResponse", () => {
offerThroughput: 400, offerThroughput: 400,
collectionThroughputInfo: { collectionThroughputInfo: {
minimumRUForCollection: 400, minimumRUForCollection: 400,
numPhysicalPartitions: 1 numPhysicalPartitions: 1,
}, },
offerAutopilotSettings: { offerAutopilotSettings: {
maxThroughput: 5000 maxThroughput: 5000,
} },
}, },
id: "test" id: "test",
} as SDKOfferDefinition; } as SDKOfferDefinition;
const mockResponse = { const mockResponse = {
resource: mockOfferDefinition resource: mockOfferDefinition,
} as OfferResponse; } as OfferResponse;
const expectedResult: Offer = { const expectedResult: Offer = {
@@ -56,7 +56,7 @@ describe("parseSDKOfferResponse", () => {
minimumThroughput: 400, minimumThroughput: 400,
id: "test", id: "test",
offerDefinition: mockOfferDefinition, offerDefinition: mockOfferDefinition,
offerReplacePending: false offerReplacePending: false,
}; };
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult); expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);

View File

@@ -2,8 +2,11 @@ import { Offer, SDKOfferDefinition } from "../Contracts/DataModels";
import { OfferResponse } from "@azure/cosmos"; import { OfferResponse } from "@azure/cosmos";
import { HttpHeaders } from "./Constants"; import { HttpHeaders } from "./Constants";
export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => { export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer | undefined => {
const offerDefinition: SDKOfferDefinition = offerResponse?.resource; const offerDefinition: SDKOfferDefinition | undefined = offerResponse?.resource;
if (!offerDefinition) {
return undefined;
}
const offerContent = offerDefinition.content; const offerContent = offerDefinition.content;
if (!offerContent) { if (!offerContent) {
return undefined; return undefined;
@@ -12,14 +15,14 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
const minimumThroughput = offerContent.collectionThroughputInfo?.minimumRUForCollection; const minimumThroughput = offerContent.collectionThroughputInfo?.minimumRUForCollection;
const autopilotSettings = offerContent.offerAutopilotSettings; const autopilotSettings = offerContent.offerAutopilotSettings;
if (autopilotSettings) { if (autopilotSettings && autopilotSettings.maxThroughput && minimumThroughput) {
return { return {
id: offerDefinition.id, id: offerDefinition.id,
autoscaleMaxThroughput: autopilotSettings.maxThroughput, autoscaleMaxThroughput: autopilotSettings.maxThroughput,
manualThroughput: undefined, manualThroughput: undefined,
minimumThroughput, minimumThroughput,
offerDefinition, offerDefinition,
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true" offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true",
}; };
} }
@@ -29,6 +32,6 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
manualThroughput: offerContent.offerThroughput, manualThroughput: offerContent.offerThroughput,
minimumThroughput, minimumThroughput,
offerDefinition, offerDefinition,
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true" 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 headers = { [authorizationHeader.header]: authorizationHeader.token };
const response = await window.fetch(url, { const response = await window.fetch(url, {
headers headers,
}); });
if (!response.ok) { if (!response.ok) {

View File

@@ -1,219 +1,219 @@
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos"; import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import * as _ from "underscore"; import * as _ from "underscore";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
import Explorer from "../Explorer/Explorer"; import Explorer from "../Explorer/Explorer";
import DocumentsTab from "../Explorer/Tabs/DocumentsTab"; import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
import DocumentId from "../Explorer/Tree/DocumentId"; import DocumentId from "../Explorer/Tree/DocumentId";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { QueryUtils } from "../Utils/QueryUtils"; import { QueryUtils } from "../Utils/QueryUtils";
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants"; import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage"; import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
import { createCollection } from "./dataAccess/createCollection"; import { createCollection } from "./dataAccess/createCollection";
import { handleError } from "./ErrorHandlingUtils"; import { handleError } from "./ErrorHandlingUtils";
import { createDocument } from "./dataAccess/createDocument"; import { createDocument } from "./dataAccess/createDocument";
import { deleteDocument } from "./dataAccess/deleteDocument"; import { deleteDocument } from "./dataAccess/deleteDocument";
import { queryDocuments } from "./dataAccess/queryDocuments"; import { queryDocuments } from "./dataAccess/queryDocuments";
export class QueriesClient { export class QueriesClient {
private static readonly PartitionKey: DataModels.PartitionKey = { private static readonly PartitionKey: DataModels.PartitionKey = {
paths: [`/${SavedQueries.PartitionKeyProperty}`], paths: [`/${SavedQueries.PartitionKeyProperty}`],
kind: BackendDefaults.partitionKeyKind, kind: BackendDefaults.partitionKeyKind,
version: BackendDefaults.partitionKeyVersion version: BackendDefaults.partitionKeyVersion,
}; };
private static readonly FetchQuery: string = "SELECT * FROM c"; private static readonly FetchQuery: string = "SELECT * FROM c";
private static readonly FetchMongoQuery: string = "{}"; private static readonly FetchMongoQuery: string = "{}";
public constructor(private container: Explorer) {} public constructor(private container: Explorer) {}
public async setupQueriesCollection(): Promise<DataModels.Collection> { public async setupQueriesCollection(): Promise<DataModels.Collection> {
const queriesCollection: ViewModels.Collection = this.findQueriesCollection(); const queriesCollection: ViewModels.Collection = this.findQueriesCollection();
if (queriesCollection) { if (queriesCollection) {
return Promise.resolve(queriesCollection.rawDataModel); return Promise.resolve(queriesCollection.rawDataModel);
} }
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Setting up account for saving queries"); const clearMessage = NotificationConsoleUtils.logConsoleProgress("Setting up account for saving queries");
return createCollection({ return createCollection({
collectionId: SavedQueries.CollectionName, collectionId: SavedQueries.CollectionName,
createNewDatabase: true, createNewDatabase: true,
databaseId: SavedQueries.DatabaseName, databaseId: SavedQueries.DatabaseName,
partitionKey: QueriesClient.PartitionKey, partitionKey: QueriesClient.PartitionKey,
offerThroughput: SavedQueries.OfferThroughput, offerThroughput: SavedQueries.OfferThroughput,
databaseLevelThroughput: false databaseLevelThroughput: false,
}) })
.then( .then(
(collection: DataModels.Collection) => { (collection: DataModels.Collection) => {
NotificationConsoleUtils.logConsoleInfo("Successfully set up account for saving queries"); NotificationConsoleUtils.logConsoleInfo("Successfully set up account for saving queries");
return Promise.resolve(collection); return Promise.resolve(collection);
}, },
(error: any) => { (error: any) => {
handleError(error, "setupQueriesCollection", "Failed to set up account for saving queries"); handleError(error, "setupQueriesCollection", "Failed to set up account for saving queries");
return Promise.reject(error); return Promise.reject(error);
} }
) )
.finally(() => clearMessage()); .finally(() => clearMessage());
} }
public async saveQuery(query: DataModels.Query): Promise<void> { public async saveQuery(query: DataModels.Query): Promise<void> {
const queriesCollection = this.findQueriesCollection(); const queriesCollection = this.findQueriesCollection();
if (!queriesCollection) { if (!queriesCollection) {
const errorMessage: string = "Account not set up to perform saved query operations"; const errorMessage: string = "Account not set up to perform saved query operations";
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`); NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
return Promise.reject(errorMessage); return Promise.reject(errorMessage);
} }
try { try {
this.validateQuery(query); this.validateQuery(query);
} catch (error) { } catch (error) {
const errorMessage: string = "Invalid query specified"; const errorMessage: string = "Invalid query specified";
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`); NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
return Promise.reject(errorMessage); return Promise.reject(errorMessage);
} }
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Saving query ${query.queryName}`); const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Saving query ${query.queryName}`);
query.id = query.queryName; query.id = query.queryName;
return createDocument(queriesCollection, query) return createDocument(queriesCollection, query)
.then( .then(
(savedQuery: DataModels.Query) => { (savedQuery: DataModels.Query) => {
NotificationConsoleUtils.logConsoleInfo(`Successfully saved query ${query.queryName}`); NotificationConsoleUtils.logConsoleInfo(`Successfully saved query ${query.queryName}`);
return Promise.resolve(); return Promise.resolve();
}, },
(error: any) => { (error: any) => {
if (error.code === HttpStatusCodes.Conflict.toString()) { if (error.code === HttpStatusCodes.Conflict.toString()) {
error = `Query ${query.queryName} already exists`; error = `Query ${query.queryName} already exists`;
} }
handleError(error, "saveQuery", `Failed to save query ${query.queryName}`); handleError(error, "saveQuery", `Failed to save query ${query.queryName}`);
return Promise.reject(error); return Promise.reject(error);
} }
) )
.finally(() => clearMessage()); .finally(() => clearMessage());
} }
public async getQueries(): Promise<DataModels.Query[]> { public async getQueries(): Promise<DataModels.Query[]> {
const queriesCollection = this.findQueriesCollection(); const queriesCollection = this.findQueriesCollection();
if (!queriesCollection) { if (!queriesCollection) {
const errorMessage: string = "Account not set up to perform saved query operations"; const errorMessage: string = "Account not set up to perform saved query operations";
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`); NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
return Promise.reject(errorMessage); return Promise.reject(errorMessage);
} }
const options: any = { enableCrossPartitionQuery: true }; const options: any = { enableCrossPartitionQuery: true };
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Fetching saved queries"); const clearMessage = NotificationConsoleUtils.logConsoleProgress("Fetching saved queries");
const queryIterator: QueryIterator<ItemDefinition & Resource> = queryDocuments( const queryIterator: QueryIterator<ItemDefinition & Resource> = queryDocuments(
SavedQueries.DatabaseName, SavedQueries.DatabaseName,
SavedQueries.CollectionName, SavedQueries.CollectionName,
this.fetchQueriesQuery(), this.fetchQueriesQuery(),
options options
); );
const fetchQueries = async (firstItemIndex: number): Promise<ViewModels.QueryResults> => const fetchQueries = async (firstItemIndex: number): Promise<ViewModels.QueryResults> =>
await queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex); await queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex);
return QueryUtils.queryAllPages(fetchQueries) return QueryUtils.queryAllPages(fetchQueries)
.then( .then(
(results: ViewModels.QueryResults) => { (results: ViewModels.QueryResults) => {
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => { let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
if (!document) { if (!document) {
return undefined; return undefined;
} }
const { id, resourceId, query, queryName } = document; const { id, resourceId, query, queryName } = document;
const parsedQuery: DataModels.Query = { const parsedQuery: DataModels.Query = {
resourceId: resourceId, resourceId: resourceId,
queryName: queryName, queryName: queryName,
query: query, query: query,
id: id id: id,
}; };
try { try {
this.validateQuery(parsedQuery); this.validateQuery(parsedQuery);
return parsedQuery; return parsedQuery;
} catch (error) { } catch (error) {
return undefined; return undefined;
} }
}); });
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery); queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
NotificationConsoleUtils.logConsoleInfo("Successfully fetched saved queries"); NotificationConsoleUtils.logConsoleInfo("Successfully fetched saved queries");
return Promise.resolve(queries); return Promise.resolve(queries);
}, },
(error: any) => { (error: any) => {
handleError(error, "getSavedQueries", "Failed to fetch saved queries"); handleError(error, "getSavedQueries", "Failed to fetch saved queries");
return Promise.reject(error); return Promise.reject(error);
} }
) )
.finally(() => clearMessage()); .finally(() => clearMessage());
} }
public async deleteQuery(query: DataModels.Query): Promise<void> { public async deleteQuery(query: DataModels.Query): Promise<void> {
const queriesCollection = this.findQueriesCollection(); const queriesCollection = this.findQueriesCollection();
if (!queriesCollection) { if (!queriesCollection) {
const errorMessage: string = "Account not set up to perform saved query operations"; const errorMessage: string = "Account not set up to perform saved query operations";
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`); NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
return Promise.reject(errorMessage); return Promise.reject(errorMessage);
} }
try { try {
this.validateQuery(query); this.validateQuery(query);
} catch (error) { } catch (error) {
const errorMessage: string = "Invalid query specified"; const errorMessage: string = "Invalid query specified";
NotificationConsoleUtils.logConsoleError(`Failed to delete query ${query.queryName}: ${errorMessage}`); NotificationConsoleUtils.logConsoleError(`Failed to delete query ${query.queryName}: ${errorMessage}`);
} }
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Deleting query ${query.queryName}`); const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Deleting query ${query.queryName}`);
query.id = query.queryName; query.id = query.queryName;
const documentId = new DocumentId( const documentId = new DocumentId(
{ {
partitionKey: QueriesClient.PartitionKey, partitionKey: QueriesClient.PartitionKey,
partitionKeyProperty: "id" partitionKeyProperty: "id",
} as DocumentsTab, } as DocumentsTab,
query, query,
query.queryName query.queryName
); // TODO: Remove DocumentId's dependency on DocumentsTab ); // TODO: Remove DocumentId's dependency on DocumentsTab
const options: any = { partitionKey: query.resourceId }; const options: any = { partitionKey: query.resourceId };
return deleteDocument(queriesCollection, documentId) return deleteDocument(queriesCollection, documentId)
.then( .then(
() => { () => {
NotificationConsoleUtils.logConsoleInfo(`Successfully deleted query ${query.queryName}`); NotificationConsoleUtils.logConsoleInfo(`Successfully deleted query ${query.queryName}`);
return Promise.resolve(); return Promise.resolve();
}, },
(error: any) => { (error: any) => {
handleError(error, "deleteQuery", `Failed to delete query ${query.queryName}`); handleError(error, "deleteQuery", `Failed to delete query ${query.queryName}`);
return Promise.reject(error); return Promise.reject(error);
} }
) )
.finally(() => clearMessage()); .finally(() => clearMessage());
} }
public getResourceId(): string { public getResourceId(): string {
const databaseAccount = userContext.databaseAccount; const databaseAccount = userContext.databaseAccount;
const databaseAccountName = (databaseAccount && databaseAccount.name) || ""; const databaseAccountName = (databaseAccount && databaseAccount.name) || "";
const subscriptionId = userContext.subscriptionId || ""; const subscriptionId = userContext.subscriptionId || "";
const resourceGroup = userContext.resourceGroup || ""; const resourceGroup = userContext.resourceGroup || "";
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}`; return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}`;
} }
private findQueriesCollection(): ViewModels.Collection { private findQueriesCollection(): ViewModels.Collection {
const queriesDatabase: ViewModels.Database = _.find( const queriesDatabase: ViewModels.Database = _.find(
this.container.databases(), this.container.databases(),
(database: ViewModels.Database) => database.id() === SavedQueries.DatabaseName (database: ViewModels.Database) => database.id() === SavedQueries.DatabaseName
); );
if (!queriesDatabase) { if (!queriesDatabase) {
return undefined; return undefined;
} }
return _.find( return _.find(
queriesDatabase.collections(), queriesDatabase.collections(),
(collection: ViewModels.Collection) => collection.id() === SavedQueries.CollectionName (collection: ViewModels.Collection) => collection.id() === SavedQueries.CollectionName
); );
} }
private validateQuery(query: DataModels.Query): void { private validateQuery(query: DataModels.Query): void {
if (!query || query.queryName == null || query.query == null || query.resourceId == null) { if (!query || query.queryName == null || query.query == null || query.resourceId == null) {
throw new Error("Invalid query specified"); throw new Error("Invalid query specified");
} }
} }
private fetchQueriesQuery(): string { private fetchQueriesQuery(): string {
if (this.container.isPreferredApiMongoDB()) { if (this.container.isPreferredApiMongoDB()) {
return QueriesClient.FetchMongoQuery; return QueriesClient.FetchMongoQuery;
} }
return QueriesClient.FetchQuery; return QueriesClient.FetchQuery;
} }
} }

View File

@@ -1,108 +1,107 @@
import * as ko from "knockout"; import * as ko from "knockout";
import { SplitterMetrics } from "./Constants"; import { SplitterMetrics } from "./Constants";
export enum SplitterDirection { export enum SplitterDirection {
Horizontal = "horizontal", Horizontal = "horizontal",
Vertical = "vertical" Vertical = "vertical",
} }
export interface SplitterBounds { export interface SplitterBounds {
max: number; max: number;
min: number; min: number;
} }
export interface SplitterOptions { export interface SplitterOptions {
splitterId: string; splitterId: string;
leftId: string; leftId: string;
bounds: SplitterBounds; bounds: SplitterBounds;
direction: SplitterDirection; direction: SplitterDirection;
} }
export class Splitter { export class Splitter {
public splitterId: string; public splitterId: string;
public leftSideId: string; public leftSideId: string;
public splitter: HTMLElement; public splitter!: HTMLElement;
public leftSide: HTMLElement; public leftSide!: HTMLElement;
public lastX: number; public lastX!: number;
public lastWidth: number; public lastWidth!: number;
private isCollapsed: ko.Observable<boolean>; private isCollapsed: ko.Observable<boolean>;
private bounds: SplitterBounds; private bounds: SplitterBounds;
private direction: SplitterDirection; private direction: SplitterDirection;
constructor(options: SplitterOptions) { constructor(options: SplitterOptions) {
this.splitterId = options.splitterId; this.splitterId = options.splitterId;
this.leftSideId = options.leftId; this.leftSideId = options.leftId;
this.isCollapsed = ko.observable<boolean>(false); this.isCollapsed = ko.observable<boolean>(false);
this.bounds = options.bounds; this.bounds = options.bounds;
this.direction = options.direction; this.direction = options.direction;
this.initialize(); this.initialize();
} }
public initialize() { public initialize() {
this.splitter = document.getElementById(this.splitterId); if (document.getElementById(this.splitterId) !== null && document.getElementById(this.leftSideId) != null) {
this.leftSide = document.getElementById(this.leftSideId); 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 = { const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
animate: true, const splitterOptions: JQueryUI.ResizableOptions = {
animateDuration: "fast", animate: true,
start: this.onResizeStart, animateDuration: "fast",
stop: this.onResizeStop start: this.onResizeStart,
}; stop: this.onResizeStop,
};
if (isVerticalSplitter) {
$(this.leftSide).css("width", this.bounds.min); if (isVerticalSplitter) {
$(this.splitter).css("height", "100%"); $(this.leftSide).css("width", this.bounds.min);
$(this.splitter).css("height", "100%");
splitterOptions.maxWidth = this.bounds.max;
splitterOptions.minWidth = this.bounds.min; splitterOptions.maxWidth = this.bounds.max;
splitterOptions.handles = { e: "#" + this.splitterId }; splitterOptions.minWidth = this.bounds.min;
} else { splitterOptions.handles = { e: "#" + this.splitterId };
$(this.leftSide).css("height", this.bounds.min); } else {
$(this.splitter).css("width", "100%"); $(this.leftSide).css("height", this.bounds.min);
$(this.splitter).css("width", "100%");
splitterOptions.maxHeight = this.bounds.max;
splitterOptions.minHeight = this.bounds.min; splitterOptions.maxHeight = this.bounds.max;
splitterOptions.handles = { s: "#" + this.splitterId }; splitterOptions.minHeight = this.bounds.min;
} splitterOptions.handles = { s: "#" + this.splitterId };
}
$(this.leftSide).resizable(splitterOptions);
} $(this.leftSide).resizable(splitterOptions);
}
private onResizeStart: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
if (this.direction === SplitterDirection.Vertical) { private onResizeStart: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
$(".ui-resizable-helper").height("100%"); if (this.direction === SplitterDirection.Vertical) {
} else { $(".ui-resizable-helper").height("100%");
$(".ui-resizable-helper").width("100%"); } else {
} $(".ui-resizable-helper").width("100%");
$("iframe").css("pointer-events", "none"); }
}; $("iframe").css("pointer-events", "none");
};
private onResizeStop: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
$("iframe").css("pointer-events", "auto"); private onResizeStop: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
}; $("iframe").css("pointer-events", "auto");
};
public collapseLeft() {
this.lastX = $(this.splitter).position().left; public collapseLeft() {
this.lastWidth = $(this.leftSide).width(); this.lastX = $(this.splitter).position().left;
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft); this.lastWidth = $(this.leftSide).width();
$(this.leftSide).css("width", ""); $(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
$(this.leftSide) $(this.leftSide).css("width", "");
.resizable("option", "disabled", true) $(this.leftSide).resizable("option", "disabled", true).removeClass("ui-resizable-disabled"); // remove class so splitter is visible
.removeClass("ui-resizable-disabled"); // remove class so splitter is visible $(this.splitter).removeClass("ui-resizable-e");
$(this.splitter).removeClass("ui-resizable-e"); this.isCollapsed(true);
this.isCollapsed(true); }
}
public expandLeft() {
public expandLeft() { $(this.splitter).addClass("ui-resizable-e");
$(this.splitter).addClass("ui-resizable-e"); $(this.leftSide).css("width", this.lastWidth);
$(this.leftSide).css("width", this.lastWidth); $(this.splitter).css("left", this.lastX);
$(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.splitter).css("left", ""); // this ensures the splitter's position is not fixed and enables movement during resizing $(this.leftSide).resizable("enable");
$(this.leftSide).resizable("enable"); this.isCollapsed(false);
this.isCollapsed(false); }
} }
}

View File

@@ -2,18 +2,16 @@
* Copyright (C) Microsoft Corporation. All rights reserved. * Copyright (C) Microsoft Corporation. All rights reserved.
*----------------------------------------------------------*/ *----------------------------------------------------------*/
export default class ThemeUtility { export function getMonacoTheme(theme: string): string {
public static getMonacoTheme(theme: string): string { switch (theme) {
switch (theme) { case "default":
case "default": case "hc-white":
case "hc-white": return "vs";
return "vs"; case "dark":
case "dark": return "vs-dark";
return "vs-dark"; case "hc-black":
case "hc-black": return "hc-black";
return "hc-black"; default:
default: return "vs";
return "vs";
}
} }
} }

View File

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

View File

@@ -14,38 +14,42 @@ describe("createCollection", () => {
collectionId: "testContainer", collectionId: "testContainer",
databaseId: "testDatabase", databaseId: "testDatabase",
databaseLevelThroughput: true, databaseLevelThroughput: true,
offerThroughput: 400 offerThroughput: 400,
}; };
beforeAll(() => { beforeAll(() => {
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
name: "test" name: "test",
} as DatabaseAccount, } as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB defaultExperience: DefaultAccountExperienceType.DocumentDB,
}); });
}); });
it("should call ARM if logged in with AAD", async () => { it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD; updateUserContext({
authType: AuthType.AAD,
});
await createCollection(createCollectionParams); await createCollection(createCollectionParams);
expect(armRequest).toHaveBeenCalled(); expect(armRequest).toHaveBeenCalled();
}); });
it("should call SDK if not logged in with non-AAD method", async () => { it("should call SDK if not logged in with non-AAD method", async () => {
window.authType = AuthType.MasterKey; updateUserContext({
authType: AuthType.MasterKey,
});
(client as jest.Mock).mockReturnValue({ (client as jest.Mock).mockReturnValue({
databases: { databases: {
createIfNotExists: () => { createIfNotExists: () => {
return { return {
database: { database: {
containers: { containers: {
create: () => ({}) create: () => ({}),
} },
} },
}; };
} },
} },
}); });
await createCollection(createCollectionParams); await createCollection(createCollectionParams);
expect(client).toHaveBeenCalled(); expect(client).toHaveBeenCalled();
@@ -59,7 +63,7 @@ describe("createCollection", () => {
collectionId: "testContainer", collectionId: "testContainer",
databaseId: "testDatabase", databaseId: "testDatabase",
databaseLevelThroughput: false, databaseLevelThroughput: false,
offerThroughput: 400 offerThroughput: 400,
}; };
expect(constructRpOptions(manualThroughputParams)).toEqual({ throughput: 400 }); expect(constructRpOptions(manualThroughputParams)).toEqual({ throughput: 400 });
@@ -69,12 +73,12 @@ describe("createCollection", () => {
databaseId: "testDatabase", databaseId: "testDatabase",
databaseLevelThroughput: false, databaseLevelThroughput: false,
offerThroughput: 400, offerThroughput: 400,
autoPilotMaxThroughput: 4000 autoPilotMaxThroughput: 4000,
}; };
expect(constructRpOptions(autoPilotThroughputParams)).toEqual({ expect(constructRpOptions(autoPilotThroughputParams)).toEqual({
autoscaleSettings: { 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 { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { import {
createUpdateCassandraTable, createUpdateCassandraTable,
getCassandraTable getCassandraTable,
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { import {
createUpdateMongoDBCollection, createUpdateMongoDBCollection,
getMongoDBCollection getMongoDBCollection,
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources"; } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { import {
createUpdateGremlinGraph, createUpdateGremlinGraph,
getGremlinGraph getGremlinGraph,
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources"; import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils"; import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
@@ -35,13 +35,13 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
); );
try { try {
let collection: DataModels.Collection; let collection: DataModels.Collection;
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) { if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
if (params.createNewDatabase) { if (params.createNewDatabase) {
const createDatabaseParams: DataModels.CreateDatabaseParams = { const createDatabaseParams: DataModels.CreateDatabaseParams = {
autoPilotMaxThroughput: params.autoPilotMaxThroughput, autoPilotMaxThroughput: params.autoPilotMaxThroughput,
databaseId: params.databaseId, databaseId: params.databaseId,
databaseLevelThroughput: params.databaseLevelThroughput, databaseLevelThroughput: params.databaseLevelThroughput,
offerThroughput: params.offerThroughput offerThroughput: params.offerThroughput,
}; };
await createDatabase(createDatabaseParams); await createDatabase(createDatabaseParams);
} }
@@ -100,7 +100,7 @@ const createSqlContainer = async (params: DataModels.CreateCollectionParams): Pr
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params); const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.SqlContainerResource = { const resource: ARMTypes.SqlContainerResource = {
id: params.collectionId id: params.collectionId,
}; };
if (params.analyticalStorageTtl) { if (params.analyticalStorageTtl) {
resource.analyticalStorageTtl = params.analyticalStorageTtl; resource.analyticalStorageTtl = params.analyticalStorageTtl;
@@ -118,8 +118,8 @@ const createSqlContainer = async (params: DataModels.CreateCollectionParams): Pr
const rpPayload: ARMTypes.SqlDatabaseCreateUpdateParameters = { const rpPayload: ARMTypes.SqlDatabaseCreateUpdateParameters = {
properties: { properties: {
resource, resource,
options options,
} },
}; };
const createResponse = await createUpdateSqlContainer( const createResponse = await createUpdateSqlContainer(
@@ -154,7 +154,7 @@ const createMongoCollection = async (params: DataModels.CreateCollectionParams):
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params); const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.MongoDBCollectionResource = { const resource: ARMTypes.MongoDBCollectionResource = {
id: params.collectionId id: params.collectionId,
}; };
if (params.analyticalStorageTtl) { if (params.analyticalStorageTtl) {
resource.analyticalStorageTtl = params.analyticalStorageTtl; resource.analyticalStorageTtl = params.analyticalStorageTtl;
@@ -170,8 +170,8 @@ const createMongoCollection = async (params: DataModels.CreateCollectionParams):
const rpPayload: ARMTypes.MongoDBCollectionCreateUpdateParameters = { const rpPayload: ARMTypes.MongoDBCollectionCreateUpdateParameters = {
properties: { properties: {
resource, resource,
options options,
} },
}; };
const createResponse = await createUpdateMongoDBCollection( const createResponse = await createUpdateMongoDBCollection(
@@ -185,7 +185,7 @@ const createMongoCollection = async (params: DataModels.CreateCollectionParams):
if (params.createMongoWildcardIndex) { if (params.createMongoWildcardIndex) {
TelemetryProcessor.trace(Action.CreateMongoCollectionWithWildcardIndex, ActionModifiers.Mark, { 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 options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.CassandraTableResource = { const resource: ARMTypes.CassandraTableResource = {
id: params.collectionId id: params.collectionId,
}; };
if (params.analyticalStorageTtl) { if (params.analyticalStorageTtl) {
resource.analyticalStorageTtl = params.analyticalStorageTtl; resource.analyticalStorageTtl = params.analyticalStorageTtl;
@@ -221,8 +221,8 @@ const createCassandraTable = async (params: DataModels.CreateCollectionParams):
const rpPayload: ARMTypes.CassandraTableCreateUpdateParameters = { const rpPayload: ARMTypes.CassandraTableCreateUpdateParameters = {
properties: { properties: {
resource, resource,
options options,
} },
}; };
const createResponse = await createUpdateCassandraTable( const createResponse = await createUpdateCassandraTable(
@@ -256,7 +256,7 @@ const createGraph = async (params: DataModels.CreateCollectionParams): Promise<D
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params); const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.GremlinGraphResource = { const resource: ARMTypes.GremlinGraphResource = {
id: params.collectionId id: params.collectionId,
}; };
if (params.indexingPolicy) { if (params.indexingPolicy) {
@@ -272,8 +272,8 @@ const createGraph = async (params: DataModels.CreateCollectionParams): Promise<D
const rpPayload: ARMTypes.GremlinGraphCreateUpdateParameters = { const rpPayload: ARMTypes.GremlinGraphCreateUpdateParameters = {
properties: { properties: {
resource, resource,
options options,
} },
}; };
const createResponse = await createUpdateGremlinGraph( const createResponse = await createUpdateGremlinGraph(
@@ -306,14 +306,14 @@ const createTable = async (params: DataModels.CreateCollectionParams): Promise<D
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params); const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.TableResource = { const resource: ARMTypes.TableResource = {
id: params.collectionId id: params.collectionId,
}; };
const rpPayload: ARMTypes.TableCreateUpdateParameters = { const rpPayload: ARMTypes.TableCreateUpdateParameters = {
properties: { properties: {
resource, resource,
options options,
} },
}; };
const createResponse = await createUpdateTable( const createResponse = await createUpdateTable(
@@ -334,13 +334,13 @@ export const constructRpOptions = (params: DataModels.CreateDatabaseParams): ARM
if (params.autoPilotMaxThroughput) { if (params.autoPilotMaxThroughput) {
return { return {
autoscaleSettings: { autoscaleSettings: {
maxThroughput: params.autoPilotMaxThroughput maxThroughput: params.autoPilotMaxThroughput,
} },
}; };
} }
return { return {
throughput: params.offerThroughput throughput: params.offerThroughput,
}; };
}; };
@@ -350,7 +350,7 @@ const createCollectionWithSDK = async (params: DataModels.CreateCollectionParams
partitionKey: params.partitionKey || undefined, partitionKey: params.partitionKey || undefined,
indexingPolicy: params.indexingPolicy || undefined, indexingPolicy: params.indexingPolicy || undefined,
uniqueKeyPolicy: params.uniqueKeyPolicy || 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 } as ContainerRequest; // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
const collectionOptions: RequestOptions = {}; const collectionOptions: RequestOptions = {};
const createDatabaseBody: DatabaseRequest = { id: params.databaseId }; const createDatabaseBody: DatabaseRequest = { id: params.databaseId };

View File

@@ -8,21 +8,21 @@ import {
GremlinDatabaseCreateUpdateParameters, GremlinDatabaseCreateUpdateParameters,
MongoDBDatabaseCreateUpdateParameters, MongoDBDatabaseCreateUpdateParameters,
SqlDatabaseCreateUpdateParameters, SqlDatabaseCreateUpdateParameters,
CreateUpdateOptions CreateUpdateOptions,
} from "../../Utils/arm/generatedClients/2020-04-01/types"; } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { import {
createUpdateCassandraKeyspace, createUpdateCassandraKeyspace,
getCassandraKeyspace getCassandraKeyspace,
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { import {
createUpdateMongoDBDatabase, createUpdateMongoDBDatabase,
getMongoDBDatabase getMongoDBDatabase,
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources"; } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { import {
createUpdateGremlinDatabase, createUpdateGremlinDatabase,
getGremlinDatabase getGremlinDatabase,
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils"; import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
@@ -34,7 +34,7 @@ export async function createDatabase(params: DataModels.CreateDatabaseParams): P
if (userContext.defaultExperience === DefaultAccountExperienceType.Table) { if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
throw new Error("Creating database resources is not allowed for tables accounts"); throw new Error("Creating database resources is not allowed for tables accounts");
} }
const database: DataModels.Database = await (window.authType === AuthType.AAD && !userContext.useSDKOperations const database: DataModels.Database = await (userContext.authType === AuthType.AAD && !userContext.useSDKOperations
? createDatabaseWithARM(params) ? createDatabaseWithARM(params)
: createDatabaseWithSDK(params)); : createDatabaseWithSDK(params));
@@ -85,10 +85,10 @@ async function createSqlDatabase(params: DataModels.CreateDatabaseParams): Promi
const rpPayload: SqlDatabaseCreateUpdateParameters = { const rpPayload: SqlDatabaseCreateUpdateParameters = {
properties: { properties: {
resource: { resource: {
id: params.databaseId id: params.databaseId,
}, },
options options,
} },
}; };
const createResponse = await createUpdateSqlDatabase( const createResponse = await createUpdateSqlDatabase(
userContext.subscriptionId, userContext.subscriptionId,
@@ -121,10 +121,10 @@ async function createMongoDatabase(params: DataModels.CreateDatabaseParams): Pro
const rpPayload: MongoDBDatabaseCreateUpdateParameters = { const rpPayload: MongoDBDatabaseCreateUpdateParameters = {
properties: { properties: {
resource: { resource: {
id: params.databaseId id: params.databaseId,
}, },
options options,
} },
}; };
const createResponse = await createUpdateMongoDBDatabase( const createResponse = await createUpdateMongoDBDatabase(
userContext.subscriptionId, userContext.subscriptionId,
@@ -157,10 +157,10 @@ async function createCassandraKeyspace(params: DataModels.CreateDatabaseParams):
const rpPayload: CassandraKeyspaceCreateUpdateParameters = { const rpPayload: CassandraKeyspaceCreateUpdateParameters = {
properties: { properties: {
resource: { resource: {
id: params.databaseId id: params.databaseId,
}, },
options options,
} },
}; };
const createResponse = await createUpdateCassandraKeyspace( const createResponse = await createUpdateCassandraKeyspace(
userContext.subscriptionId, userContext.subscriptionId,
@@ -193,10 +193,10 @@ async function createGremlineDatabase(params: DataModels.CreateDatabaseParams):
const rpPayload: GremlinDatabaseCreateUpdateParameters = { const rpPayload: GremlinDatabaseCreateUpdateParameters = {
properties: { properties: {
resource: { resource: {
id: params.databaseId id: params.databaseId,
}, },
options options,
} },
}; };
const createResponse = await createUpdateGremlinDatabase( const createResponse = await createUpdateGremlinDatabase(
userContext.subscriptionId, userContext.subscriptionId,
@@ -231,12 +231,12 @@ function constructRpOptions(params: DataModels.CreateDatabaseParams): CreateUpda
if (params.autoPilotMaxThroughput) { if (params.autoPilotMaxThroughput) {
return { return {
autoscaleSettings: { autoscaleSettings: {
maxThroughput: params.autoPilotMaxThroughput maxThroughput: params.autoPilotMaxThroughput,
} },
}; };
} }
return { return {
throughput: params.offerThroughput throughput: params.offerThroughput,
}; };
} }

View File

@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
import { Resource, StoredProcedureDefinition } from "@azure/cosmos"; import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import { import {
SqlStoredProcedureCreateUpdateParameters, SqlStoredProcedureCreateUpdateParameters,
SqlStoredProcedureResource SqlStoredProcedureResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types"; } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { import {
createUpdateSqlStoredProcedure, createUpdateSqlStoredProcedure,
getSqlStoredProcedure getSqlStoredProcedure,
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
@@ -22,7 +22,7 @@ export async function createStoredProcedure(
const clearMessage = logConsoleProgress(`Creating stored procedure ${storedProcedure.id}`); const clearMessage = logConsoleProgress(`Creating stored procedure ${storedProcedure.id}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -49,8 +49,8 @@ export async function createStoredProcedure(
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = { const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
properties: { properties: {
resource: storedProcedure as SqlStoredProcedureResource, resource: storedProcedure as SqlStoredProcedureResource,
options: {} options: {},
} },
}; };
const rpResponse = await createUpdateSqlStoredProcedure( const rpResponse = await createUpdateSqlStoredProcedure(
userContext.subscriptionId, userContext.subscriptionId,

View File

@@ -3,7 +3,7 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
import { Resource, TriggerDefinition } from "@azure/cosmos"; import { Resource, TriggerDefinition } from "@azure/cosmos";
import { import {
SqlTriggerCreateUpdateParameters, SqlTriggerCreateUpdateParameters,
SqlTriggerResource SqlTriggerResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types"; } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
@@ -19,7 +19,7 @@ export async function createTrigger(
const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`); const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -44,8 +44,8 @@ export async function createTrigger(
const createTriggerParams: SqlTriggerCreateUpdateParameters = { const createTriggerParams: SqlTriggerCreateUpdateParameters = {
properties: { properties: {
resource: trigger as SqlTriggerResource, resource: trigger as SqlTriggerResource,
options: {} options: {},
} },
}; };
const rpResponse = await createUpdateSqlTrigger( const rpResponse = await createUpdateSqlTrigger(
userContext.subscriptionId, userContext.subscriptionId,
@@ -59,10 +59,7 @@ export async function createTrigger(
return rpResponse && (rpResponse.properties?.resource as TriggerDefinition & Resource); return rpResponse && (rpResponse.properties?.resource as TriggerDefinition & Resource);
} }
const response = await client() const response = await client().database(databaseId).container(collectionId).scripts.triggers.create(trigger);
.database(databaseId)
.container(collectionId)
.scripts.triggers.create(trigger);
return response.resource; return response.resource;
} catch (error) { } catch (error) {
handleError(error, "CreateTrigger", `Error while creating trigger ${trigger.id}`); 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 { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import { import {
SqlUserDefinedFunctionCreateUpdateParameters, SqlUserDefinedFunctionCreateUpdateParameters,
SqlUserDefinedFunctionResource SqlUserDefinedFunctionResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types"; } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { import {
createUpdateSqlUserDefinedFunction, createUpdateSqlUserDefinedFunction,
getSqlUserDefinedFunction getSqlUserDefinedFunction,
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
@@ -22,7 +22,7 @@ export async function createUserDefinedFunction(
const clearMessage = logConsoleProgress(`Creating user defined function ${userDefinedFunction.id}`); const clearMessage = logConsoleProgress(`Creating user defined function ${userDefinedFunction.id}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -49,8 +49,8 @@ export async function createUserDefinedFunction(
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = { const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
properties: { properties: {
resource: userDefinedFunction as SqlUserDefinedFunctionResource, resource: userDefinedFunction as SqlUserDefinedFunctionResource,
options: {} options: {},
} },
}; };
const rpResponse = await createUpdateSqlUserDefinedFunction( const rpResponse = await createUpdateSqlUserDefinedFunction(
userContext.subscriptionId, userContext.subscriptionId,

View File

@@ -13,30 +13,34 @@ describe("deleteCollection", () => {
beforeAll(() => { beforeAll(() => {
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
name: "test" name: "test",
} as DatabaseAccount, } as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB defaultExperience: DefaultAccountExperienceType.DocumentDB,
}); });
}); });
it("should call ARM if logged in with AAD", async () => { it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD; updateUserContext({
authType: AuthType.AAD,
});
await deleteCollection("database", "collection"); await deleteCollection("database", "collection");
expect(armRequest).toHaveBeenCalled(); expect(armRequest).toHaveBeenCalled();
}); });
it("should call SDK if not logged in with non-AAD method", async () => { it("should call SDK if not logged in with non-AAD method", async () => {
window.authType = AuthType.MasterKey; updateUserContext({
authType: AuthType.MasterKey,
});
(client as jest.Mock).mockReturnValue({ (client as jest.Mock).mockReturnValue({
database: () => { database: () => {
return { return {
container: () => { container: () => {
return { return {
delete: (): unknown => undefined delete: (): unknown => undefined,
}; };
} },
}; };
} },
}); });
await deleteCollection("database", "collection"); await deleteCollection("database", "collection");
expect(client).toHaveBeenCalled(); expect(client).toHaveBeenCalled();

View File

@@ -13,13 +13,10 @@ import { client } from "../CosmosClient";
export async function deleteCollection(databaseId: string, collectionId: string): Promise<void> { export async function deleteCollection(databaseId: string, collectionId: string): Promise<void> {
const clearMessage = logConsoleProgress(`Deleting container ${collectionId}`); const clearMessage = logConsoleProgress(`Deleting container ${collectionId}`);
try { try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) { if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
await deleteCollectionWithARM(databaseId, collectionId); await deleteCollectionWithARM(databaseId, collectionId);
} else { } else {
await client() await client().database(databaseId).container(collectionId).delete();
.database(databaseId)
.container(collectionId)
.delete();
} }
logConsoleInfo(`Successfully deleted container ${collectionId}`); logConsoleInfo(`Successfully deleted container ${collectionId}`);
} catch (error) { } catch (error) {

View File

@@ -10,7 +10,7 @@ export const deleteConflict = async (collection: CollectionBase, conflictId: Con
try { try {
const options = { const options = {
partitionKey: getPartitionKeyHeaderForConflict(conflictId) partitionKey: getPartitionKeyHeaderForConflict(conflictId),
}; };
await client() await client()

View File

@@ -13,26 +13,30 @@ describe("deleteDatabase", () => {
beforeAll(() => { beforeAll(() => {
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
name: "test" name: "test",
} as DatabaseAccount, } as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB defaultExperience: DefaultAccountExperienceType.DocumentDB,
}); });
}); });
it("should call ARM if logged in with AAD", async () => { it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD; updateUserContext({
authType: AuthType.AAD,
});
await deleteDatabase("database"); await deleteDatabase("database");
expect(armRequest).toHaveBeenCalled(); expect(armRequest).toHaveBeenCalled();
}); });
it("should call SDK if not logged in with non-AAD method", async () => { it("should call SDK if not logged in with non-AAD method", async () => {
window.authType = AuthType.MasterKey; updateUserContext({
authType: AuthType.MasterKey,
});
(client as jest.Mock).mockReturnValue({ (client as jest.Mock).mockReturnValue({
database: () => { database: () => {
return { return {
delete: (): unknown => undefined delete: (): unknown => undefined,
}; };
} },
}); });
await deleteDatabase("database"); await deleteDatabase("database");
expect(client).toHaveBeenCalled(); expect(client).toHaveBeenCalled();

View File

@@ -16,12 +16,10 @@ export async function deleteDatabase(databaseId: string): Promise<void> {
if (userContext.defaultExperience === DefaultAccountExperienceType.Table) { if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
throw new Error("Deleting database resources is not allowed for tables accounts"); throw new Error("Deleting database resources is not allowed for tables accounts");
} }
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) { if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
await deleteDatabaseWithARM(databaseId); await deleteDatabaseWithARM(databaseId);
} else { } else {
await client() await client().database(databaseId).delete();
.database(databaseId)
.delete();
} }
logConsoleInfo(`Successfully deleted database ${databaseId}`); logConsoleInfo(`Successfully deleted database ${databaseId}`);
} catch (error) { } catch (error) {

View File

@@ -14,7 +14,7 @@ export async function deleteStoredProcedure(
const clearMessage = logConsoleProgress(`Deleting stored procedure ${storedProcedureId}`); const clearMessage = logConsoleProgress(`Deleting stored procedure ${storedProcedureId}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -27,11 +27,7 @@ export async function deleteStoredProcedure(
storedProcedureId storedProcedureId
); );
} else { } else {
await client() await client().database(databaseId).container(collectionId).scripts.storedProcedure(storedProcedureId).delete();
.database(databaseId)
.container(collectionId)
.scripts.storedProcedure(storedProcedureId)
.delete();
} }
} catch (error) { } catch (error) {
handleError(error, "DeleteStoredProcedure", `Error while deleting stored procedure ${storedProcedureId}`); handleError(error, "DeleteStoredProcedure", `Error while deleting stored procedure ${storedProcedureId}`);

View File

@@ -10,7 +10,7 @@ export async function deleteTrigger(databaseId: string, collectionId: string, tr
const clearMessage = logConsoleProgress(`Deleting trigger ${triggerId}`); const clearMessage = logConsoleProgress(`Deleting trigger ${triggerId}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -23,11 +23,7 @@ export async function deleteTrigger(databaseId: string, collectionId: string, tr
triggerId triggerId
); );
} else { } else {
await client() await client().database(databaseId).container(collectionId).scripts.trigger(triggerId).delete();
.database(databaseId)
.container(collectionId)
.scripts.trigger(triggerId)
.delete();
} }
} catch (error) { } catch (error) {
handleError(error, "DeleteTrigger", `Error while deleting trigger ${triggerId}`); handleError(error, "DeleteTrigger", `Error while deleting trigger ${triggerId}`);

View File

@@ -10,7 +10,7 @@ export async function deleteUserDefinedFunction(databaseId: string, collectionId
const clearMessage = logConsoleProgress(`Deleting user defined function ${id}`); const clearMessage = logConsoleProgress(`Deleting user defined function ${id}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -23,11 +23,7 @@ export async function deleteUserDefinedFunction(databaseId: string, collectionId
id id
); );
} else { } else {
await client() await client().database(databaseId).container(collectionId).scripts.userDefinedFunction(id).delete();
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunction(id)
.delete();
} }
} catch (error) { } catch (error) {
handleError(error, "DeleteUserDefinedFunction", `Error while deleting user defined function ${id}`); handleError(error, "DeleteUserDefinedFunction", `Error while deleting user defined function ${id}`);

View File

@@ -33,7 +33,7 @@ export const executeStoredProcedure = async (
); );
return { return {
result: response.resource, result: response.resource,
scriptLogs: response.headers[HttpHeaders.scriptLogResults] as string scriptLogs: response.headers[HttpHeaders.scriptLogResults] as string,
}; };
} catch (error) { } catch (error) {
handleError( handleError(

View File

@@ -41,7 +41,7 @@ interface MetricsResponse {
} }
export const getCollectionUsageSizeInKB = async (databaseName: string, containerName: string): Promise<number> => { export const getCollectionUsageSizeInKB = async (databaseName: string, containerName: string): Promise<number> => {
if (window.authType !== AuthType.AAD) { if (userContext.authType !== AuthType.AAD) {
return undefined; return undefined;
} }
@@ -60,8 +60,8 @@ export const getCollectionUsageSizeInKB = async (databaseName: string, container
apiVersion: "2018-01-01", apiVersion: "2018-01-01",
queryParams: { queryParams: {
filter, filter,
metricNames metricNames,
} },
}); });
if (metricsResponse?.value?.length !== 2) { if (metricsResponse?.value?.length !== 2) {
@@ -76,7 +76,7 @@ export const getCollectionUsageSizeInKB = async (databaseName: string, container
return dataUsageSizeInKb + indexUsageSizeInKb; return dataUsageSizeInKb + indexUsageSizeInKb;
} catch (error) { } catch (error) {
handleError(error, "getCollectionUsageSize"); handleError(error, "getCollectionUsageSize");
throw error; return undefined;
} }
}; };

View File

@@ -3,18 +3,16 @@ import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import * as Constants from "../Constants"; import * as Constants from "../Constants";
import { AuthType } from "../../AuthType"; import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
export async function getIndexTransformationProgress(databaseId: string, collectionId: string): Promise<number> { export async function getIndexTransformationProgress(databaseId: string, collectionId: string): Promise<number> {
if (window.authType !== AuthType.AAD) { if (userContext.authType !== AuthType.AAD) {
return undefined; return undefined;
} }
let indexTransformationPercentage: number; let indexTransformationPercentage: number;
const clearMessage = logConsoleProgress(`Reading container ${collectionId}`); const clearMessage = logConsoleProgress(`Reading container ${collectionId}`);
try { try {
const response = await client() const response = await client().database(databaseId).container(collectionId).read({ populateQuotaInfo: true });
.database(databaseId)
.container(collectionId)
.read({ populateQuotaInfo: true });
indexTransformationPercentage = parseInt( indexTransformationPercentage = parseInt(
response.headers[Constants.HttpHeaders.collectionIndexTransformationProgress] as string response.headers[Constants.HttpHeaders.collectionIndexTransformationProgress] as string

View File

@@ -7,8 +7,5 @@ export const queryConflicts = (
query: string, query: string,
options: FeedOptions options: FeedOptions
): QueryIterator<ConflictDefinition & Resource> => { ): QueryIterator<ConflictDefinition & Resource> => {
return client() return client().database(databaseId).container(containerId).conflicts.query(query, options);
.database(databaseId)
.container(containerId)
.conflicts.query(query, options);
}; };

View File

@@ -10,10 +10,7 @@ export const queryDocuments = (
options: FeedOptions options: FeedOptions
): QueryIterator<ItemDefinition & Resource> => { ): QueryIterator<ItemDefinition & Resource> => {
options = getCommonQueryOptions(options); options = getCommonQueryOptions(options);
return client() return client().database(databaseId).container(containerId).items.query(query, options);
.database(databaseId)
.container(containerId)
.items.query(query, options);
}; };
export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => { export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {

View File

@@ -9,25 +9,25 @@ import { updateUserContext } from "../../UserContext";
describe("readCollection", () => { describe("readCollection", () => {
beforeAll(() => { beforeAll(() => {
updateUserContext({ updateUserContext({
authType: AuthType.ResourceToken,
databaseAccount: { databaseAccount: {
name: "test" name: "test",
} as DatabaseAccount, } as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB defaultExperience: DefaultAccountExperienceType.DocumentDB,
}); });
}); });
it("should call SDK if logged in with resource token", async () => { it("should call SDK if logged in with resource token", async () => {
window.authType = AuthType.ResourceToken;
(client as jest.Mock).mockReturnValue({ (client as jest.Mock).mockReturnValue({
database: () => { database: () => {
return { return {
container: () => { container: () => {
return { return {
read: (): unknown => ({}) read: (): unknown => ({}),
}; };
} },
}; };
} },
}); });
await readCollection("database", "collection"); await readCollection("database", "collection");
expect(client).toHaveBeenCalled(); expect(client).toHaveBeenCalled();

View File

@@ -7,10 +7,7 @@ export async function readCollection(databaseId: string, collectionId: string):
let collection: DataModels.Collection; let collection: DataModels.Collection;
const clearMessage = logConsoleProgress(`Querying container ${collectionId}`); const clearMessage = logConsoleProgress(`Querying container ${collectionId}`);
try { try {
const response = await client() const response = await client().database(databaseId).container(collectionId).read();
.database(databaseId)
.container(collectionId)
.read();
collection = response.resource as DataModels.Collection; collection = response.resource as DataModels.Collection;
} catch (error) { } catch (error) {
handleError(error, "ReadCollection", `Error while querying container ${collectionId}`); handleError(error, "ReadCollection", `Error while querying container ${collectionId}`);

View File

@@ -16,7 +16,7 @@ export const readCollectionOffer = async (params: ReadCollectionOfferParams): Pr
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table userContext.defaultExperience !== DefaultAccountExperienceType.Table
) { ) {
@@ -106,7 +106,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
autoscaleMaxThroughput: autoscaleSettings.maxThroughput, autoscaleMaxThroughput: autoscaleSettings.maxThroughput,
manualThroughput: undefined, manualThroughput: undefined,
minimumThroughput, minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true" offerReplacePending: resource.offerReplacePending === "true",
}; };
} }
@@ -115,7 +115,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
autoscaleMaxThroughput: undefined, autoscaleMaxThroughput: undefined,
manualThroughput: resource.throughput, manualThroughput: resource.throughput,
minimumThroughput, minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true" offerReplacePending: resource.offerReplacePending === "true",
}; };
} }

View File

@@ -12,32 +12,36 @@ describe("readCollections", () => {
beforeAll(() => { beforeAll(() => {
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
name: "test" name: "test",
} as DatabaseAccount, } as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB defaultExperience: DefaultAccountExperienceType.DocumentDB,
}); });
}); });
it("should call ARM if logged in with AAD", async () => { it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD; updateUserContext({
authType: AuthType.AAD,
});
await readCollections("database"); await readCollections("database");
expect(armRequest).toHaveBeenCalled(); expect(armRequest).toHaveBeenCalled();
}); });
it("should call SDK if not logged in with non-AAD method", async () => { it("should call SDK if not logged in with non-AAD method", async () => {
window.authType = AuthType.MasterKey; updateUserContext({
authType: AuthType.MasterKey,
});
(client as jest.Mock).mockReturnValue({ (client as jest.Mock).mockReturnValue({
database: () => { database: () => {
return { return {
containers: { containers: {
readAll: () => { readAll: () => {
return { return {
fetchAll: (): unknown => [] fetchAll: (): unknown => [],
}; };
} },
} },
}; };
} },
}); });
await readCollections("database"); await readCollections("database");
expect(client).toHaveBeenCalled(); expect(client).toHaveBeenCalled();

View File

@@ -15,7 +15,7 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`); const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB && userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table userContext.defaultExperience !== DefaultAccountExperienceType.Table
@@ -23,10 +23,7 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
return await readCollectionsWithARM(databaseId); return await readCollectionsWithARM(databaseId);
} }
const sdkResponse = await client() const sdkResponse = await client().database(databaseId).containers.readAll().fetchAll();
.database(databaseId)
.containers.readAll()
.fetchAll();
return sdkResponse.resources as DataModels.Collection[]; return sdkResponse.resources as DataModels.Collection[];
} catch (error) { } catch (error) {
handleError(error, "ReadCollections", `Error while querying containers for database ${databaseId}`); handleError(error, "ReadCollections", `Error while querying containers for database ${databaseId}`);
@@ -63,5 +60,5 @@ async function readCollectionsWithARM(databaseId: string): Promise<DataModels.Co
throw new Error(`Unsupported default experience type: ${defaultExperience}`); 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

@@ -15,7 +15,7 @@ export const readDatabaseOffer = async (params: ReadDatabaseOfferParams): Promis
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table userContext.defaultExperience !== DefaultAccountExperienceType.Table
) { ) {
@@ -78,7 +78,7 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
autoscaleMaxThroughput: autoscaleSettings.maxThroughput, autoscaleMaxThroughput: autoscaleSettings.maxThroughput,
manualThroughput: undefined, manualThroughput: undefined,
minimumThroughput, minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true" offerReplacePending: resource.offerReplacePending === "true",
}; };
} }
@@ -87,7 +87,7 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
autoscaleMaxThroughput: undefined, autoscaleMaxThroughput: undefined,
manualThroughput: resource.throughput, manualThroughput: resource.throughput,
minimumThroughput, minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true" offerReplacePending: resource.offerReplacePending === "true",
}; };
} }

View File

@@ -12,28 +12,32 @@ describe("readDatabases", () => {
beforeAll(() => { beforeAll(() => {
updateUserContext({ updateUserContext({
databaseAccount: { databaseAccount: {
name: "test" name: "test",
} as DatabaseAccount, } as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB defaultExperience: DefaultAccountExperienceType.DocumentDB,
}); });
}); });
it("should call ARM if logged in with AAD", async () => { it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD; updateUserContext({
authType: AuthType.AAD,
});
await readDatabases(); await readDatabases();
expect(armRequest).toHaveBeenCalled(); expect(armRequest).toHaveBeenCalled();
}); });
it("should call SDK if not logged in with non-AAD method", async () => { it("should call SDK if not logged in with non-AAD method", async () => {
window.authType = AuthType.MasterKey; updateUserContext({
authType: AuthType.MasterKey,
});
(client as jest.Mock).mockReturnValue({ (client as jest.Mock).mockReturnValue({
databases: { databases: {
readAll: () => { readAll: () => {
return { return {
fetchAll: (): unknown => [] fetchAll: (): unknown => [],
}; };
} },
} },
}); });
await readDatabases(); await readDatabases();
expect(client).toHaveBeenCalled(); expect(client).toHaveBeenCalled();

View File

@@ -15,15 +15,13 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
const clearMessage = logConsoleProgress(`Querying databases`); const clearMessage = logConsoleProgress(`Querying databases`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table userContext.defaultExperience !== DefaultAccountExperienceType.Table
) { ) {
databases = await readDatabasesWithARM(); databases = await readDatabasesWithARM();
} else { } else {
const sdkResponse = await client() const sdkResponse = await client().databases.readAll().fetchAll();
.databases.readAll()
.fetchAll();
databases = sdkResponse.resources as DataModels.Database[]; databases = sdkResponse.resources as DataModels.Database[];
} }
} catch (error) { } catch (error) {
@@ -58,5 +56,5 @@ async function readDatabasesWithARM(): Promise<DataModels.Database[]> {
throw new Error(`Unsupported default experience type: ${defaultExperience}`); 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

@@ -9,7 +9,7 @@ export async function readMongoDBCollectionThroughRP(
databaseId: string, databaseId: string,
collectionId: string collectionId: string
): Promise<MongoDBCollectionResource> { ): Promise<MongoDBCollectionResource> {
if (window.authType !== AuthType.AAD) { if (userContext.authType !== AuthType.AAD) {
return undefined; return undefined;
} }
let collection: MongoDBCollectionResource; let collection: MongoDBCollectionResource;

View File

@@ -8,7 +8,7 @@ import { readOffers } from "./readOffers";
export const readOfferWithSDK = async (offerId: string, resourceId: string): Promise<Offer> => { export const readOfferWithSDK = async (offerId: string, resourceId: string): Promise<Offer> => {
if (!offerId) { if (!offerId) {
const offers = await readOffers(); const offers = await readOffers();
const offer = offers.find(offer => offer.resource === resourceId); const offer = offers.find((offer) => offer.resource === resourceId);
if (!offer) { if (!offer) {
return undefined; return undefined;
@@ -18,12 +18,10 @@ export const readOfferWithSDK = async (offerId: string, resourceId: string): Pro
const options: RequestOptions = { const options: RequestOptions = {
initialHeaders: { initialHeaders: {
[HttpHeaders.populateCollectionThroughputInfo]: true [HttpHeaders.populateCollectionThroughputInfo]: true,
} },
}; };
const response = await client() const response = await client().offer(offerId).read(options);
.offer(offerId)
.read(options);
return parseSDKOfferResponse(response); return parseSDKOfferResponse(response);
}; };

View File

@@ -7,9 +7,7 @@ export const readOffers = async (): Promise<SDKOfferDefinition[]> => {
const clearMessage = logConsoleProgress(`Querying offers`); const clearMessage = logConsoleProgress(`Querying offers`);
try { try {
const response = await client() const response = await client().offers.readAll().fetchAll();
.offers.readAll()
.fetchAll();
return response?.resources; return response?.resources;
} catch (error) { } catch (error) {
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too. // This should be removed when we can correctly identify if an account is serverless when connected using connection string too.

View File

@@ -14,7 +14,7 @@ export async function readStoredProcedures(
const clearMessage = logConsoleProgress(`Querying stored procedures for container ${collectionId}`); const clearMessage = logConsoleProgress(`Querying stored procedures for container ${collectionId}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -25,7 +25,7 @@ export async function readStoredProcedures(
databaseId, databaseId,
collectionId 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() const response = await client()

View File

@@ -14,7 +14,7 @@ export async function readTriggers(
const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`); const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -25,14 +25,10 @@ export async function readTriggers(
databaseId, databaseId,
collectionId 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() const response = await client().database(databaseId).container(collectionId).scripts.triggers.readAll().fetchAll();
.database(databaseId)
.container(collectionId)
.scripts.triggers.readAll()
.fetchAll();
return response?.resources; return response?.resources;
} catch (error) { } catch (error) {
handleError(error, "ReadTriggers", `Failed to query triggers for container ${collectionId}`); handleError(error, "ReadTriggers", `Failed to query triggers for container ${collectionId}`);

View File

@@ -14,7 +14,7 @@ export async function readUserDefinedFunctions(
const clearMessage = logConsoleProgress(`Querying user defined functions for container ${collectionId}`); const clearMessage = logConsoleProgress(`Querying user defined functions for container ${collectionId}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -25,7 +25,7 @@ export async function readUserDefinedFunctions(
databaseId, databaseId,
collectionId 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() const response = await client()

View File

@@ -8,22 +8,22 @@ import {
MongoDBCollectionCreateUpdateParameters, MongoDBCollectionCreateUpdateParameters,
MongoDBCollectionResource, MongoDBCollectionResource,
SqlContainerCreateUpdateParameters, SqlContainerCreateUpdateParameters,
SqlContainerResource SqlContainerResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types"; } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { RequestOptions } from "@azure/cosmos/dist-esm"; import { RequestOptions } from "@azure/cosmos/dist-esm";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { import {
createUpdateCassandraTable, createUpdateCassandraTable,
getCassandraTable getCassandraTable,
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { import {
createUpdateMongoDBCollection, createUpdateMongoDBCollection,
getMongoDBCollection getMongoDBCollection,
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources"; } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { import {
createUpdateGremlinGraph, createUpdateGremlinGraph,
getGremlinGraph getGremlinGraph,
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources"; import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
@@ -41,7 +41,7 @@ export async function updateCollection(
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB && userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table userContext.defaultExperience !== DefaultAccountExperienceType.Table
@@ -130,8 +130,8 @@ export async function updateMongoDBCollectionThroughRP(
const updateParams: MongoDBCollectionCreateUpdateParameters = { const updateParams: MongoDBCollectionCreateUpdateParameters = {
properties: { properties: {
resource: newCollection, resource: newCollection,
options: updateOptions options: updateOptions,
} },
}; };
const updateResponse = await createUpdateMongoDBCollection( const updateResponse = await createUpdateMongoDBCollection(

View File

@@ -17,7 +17,7 @@ import {
migrateSqlDatabaseToManualThroughput, migrateSqlDatabaseToManualThroughput,
migrateSqlContainerToAutoscale, migrateSqlContainerToAutoscale,
migrateSqlContainerToManualThroughput, migrateSqlContainerToManualThroughput,
updateSqlContainerThroughput updateSqlContainerThroughput,
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { import {
updateCassandraKeyspaceThroughput, updateCassandraKeyspaceThroughput,
@@ -25,7 +25,7 @@ import {
migrateCassandraKeyspaceToManualThroughput, migrateCassandraKeyspaceToManualThroughput,
migrateCassandraTableToAutoscale, migrateCassandraTableToAutoscale,
migrateCassandraTableToManualThroughput, migrateCassandraTableToManualThroughput,
updateCassandraTableThroughput updateCassandraTableThroughput,
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { import {
updateMongoDBDatabaseThroughput, updateMongoDBDatabaseThroughput,
@@ -33,7 +33,7 @@ import {
migrateMongoDBDatabaseToManualThroughput, migrateMongoDBDatabaseToManualThroughput,
migrateMongoDBCollectionToAutoscale, migrateMongoDBCollectionToAutoscale,
migrateMongoDBCollectionToManualThroughput, migrateMongoDBCollectionToManualThroughput,
updateMongoDBCollectionThroughput updateMongoDBCollectionThroughput,
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources"; } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { import {
updateGremlinDatabaseThroughput, updateGremlinDatabaseThroughput,
@@ -41,13 +41,13 @@ import {
migrateGremlinDatabaseToManualThroughput, migrateGremlinDatabaseToManualThroughput,
migrateGremlinGraphToAutoscale, migrateGremlinGraphToAutoscale,
migrateGremlinGraphToManualThroughput, migrateGremlinGraphToManualThroughput,
updateGremlinGraphThroughput updateGremlinGraphThroughput,
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
import { import {
migrateTableToAutoscale, migrateTableToAutoscale,
migrateTableToManualThroughput, migrateTableToManualThroughput,
updateTableThroughput updateTableThroughput,
} from "../../Utils/arm/generatedClients/2020-04-01/tableResources"; } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> => { export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> => {
@@ -58,7 +58,7 @@ export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> =>
const clearMessage = logConsoleProgress(`Updating offer for ${offerResourceText}`); const clearMessage = logConsoleProgress(`Updating offer for ${offerResourceText}`);
try { try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) { if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
if (params.collectionId) { if (params.collectionId) {
updatedOffer = await updateCollectionOfferWithARM(params); updatedOffer = await updateCollectionOfferWithARM(params);
} else if (userContext.defaultExperience === DefaultAccountExperienceType.Table) { } else if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
@@ -110,7 +110,7 @@ const updateCollectionOfferWithARM = async (params: UpdateOfferParams): Promise<
return await readCollectionOffer({ return await readCollectionOffer({
collectionId: params.collectionId, collectionId: params.collectionId,
databaseId: params.databaseId, 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({ return await readDatabaseOffer({
databaseId: params.databaseId, 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 createUpdateOfferBody = (params: UpdateOfferParams): ThroughputSettingsUpdateParameters => {
const body: ThroughputSettingsUpdateParameters = { const body: ThroughputSettingsUpdateParameters = {
properties: { properties: {
resource: {} resource: {},
} },
}; };
if (params.autopilotThroughput) { if (params.autopilotThroughput) {
body.properties.resource.autoscaleSettings = { body.properties.resource.autoscaleSettings = {
maxThroughput: params.autopilotThroughput maxThroughput: params.autopilotThroughput,
}; };
} else { } else {
body.properties.resource.throughput = params.manualThroughput; body.properties.resource.throughput = params.manualThroughput;
@@ -378,7 +378,7 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
const newOffer: SDKOfferDefinition = { const newOffer: SDKOfferDefinition = {
content: { content: {
offerThroughput: undefined, offerThroughput: undefined,
offerIsRUPerMinuteThroughputEnabled: false offerIsRUPerMinuteThroughputEnabled: false,
}, },
_etag: undefined, _etag: undefined,
_ts: undefined, _ts: undefined,
@@ -388,12 +388,12 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
offerResourceId: sdkOfferDefinition.offerResourceId, offerResourceId: sdkOfferDefinition.offerResourceId,
offerVersion: sdkOfferDefinition.offerVersion, offerVersion: sdkOfferDefinition.offerVersion,
offerType: sdkOfferDefinition.offerType, offerType: sdkOfferDefinition.offerType,
resource: sdkOfferDefinition.resource resource: sdkOfferDefinition.resource,
}; };
if (params.autopilotThroughput) { if (params.autopilotThroughput) {
newOffer.content.offerAutopilotSettings = { newOffer.content.offerAutopilotSettings = {
maxThroughput: params.autopilotThroughput maxThroughput: params.autopilotThroughput,
}; };
} else { } else {
newOffer.content.offerThroughput = params.manualThroughput; newOffer.content.offerThroughput = params.manualThroughput;
@@ -402,12 +402,12 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
const options: RequestOptions = {}; const options: RequestOptions = {};
if (params.migrateToAutoPilot) { if (params.migrateToAutoPilot) {
options.initialHeaders = { options.initialHeaders = {
[HttpHeaders.migrateOfferToAutopilot]: "true" [HttpHeaders.migrateOfferToAutopilot]: "true",
}; };
delete newOffer.content.offerAutopilotSettings; delete newOffer.content.offerAutopilotSettings;
} else if (params.migrateToManual) { } else if (params.migrateToManual) {
options.initialHeaders = { options.initialHeaders = {
[HttpHeaders.migrateOfferToManualThroughput]: "true" [HttpHeaders.migrateOfferToManualThroughput]: "true",
}; };
newOffer.content.offerAutopilotSettings = { maxThroughput: 0 }; newOffer.content.offerAutopilotSettings = { maxThroughput: 0 };
} }

View File

@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
import { Resource, StoredProcedureDefinition } from "@azure/cosmos"; import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import { import {
SqlStoredProcedureCreateUpdateParameters, SqlStoredProcedureCreateUpdateParameters,
SqlStoredProcedureResource SqlStoredProcedureResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types"; } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { import {
createUpdateSqlStoredProcedure, createUpdateSqlStoredProcedure,
getSqlStoredProcedure getSqlStoredProcedure,
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
@@ -22,7 +22,7 @@ export async function updateStoredProcedure(
const clearMessage = logConsoleProgress(`Updating stored procedure ${storedProcedure.id}`); const clearMessage = logConsoleProgress(`Updating stored procedure ${storedProcedure.id}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -39,8 +39,8 @@ export async function updateStoredProcedure(
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = { const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
properties: { properties: {
resource: storedProcedure as SqlStoredProcedureResource, resource: storedProcedure as SqlStoredProcedureResource,
options: {} options: {},
} },
}; };
const rpResponse = await createUpdateSqlStoredProcedure( const rpResponse = await createUpdateSqlStoredProcedure(
userContext.subscriptionId, userContext.subscriptionId,

View File

@@ -2,7 +2,7 @@ import { AuthType } from "../../AuthType";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType"; import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
import { import {
SqlTriggerCreateUpdateParameters, SqlTriggerCreateUpdateParameters,
SqlTriggerResource SqlTriggerResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types"; } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { TriggerDefinition } from "@azure/cosmos"; import { TriggerDefinition } from "@azure/cosmos";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
@@ -19,7 +19,7 @@ export async function updateTrigger(
const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`); const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -36,8 +36,8 @@ export async function updateTrigger(
const createTriggerParams: SqlTriggerCreateUpdateParameters = { const createTriggerParams: SqlTriggerCreateUpdateParameters = {
properties: { properties: {
resource: trigger as SqlTriggerResource, resource: trigger as SqlTriggerResource,
options: {} options: {},
} },
}; };
const rpResponse = await createUpdateSqlTrigger( const rpResponse = await createUpdateSqlTrigger(
userContext.subscriptionId, userContext.subscriptionId,

View File

@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos"; import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import { import {
SqlUserDefinedFunctionCreateUpdateParameters, SqlUserDefinedFunctionCreateUpdateParameters,
SqlUserDefinedFunctionResource SqlUserDefinedFunctionResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types"; } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { import {
createUpdateSqlUserDefinedFunction, createUpdateSqlUserDefinedFunction,
getSqlUserDefinedFunction getSqlUserDefinedFunction,
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { handleError } from "../ErrorHandlingUtils"; import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
@@ -22,7 +22,7 @@ export async function updateUserDefinedFunction(
const clearMessage = logConsoleProgress(`Updating user defined function ${userDefinedFunction.id}`); const clearMessage = logConsoleProgress(`Updating user defined function ${userDefinedFunction.id}`);
try { try {
if ( if (
window.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) { ) {
@@ -39,8 +39,8 @@ export async function updateUserDefinedFunction(
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = { const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
properties: { properties: {
resource: userDefinedFunction as SqlUserDefinedFunctionResource, resource: userDefinedFunction as SqlUserDefinedFunctionResource,
options: {} options: {},
} },
}; };
const rpResponse = await createUpdateSqlUserDefinedFunction( const rpResponse = await createUpdateSqlUserDefinedFunction(
userContext.subscriptionId, userContext.subscriptionId,

View File

@@ -1,10 +1,10 @@
export enum Platform { export enum Platform {
Portal = "Portal", Portal = "Portal",
Hosted = "Hosted", Hosted = "Hosted",
Emulator = "Emulator" Emulator = "Emulator",
} }
interface ConfigContext { export interface ConfigContext {
platform: Platform; platform: Platform;
allowedParentFrameOrigins: string[]; allowedParentFrameOrigins: string[];
gitSha?: string; gitSha?: string;
@@ -37,7 +37,7 @@ let configContext: Readonly<ConfigContext> = {
`^https:\\/\\/[\\.\\w]*portal\\.microsoftazure.de$`, `^https:\\/\\/[\\.\\w]*portal\\.microsoftazure.de$`,
`^https:\\/\\/[\\.\\w]*ext\\.azure\\.(com|cn|us)$`, `^https:\\/\\/[\\.\\w]*ext\\.azure\\.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]*\\.ext\\.microsoftazure\\.de$`, `^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 // Webpack injects this at build time
gitSha: process.env.GIT_SHA, gitSha: process.env.GIT_SHA,
@@ -52,7 +52,7 @@ let configContext: Readonly<ConfigContext> = {
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net", ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306 GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
JUNO_ENDPOINT: "https://tools.cosmos.azure.com", 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 { export function resetConfigContext(): void {
@@ -73,20 +73,24 @@ if (process.env.NODE_ENV === "development") {
BACKEND_ENDPOINT: "https://localhost:" + port, BACKEND_ENDPOINT: "https://localhost:" + port,
MONGO_BACKEND_ENDPOINT: "https://localhost:" + port, MONGO_BACKEND_ENDPOINT: "https://localhost:" + port,
PROXY_PATH: "/proxy", PROXY_PATH: "/proxy",
EMULATOR_ENDPOINT: "https://localhost:8081" EMULATOR_ENDPOINT: "https://localhost:8081",
}); });
} }
export async function initializeConfiguration(): Promise<ConfigContext> { export async function initializeConfiguration(): Promise<ConfigContext> {
try { try {
const response = await fetch("./config.json"); const response = await fetch("./config.json", {
headers: {
"If-None-Match": "", // disable client side cache
},
});
if (response.status === 200) { if (response.status === 200) {
try { try {
const { allowedParentFrameOrigins, ...externalConfig } = await response.json(); const { allowedParentFrameOrigins, ...externalConfig } = await response.json();
Object.assign(configContext, externalConfig); Object.assign(configContext, externalConfig);
if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) { if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) {
updateConfigContext({ updateConfigContext({
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins] allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins],
}); });
} }
} catch (error) { } catch (error) {
@@ -104,7 +108,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
const platform = params.get("platform"); const platform = params.get("platform");
switch (platform) { switch (platform) {
default: default:
console.log("Invalid platform query parameter given, ignoring"); console.error(`Invalid platform query parameter: ${platform}`);
break; break;
case Platform.Portal: case Platform.Portal:
case Platform.Hosted: case Platform.Hosted:
@@ -113,7 +117,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
} }
} }
} catch (error) { } catch (error) {
console.log("No configuration file found using defaults"); console.error("No configuration file found using defaults");
} }
return configContext; return configContext;
} }

View File

@@ -7,7 +7,7 @@ export enum TabKind {
TableEntities, TableEntities,
Graph, Graph,
SQLQuery, SQLQuery,
ScaleSettings ScaleSettings,
} }
/** /**
@@ -20,7 +20,7 @@ export enum PaneKind {
DeleteDatabase, DeleteDatabase,
GlobalSettings, GlobalSettings,
AdHocAccess, AdHocAccess,
SwitchDirectory SwitchDirectory,
} }
/** /**
@@ -79,5 +79,5 @@ export enum ActionType {
OpenCollectionTab, OpenCollectionTab,
OpenPane, OpenPane,
TransmitCachedData, 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 level.
*/ */
Error = 2 Error = 2,
} }
/** /**
* Schema of a log entry. * Schema of a log entry.

View File

@@ -1,39 +1,38 @@
import * as Versions from "./Versions"; import * as Versions from "./Versions";
import * as ActionContracts from "./ActionContracts"; import * as ActionContracts from "./ActionContracts";
import * as Diagnostics from "./Diagnostics"; import * as Diagnostics from "./Diagnostics";
/** /**
* Messaging types used with Data Explorer <-> Portal communication * Messaging types used with Data Explorer <-> Portal communication
* and Hosted <-> Explorer communication * and Hosted <-> Explorer communication
*/ */
export enum MessageTypes { export enum MessageTypes {
TelemetryInfo, TelemetryInfo,
LogInfo, LogInfo,
RefreshResources, RefreshResources,
AllDatabases, AllDatabases,
CollectionsForDatabase, CollectionsForDatabase,
RefreshOffers, RefreshOffers,
AllOffers, AllOffers,
UpdateLocationHash, UpdateLocationHash,
SingleOffer, SingleOffer,
RefreshOffer, RefreshOffer,
UpdateAccountName, UpdateAccountName,
ForbiddenError, ForbiddenError,
AadSignIn, AadSignIn,
GetAccessAadRequest, GetAccessAadRequest,
GetAccessAadResponse, GetAccessAadResponse,
UpdateAccountSwitch, UpdateAccountSwitch,
UpdateDirectoryControl, UpdateDirectoryControl,
SwitchAccount, SwitchAccount,
SendNotification, SendNotification,
ClearNotification, ClearNotification,
ExplorerClickEvent, ExplorerClickEvent,
LoadingStatus, LoadingStatus,
GetArcadiaToken, GetArcadiaToken,
CreateWorkspace, CreateWorkspace,
CreateSparkPool, CreateSparkPool,
RefreshDatabaseAccount, RefreshDatabaseAccount,
InitTestExplorer }
}
export { Versions, ActionContracts, Diagnostics };
export { Versions, ActionContracts, Diagnostics };

View File

@@ -0,0 +1,9 @@
/**
* Messaging types used with SelfServe Component <-> Portal communication
* and Hosted <-> SelfServe Component communication
*/
export enum SelfServeMessageTypes {
TelemetryInfo = "TelemetryInfo",
Notification = "Notification",
}

View File

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

View File

@@ -1,4 +1,4 @@
/** /**
* Data Explorer version {major.minor.patch} * Data Explorer version {major.minor.patch}
*/ */
export const DataExplorer: string = "1.0.1"; export const DataExplorer: string = "1.0.1";

View File

@@ -1,438 +1,446 @@
import { import {
QueryMetrics, QueryMetrics,
Resource, Resource,
StoredProcedureDefinition, StoredProcedureDefinition,
TriggerDefinition, TriggerDefinition,
UserDefinedFunctionDefinition UserDefinedFunctionDefinition,
} from "@azure/cosmos"; } from "@azure/cosmos";
import Q from "q"; import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent"; import Explorer from "../Explorer/Explorer";
import Explorer from "../Explorer/Explorer"; import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent"; import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient";
import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient"; import ConflictId from "../Explorer/Tree/ConflictId";
import ConflictId from "../Explorer/Tree/ConflictId"; import DocumentId from "../Explorer/Tree/DocumentId";
import DocumentId from "../Explorer/Tree/DocumentId"; import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import StoredProcedure from "../Explorer/Tree/StoredProcedure"; import Trigger from "../Explorer/Tree/Trigger";
import Trigger from "../Explorer/Tree/Trigger"; import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction"; import { SelfServeType } from "../SelfServe/SelfServeUtils";
import { UploadDetails } from "../workers/upload/definitions"; import { UploadDetails } from "../workers/upload/definitions";
import * as DataModels from "./DataModels"; import * as DataModels from "./DataModels";
import { SubscriptionType } from "./SubscriptionType"; import { SubscriptionType } from "./SubscriptionType";
export interface TokenProvider { export interface TokenProvider {
getAuthHeader(): Promise<Headers>; getAuthHeader(): Promise<Headers>;
} }
export interface QueryResultsMetadata { export interface QueryResultsMetadata {
hasMoreResults: boolean; hasMoreResults: boolean;
firstItemIndex: number; firstItemIndex: number;
lastItemIndex: number; lastItemIndex: number;
itemCount: number; itemCount: number;
} }
export interface QueryResults extends QueryResultsMetadata { export interface QueryResults extends QueryResultsMetadata {
documents: any[]; documents: any[];
activityId: string; activityId: string;
requestCharge: number; requestCharge: number;
roundTrips?: number; roundTrips?: number;
headers?: any; headers?: any;
queryMetrics?: QueryMetrics; queryMetrics?: QueryMetrics;
} }
export interface Button { export interface Button {
visible: ko.Computed<boolean>; visible: ko.Computed<boolean>;
enabled: ko.Computed<boolean>; enabled: ko.Computed<boolean>;
isSelected?: ko.Computed<boolean>; isSelected?: ko.Computed<boolean>;
} }
export interface NotificationConsole { export interface NotificationConsole {
filteredConsoleData: ko.ObservableArray<ConsoleData>; filteredConsoleData: ko.ObservableArray<ConsoleData>;
isConsoleExpanded: ko.Observable<boolean>; isConsoleExpanded: ko.Observable<boolean>;
expandConsole(source: any, evt: MouseEvent): void; expandConsole(source: any, evt: MouseEvent): void;
collapseConsole(source: any, evt: MouseEvent): void; collapseConsole(source: any, evt: MouseEvent): void;
} }
export interface WaitsForTemplate { export interface WaitsForTemplate {
isTemplateReady: ko.Observable<boolean>; isTemplateReady: ko.Observable<boolean>;
} }
export interface TreeNode { export interface TreeNode {
nodeKind: string; nodeKind: string;
rid: string; rid: string;
id: ko.Observable<string>; id: ko.Observable<string>;
database?: Database; database?: Database;
collection?: Collection; collection?: Collection;
onNewQueryClick?(source: any, event: MouseEvent): void; onNewQueryClick?(source: any, event: MouseEvent): void;
onNewStoredProcedureClick?(source: Collection, event: MouseEvent): void; onNewStoredProcedureClick?(source: Collection, event: MouseEvent): void;
onNewUserDefinedFunctionClick?(source: Collection, event: MouseEvent): void; onNewUserDefinedFunctionClick?(source: Collection, event: MouseEvent): void;
onNewTriggerClick?(source: Collection, event: MouseEvent): void; onNewTriggerClick?(source: Collection, event: MouseEvent): void;
} }
export interface Database extends TreeNode { export interface Database extends TreeNode {
container: Explorer; container: Explorer;
self: string; self: string;
id: ko.Observable<string>; id: ko.Observable<string>;
collections: ko.ObservableArray<Collection>; collections: ko.ObservableArray<Collection>;
offer: ko.Observable<DataModels.Offer>; offer: ko.Observable<DataModels.Offer>;
isDatabaseExpanded: ko.Observable<boolean>; isDatabaseExpanded: ko.Observable<boolean>;
isDatabaseShared: ko.Computed<boolean>; isDatabaseShared: ko.Computed<boolean>;
selectedSubnodeKind: ko.Observable<CollectionTabKind>; selectedSubnodeKind: ko.Observable<CollectionTabKind>;
selectDatabase(): void; selectDatabase(): void;
expandDatabase(): Promise<void>; expandDatabase(): Promise<void>;
collapseDatabase(): void; collapseDatabase(): void;
loadCollections(): Promise<void>; loadCollections(): Promise<void>;
findCollectionWithId(collectionId: string): Collection; findCollectionWithId(collectionId: string): Collection;
openAddCollection(database: Database, event: MouseEvent): void; openAddCollection(database: Database, event: MouseEvent): void;
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void; onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
onSettingsClick: () => void; onSettingsClick: () => void;
loadOffer(): Promise<void>; loadOffer(): Promise<void>;
} getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
}
export interface CollectionBase extends TreeNode {
container: Explorer; export interface CollectionBase extends TreeNode {
databaseId: string; container: Explorer;
self: string; databaseId: string;
rawDataModel: DataModels.Collection; self: string;
partitionKey: DataModels.PartitionKey; rawDataModel: DataModels.Collection;
partitionKeyProperty: string; partitionKey: DataModels.PartitionKey;
partitionKeyPropertyHeader: string; partitionKeyProperty: string;
id: ko.Observable<string>; partitionKeyPropertyHeader: string;
selectedSubnodeKind: ko.Observable<CollectionTabKind>; id: ko.Observable<string>;
children: ko.ObservableArray<TreeNode>; selectedSubnodeKind: ko.Observable<CollectionTabKind>;
isCollectionExpanded: ko.Observable<boolean>; children: ko.ObservableArray<TreeNode>;
isCollectionExpanded: ko.Observable<boolean>;
onDocumentDBDocumentsClick(): void;
onNewQueryClick(source: any, event: MouseEvent, queryText?: string): void; onDocumentDBDocumentsClick(): void;
expandCollection(): Q.Promise<any>; onNewQueryClick(source: any, event?: MouseEvent, queryText?: string): void;
collapseCollection(): void; expandCollection(): void;
getDatabase(): Database; collapseCollection(): void;
} getDatabase(): Database;
}
export interface Collection extends CollectionBase {
defaultTtl: ko.Observable<number>; export interface Collection extends CollectionBase {
analyticalStorageTtl: ko.Observable<number>; defaultTtl: ko.Observable<number>;
schema?: DataModels.ISchema; analyticalStorageTtl: ko.Observable<number>;
requestSchema?: () => void; schema?: DataModels.ISchema;
indexingPolicy: ko.Observable<DataModels.IndexingPolicy>; requestSchema?: () => void;
uniqueKeyPolicy: DataModels.UniqueKeyPolicy; indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
usageSizeInKB: ko.Observable<number>; uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
offer: ko.Observable<DataModels.Offer>; usageSizeInKB: ko.Observable<number>;
conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>; offer: ko.Observable<DataModels.Offer>;
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>; conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
geospatialConfig: ko.Observable<DataModels.GeospatialConfig>; changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
documentIds: ko.ObservableArray<DocumentId>; geospatialConfig: ko.Observable<DataModels.GeospatialConfig>;
documentIds: ko.ObservableArray<DocumentId>;
cassandraKeys: CassandraTableKeys;
cassandraSchema: CassandraTableKey[]; cassandraKeys: CassandraTableKeys;
cassandraSchema: CassandraTableKey[];
onConflictsClick(): void;
onTableEntitiesClick(): void; onConflictsClick(): void;
onGraphDocumentsClick(): void; onTableEntitiesClick(): void;
onMongoDBDocumentsClick(): void; onGraphDocumentsClick(): void;
openTab(): void; onMongoDBDocumentsClick(): void;
openTab(): void;
onSettingsClick: () => Promise<void>;
onDeleteCollectionContextMenuClick(source: Collection, event: MouseEvent): void; onSettingsClick: () => Promise<void>;
onNewGraphClick(): void; onNewGraphClick(): void;
onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string): void; onNewMongoQueryClick(source: any, event?: MouseEvent, queryText?: string): void;
onNewMongoShellClick(): void; onNewMongoShellClick(): void;
onNewStoredProcedureClick(source: Collection, event: MouseEvent): void; onNewStoredProcedureClick(source: Collection, event?: MouseEvent): void;
onNewUserDefinedFunctionClick(source: Collection, event: MouseEvent): void; onNewUserDefinedFunctionClick(source: Collection, event?: MouseEvent): void;
onNewTriggerClick(source: Collection, event: MouseEvent): void; onNewTriggerClick(source: Collection, event?: MouseEvent): void;
storedProcedures: ko.Computed<StoredProcedure[]>; storedProcedures: ko.Computed<StoredProcedure[]>;
userDefinedFunctions: ko.Computed<UserDefinedFunction[]>; userDefinedFunctions: ko.Computed<UserDefinedFunction[]>;
triggers: ko.Computed<Trigger[]>; triggers: ko.Computed<Trigger[]>;
isStoredProceduresExpanded: ko.Observable<boolean>; isStoredProceduresExpanded: ko.Observable<boolean>;
isTriggersExpanded: ko.Observable<boolean>; isTriggersExpanded: ko.Observable<boolean>;
isUserDefinedFunctionsExpanded: ko.Observable<boolean>; isUserDefinedFunctionsExpanded: ko.Observable<boolean>;
expandStoredProcedures(): void; expandStoredProcedures(): void;
expandUserDefinedFunctions(): void; expandUserDefinedFunctions(): void;
expandTriggers(): void; expandTriggers(): void;
collapseStoredProcedures(): void; collapseStoredProcedures(): void;
collapseUserDefinedFunctions(): void; collapseUserDefinedFunctions(): void;
collapseTriggers(): void; collapseTriggers(): void;
loadUserDefinedFunctions(): Promise<any>; loadUserDefinedFunctions(): Promise<any>;
loadStoredProcedures(): Promise<any>; loadStoredProcedures(): Promise<any>;
loadTriggers(): Promise<any>; loadTriggers(): Promise<any>;
loadOffer(): Promise<void>; loadOffer(): Promise<void>;
createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure; createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure;
createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction; createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction;
createTriggerNode(data: TriggerDefinition & Resource): Trigger; createTriggerNode(data: TriggerDefinition & Resource): Trigger;
findStoredProcedureWithId(sprocRid: string): StoredProcedure; findStoredProcedureWithId(sprocRid: string): StoredProcedure;
findTriggerWithId(triggerRid: string): Trigger; findTriggerWithId(triggerRid: string): Trigger;
findUserDefinedFunctionWithId(udfRid: string): UserDefinedFunction; findUserDefinedFunctionWithId(udfRid: string): UserDefinedFunction;
onDragOver(source: Collection, event: { originalEvent: DragEvent }): void; onDragOver(source: Collection, event: { originalEvent: DragEvent }): void;
onDrop(source: Collection, event: { originalEvent: DragEvent }): void; onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
uploadFiles(fileList: FileList): Q.Promise<UploadDetails>; uploadFiles(fileList: FileList): Promise<UploadDetails>;
getLabel(): string; getLabel(): string;
} getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
}
/**
* Options used to initialize pane /**
*/ * Options used to initialize pane
export interface PaneOptions { */
id: string; export interface PaneOptions {
visible: ko.Observable<boolean>; id: string;
container?: Explorer; visible: ko.Observable<boolean>;
} container?: Explorer;
}
/**
* Graph configuration /**
*/ * Graph configuration
export enum NeighborType { */
SOURCES_ONLY, export enum NeighborType {
TARGETS_ONLY, SOURCES_ONLY,
BOTH TARGETS_ONLY,
} BOTH,
}
/**
* Set of observable related to graph configuration by user /**
*/ * Set of observable related to graph configuration by user
export interface GraphConfigUiData { */
showNeighborType: ko.Observable<NeighborType>; export interface GraphConfigUiData {
nodeProperties: ko.ObservableArray<string>; showNeighborType: ko.Observable<NeighborType>;
nodePropertiesWithNone: ko.ObservableArray<string>; nodeProperties: ko.ObservableArray<string>;
nodeCaptionChoice: ko.Observable<string>; nodePropertiesWithNone: ko.ObservableArray<string>;
nodeColorKeyChoice: ko.Observable<string>; nodeCaptionChoice: ko.Observable<string>;
nodeIconChoice: ko.Observable<string>; nodeColorKeyChoice: ko.Observable<string>;
nodeIconSet: ko.Observable<string>; nodeIconChoice: ko.Observable<string>;
} nodeIconSet: ko.Observable<string>;
}
/**
* User input for creating new vertex /**
*/ * User input for creating new vertex
export interface NewVertexData { */
label: string; export interface NewVertexData {
properties: InputProperty[]; label: string;
} properties: InputProperty[];
}
export type GremlinPropertyValueType = string | boolean | number | null | undefined;
export type InputPropertyValueTypeString = "string" | "number" | "boolean" | "null"; export type GremlinPropertyValueType = string | boolean | number | null | undefined;
export interface InputPropertyValue { export type InputPropertyValueTypeString = "string" | "number" | "boolean" | "null";
value: GremlinPropertyValueType; export interface InputPropertyValue {
type: InputPropertyValueTypeString; value: GremlinPropertyValueType;
} type: InputPropertyValueTypeString;
/** }
* Property input by user /**
*/ * Property input by user
export interface InputProperty { */
key: string; export interface InputProperty {
values: InputPropertyValue[]; key: string;
} values: InputPropertyValue[];
}
export interface Editable<T> extends ko.Observable<T> {
setBaseline(baseline: T): void; export interface Editable<T> extends ko.Observable<T> {
setBaseline(baseline: T): void;
editableIsDirty: ko.Computed<boolean>;
editableIsValid: ko.Observable<boolean>; editableIsDirty: ko.Computed<boolean>;
getEditableCurrentValue?: ko.Computed<T>; editableIsValid: ko.Observable<boolean>;
getEditableOriginalValue?: ko.Computed<T>; getEditableCurrentValue?: ko.Computed<T>;
edits?: ko.ObservableArray<T>; getEditableOriginalValue?: ko.Computed<T>;
validations?: ko.ObservableArray<(value: T) => boolean>; edits?: ko.ObservableArray<T>;
} validations?: ko.ObservableArray<(value: T) => boolean>;
}
export interface QueryError {
message: string; export interface QueryError {
start: string; message: string;
end: string; start: string;
code: string; end: string;
severity: string; code: string;
} severity: string;
}
export interface DocumentRequestContainer {
self: string; export interface DocumentRequestContainer {
rid?: string; self: string;
resourceName?: string; rid?: string;
} resourceName?: string;
}
export interface DocumentClientOption {
endpoint?: string; export interface DocumentClientOption {
masterKey?: string; endpoint?: string;
requestTimeoutMs?: number; masterKey?: string;
} requestTimeoutMs?: number;
}
// Tab options
export interface TabOptions { // Tab options
tabKind: CollectionTabKind; export interface TabOptions {
title: string; tabKind: CollectionTabKind;
tabPath: string; title: string;
isActive: ko.Observable<boolean>; tabPath: string;
hashLocation: string; isActive: ko.Observable<boolean>;
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]) => void; hashLocation: string;
isTabsContentExpanded?: ko.Observable<boolean>; onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]) => void;
onLoadStartKey?: number; 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 // TODO Remove the flag and use a context to handle this
collection?: CollectionBase; // TODO: 145357 Remove dependency on collection/database and add abstraction
database?: Database; collection?: CollectionBase;
rid?: string; database?: Database;
node?: TreeNode; rid?: string;
theme?: string; node?: TreeNode;
} theme?: string;
}
export interface DocumentsTabOptions extends TabOptions {
partitionKey: DataModels.PartitionKey; export interface DocumentsTabOptions extends TabOptions {
documentIds: ko.ObservableArray<DocumentId>; partitionKey: DataModels.PartitionKey;
container?: Explorer; documentIds: ko.ObservableArray<DocumentId>;
isPreferredApiMongoDB?: boolean; container?: Explorer;
resourceTokenPartitionKey?: string; 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>;
export interface ConflictsTabOptions extends TabOptions { container?: Explorer;
partitionKey: DataModels.PartitionKey; }
conflictIds: ko.ObservableArray<ConflictId>;
container?: Explorer; export interface QueryTabOptions extends TabOptions {
} partitionKey?: DataModels.PartitionKey;
queryText?: string;
export interface QueryTabOptions extends TabOptions { resourceTokenPartitionKey?: string;
partitionKey?: DataModels.PartitionKey; }
queryText?: string;
resourceTokenPartitionKey?: string; export interface ScriptTabOption extends TabOptions {
} resource: any;
isNew: boolean;
export interface ScriptTabOption extends TabOptions { partitionKey?: DataModels.PartitionKey;
resource: any; }
isNew: boolean;
partitionKey?: DataModels.PartitionKey; export interface EditorPosition {
} line: number;
column: number;
export interface EditorPosition { }
line: number;
column: number; export enum DocumentExplorerState {
} noDocumentSelected,
newDocumentValid,
export enum DocumentExplorerState { newDocumentInvalid,
noDocumentSelected, exisitingDocumentNoEdits,
newDocumentValid, exisitingDocumentDirtyValid,
newDocumentInvalid, exisitingDocumentDirtyInvalid,
exisitingDocumentNoEdits, }
exisitingDocumentDirtyValid,
exisitingDocumentDirtyInvalid export enum IndexingPolicyEditorState {
} noCollectionSelected,
noEdits,
export enum IndexingPolicyEditorState { dirtyValid,
noCollectionSelected, dirtyInvalid,
noEdits, }
dirtyValid,
dirtyInvalid export enum ScriptEditorState {
} newInvalid,
newValid,
export enum ScriptEditorState { exisitingNoEdits,
newInvalid, exisitingDirtyValid,
newValid, exisitingDirtyInvalid,
exisitingNoEdits, }
exisitingDirtyValid,
exisitingDirtyInvalid export enum CollectionTabKind {
} Documents = 0,
Settings = 1,
export enum CollectionTabKind { StoredProcedures = 2,
Documents = 0, UserDefinedFunctions = 3,
Settings = 1, Triggers = 4,
StoredProcedures = 2, Query = 5,
UserDefinedFunctions = 3, Graph = 6,
Triggers = 4, QueryTables = 9,
Query = 5, MongoShell = 10,
Graph = 6, DatabaseSettings = 11,
QueryTables = 9, Conflicts = 12,
MongoShell = 10, Notebook = 13 /* Deprecated */,
DatabaseSettings = 11, Terminal = 14,
Conflicts = 12, NotebookV2 = 15,
Notebook = 13 /* Deprecated */, SparkMasterTab = 16 /* Deprecated */,
Terminal = 14, Gallery = 17,
NotebookV2 = 15, NotebookViewer = 18,
SparkMasterTab = 16, Schema = 19,
Gallery = 17, CollectionSettingsV2 = 20,
NotebookViewer = 18, DatabaseSettingsV2 = 21,
Schema = 19, }
SettingsV2 = 20
} export enum TerminalKind {
Default = 0,
export enum TerminalKind { Mongo = 1,
Default = 0, Cassandra = 2,
Mongo = 1, }
Cassandra = 2
} export interface DataExplorerInputsFrame {
databaseAccount: any;
export interface DataExplorerInputsFrame { subscriptionId?: string;
databaseAccount: any; resourceGroup?: string;
subscriptionId: string; masterKey?: string;
resourceGroup: string; hasWriteAccess?: boolean;
masterKey: string; authorizationToken?: string;
hasWriteAccess: boolean; features: { [key: string]: string };
authorizationToken: string; csmEndpoint?: string;
features: any; dnsSuffix?: string;
csmEndpoint: string; serverId?: string;
dnsSuffix: string; extensionEndpoint?: string;
serverId: string; subscriptionType?: SubscriptionType;
extensionEndpoint: string; quotaId?: string;
subscriptionType: SubscriptionType; addCollectionDefaultFlight?: string;
quotaId: string; isTryCosmosDBSubscription?: boolean;
addCollectionDefaultFlight: string; loadDatabaseAccountTimestamp?: number;
isTryCosmosDBSubscription: boolean; sharedThroughputMinimum?: number;
loadDatabaseAccountTimestamp?: number; sharedThroughputMaximum?: number;
sharedThroughputMinimum?: number; sharedThroughputDefault?: number;
sharedThroughputMaximum?: number; dataExplorerVersion?: string;
sharedThroughputDefault?: number; isAuthWithresourceToken?: boolean;
dataExplorerVersion?: string; defaultCollectionThroughput?: CollectionCreationDefaults;
isAuthWithresourceToken?: boolean; flights?: readonly string[];
defaultCollectionThroughput?: CollectionCreationDefaults; }
flights?: readonly string[];
} export interface SelfServeFrameInputs {
selfServeType: SelfServeType;
export interface CollectionCreationDefaults { databaseAccount: any;
storage: string; subscriptionId: string;
throughput: ThroughputDefaults; resourceGroup: string;
} authorizationToken: string;
csmEndpoint: string;
export interface ThroughputDefaults { flights?: readonly string[];
fixed: number; }
unlimited:
| number export interface CollectionCreationDefaults {
| { storage: string;
collectionThreshold: number; throughput: ThroughputDefaults;
lessThanOrEqualToThreshold: number; }
greatThanThreshold: number;
}; export interface ThroughputDefaults {
unlimitedmax: number; fixed: number;
unlimitedmin: number; unlimited:
shared: number; | number
} | {
collectionThreshold: number;
export class MonacoEditorSettings { lessThanOrEqualToThreshold: number;
public readonly language: string; greatThanThreshold: number;
public readonly readOnly: boolean; };
unlimitedmax: number;
constructor(supportedLanguage: string, isReadOnly: boolean) { unlimitedmin: number;
this.language = supportedLanguage; shared: number;
this.readOnly = isReadOnly; }
}
} export class MonacoEditorSettings {
public readonly language: string;
export interface AuthorizationTokenHeaderMetadata { public readonly readOnly: boolean;
header: string;
token: string; constructor(supportedLanguage: string, isReadOnly: boolean) {
} this.language = supportedLanguage;
this.readOnly = isReadOnly;
export interface DropdownOption<T> { }
text: string; }
value: T;
disable?: boolean; export interface AuthorizationTokenHeaderMetadata {
} header: string;
token: string;
}
export interface DropdownOption<T> {
text: string;
value: T;
disable?: boolean;
}

View File

@@ -6,19 +6,19 @@ describe("The Heatmap Control", () => {
const dataPoints = { const dataPoints = {
"1": { "1": {
"2019-06-19T00:59:10Z": { "2019-06-19T00:59:10Z": {
"Normalized Throughput": 0.35 "Normalized Throughput": 0.35,
}, },
"2019-06-19T00:48:10Z": { "2019-06-19T00:48:10Z": {
"Normalized Throughput": 0.25 "Normalized Throughput": 0.25,
} },
} },
}; };
const chartCaptions = { const chartCaptions = {
chartTitle: "chart title", chartTitle: "chart title",
yAxisTitle: "YAxisTitle", yAxisTitle: "YAxisTitle",
tooltipText: "Tooltip text", tooltipText: "Tooltip text",
timeWindow: 123456789 timeWindow: 123456789,
}; };
let heatmap: Heatmap; let heatmap: Heatmap;
@@ -75,12 +75,12 @@ describe("The Heatmap Control", () => {
if (dayjs().utcOffset()) { if (dayjs().utcOffset()) {
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([ expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
"2019-06-19T00:48:10Z", "2019-06-19T00:48:10Z",
"2019-06-19T00:59:10Z" "2019-06-19T00:59:10Z",
]); ]);
} else { } else {
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([ expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
"2019-06-19T00:48:10Z", "2019-06-19T00:48:10Z",
"2019-06-19T00:59:10Z" "2019-06-19T00:59:10Z",
]); ]);
} }
}); });
@@ -106,9 +106,9 @@ describe("iframe rendering when there is no data", () => {
data: { data: {
chartData: {}, chartData: {},
chartSettings: {}, chartSettings: {},
theme: 4 theme: 4,
} },
} },
}; };
const divElement: string = `<div id="${Heatmap.elementId}"></div>`; const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
@@ -126,9 +126,9 @@ describe("iframe rendering when there is no data", () => {
data: { data: {
chartData: {}, chartData: {},
chartSettings: {}, chartSettings: {},
theme: 2 theme: 2,
} },
} },
}; };
const divElement: string = `<div id="${Heatmap.elementId}"></div>`; const divElement: string = `<div id="${Heatmap.elementId}"></div>`;

View File

@@ -9,7 +9,7 @@ import {
HeatmapData, HeatmapData,
LayoutSettings, LayoutSettings,
PartitionTimeStampToData, PartitionTimeStampToData,
PortalTheme PortalTheme,
} from "./HeatmapDatatypes"; } from "./HeatmapDatatypes";
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation"; import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
import { sendCachedDataMessage, sendMessage } from "../../Common/MessageHandler"; import { sendCachedDataMessage, sendMessage } from "../../Common/MessageHandler";
@@ -43,7 +43,7 @@ export class Heatmap {
return { return {
family: StyleConstants.DataExplorerFont, family: StyleConstants.DataExplorerFont,
size, size,
color color,
}; };
} }
@@ -73,7 +73,7 @@ export class Heatmap {
return 0; return 0;
} }
} }
}) }),
}; };
// go thru all rows and create 2d matrix for heatmap... // go thru all rows and create 2d matrix for heatmap...
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
@@ -115,7 +115,7 @@ export class Heatmap {
[0.7, "#E46612"], [0.7, "#E46612"],
[0.8, "#E64914"], [0.8, "#E64914"],
[0.9, "#B80016"], [0.9, "#B80016"],
[1.0, "#B80016"] [1.0, "#B80016"],
], ],
name: "", name: "",
hovertemplate: this._heatmapCaptions.tooltipText, hovertemplate: this._heatmapCaptions.tooltipText,
@@ -123,11 +123,11 @@ export class Heatmap {
thickness: 15, thickness: 15,
outlinewidth: 0, outlinewidth: 0,
tickcolor: StyleConstants.BaseDark, tickcolor: StyleConstants.BaseDark,
tickfont: this._getFontStyles(10, this._defaultFontColor) tickfont: this._getFontStyles(10, this._defaultFontColor),
}, },
y: this._chartData.yAxisPoints, y: this._chartData.yAxisPoints,
x: this._chartData.xAxisPoints x: this._chartData.xAxisPoints,
} },
]; ];
} }
@@ -138,7 +138,7 @@ export class Heatmap {
r: 10, r: 10,
b: 35, b: 35,
t: 30, t: 30,
pad: 0 pad: 0,
}, },
paper_bgcolor: "transparent", paper_bgcolor: "transparent",
plot_bgcolor: "transparent", plot_bgcolor: "transparent",
@@ -154,7 +154,7 @@ export class Heatmap {
autotick: true, autotick: true,
fixedrange: true, fixedrange: true,
ticks: "", ticks: "",
showticklabels: false showticklabels: false,
}, },
xaxis: { xaxis: {
fixedrange: true, fixedrange: true,
@@ -167,13 +167,13 @@ export class Heatmap {
autotick: true, autotick: true,
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e", tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
showticklabels: true, showticklabels: true,
tickfont: this._getFontStyles(10) tickfont: this._getFontStyles(10),
}, },
title: { title: {
text: this._heatmapCaptions.chartTitle, text: this._heatmapCaptions.chartTitle,
x: 0.01, x: 0.01,
font: this._getFontStyles(13, this._defaultFontColor) font: this._getFontStyles(13, this._defaultFontColor),
} },
}; };
} }
@@ -181,7 +181,7 @@ export class Heatmap {
return { return {
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings /* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
responsive: true,*/ responsive: true,*/
displayModeBar: false displayModeBar: false,
}; };
} }

View File

@@ -8,7 +8,7 @@ export enum PortalTheme {
blue = 1, blue = 1,
azure, azure,
light, light,
dark dark,
} }
export interface HeatmapData { export interface HeatmapData {

View File

@@ -4,5 +4,5 @@ export enum DefaultAccountExperienceType {
MongoDB = "MongoDB", MongoDB = "MongoDB",
Table = "Table", Table = "Table",
Cassandra = "Cassandra", Cassandra = "Cassandra",
ApiForMongoDB = "Azure Cosmos DB for MongoDB API" ApiForMongoDB = "Azure Cosmos DB for MongoDB API",
} }

File diff suppressed because it is too large Load Diff

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