Compare commits

..

200 Commits

Author SHA1 Message Date
Tanuj Mittal
9aeb349d74 Sandbox HTML and JavaScript outputs in iFrame 2021-04-02 17:27:17 -07:00
Hardikkumar Nai
d8fe4ed77f Fix lint file system util (#481)
Co-authored-by: Steve Faulkner <471400+southpolesteve@users.noreply.github.com>
2021-03-31 22:28:16 -05:00
Sunil Kumar Yadav
75ea475217 [WIP]Cleanup/removed knockout database confirmation panel (#546)
* complete delete database component ui in react

* fixed functional issue and added feedback input

* test cases for deleteDatabaseConfirmationPanel

* Removed Q and fixed PR change request

* removed knockout database confirmation panel and references

* delete deleteDatabaseConfirmationPane.html

* remove test

Co-authored-by: Steve Faulkner <471400+southpolesteve@users.noreply.github.com>
2021-03-31 19:58:38 -05:00
Steve Faulkner
dc20aa96d2 Remove accidentally checked in screenshots 2021-03-31 19:49:45 -05:00
Hardikkumar Nai
5307f6bb5b Migrate Notebook Upload File to React (#581)
Co-authored-by: Steve Faulkner <471400+southpolesteve@users.noreply.github.com>
2021-03-31 19:25:45 -05:00
Sunil Kumar Yadav
c68e84a4b9 Migration/delete database confirmation in react (#542)
Co-authored-by: Steve Faulkner <471400+southpolesteve@users.noreply.github.com>
Co-authored-by: Steve Faulkner <stfaul@microsoft.com>
2021-03-31 17:44:07 -05:00
Hardikkumar Nai
6a69d3a77b Move upload items panel to react (#558)
Co-authored-by: Steve Faulkner <471400+southpolesteve@users.noreply.github.com>
2021-03-31 15:43:05 -05:00
Hardikkumar Nai
458cca8e01 Move setting pane to react (#543)
Co-authored-by: Steve Faulkner <471400+southpolesteve@users.noreply.github.com>
2021-03-31 15:22:52 -05:00
Sunil Kumar Yadav
69ac4e218d Migration/execute sproc params pane in react (#576)
Co-authored-by: Steve Faulkner <471400+southpolesteve@users.noreply.github.com>
2021-03-31 14:43:55 -05:00
Steve Faulkner
b1a904a98f Remove TelemetrtData Type Restriction (#595) 2021-03-30 16:35:45 -05:00
Jordi Bunster
813dbfee5b Defensively set feature flags from window.parent (#594)
This doesn't really fix the fact that portal feature flags are not
being set, but it does re-enable feature flags in hosted mode.
2021-03-30 14:01:30 -07:00
Jordi Bunster
a9ed187213 Bugfix: this was not really used at all (#596) 2021-03-30 13:59:04 -07:00
Srinath Narayanan
6cdac3c53b Added support for self serve telemetry + Localization fixes (#580)
* initial telemetry commit

* Added localization changes

* moved telemetrymessage types to selfservetypes

* fixed compile errors

* fixed failing test

* changed translation file format

* Addressed PR comments

* modified test
2021-03-30 10:11:43 -07:00
Steve Faulkner
63e13cdabe Remove Explorer.nonSystemDatabases (#538)
* Remove Explorer.nonSystemDatabases

* Fix tests
2021-03-30 09:31:21 -05:00
Steve Faulkner
c9eb61351a Remove unused inline-css package (#593) 2021-03-29 21:56:25 -05:00
Hardikkumar Nai
343e82c102 Fix lint query utils (#487)
* Fix Lint errors in QueryUtils

* Format

* Simplify diff

Co-authored-by: Your Name <you@example.com>
Co-authored-by: Steve Faulkner <southpolesteve@gmail.com>
Co-authored-by: Steve Faulkner <471400+southpolesteve@users.noreply.github.com>
Co-authored-by: Jordi Bunster <jbunster@microsoft.com>
2021-03-29 21:26:41 -05:00
dependabot[bot]
fad3a08fdf Bump node-fetch from 2.6.0 to 2.6.1 in /utils/deployment-status (#587)
Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/bitinn/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-29 21:22:31 -05:00
vaidankarswapnil
72c2d8592b Fix lint for Database Account Utils (#583) 2021-03-29 17:13:35 -05:00
Jordi Bunster
6b73560122 Create dependabot.yml 2021-03-29 15:09:18 -07:00
Steve Faulkner
9108c01e62 Drop IE11 Support (#476) 2021-03-29 13:31:52 -05:00
Jordi Bunster
4c2f22c2b1 When switching between tabs, editors remain open (#586)
I misunderstood the purpose of a ko.foreach() loop in my changes
back in #534. This re-introduces the loop.
2021-03-29 12:38:41 -05:00
Jordi Bunster
f82b0b442e Update README.md 2021-03-27 11:18:59 -07:00
Armando Trejo Oliver
a66f042c10 Fix table api query projections (#584)
When building queries with projections, the resulting query does not include the "id" property as part of the projection. The "id" property is used by the results grid to display as the RowKey so the result is queries wih projections do not show the RowKey.

This change fixes this by including "id" as part of the projections.
2021-03-26 12:18:04 -07:00
Jordi Bunster
8cc04bab87 MongoDocumentsTab.html was not used here (#582) 2021-03-25 15:24:43 -07:00
victor-meng
ca7cd139ba Use window instead of window.parent in extractFeatures function (#577) 2021-03-23 17:21:41 -07:00
Jordi Bunster
f33ec09040 Remove Explorer.features (#563)
In addition this makes the URL-passed feature flags type safe
2021-03-22 19:04:06 +00:00
Jordi Bunster
b1aeab6b84 Knockout tab changes
This reduces the work involved in rendering knockout tabs, which will make moving them to React under a feature flag a bit easier.
2021-03-22 10:13:44 -07:00
Steve Faulkner
8bf976026f Pass masterkey in connection string mode (#572) 2021-03-19 18:37:13 -05:00
Steve Faulkner
c7ba5de90d Fix Test Explorer AAD Authority (#571) 2021-03-19 14:45:58 -05:00
Steve Faulkner
ddf59d6b24 Fix SplashScreen/Tabs Visible bug (#570) 2021-03-19 12:53:34 -05:00
Tanuj Mittal
316fe7e8bb Reset cell status after execution is canceled (#564) 2021-03-19 22:55:54 +05:30
Steve Faulkner
ee8d2070bf Remove Explorer.isRefreshing (#539) 2021-03-19 12:13:52 -05:00
Armando Trejo Oliver
e97a1643fb Make ready message backwards compatible (#569) 2021-03-19 08:38:57 -07:00
Srinath Narayanan
049e3c36d8 Dedicated gateway portal changes (#568)
* 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

* 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

* compile errors fixed

* New changes

* added operationstatus link

* merged sqlxEdits

* undid sqlx changes

* added completed notification

* passed retryInterval in notif options

* 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

* Second cut of the Portal

* OnChange for Number of instances

* added keys for texts

* fixed lint errors

* Added support for undefined dynamic description

* fixed failing test

* disable save/discard buttons

* fixed sqlx errors

* Dedicated Gateway changes to add the keys blade

* Change connectionStringText

* Change connectionStringText

* Text changes

* Added UI improvements

* Code review feedback

* undid package lock changes

* updated package.json

* undid package reverts

Co-authored-by: Balaji Sridharan <fnbalaji@microsoft.com>
Co-authored-by: fnbalaji <75445927+fnbalaji@users.noreply.github.com>
2021-03-19 00:54:13 -07:00
Srinath Narayanan
159c297e8d removed change to package.json (#567) 2021-03-19 00:05:15 -07:00
Srinath Narayanan
4e09e4c7fa Revert "Dedicated Gateway Portal Changes (#540)" (#566)
This reverts commit 909a9fa522.
2021-03-19 00:01:15 -07:00
Armando Trejo Oliver
19880203ec Fix ready message (format) (#565)
* Fix ready message sent to parent frame

* format
2021-03-18 21:40:31 -07:00
artrejo
f929a638d6 Fix ready message sent to parent frame 2021-03-18 20:41:43 -07:00
Steve Faulkner
3cccbdfe81 Fix Lint errors in URLUtility (#462) 2021-03-18 22:19:35 -05:00
victor-meng
65c859c835 Move add collection pane to React (#486)
* Move add collection pane to React

* Add feature flag

* fix unit tests

* FIx merge conflicts and address comments

* Resolve merge conflicts

* Address comments

* Fix e2e test failure

* Update test snapshots

* Update test snapshots
2021-03-18 20:06:13 -05:00
Steve Faulkner
c6090e2663 Update Puppeteer (#562) 2021-03-18 17:53:15 -05:00
Steve Faulkner
c43e24061c [Tables] Check for undefined before compare (#561) 2021-03-18 16:16:10 -05:00
fnbalaji
909a9fa522 Dedicated Gateway Portal Changes (#540)
* 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

* 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

* compile errors fixed

* New changes

* added operationstatus link

* merged sqlxEdits

* undid sqlx changes

* added completed notification

* passed retryInterval in notif options

* 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

* Second cut of the Portal

* OnChange for Number of instances

* added keys for texts

* fixed lint errors

* Added support for undefined dynamic description

* fixed failing test

* disable save/discard buttons

* fixed sqlx errors

* Dedicated Gateway changes to add the keys blade

* Change connectionStringText

* Change connectionStringText

* Text changes

* Added UI improvements

* Code review feedback

* undid package lock changes

Co-authored-by: Srinath Narayanan <srnara@microsoft.com>
2021-03-18 14:00:28 -07:00
Srinath Narayanan
be4e490a64 Added SelfServe UI updates (#559)
* Added SelfServe UI modifications

* fixed lint error

* addressed PR comments
2021-03-18 13:40:48 -07:00
Steve Faulkner
9db0975f7f Remove Explorer.isTryCosmos (#556) 2021-03-17 16:02:20 -05:00
Steve Faulkner
a2e3be9680 Remove Explorer.serverId (#555) 2021-03-17 15:24:21 -05:00
Srinath Narayanan
eab9b0ce9c mongo index policy editor bug fix (#550) 2021-03-17 11:52:16 -07:00
Steve Faulkner
d9d88c1517 Remove isAuthWithResourceToken from Explorer (#553)
* Remove isAuthWithResourceToken from Explorer

* Update test

* Remove ifs
2021-03-17 10:41:15 -05:00
Ken Dale
e10ab08d5c Small README.md update (#554) 2021-03-17 10:14:32 -05:00
Tanuj Mittal
3eda8029ba Change default Sort order for Gallery to MostRecent & fix gallery card height for empty tags (#545) 2021-03-17 01:40:38 +00:00
Steve Faulkner
6582d3be37 Fix Mongo AAD bug where collections are not load (#552) 2021-03-16 18:04:26 -05:00
Jordi Bunster
3530633fa2 Test coverage: include all source files (#551)
In addition this changes the thresholds to meet the existing level
of test coverage. This was motivated by work that imported a lot
of existing yet untested (and unexercised) code, which brought down
the total % without adding any more code.
2021-03-16 14:13:36 -07:00
Steve Faulkner
732d7ce8fa Fix upload worker error display (#547) 2021-03-15 22:47:49 -05: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
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
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
Steve Faulkner
cc63cdc1fd Remove dependency on canvas (#354) 2020-12-26 21:56:37 -06:00
Steve Faulkner
c3058ee5a9 Check for undefined query results (#350) 2020-12-18 19:55:32 -06:00
Steve Faulkner
b000631a0c Revert web.config changes (#349) 2020-12-18 19:26:10 -06:00
vchske
e8f4c8f93c Cost Estimate Changes (#342)
* Initial change of estimated cost to table format

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

* lint fixes

* Changed the names of some interfaces

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

* Changed the severity of the save warning

* Format fix

* Fixed test due to styling change

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

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

* Minor tweak to cost messaging

* Updated unit tests

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

* Remove enableCodeOfConduct feature flag

* Fix reporting abuse

* Add empty screen for Liked and Published tabs in Gallery

* fix build

* Remove unused code

* Fix standalone public gallery
2020-12-10 13:09:18 -08:00
Tanuj Mittal
e133df18dd Record baseUrl for OpenTerminal success/failure telemetry (#335)
This is useful to know which terminal is opening.
2020-12-10 19:54:21 +00:00
462 changed files with 68680 additions and 24102 deletions

View File

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

View File

@@ -11,16 +11,9 @@ src/Common/CosmosClient.test.ts
src/Common/CosmosClient.ts
src/Common/DataAccessUtilityBase.test.ts
src/Common/DataAccessUtilityBase.ts
src/Common/DeleteFeedback.ts
src/Common/DocumentClientUtilityBase.ts
src/Common/EditableUtility.ts
src/Common/EnvironmentUtility.ts
src/Common/HashMap.test.ts
src/Common/HashMap.ts
src/Common/HeadersUtility.test.ts
src/Common/HeadersUtility.ts
src/Common/IteratorUtilities.test.ts
src/Common/IteratorUtilities.ts
src/Common/Logger.test.ts
src/Common/MessageHandler.test.ts
src/Common/MessageHandler.ts
@@ -31,8 +24,6 @@ src/Common/ObjectCache.test.ts
src/Common/ObjectCache.ts
src/Common/QueriesClient.ts
src/Common/Splitter.ts
src/Common/ThemeUtility.ts
src/Common/UrlUtility.ts
src/Config.ts
src/Contracts/ActionContracts.ts
src/Contracts/DataModels.ts
@@ -43,7 +34,6 @@ src/Contracts/ViewModels.ts
src/Controls/Heatmap/Heatmap.test.ts
src/Controls/Heatmap/Heatmap.ts
src/Controls/Heatmap/HeatmapDatatypes.ts
src/Definitions/adal.d.ts
src/Definitions/datatables.d.ts
src/Definitions/gif.d.ts
src/Definitions/globals.d.ts
@@ -60,8 +50,6 @@ src/Explorer/ComponentRegisterer.test.ts
src/Explorer/ComponentRegisterer.ts
src/Explorer/ContextMenuButtonFactory.ts
src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts
src/Explorer/Controls/CommandButton/CommandButton.test.ts
src/Explorer/Controls/CommandButton/CommandButton.ts
src/Explorer/Controls/DiffEditor/DiffEditorComponent.ts
src/Explorer/Controls/DynamicList/DynamicList.test.ts
src/Explorer/Controls/DynamicList/DynamicListComponent.ts
@@ -89,7 +77,7 @@ src/Explorer/DataSamples/ContainerSampleGenerator.test.ts
src/Explorer/DataSamples/ContainerSampleGenerator.ts
src/Explorer/DataSamples/DataSamplesUtil.test.ts
src/Explorer/DataSamples/DataSamplesUtil.ts
src/Explorer/Explorer.ts
src/Explorer/Explorer.tsx
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.test.ts
src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts
src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.test.ts
@@ -97,8 +85,6 @@ src/Explorer/Graph/GraphExplorerComponent/D3ForceGraph.ts
src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts
src/Explorer/Graph/GraphExplorerComponent/GraphData.test.ts
src/Explorer/Graph/GraphExplorerComponent/GraphData.ts
src/Explorer/Graph/GraphExplorerComponent/GraphUtil.test.ts
src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts
src/Explorer/Graph/GraphExplorerComponent/GremlinClient.test.ts
src/Explorer/Graph/GraphExplorerComponent/GremlinClient.ts
src/Explorer/Graph/GraphExplorerComponent/GremlinSimpleClient.test.ts
@@ -111,8 +97,6 @@ src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.test.ts
src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.ts
src/Explorer/Menus/ContextMenu.ts
src/Explorer/MostRecentActivity/MostRecentActivity.ts
src/Explorer/Notebook/FileSystemUtil.ts
src/Explorer/Notebook/NTeractUtil.ts
src/Explorer/Notebook/NotebookClientV2.ts
src/Explorer/Notebook/NotebookComponent/NotebookContentProvider.ts
src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts
@@ -141,15 +125,12 @@ src/Explorer/Panes/DeleteCollectionConfirmationPane.test.ts
src/Explorer/Panes/DeleteCollectionConfirmationPane.ts
src/Explorer/Panes/DeleteDatabaseConfirmationPane.test.ts
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
src/Explorer/Panes/ExecuteSprocParamsPane.ts
src/Explorer/Panes/GraphStylingPane.ts
src/Explorer/Panes/LoadQueryPane.ts
src/Explorer/Panes/NewVertexPane.ts
src/Explorer/Panes/PaneComponents.ts
src/Explorer/Panes/RenewAdHocAccessPane.ts
src/Explorer/Panes/SaveQueryPane.ts
src/Explorer/Panes/SettingsPane.test.ts
src/Explorer/Panes/SettingsPane.ts
src/Explorer/Panes/SetupNotebooksPane.ts
src/Explorer/Panes/StringInputPane.ts
src/Explorer/Panes/SwitchDirectoryPane.ts
@@ -162,9 +143,7 @@ src/Explorer/Panes/Tables/TableEntityPane.ts
src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts
src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts
src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts
src/Explorer/Panes/UploadFilePane.ts
src/Explorer/Panes/UploadItemsPane.ts
src/Explorer/SplashScreen/SplashScreenComponentAdapter.test.ts
src/Explorer/SplashScreen/SplashScreen.test.ts
src/Explorer/Tables/Constants.ts
src/Explorer/Tables/DataTable/CacheBase.ts
src/Explorer/Tables/DataTable/DataTableBindingManager.ts
@@ -172,7 +151,6 @@ src/Explorer/Tables/DataTable/DataTableBuilder.ts
src/Explorer/Tables/DataTable/DataTableContextMenu.ts
src/Explorer/Tables/DataTable/DataTableOperationManager.ts
src/Explorer/Tables/DataTable/DataTableOperations.ts
src/Explorer/Tables/DataTable/DataTableUtilities.ts
src/Explorer/Tables/DataTable/DataTableViewModel.ts
src/Explorer/Tables/DataTable/TableCommands.ts
src/Explorer/Tables/DataTable/TableEntityCache.ts
@@ -181,8 +159,6 @@ src/Explorer/Tables/Entities.ts
src/Explorer/Tables/QueryBuilder/ClauseGroup.ts
src/Explorer/Tables/QueryBuilder/ClauseGroupViewModel.ts
src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts
src/Explorer/Tables/QueryBuilder/DateTimeUtilities.test.ts
src/Explorer/Tables/QueryBuilder/DateTimeUtilities.ts
src/Explorer/Tables/QueryBuilder/QueryBuilderViewModel.ts
src/Explorer/Tables/QueryBuilder/QueryClauseViewModel.ts
src/Explorer/Tables/QueryBuilder/QueryViewModel.ts
@@ -202,7 +178,6 @@ src/Explorer/Tabs/QueryTab.test.ts
src/Explorer/Tabs/QueryTab.ts
src/Explorer/Tabs/QueryTablesTab.ts
src/Explorer/Tabs/ScriptTabBase.ts
src/Explorer/Tabs/SparkMasterTab.ts
src/Explorer/Tabs/StoredProcedureTab.ts
src/Explorer/Tabs/TabComponents.ts
src/Explorer/Tabs/TabsBase.ts
@@ -243,9 +218,6 @@ src/Platform/Hosted/Authorization.ts
src/Platform/Hosted/DataAccessUtility.ts
src/Platform/Hosted/ExplorerFactory.ts
src/Platform/Hosted/Helpers/ConnectionStringParser.test.ts
src/Platform/Hosted/Helpers/ConnectionStringParser.ts
src/Platform/Hosted/HostedUtils.test.ts
src/Platform/Hosted/HostedUtils.ts
src/Platform/Hosted/Main.ts
src/Platform/Hosted/Maint.test.ts
src/Platform/Hosted/NotificationsClient.ts
@@ -269,8 +241,6 @@ src/Shared/ExplorerSettings.ts
src/Shared/PriceEstimateCalculator.ts
src/Shared/StorageUtility.test.ts
src/Shared/StorageUtility.ts
src/Shared/StringUtility.test.ts
src/Shared/StringUtility.ts
src/Shared/appInsights.ts
src/SparkClusterManager/ArcadiaResourceManager.ts
src/SparkClusterManager/SparkClusterManager.ts
@@ -279,25 +249,11 @@ src/Terminal/NotebookAppContracts.d.ts
src/Terminal/index.ts
src/TokenProviders/PortalTokenProvider.ts
src/TokenProviders/TokenProviderFactory.ts
src/Utils/AuthorizationUtils.test.ts
src/Utils/AuthorizationUtils.ts
src/Utils/AutoPilotUtils.test.ts
src/Utils/AutoPilotUtils.ts
src/Utils/DatabaseAccountUtils.test.ts
src/Utils/DatabaseAccountUtils.ts
src/Utils/JunoUtils.ts
src/Utils/MessageValidation.ts
src/Utils/NotebookConfigurationUtils.ts
src/Utils/PricingUtils.test.ts
src/Utils/QueryUtils.test.ts
src/Utils/QueryUtils.ts
src/Utils/StringUtils.test.ts
src/Utils/StringUtils.ts
src/applyExplorerBindings.ts
src/global.d.ts
src/quickstart.ts
src/setupTests.ts
src/workers/upload/definitions.ts
src/workers/upload/index.ts
src/Explorer/Controls/AccessibleElement/AccessibleElement.tsx
src/Explorer/Controls/Accordion/AccordionComponent.tsx
@@ -344,15 +300,7 @@ src/Explorer/Graph/GraphExplorerComponent/QueryContainerComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNeighborsComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx
src/Explorer/Menus/CommandBar/CommandBarComponentAdapter.tsx
src/Explorer/Menus/CommandBar/CommandBarUtil.test.tsx
src/Explorer/Menus/CommandBar/CommandBarUtil.tsx
src/Explorer/Menus/CommandBar/MemoryTrackerComponent.tsx
src/Explorer/Menus/NavBar/ControlBarComponent.tsx
src/Explorer/Menus/NavBar/ControlBarComponentAdapter.tsx
src/Explorer/Menus/NavBar/MeControlComponent.test.tsx
src/Explorer/Menus/NavBar/MeControlComponent.tsx
src/Explorer/Menus/NavBar/MeControlComponentAdapter.tsx
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.test.tsx
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.tsx
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponentAdapter.tsx
@@ -382,8 +330,7 @@ src/Explorer/Notebook/temp/inputs/editor.tsx
src/Explorer/Notebook/temp/markdown-cell.tsx
src/Explorer/Notebook/temp/source.tsx
src/Explorer/Notebook/temp/syntax-highlighter/index.tsx
src/Explorer/SplashScreen/SplashScreenComponent.tsx
src/Explorer/SplashScreen/SplashScreenComponentApdapter.tsx
src/Explorer/SplashScreen/SplashScreen.tsx
src/Explorer/Tabs/GalleryTab.tsx
src/Explorer/Tabs/NotebookViewerTab.tsx
src/Explorer/Tabs/TerminalTab.tsx

View File

@@ -20,10 +20,7 @@ module.exports = {
overrides: [
{
files: ["**/*.tsx"],
env: {
jest: true,
},
extends: ["plugin:react/recommended"],
extends: ["plugin:react/recommended"], // TODO: Add react-hooks
plugins: ["react"],
},
{
@@ -36,6 +33,7 @@ module.exports = {
},
],
rules: {
"no-console": ["error", { allow: ["error", "warn", "dir"] }],
curly: "error",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-extraneous-class": "error",
@@ -43,6 +41,7 @@ module.exports = {
"@typescript-eslint/no-explicit-any": "error",
"prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }],
eqeqeq: "error",
"react/display-name": "off",
"no-restricted-syntax": [
"error",
{

9
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"

View File

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

BIN
.vs/slnx.sqlite Normal file

Binary file not shown.

View File

@@ -17,5 +17,10 @@
"test/out/**": 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

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

View File

@@ -13,29 +13,17 @@ UI for Azure Cosmos DB. Powers the [Azure Portal](https://portal.azure.com/), ht
### Watch mode
Run `npm run watch` to start the development server and automatically rebuild on changes
Run `npm start` to start the development server and automatically rebuild on changes
### Specifying Development Platform
### Hosted Development (https://cosmos.azure.com)
Setting the environment variable `PLATFORM` during the build process will force the explorer to load the specified platform. By default in development it will run in `Hosted` mode. Valid options:
- Hosted
- Emulator
- Portal
`PLATFORM=Emulator npm run watch`
### Hosted Development
The default webpack dev server configuration will proxy requests to the production portal backend: `https://main.documentdb.ext.azure.com`. This will allow you to use production connection strings on your local machine.
To run pure hosted mode, in `webpack.config.js` change index HtmlWebpackPlugin to use hostedExplorer.html and change entry for index to use HostedExplorer.ts.
- Visit: `https://localhost:1234/hostedExplorer.html`
- The default webpack dev server configuration will proxy requests to the production portal backend: `https://main.documentdb.ext.azure.com`. This will allow you to use production connection strings on your local machine.
### Emulator Development
In a window environment, running `npm run build` will automatically copy the built files from `/dist` over to the default emulator install paths. In a non-windows environment you can specify an alternate endpoint using `EMULATOR_ENDPOINT` and webpack dev server will proxy requests for you.
`PLATFORM=Emulator EMULATOR_ENDPOINT=https://my-vm.azure.com:8081 npm run watch`
- Start the Cosmos Emulator
- Visit: https://localhost:1234/index.html
#### Setting up a Remote Emulator
@@ -55,16 +43,8 @@ The Cosmos emulator currently only runs in Windows environments. You can still d
### Portal Development
The Cosmos Portal that consumes this repo is not currently open source. If you have access to this project, `npm run build` will copy the built files over to the portal where they will be loaded by the portal development environment
You can however load a local running instance of data explorer in the production portal.
1. Turn off browser SSL validation for localhost: chrome://flags/#allow-insecure-localhost OR Install valid SSL certs for localhost (on IE, follow these [instructions](https://www.technipages.com/ie-bypass-problem-with-this-websites-security-certificate) to install the localhost certificate in the right place)
2. Allowlist `https://localhost:1234` domain for CORS in the Azure Cosmos DB portal
3. Start the project in portal mode: `PLATFORM=Portal npm run watch`
4. Load the portal using the following link: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html
Live reload will occur, but data explorer will not properly integrate again with the parent iframe. You will have to manually reload the page.
- Visit: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html
- You may have to manually visit https://localhost:1234/explorer.html first and click through any SSL certificate warnings
### Testing
@@ -88,6 +68,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.
### Architecture
[![](https://mermaid.ink/img/eyJjb2RlIjoiZ3JhcGggTFJcbiAgaG9zdGVkKGh0dHBzOi8vY29zbW9zLmF6dXJlLmNvbSlcbiAgcG9ydGFsKFBvcnRhbClcbiAgZW11bGF0b3IoRW11bGF0b3IpXG4gIGFhZFtBQURdXG4gIHJlc291cmNlVG9rZW5bUmVzb3VyY2UgVG9rZW5dXG4gIGNvbm5lY3Rpb25TdHJpbmdbQ29ubmVjdGlvbiBTdHJpbmddXG4gIHBvcnRhbFRva2VuW0VuY3J5cHRlZCBQb3J0YWwgVG9rZW5dXG4gIG1hc3RlcktleVtNYXN0ZXIgS2V5XVxuICBhcm1bQVJNIFJlc291cmNlIFByb3ZpZGVyXVxuICBkYXRhcGxhbmVbRGF0YSBQbGFuZV1cbiAgcHJveHlbUG9ydGFsIEFQSSBQcm94eV1cbiAgc3FsW1NRTF1cbiAgbW9uZ29bTW9uZ29dXG4gIHRhYmxlc1tUYWJsZXNdXG4gIGNhc3NhbmRyYVtDYXNzYW5kcmFdXG4gIGdyYWZbR3JhcGhdXG5cblxuICBlbXVsYXRvciAtLT4gbWFzdGVyS2V5IC0tLS0-IGRhdGFwbGFuZVxuICBwb3J0YWwgLS0-IGFhZFxuICBob3N0ZWQgLS0-IHBvcnRhbFRva2VuICYgcmVzb3VyY2VUb2tlbiAmIGNvbm5lY3Rpb25TdHJpbmcgJiBhYWRcbiAgYWFkIC0tLT4gYXJtXG4gIGFhZCAtLS0-IGRhdGFwbGFuZVxuICBhYWQgLS0tPiBwcm94eVxuICByZXNvdXJjZVRva2VuIC0tLT4gc3FsIC0tPiBkYXRhcGxhbmVcbiAgcG9ydGFsVG9rZW4gLS0tPiBwcm94eVxuICBwcm94eSAtLT4gZGF0YXBsYW5lXG4gIGNvbm5lY3Rpb25TdHJpbmcgLS0-IHNxbCAmIG1vbmdvICYgY2Fzc2FuZHJhICYgZ3JhZiAmIHRhYmxlc1xuICBzcWwgLS0-IGRhdGFwbGFuZVxuICB0YWJsZXMgLS0-IGRhdGFwbGFuZVxuICBtb25nbyAtLT4gcHJveHlcbiAgY2Fzc2FuZHJhIC0tPiBwcm94eVxuICBncmFmIC0tPiBwcm94eVxuXG5cdFx0IiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifSwidXBkYXRlRWRpdG9yIjpmYWxzZX0)](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggTFJcbiAgaG9zdGVkKGh0dHBzOi8vY29zbW9zLmF6dXJlLmNvbSlcbiAgcG9ydGFsKFBvcnRhbClcbiAgZW11bGF0b3IoRW11bGF0b3IpXG4gIGFhZFtBQURdXG4gIHJlc291cmNlVG9rZW5bUmVzb3VyY2UgVG9rZW5dXG4gIGNvbm5lY3Rpb25TdHJpbmdbQ29ubmVjdGlvbiBTdHJpbmddXG4gIHBvcnRhbFRva2VuW0VuY3J5cHRlZCBQb3J0YWwgVG9rZW5dXG4gIG1hc3RlcktleVtNYXN0ZXIgS2V5XVxuICBhcm1bQVJNIFJlc291cmNlIFByb3ZpZGVyXVxuICBkYXRhcGxhbmVbRGF0YSBQbGFuZV1cbiAgcHJveHlbUG9ydGFsIEFQSSBQcm94eV1cbiAgc3FsW1NRTF1cbiAgbW9uZ29bTW9uZ29dXG4gIHRhYmxlc1tUYWJsZXNdXG4gIGNhc3NhbmRyYVtDYXNzYW5kcmFdXG4gIGdyYWZbR3JhcGhdXG5cblxuICBlbXVsYXRvciAtLT4gbWFzdGVyS2V5IC0tLS0-IGRhdGFwbGFuZVxuICBwb3J0YWwgLS0-IGFhZFxuICBob3N0ZWQgLS0-IHBvcnRhbFRva2VuICYgcmVzb3VyY2VUb2tlbiAmIGNvbm5lY3Rpb25TdHJpbmcgJiBhYWRcbiAgYWFkIC0tLT4gYXJtXG4gIGFhZCAtLS0-IGRhdGFwbGFuZVxuICBhYWQgLS0tPiBwcm94eVxuICByZXNvdXJjZVRva2VuIC0tLT4gc3FsIC0tPiBkYXRhcGxhbmVcbiAgcG9ydGFsVG9rZW4gLS0tPiBwcm94eVxuICBwcm94eSAtLT4gZGF0YXBsYW5lXG4gIGNvbm5lY3Rpb25TdHJpbmcgLS0-IHNxbCAmIG1vbmdvICYgY2Fzc2FuZHJhICYgZ3JhZiAmIHRhYmxlc1xuICBzcWwgLS0-IGRhdGFwbGFuZVxuICB0YWJsZXMgLS0-IGRhdGFwbGFuZVxuICBtb25nbyAtLT4gcHJveHlcbiAgY2Fzc2FuZHJhIC0tPiBwcm94eVxuICBncmFmIC0tPiBwcm94eVxuXG5cdFx0IiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifSwidXBkYXRlRWRpdG9yIjpmYWxzZX0)
# Contributing
Please read the [contribution guidelines](./CONTRIBUTING.md).

View File

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

7
canvas/README.md Normal file
View File

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

1
canvas/index.js Normal file
View File

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

11
canvas/package.json Normal file
View File

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

1963
externals/adal.js vendored

File diff suppressed because it is too large Load Diff

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

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

View File

@@ -21,17 +21,13 @@ module.exports = {
collectCoverage: true,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: [
// "src/Common/Headers*"
// ],
collectCoverageFrom: ["src/**/*.{js,jsx,ts,tsx}"],
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
coveragePathIgnorePatterns: ["/node_modules/"],
// A list of reporter names that Jest uses when writing coverage reports
coverageReporters: ["json", "text", "cobertura"],
@@ -39,10 +35,10 @@ module.exports = {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 20,
functions: 24,
branches: 25,
functions: 25,
lines: 30,
statements: 29.0,
statements: 30,
},
},
@@ -71,7 +67,8 @@ module.exports = {
// A map from regular expressions to module names that allow to stub out resources with a single module
moduleNameMapper: {
"^.*[.](svg|png|gif|less)$": "<rootDir>/mockModule",
"^.*[.](svg|png|gif|less|css)$": "<rootDir>/mockModule",
"@nteract/stateful-components/(.*)$": "<rootDir>/mockModule",
"worker-loader": "<rootDir>/mockModule",
"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",

View File

@@ -4,7 +4,7 @@
@font-face {
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;
@@ -20,26 +20,26 @@
COLORS
/******************************************************************************/
@AccentMediumHigh: #0058AD;
@AccentMedium: #004E87;
@AccentHigh: #1EBAED;
@AccentExtraHigh: #55B3FF;
@AccentLow: #EDF6FF;
@AccentMediumLow: #DDEEFE;
@AccentLight: #EEF7FF;
@AccentExtra: #DDF0FF;
@AccentMediumHigh: #0058ad;
@AccentMedium: #004e87;
@AccentHigh: #1ebaed;
@AccentExtraHigh: #55b3ff;
@AccentLow: #edf6ff;
@AccentMediumLow: #ddeefe;
@AccentLight: #eef7ff;
@AccentExtra: #ddf0ff;
@SelectionHigh: #B91F26;
@BaseLight: #FFFFFF;
@SelectionHigh: #b91f26;
@BaseLight: #ffffff;
@BaseDark: #000000;
@NotificationLow: #FFF4CE;
@NotificationHigh: #F9E9B0;
@Purple1: #8A2DA5;
@NotificationLow: #fff4ce;
@NotificationHigh: #f9e9b0;
@Purple1: #8a2da5;
@Dirty: #9b4f96;
@BaseLow: #F2F2F2;
@BaseMediumLow: #E6E6E6;
@BaseMedium: #CCCCCC;
@BaseLow: #f2f2f2;
@BaseMediumLow: #e6e6e6;
@BaseMedium: #cccccc;
@BaseMediumHigh: #767676;
@BaseHigh: #393939;
@@ -53,10 +53,17 @@
@ErrorColor: @SelectionHigh;
@SelectionColor: #3074B0;
@SelectionColor: #3074b0;
@FocusColor: #605e5c;
@GalleryBackgroundColor: #fdfdfd;
//Icons
@InfoIconColor: #0072c6;
@WarningIconColor: #db7500;
@ErrorIconColor: #b91f26;
/******************************************************************************
METRICS
/******************************************************************************/
@@ -165,8 +172,8 @@
.selectedRadio:hover,
.selectedRadio:active,
.selectedRadio.dirty,
.tab [type=radio]:checked ~ label,
.tab [type=radio]:checked ~ label:hover {
.tab [type="radio"]:checked ~ label,
.tab [type="radio"]:checked ~ label:hover {
-ms-high-contrast-adjust: none;
-webkit-text-fill-color: HighlightText;
color: HighlightText;
@@ -175,9 +182,8 @@
}
.queryMetricsSummaryTuple {
th, td {
th,
td {
&:nth-child(2) {
width: @IETableDataWidth;
}
@@ -272,3 +278,27 @@
.tooltipVisible() {
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

@@ -16,7 +16,7 @@ body {
height: 100%;
:focus {
.focus()
.focus();
}
}
@@ -59,7 +59,7 @@ body {
right: 14px;
border-width: 0 9px 9px;
border-style: solid;
border-color: #FFF rgba(0, 0, 0, 0);
border-color: #fff rgba(0, 0, 0, 0);
display: block;
width: 0;
}
@@ -88,7 +88,6 @@ body {
height: 100%;
width: 100%;
.urlContainer {
margin-left: @DefaultSpace;
@@ -179,7 +178,8 @@ body {
.active();
}
&:focus .urlTokenCopyTooltiptext, &:focus .urlTokenCopyTooltiptext {
&:focus .urlTokenCopyTooltiptext,
&:focus .urlTokenCopyTooltiptext {
.tooltipVisible();
}
@@ -217,7 +217,7 @@ body {
.shareLink {
width: 300px;
background-color: #FFFFFF;
background-color: #ffffff;
border: 1px solid @BaseMedium;
overflow: hidden;
text-overflow: ellipsis;
@@ -718,11 +718,12 @@ execute-sproc-params-pane {
}
}
stored-procedure-tab {
.stored-procedure-tab {
@ToggleHeight: 30px;
@ToggleWidth: 180px;
.results-container, .errors-container {
.results-container,
.errors-container {
padding: @MediumSpace 0px 0px @MediumSpace;
height: 100%;
.flex-display();
@@ -934,19 +935,19 @@ menuQuickStart {
.content {
display: inline-block;
width: 100%;
transition: all .4s ease-in-out;
-ms-transition: all .4s ease-in-out;
-webkit-transition: all .4s ease-in-out;
-moz-transition: all .4s ease-in-out;
transition: all 0.4s ease-in-out;
-ms-transition: all 0.4s ease-in-out;
-webkit-transition: all 0.4s ease-in-out;
-moz-transition: all 0.4s ease-in-out;
height: 100vh;
}
.mini {
width: 0%;
float: left;
transition: all .4s ease-in-out;
-webkit-transition: all .4s ease-in-out;
-moz-transition: all .4s ease-in-out;
transition: all 0.4s ease-in-out;
-webkit-transition: all 0.4s ease-in-out;
-moz-transition: all 0.4s ease-in-out;
height: 100vh;
background-color: white;
}
@@ -1097,7 +1098,7 @@ menuQuickStart {
}
#tbodycontent > tr > td {
border-bottom: 1px solid #CCCCCC;
border-bottom: 1px solid #cccccc;
border-top: none;
padding: 6px;
}
@@ -1205,7 +1206,7 @@ menuQuickStart {
.gridRowSelected:hover {
cursor: default;
.hover()
.hover();
}
.gridRowHighlighted {
@@ -1240,7 +1241,7 @@ menuQuickStart {
border-top: 1px solid #eee;
margin-left: -17px;
width: 100%;
color: 1px solid #53575B;
color: 1px solid #53575b;
}
.partitioning-btn {
@@ -1308,7 +1309,7 @@ menuQuickStart {
.collid-white {
width: 100%;
border: solid 1px #DDD;
border: solid 1px #ddd;
}
.plusimg-but {
@@ -1522,6 +1523,21 @@ p {
.tooltipVisible();
}
.inputTooltip {
.inputTooltip();
}
.inputTooltip .inputTooltipText {
top: -68px;
.inputTooltipText();
}
.inputTooltip .inputTooltipText::after {
border-width: @MediumSpace @MediumSpace 0 @MediumSpace;
top: 55px;
.inputTooltipTextAfter();
}
.infoTooltip a {
color: @AccentHigh;
}
@@ -1631,7 +1647,6 @@ p {
margin-left: -32px;
}
/* Variant of paddingspan3 without the margins */
.contextual-pane .paddingspan3b {
@@ -1644,7 +1659,7 @@ p {
}
.contextual-pane hr {
border: 1px solid #53575B;
border: 1px solid #53575b;
margin-right: 20px;
}
@@ -1694,6 +1709,7 @@ input::-webkit-calendar-picker-indicator {
display: flex;
flex-direction: column;
height: 100%;
max-height: 100vh;
}
.contextual-pane .paneErrorDetailsContainer {
@@ -1818,11 +1834,11 @@ label {
.datalist-arrow:focus:after,
.datalist-arrow:active:after {
background: #1EBBEE;
background: #1ebbee;
}
input::-webkit-calendar-picker-indicator::after {
content: '\276F';
content: "\276F";
right: 0;
top: -8%;
display: block;
@@ -1836,7 +1852,7 @@ input::-webkit-calendar-picker-indicator::after {
}
.datalist-arrow:after:hover {
content: '\276F';
content: "\276F";
position: absolute;
right: 1px;
top: 6%;
@@ -1848,7 +1864,7 @@ input::-webkit-calendar-picker-indicator::after {
color: #fff;
text-align: center;
pointer-events: none;
background-color: #1EBBEE;
background-color: #1ebbee;
}
.Introline3 {
@@ -1912,7 +1928,7 @@ input::-webkit-calendar-picker-indicator::after {
.nav-tabs-margin {
padding-top: 8px;
background-color: #F2F2F2;
background-color: #f2f2f2;
}
.navTabHeight {
@@ -1978,7 +1994,7 @@ input::-webkit-calendar-picker-indicator::after {
.atags {
color: @AccentMediumHigh;
font-weight: 400;
cursor: pointer
cursor: pointer;
}
.qsmenuicons {
@@ -2083,7 +2099,7 @@ a:link {
display: flex;
flex: 1 1 auto;
overflow-x: auto;
overflow-y: hidden;
overflow-y: auto;
height: 100%;
}
@@ -2218,7 +2234,7 @@ a:link {
.documentsGridHeaderContainer {
padding-left: 5px;
width: 100%;
border-bottom: 1px solid #CCCCCC;
border-bottom: 1px solid #cccccc;
}
.documentsGridHeaderContainer > table {
@@ -2234,7 +2250,7 @@ a:link {
position: sticky;
top: 0;
background-color: #fff !important;
border-bottom: 1px solid #CCCCCC !important;
border-bottom: 1px solid #cccccc !important;
}
}
@@ -2383,7 +2399,7 @@ a:link {
color: #393939;
}
.tab [type=radio] {
.tab [type="radio"] {
display: none;
}
@@ -2395,40 +2411,40 @@ a:link {
padding: @MediumSpace 0px;
}
.tab [type=radio]:checked~label {
.tab [type="radio"]:checked ~ label {
border: 1px solid #0072c6;
background-color: @AccentMediumHigh;
color: white;
z-index: 2;
}
.tab [type=radio]:checked~label:hover {
.tab [type="radio"]:checked ~ label:hover {
border: 1px solid @AccentMediumHigh;
background-color: @AccentMediumHigh;
color: white;
z-index: 2;
}
.tab [type=radio]:checked~label:active {
.tab [type="radio"]:checked ~ label:active {
border: 1px solid #0072c6;
background-color: #0072c6;
color: white;
z-index: 2;
}
.tab [type=radio]:checked~label~.tabcontent {
.tab [type="radio"]:checked ~ label ~ .tabcontent {
z-index: 1;
display: initial;
}
.tab [type=radio]:not(:checked)~label:hover {
.tab [type="radio"]:not(:checked) ~ label:hover {
border: 1px solid #969696;
background-color: #969696;
color: white;
cursor: pointer;
}
.tab [type=radio]:not(:checked)~label~.tabcontent {
.tab [type="radio"]:not(:checked) ~ label ~ .tabcontent {
display: none;
}
@@ -2732,7 +2748,7 @@ a:link {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
flex-grow: 1
flex-grow: 1;
}
.tabIconSection {
@@ -2822,17 +2838,17 @@ a:link {
}
.tabCommandDisabled {
color: #CCCCCC;
color: #cccccc;
cursor: default;
background-color: #FFFFFF;
background-color: #ffffff;
}
.tabCommandDisabled:active {
border: 1px solid #FFFFFF;
border: 1px solid #ffffff;
}
.tabCommandDisabled:hover {
background-color: #FFFFFF;
background-color: #ffffff;
}
#explorerNotificationConsole {
@@ -2940,13 +2956,13 @@ settings-pane {
}
.linkDarkBackground {
color: @AccentExtraHigh
color: @AccentExtraHigh;
}
.linkDarkBackground:hover,
.linkDarkBackground:active,
.linkDarkBackground:focus {
color: @AccentHigh
color: @AccentHigh;
}
.library-add-button {
@@ -2983,7 +2999,7 @@ settings-pane {
}
.enableAnalyticalStorageRadioLabel {
padding: 0px
padding: 0px;
}
}
@@ -2993,18 +3009,18 @@ settings-pane {
}
.button.enabled {
background: #FFF;
background: #fff;
border-radius: 2px;
color: #323130;
padding: 3px 20px;
border: 1px solid #8A8886;
border: 1px solid #8a8886;
}
.button.disabled {
background: #F3F2F1;
border: 0px solid #8A8886;
background: #f3f2f1;
border: 0px solid #8a8886;
border-radius: 2px;
color: #A19F9D;
color: #a19f9d;
padding: 3px 20px;
}
@@ -3017,13 +3033,55 @@ settings-pane {
}
.warningErrorContent a {
color: @AccentMediumHigh
color: @AccentMediumHigh;
}
.infoBoxContent a {
color: @AccentMediumHigh
color: @AccentMediumHigh;
}
.collapsibleSection :hover {
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;
@NavLargeSpace: 15px;
.skip-link {
position: fixed;
top: -200px;
}
html {
font-family: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
padding: 0px;

View File

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

33747
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,15 @@
"dependencies": {
"@azure/arm-cosmosdb": "9.1.0",
"@azure/cosmos": "3.9.0",
"@azure/identity": "1.1.0",
"@azure/cosmos-language-service": "0.0.5",
"@jupyterlab/services": "6.0.0-rc.2",
"@jupyterlab/terminal": "3.0.0-rc.2",
"@azure/identity": "1.2.1",
"@azure/ms-rest-nodeauth": "3.0.7",
"@babel/plugin-proposal-class-properties": "7.12.1",
"@babel/plugin-proposal-decorators": "7.12.12",
"@jupyterlab/services": "6.0.2",
"@jupyterlab/terminal": "3.0.3",
"@microsoft/applicationinsights-web": "2.5.9",
"@nteract/commutable": "7.3.2",
"@nteract/commutable": "7.4.2",
"@nteract/connected-components": "6.8.2",
"@nteract/core": "15.1.0",
"@nteract/data-explorer": "8.0.3",
@@ -36,16 +39,17 @@
"@nteract/transform-vega": "7.0.6",
"@octokit/rest": "17.9.2",
"@phosphor/widgets": "1.9.3",
"@testing-library/jest-dom": "5.11.9",
"@types/mkdirp": "1.0.1",
"@types/node-fetch": "2.5.7",
"@uifabric/react-cards": "0.109.110",
"@uifabric/react-hooks": "7.14.0",
"@uifabric/styling": "7.13.7",
"abort-controller": "3.0.0",
"applicationinsights": "1.8.0",
"babel-polyfill": "6.26.0",
"bootstrap": "3.4.1",
"canvas": "2.6.1",
"canvas": "file:./canvas",
"clean-webpack-plugin": "0.1.19",
"clipboard-copy": "4.0.1",
"copy-webpack-plugin": "6.0.2",
"crossroads": "0.12.2",
"css-element-queries": "1.1.1",
@@ -55,12 +59,13 @@
"date-fns": "1.29.0",
"dayjs": "1.8.19",
"dotenv": "8.2.0",
"es6-object-assign": "1.1.0",
"es6-symbol": "3.1.3",
"eslint-plugin-jest": "23.13.2",
"eslint-plugin-react": "7.20.0",
"hasher": "1.2.0",
"html2canvas": "1.0.0-rc.5",
"i18next": "19.8.4",
"i18next-browser-languagedetector": "6.0.1",
"i18next-http-backend": "1.0.23",
"immutable": "4.0.0-rc.12",
"is-ci": "2.0.0",
"jquery": "3.5.1",
@@ -69,66 +74,62 @@
"knockout": "3.5.1",
"mkdirp": "1.0.4",
"monaco-editor": "0.18.1",
"object.entries": "1.1.0",
"office-ui-fabric-react": "7.134.1",
"ms": "2.1.3",
"msal": "1.4.4",
"office-ui-fabric-react": "7.164.2",
"p-retry": "4.2.0",
"plotly.js-cartesian-dist-min": "1.52.3",
"promise-polyfill": "8.1.0",
"promise.prototype.finally": "3.1.0",
"q": "1.5.1",
"react": "16.13.1",
"react-animate-height": "2.0.8",
"react-dnd": "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-i18next": "11.8.5",
"react-notification-system": "0.2.17",
"react-redux": "7.1.3",
"redux": "4.0.4",
"reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12",
"rxjs": "6.6.3",
"styled-components": "4.3.2",
"text-encoding": "0.7.0",
"swr": "0.4.0",
"terser-webpack-plugin": "3.1.0",
"underscore": "1.9.1",
"url-polyfill": "1.1.7",
"utility-types": "3.10.0",
"webcrypto-liner": "1.1.4",
"webfontloader": "1.6.28",
"whatwg-fetch": "3.0.0"
"utility-types": "3.10.0"
},
"devDependencies": {
"@babel/core": "7.9.0",
"@babel/preset-env": "7.9.0",
"@babel/preset-react": "7.9.4",
"@babel/preset-typescript": "7.9.0",
"@testing-library/react": "11.2.3",
"@types/applicationinsights-js": "1.0.7",
"@types/codemirror": "0.0.56",
"@types/crossroads": "0.0.30",
"@types/d3": "5.9.2",
"@types/enzyme": "3.10.7",
"@types/enzyme-adapter-react-16": "1.0.6",
"@types/expect-puppeteer": "4.4.3",
"@types/expect-puppeteer": "4.4.5",
"@types/hasher": "0.0.31",
"@types/jest": "23.3.10",
"@types/jest-environment-puppeteer": "4.3.2",
"@types/jest": "26.0.20",
"@types/jest-environment-puppeteer": "4.4.1",
"@types/memoize-one": "4.1.1",
"@types/node": "12.11.1",
"@types/promise.prototype.finally": "2.0.3",
"@types/prop-types": "15.5.8",
"@types/puppeteer": "3.0.1",
"@types/puppeteer": "5.4.3",
"@types/q": "1.5.1",
"@types/react": "16.9.56",
"@types/react-dom": "16.0.7",
"@types/react": "17.0.0",
"@types/react-dom": "17.0.0",
"@types/react-notification-system": "0.2.39",
"@types/react-redux": "7.1.7",
"@types/sinon": "2.3.3",
"@types/styled-components": "5.1.1",
"@types/text-encoding": "0.0.33",
"@types/underscore": "1.7.36",
"@types/webfontloader": "1.6.29",
"@typescript-eslint/eslint-plugin": "4.0.1",
"@typescript-eslint/parser": "4.0.1",
"adal-angular": "1.0.15",
"axe-puppeteer": "1.1.0",
"babel-jest": "24.9.0",
"babel-loader": "8.1.0",
@@ -143,13 +144,14 @@
"eslint-cli": "1.1.1",
"eslint-plugin-no-null": "1.0.2",
"eslint-plugin-prefer-arrow": "1.2.2",
"eslint-plugin-react-hooks": "4.2.0",
"expose-loader": "0.7.5",
"fast-glob": "3.2.5",
"file-loader": "2.0.0",
"fs-extra": "7.0.0",
"html-loader": "0.5.5",
"html-loader-jest": "0.2.1",
"html-webpack-plugin": "3.2.0",
"inline-css": "2.2.5",
"jest": "25.5.4",
"jest-canvas-mock": "2.1.0",
"jest-puppeteer": "4.4.0",
@@ -161,16 +163,15 @@
"monaco-editor-webpack-plugin": "1.7.0",
"node-fetch": "2.6.1",
"prettier": "2.2.1",
"puppeteer": "4.0.0",
"puppeteer": "8.0.0",
"raw-loader": "0.5.1",
"rimraf": "3.0.0",
"sinon": "3.2.1",
"style-loader": "0.23.0",
"terser-webpack-plugin": "3.0.5",
"ts-loader": "6.2.2",
"tslint": "5.11.0",
"tslint-microsoft-contrib": "6.0.0",
"typescript": "4.1.2",
"typescript": "4.0.2",
"url-loader": "1.1.1",
"wait-on": "4.0.2",
"webpack": "4.43.0",

View File

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

View File

@@ -3,4 +3,5 @@ export enum AuthType {
EncryptedToken = "encryptedtoken",
MasterKey = "masterkey",
ResourceToken = "resourcetoken",
ConnectionString = "connectionstring",
}

View File

@@ -1,5 +1,6 @@
import * as ko from "knockout";
import * as ReactBindingHandler from "./ReactBindingHandler";
import "../Explorer/Tables/DataTable/DataTableBindingManager";
export class BindingHandlersRegisterer {
public static registerBindingHandlers() {

View File

@@ -1,10 +1,3 @@
import { HashMap } from "./HashMap";
export class AuthorizationEndpoints {
public static arm: string = "https://management.core.windows.net/";
public static common: string = "https://login.windows.net/";
}
export class CodeOfConductEndpoints {
public static privacyStatement: string = "https://aka.ms/ms-privacy-policy";
public static codeOfConduct: string = "https://aka.ms/cosmos-code-of-conduct";
@@ -105,34 +98,12 @@ export class CapabilityNames {
public static readonly EnableServerless: string = "EnableServerless";
}
export class Features {
public static readonly cosmosdb = "cosmosdb";
public static readonly enableChangeFeedPolicy = "enablechangefeedpolicy";
public static readonly executeSproc = "dataexplorerexecutesproc";
public static readonly hostedDataExplorer = "hosteddataexplorerenabled";
public static readonly enableTtl = "enablettl";
public static readonly enableNotebooks = "enablenotebooks";
public static readonly enableGalleryPublish = "enablegallerypublish";
public static readonly enableCodeOfConduct = "enablecodeofconduct";
public static readonly enableLinkInjection = "enablelinkinjection";
public static readonly enableSpark = "enablespark";
public static readonly livyEndpoint = "livyendpoint";
public static readonly notebookServerUrl = "notebookserverurl";
public static readonly notebookServerToken = "notebookservertoken";
public static readonly notebookBasePath = "notebookbasepath";
public static readonly canExceedMaximumValue = "canexceedmaximumvalue";
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
public static readonly ttl90Days = "ttl90days";
public static readonly enableRightPanelV2 = "enablerightpanelv2";
public static readonly enableSchema = "enableschema";
public static readonly enableSDKoperations = "enablesdkoperations";
public static readonly showMinRUSurvey = "showminrusurvey";
}
// flight names returned from the portal are always lowercase
export class Flights {
public static readonly SettingsV2 = "settingsv2";
public static readonly MongoIndexEditor = "mongoindexeditor";
public static readonly MongoIndexing = "mongoindexing";
public static readonly AutoscaleTest = "autoscaletest";
}
export class AfecFeatures {
@@ -141,19 +112,6 @@ export class AfecFeatures {
public static readonly StorageAnalytics = "storageanalytics-public-preview";
}
export class Spark {
public static readonly MaxWorkerCount = 10;
public static readonly SKUs: HashMap<string> = new HashMap({
"Cosmos.Spark.D1s": "D1s / 1 core / 4GB RAM",
"Cosmos.Spark.D2s": "D2s / 2 cores / 8GB RAM",
"Cosmos.Spark.D4s": "D4s / 4 cores / 16GB RAM",
"Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM",
"Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM",
"Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM",
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM",
});
}
export class TagNames {
public static defaultExperience: string = "defaultExperience";
}

View File

@@ -1,10 +1,10 @@
import * as Cosmos from "@azure/cosmos";
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
import { configContext, Platform } from "../ConfigContext";
import { getErrorMessage } from "./ErrorHandlingUtils";
import { userContext } from "../UserContext";
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
import { userContext } from "../UserContext";
import { getErrorMessage } from "./ErrorHandlingUtils";
const _global = typeof self === "undefined" ? window : self;
@@ -77,7 +77,7 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
export function client(): Cosmos.CosmosClient {
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,
tokenProvider,
connectionPolicy: {

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,6 @@
export default class EnvironmentUtility {
public static normalizeArmEndpointUri(uri: string): string {
export function normalizeArmEndpoint(uri: string): string {
if (uri && uri.slice(-1) !== "/") {
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 { SubscriptionType } from "../Contracts/SubscriptionType";
import { userContext } from "../UserContext";
import { ARMError } from "../Utils/arm/request";
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import { HttpStatusCodes } from "./Constants";
import { logError } from "./Logger";
import { sendMessage } from "./MessageHandler";
@@ -21,7 +22,7 @@ export const handleError = (error: string | ARMError | Error, area: string, cons
sendNotificationForError(errorMessage, errorCode);
};
export const getErrorMessage = (error: string | Error): string => {
export const getErrorMessage = (error: string | Error = ""): string => {
const errorMessage = typeof error === "string" ? error : error.message;
return replaceKnownError(errorMessage);
};
@@ -44,11 +45,11 @@ const sendNotificationForError = (errorMessage: string, errorCode: number | stri
const replaceKnownError = (errorMessage: string): string => {
if (
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal &&
errorMessage.indexOf("SharedOffer is Disabled for your account") >= 0
userContext.subscriptionType === SubscriptionType.Internal &&
errorMessage?.indexOf("SharedOffer is Disabled for your account") >= 0
) {
return "Database throughput is not supported for internal subscriptions.";
} else if (errorMessage.indexOf("Partition key paths must contain only valid") >= 0) {
} else if (errorMessage?.indexOf("Partition key paths must contain only valid") >= 0) {
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
}

View File

@@ -1,28 +1,5 @@
import * as Constants from "./Constants";
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
// x-ms-resource-quota: databases = 100; collections = 5000; users = 500000; permissions = 2000000;
export function getQuota(responseHeaders: any): any {
return responseHeaders && responseHeaders[Constants.HttpHeaders.resourceQuota]
? parseStringIntoObject(responseHeaders[Constants.HttpHeaders.resourceQuota])
: null;
}
export function shouldEnableCrossPartitionKey(): boolean {
return LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true";
}
function parseStringIntoObject(resourceString: string) {
var entityObject: any = {};
if (resourceString) {
var entitiesArray: string[] = resourceString.split(";");
for (var i: any = 0; i < entitiesArray.length; i++) {
var entity: string[] = entitiesArray[i].split("=");
entityObject[entity[0]] = entity[1];
}
}
return entityObject;
}

View File

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

View File

@@ -1,8 +1,8 @@
import { MessageTypes } from "../Contracts/ExplorerContracts";
import Q from "q";
import * as _ from "underscore";
import * as Constants from "./Constants";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { getDataExplorerWindow } from "../Utils/WindowUtils";
import * as Constants from "./Constants";
export interface CachedDataPromise<T> {
deferred: Q.Deferred<T>;
@@ -61,6 +61,21 @@ export function sendMessage(data: any): void {
}
}
export function sendReadyMessage(): void {
if (canSendMessage()) {
// We try to find data explorer window first, then fallback to current window
const portalChildWindow = getDataExplorerWindow(window) || window;
portalChildWindow.parent.postMessage(
{
signature: "pcIframe",
kind: "ready",
data: "ready",
},
portalChildWindow.document.referrer
);
}
}
export function canSendMessage(): boolean {
return window.parent !== window;
}

View File

@@ -220,7 +220,6 @@ describe("MongoProxyClient", () => {
describe("getEndpoint", () => {
beforeEach(() => {
resetConfigContext();
delete window.authType;
updateUserContext({
databaseAccount,
});
@@ -241,7 +240,9 @@ describe("MongoProxyClient", () => {
});
it("returns a guest endpoint", () => {
window.authType = AuthType.EncryptedToken;
updateUserContext({
authType: AuthType.EncryptedToken,
});
const endpoint = getEndpoint();
expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/guest/mongo/explorer");
});

View File

@@ -20,7 +20,7 @@ const defaultHeaders = {
};
function authHeaders() {
if (window.authType === AuthType.EncryptedToken) {
if (userContext.authType === AuthType.EncryptedToken) {
return { [HttpHeaders.guestAccessToken]: userContext.accessToken };
} else {
return { [HttpHeaders.authorization]: userContext.authorizationToken };
@@ -337,7 +337,7 @@ export function createMongoCollectionWithProxy(
export function getEndpoint(): string {
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");
}
return url;

View File

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

View File

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

View File

@@ -3,16 +3,18 @@ import * as _ from "underscore";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import Explorer from "../Explorer/Explorer";
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
import DocumentId from "../Explorer/Tree/DocumentId";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { QueryUtils } from "../Utils/QueryUtils";
import * as QueryUtils from "../Utils/QueryUtils";
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
import { userContext } from "../UserContext";
import { createDocument, deleteDocument, queryDocuments, queryDocumentsPage } from "./DocumentClientUtilityBase";
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
import { createCollection } from "./dataAccess/createCollection";
import { handleError } from "./ErrorHandlingUtils";
import { createDocument } from "./dataAccess/createDocument";
import { deleteDocument } from "./dataAccess/deleteDocument";
import { queryDocuments } from "./dataAccess/queryDocuments";
export class QueriesClient {
private static readonly PartitionKey: DataModels.PartitionKey = {
@@ -31,10 +33,7 @@ export class QueriesClient {
return Promise.resolve(queriesCollection.rawDataModel);
}
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
"Setting up account for saving queries"
);
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Setting up account for saving queries");
return createCollection({
collectionId: SavedQueries.CollectionName,
createNewDatabase: true,
@@ -45,10 +44,7 @@ export class QueriesClient {
})
.then(
(collection: DataModels.Collection) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
"Successfully set up account for saving queries"
);
NotificationConsoleUtils.logConsoleInfo("Successfully set up account for saving queries");
return Promise.resolve(collection);
},
(error: any) => {
@@ -56,17 +52,14 @@ export class QueriesClient {
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
.finally(() => clearMessage());
}
public async saveQuery(query: DataModels.Query): Promise<void> {
const queriesCollection = this.findQueriesCollection();
if (!queriesCollection) {
const errorMessage: string = "Account not set up to perform saved query operations";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to save query ${query.queryName}: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
return Promise.reject(errorMessage);
}
@@ -74,25 +67,16 @@ export class QueriesClient {
this.validateQuery(query);
} catch (error) {
const errorMessage: string = "Invalid query specified";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to save query ${query.queryName}: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
return Promise.reject(errorMessage);
}
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Saving query ${query.queryName}`
);
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Saving query ${query.queryName}`);
query.id = query.queryName;
return createDocument(queriesCollection, query)
.then(
(savedQuery: DataModels.Query) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully saved query ${query.queryName}`
);
NotificationConsoleUtils.logConsoleInfo(`Successfully saved query ${query.queryName}`);
return Promise.resolve();
},
(error: any) => {
@@ -103,28 +87,29 @@ export class QueriesClient {
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
.finally(() => clearMessage());
}
public async getQueries(): Promise<DataModels.Query[]> {
const queriesCollection = this.findQueriesCollection();
if (!queriesCollection) {
const errorMessage: string = "Account not set up to perform saved query operations";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to fetch saved queries: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
return Promise.reject(errorMessage);
}
const options: any = { enableCrossPartitionQuery: true };
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
return queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Fetching saved queries");
const queryIterator: QueryIterator<ItemDefinition & Resource> = queryDocuments(
SavedQueries.DatabaseName,
SavedQueries.CollectionName,
this.fetchQueriesQuery(),
options
);
const fetchQueries = async (firstItemIndex: number): Promise<ViewModels.QueryResults> =>
await queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex);
return QueryUtils.queryAllPages(fetchQueries)
.then(
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex, options);
return QueryUtils.queryAllPages(fetchQueries).then(
(results: ViewModels.QueryResults) => {
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
if (!document) {
@@ -145,32 +130,22 @@ export class QueriesClient {
}
});
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, "Successfully fetched saved queries");
NotificationConsoleUtils.logConsoleInfo("Successfully fetched saved queries");
return Promise.resolve(queries);
},
(error: any) => {
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
return Promise.reject(error);
}
);
},
(error: any) => {
// should never get into this state but we handle this regardless
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
.finally(() => clearMessage());
}
public async deleteQuery(query: DataModels.Query): Promise<void> {
const queriesCollection = this.findQueriesCollection();
if (!queriesCollection) {
const errorMessage: string = "Account not set up to perform saved query operations";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to fetch saved queries: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
return Promise.reject(errorMessage);
}
@@ -178,16 +153,10 @@ export class QueriesClient {
this.validateQuery(query);
} catch (error) {
const errorMessage: string = "Invalid query specified";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to delete query ${query.queryName}: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to delete query ${query.queryName}: ${errorMessage}`);
}
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Deleting query ${query.queryName}`
);
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Deleting query ${query.queryName}`);
query.id = query.queryName;
const documentId = new DocumentId(
{
@@ -201,10 +170,7 @@ export class QueriesClient {
return deleteDocument(queriesCollection, documentId)
.then(
() => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully deleted query ${query.queryName}`
);
NotificationConsoleUtils.logConsoleInfo(`Successfully deleted query ${query.queryName}`);
return Promise.resolve();
},
(error: any) => {
@@ -212,7 +178,7 @@ export class QueriesClient {
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
.finally(() => clearMessage());
}
public getResourceId(): string {

View File

@@ -23,10 +23,10 @@ export class Splitter {
public splitterId: string;
public leftSideId: string;
public splitter: HTMLElement;
public leftSide: HTMLElement;
public lastX: number;
public lastWidth: number;
public splitter!: HTMLElement;
public leftSide!: HTMLElement;
public lastX!: number;
public lastWidth!: number;
private isCollapsed: ko.Observable<boolean>;
private bounds: SplitterBounds;
@@ -42,9 +42,10 @@ export class Splitter {
}
public initialize() {
this.splitter = document.getElementById(this.splitterId);
this.leftSide = document.getElementById(this.leftSideId);
if (document.getElementById(this.splitterId) !== null && document.getElementById(this.leftSideId) != null) {
this.splitter = <HTMLElement>document.getElementById(this.splitterId);
this.leftSide = <HTMLElement>document.getElementById(this.leftSideId);
}
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
const splitterOptions: JQueryUI.ResizableOptions = {
animate: true,

View File

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

View File

@@ -0,0 +1,24 @@
import { useId } from "@uifabric/react-hooks";
import { ITooltipHostStyles, TooltipHost } from "office-ui-fabric-react/lib/Tooltip";
import * as React from "react";
import InfoBubble from "../../../images/info-bubble.svg";
const calloutProps = { gapSpace: 0 };
const hostStyles: Partial<ITooltipHostStyles> = { root: { display: "inline-block" } };
export interface TooltipProps {
children: string;
}
export const Tooltip: React.FunctionComponent = ({ children }: TooltipProps) => {
const tooltipId = useId("tooltip");
return children ? (
<span>
<TooltipHost content={children} id={tooltipId} calloutProps={calloutProps} styles={hostStyles}>
<img className="infoImg" src={InfoBubble} alt="More information" />
</TooltipHost>
</span>
) : (
<></>
);
};

View File

@@ -0,0 +1,75 @@
import { Image, Stack, TextField } from "office-ui-fabric-react";
import React, { ChangeEvent, FunctionComponent, KeyboardEvent, useRef, useState } from "react";
import FolderIcon from "../../../images/folder_16x16.svg";
import * as Constants from "../../Common/Constants";
import { Tooltip } from "../Tooltip";
interface UploadProps {
label: string;
accept?: string;
tooltip?: string;
multiple?: boolean;
tabIndex?: number;
onUpload: (event: ChangeEvent<HTMLInputElement>) => void;
}
export const Upload: FunctionComponent<UploadProps> = ({
label,
accept,
tooltip,
multiple,
tabIndex,
...props
}: UploadProps) => {
const [selectedFilesTitle, setSelectedFilesTitle] = useState<string[]>([]);
const fileRef = useRef<HTMLInputElement>();
const onImportLinkKeyPress = (event: KeyboardEvent<HTMLAnchorElement>): void => {
if (event.keyCode === Constants.KeyCodes.Enter || event.keyCode === Constants.KeyCodes.Space) {
onImportLinkClick();
}
};
const onImportLinkClick = (): void => {
fileRef?.current?.click();
};
const onUpload = (event: ChangeEvent<HTMLInputElement>): void => {
const { files } = event.target;
const newFileList = [];
for (let i = 0; i < files.length; i++) {
newFileList.push(files.item(i).name);
}
if (newFileList) {
setSelectedFilesTitle(newFileList);
props.onUpload(event);
}
};
const title = label + " to upload";
return (
<div>
<span className="renewUploadItemsHeader">{label}</span>
<Tooltip>{tooltip}</Tooltip>
<Stack horizontal>
<TextField styles={{ fieldGroup: { width: 300 } }} readOnly value={selectedFilesTitle.toString()} />
<input
type="file"
id="importFileInput"
style={{ display: "none" }}
ref={fileRef}
accept={accept}
tabIndex={tabIndex}
multiple={multiple}
title="Upload Icon"
onChange={onUpload}
role="button"
/>
<a href="#" id="fileImportLinkNotebook" onClick={onImportLinkClick} onKeyPress={onImportLinkKeyPress}>
<Image className="fileImportImg" src={FolderIcon} alt={title} title={title} />
</a>
</Stack>
</div>
);
};

View File

@@ -1,5 +1,12 @@
export default class UrlUtility {
public static parseDocumentsPath(resourcePath: string): any {
interface Result {
type?: string;
objectBody?: {
id: string;
self: string;
};
}
export function parseDocumentsPath(resourcePath: string): Result {
if (typeof resourcePath !== "string") {
return {};
}
@@ -16,9 +23,9 @@ export default class UrlUtility {
resourcePath = "/" + resourcePath;
}
var id: string;
var type: string;
var pathParts = resourcePath.split("/");
let id: string;
let type: string;
const pathParts = resourcePath.split("/");
if (pathParts.length % 2 === 0) {
id = pathParts[pathParts.length - 2];
@@ -28,7 +35,7 @@ export default class UrlUtility {
type = pathParts[pathParts.length - 2];
}
var result = {
const result = {
type: type,
objectBody: {
id: id,
@@ -39,17 +46,16 @@ export default class UrlUtility {
return result;
}
public static createUri(baseUri: string, relativeUri: string): string {
export function createUri(baseUri: string, relativeUri: string): string {
if (!baseUri) {
throw new Error("baseUri is null or empty");
}
var slashAtEndOfUriRegex = /\/$/,
const slashAtEndOfUriRegex = /\/$/,
slashAtStartOfUriRegEx = /^\//;
var normalizedBaseUri = baseUri.replace(slashAtEndOfUriRegex, "") + "/",
const normalizedBaseUri = baseUri.replace(slashAtEndOfUriRegex, "") + "/",
normalizedRelativeUri = (relativeUri && relativeUri.replace(slashAtStartOfUriRegEx, "")) || "";
return normalizedBaseUri + normalizedRelativeUri;
}
}

View File

@@ -1,6 +1,5 @@
jest.mock("../../Utils/arm/request");
jest.mock("../CosmosClient");
jest.mock("../DataAccessUtilityBase");
import { AuthType } from "../../AuthType";
import { CreateCollectionParams, DatabaseAccount } from "../../Contracts/DataModels";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
@@ -28,13 +27,17 @@ describe("createCollection", () => {
});
it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD;
updateUserContext({
authType: AuthType.AAD,
});
await createCollection(createCollectionParams);
expect(armRequest).toHaveBeenCalled();
});
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({
databases: {
createIfNotExists: () => {

View File

@@ -35,7 +35,7 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
);
try {
let collection: DataModels.Collection;
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
if (params.createNewDatabase) {
const createDatabaseParams: DataModels.CreateDatabaseParams = {
autoPilotMaxThroughput: params.autoPilotMaxThroughput,

View File

@@ -34,7 +34,7 @@ export async function createDatabase(params: DataModels.CreateDatabaseParams): P
if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
throw new Error("Creating database resources is not allowed for tables accounts");
}
const database: DataModels.Database = await (window.authType === AuthType.AAD && !userContext.useSDKOperations
const database: DataModels.Database = await (userContext.authType === AuthType.AAD && !userContext.useSDKOperations
? createDatabaseWithARM(params)
: createDatabaseWithSDK(params));

View File

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

View File

@@ -22,7 +22,7 @@ export async function createStoredProcedure(
const clearMessage = logConsoleProgress(`Creating stored procedure ${storedProcedure.id}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -19,7 +19,7 @@ export async function createTrigger(
const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -22,7 +22,7 @@ export async function createUserDefinedFunction(
const clearMessage = logConsoleProgress(`Creating user defined function ${userDefinedFunction.id}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -20,13 +20,17 @@ describe("deleteCollection", () => {
});
it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD;
updateUserContext({
authType: AuthType.AAD,
});
await deleteCollection("database", "collection");
expect(armRequest).toHaveBeenCalled();
});
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({
database: () => {
return {

View File

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

View File

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

View File

@@ -20,13 +20,17 @@ describe("deleteDatabase", () => {
});
it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD;
updateUserContext({
authType: AuthType.AAD,
});
await deleteDatabase("database");
expect(armRequest).toHaveBeenCalled();
});
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({
database: () => {
return {

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ export async function deleteStoredProcedure(
const clearMessage = logConsoleProgress(`Deleting stored procedure ${storedProcedureId}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -10,7 +10,7 @@ export async function deleteTrigger(databaseId: string, collectionId: string, tr
const clearMessage = logConsoleProgress(`Deleting trigger ${triggerId}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -10,7 +10,7 @@ export async function deleteUserDefinedFunction(databaseId: string, collectionId
const clearMessage = logConsoleProgress(`Deleting user defined function ${id}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

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

View File

@@ -1,3 +1,4 @@
import { AuthType } from "../../AuthType";
import { armRequest } from "../../Utils/arm/request";
import { configContext } from "../../ConfigContext";
import { handleError } from "../ErrorHandlingUtils";
@@ -40,6 +41,10 @@ interface MetricsResponse {
}
export const getCollectionUsageSizeInKB = async (databaseName: string, containerName: string): Promise<number> => {
if (userContext.authType !== AuthType.AAD) {
return undefined;
}
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
@@ -71,7 +76,7 @@ export const getCollectionUsageSizeInKB = async (databaseName: string, container
return dataUsageSizeInKb + indexUsageSizeInKb;
} catch (error) {
handleError(error, "getCollectionUsageSize");
throw error;
return undefined;
}
};

View File

@@ -3,9 +3,10 @@ import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import * as Constants from "../Constants";
import { AuthType } from "../../AuthType";
import { userContext } from "../../UserContext";
export async function getIndexTransformationProgress(databaseId: string, collectionId: string): Promise<number> {
if (window.authType !== AuthType.AAD) {
if (userContext.authType !== AuthType.AAD) {
return undefined;
}
let indexTransformationPercentage: number;

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,7 @@ import { updateUserContext } from "../../UserContext";
describe("readCollection", () => {
beforeAll(() => {
updateUserContext({
authType: AuthType.ResourceToken,
databaseAccount: {
name: "test",
} as DatabaseAccount,
@@ -17,7 +18,6 @@ describe("readCollection", () => {
});
it("should call SDK if logged in with resource token", async () => {
window.authType = AuthType.ResourceToken;
(client as jest.Mock).mockReturnValue({
database: () => {
return {

View File

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

View File

@@ -19,13 +19,17 @@ describe("readCollections", () => {
});
it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD;
updateUserContext({
authType: AuthType.AAD,
});
await readCollections("database");
expect(armRequest).toHaveBeenCalled();
});
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({
database: () => {
return {

View File

@@ -15,7 +15,7 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table

View File

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

View File

@@ -19,13 +19,17 @@ describe("readDatabases", () => {
});
it("should call ARM if logged in with AAD", async () => {
window.authType = AuthType.AAD;
updateUserContext({
authType: AuthType.AAD,
});
await readDatabases();
expect(armRequest).toHaveBeenCalled();
});
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({
databases: {
readAll: () => {

View File

@@ -15,7 +15,7 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
const clearMessage = logConsoleProgress(`Querying databases`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table
) {

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ export async function readStoredProcedures(
const clearMessage = logConsoleProgress(`Querying stored procedures for container ${collectionId}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -14,7 +14,7 @@ export async function readTriggers(
const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -14,7 +14,7 @@ export async function readUserDefinedFunctions(
const clearMessage = logConsoleProgress(`Querying user defined functions for container ${collectionId}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -41,7 +41,7 @@ export async function updateCollection(
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table

View File

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

View File

@@ -58,7 +58,7 @@ export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> =>
const clearMessage = logConsoleProgress(`Updating offer for ${offerResourceText}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
if (userContext.authType === AuthType.AAD && !userContext.useSDKOperations) {
if (params.collectionId) {
updatedOffer = await updateCollectionOfferWithARM(params);
} else if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {

View File

@@ -22,7 +22,7 @@ export async function updateStoredProcedure(
const clearMessage = logConsoleProgress(`Updating stored procedure ${storedProcedure.id}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -19,7 +19,7 @@ export async function updateTrigger(
const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -22,7 +22,7 @@ export async function updateUserDefinedFunction(
const clearMessage = logConsoleProgress(`Updating user defined function ${userDefinedFunction.id}`);
try {
if (
window.authType === AuthType.AAD &&
userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience === DefaultAccountExperienceType.DocumentDB
) {

View File

@@ -4,7 +4,7 @@ export enum Platform {
Emulator = "Emulator",
}
interface ConfigContext {
export interface ConfigContext {
platform: Platform;
allowedParentFrameOrigins: string[];
gitSha?: string;
@@ -79,7 +79,11 @@ if (process.env.NODE_ENV === "development") {
export async function initializeConfiguration(): Promise<ConfigContext> {
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) {
try {
const { allowedParentFrameOrigins, ...externalConfig } = await response.json();
@@ -104,7 +108,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
const platform = params.get("platform");
switch (platform) {
default:
console.log("Invalid platform query parameter given, ignoring");
console.error(`Invalid platform query parameter: ${platform}`);
break;
case Platform.Portal:
case Platform.Hosted:
@@ -113,7 +117,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
}
}
} catch (error) {
console.log("No configuration file found using defaults");
console.error("No configuration file found using defaults");
}
return configContext;
}

View File

@@ -9,10 +9,10 @@ export interface DatabaseAccount {
}
export interface DatabaseAccountExtendedProperties {
documentEndpoint: string;
tableEndpoint: string;
gremlinEndpoint: string;
cassandraEndpoint: string;
documentEndpoint?: string;
tableEndpoint?: string;
gremlinEndpoint?: string;
cassandraEndpoint?: string;
configurationOverrides?: ConfigurationOverrides;
capabilities?: Capability[];
enableMultipleWriteLocations?: boolean;
@@ -210,11 +210,11 @@ export interface QueryMetrics {
export interface Offer {
id: string;
autoscaleMaxThroughput: number;
manualThroughput: number;
minimumThroughput: number;
autoscaleMaxThroughput: number | undefined;
manualThroughput: number | undefined;
minimumThroughput: number | undefined;
offerDefinition?: SDKOfferDefinition;
headers?: any;
offerReplacePending: boolean;
}
export interface SDKOfferDefinition extends Resource {
@@ -587,11 +587,3 @@ export interface MemoryUsageInfo {
freeKB: number;
totalKB: number;
}
export interface resourceTokenConnectionStringProperties {
accountEndpoint: string;
collectionId: string;
databaseId: string;
partitionKey?: string;
resourceToken: string;
}

View File

@@ -33,7 +33,6 @@ export enum MessageTypes {
CreateWorkspace,
CreateSparkPool,
RefreshDatabaseAccount,
InitTestExplorer,
}
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

@@ -5,7 +5,6 @@ import {
TriggerDefinition,
UserDefinedFunctionDefinition,
} from "@azure/cosmos";
import Q from "q";
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
import Explorer from "../Explorer/Explorer";
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
@@ -15,6 +14,7 @@ import DocumentId from "../Explorer/Tree/DocumentId";
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import Trigger from "../Explorer/Tree/Trigger";
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
import { SelfServeType } from "../SelfServe/SelfServeUtils";
import { UploadDetails } from "../workers/upload/definitions";
import * as DataModels from "./DataModels";
import { SubscriptionType } from "./SubscriptionType";
@@ -88,9 +88,9 @@ export interface Database extends TreeNode {
loadCollections(): Promise<void>;
findCollectionWithId(collectionId: string): Collection;
openAddCollection(database: Database, event: MouseEvent): void;
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
onSettingsClick: () => void;
loadOffer(): Promise<void>;
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
}
export interface CollectionBase extends TreeNode {
@@ -107,8 +107,8 @@ export interface CollectionBase extends TreeNode {
isCollectionExpanded: ko.Observable<boolean>;
onDocumentDBDocumentsClick(): void;
onNewQueryClick(source: any, event: MouseEvent, queryText?: string): void;
expandCollection(): Q.Promise<any>;
onNewQueryClick(source: any, event?: MouseEvent, queryText?: string): void;
expandCollection(): void;
collapseCollection(): void;
getDatabase(): Database;
}
@@ -137,14 +137,13 @@ export interface Collection extends CollectionBase {
openTab(): void;
onSettingsClick: () => Promise<void>;
onDeleteCollectionContextMenuClick(source: Collection, event: MouseEvent): void;
onNewGraphClick(): void;
onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string): void;
onNewMongoQueryClick(source: any, event?: MouseEvent, queryText?: string): void;
onNewMongoShellClick(): void;
onNewStoredProcedureClick(source: Collection, event: MouseEvent): void;
onNewUserDefinedFunctionClick(source: Collection, event: MouseEvent): void;
onNewTriggerClick(source: Collection, event: MouseEvent): void;
onNewStoredProcedureClick(source: Collection, event?: MouseEvent): void;
onNewUserDefinedFunctionClick(source: Collection, event?: MouseEvent): void;
onNewTriggerClick(source: Collection, event?: MouseEvent): void;
storedProcedures: ko.Computed<StoredProcedure[]>;
userDefinedFunctions: ko.Computed<UserDefinedFunction[]>;
triggers: ko.Computed<Trigger[]>;
@@ -175,9 +174,10 @@ export interface Collection extends CollectionBase {
onDragOver(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;
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
}
/**
@@ -292,10 +292,6 @@ export interface DocumentsTabOptions extends TabOptions {
resourceTokenPartitionKey?: string;
}
export interface SettingsTabV2Options extends TabOptions {
getPendingNotification: Q.Promise<DataModels.Notification>;
}
export interface ConflictsTabOptions extends TabOptions {
partitionKey: DataModels.PartitionKey;
conflictIds: ko.ObservableArray<ConflictId>;
@@ -358,11 +354,12 @@ export enum CollectionTabKind {
Notebook = 13 /* Deprecated */,
Terminal = 14,
NotebookV2 = 15,
SparkMasterTab = 16,
SparkMasterTab = 16 /* Deprecated */,
Gallery = 17,
NotebookViewer = 18,
Schema = 19,
SettingsV2 = 19,
CollectionSettingsV2 = 20,
DatabaseSettingsV2 = 21,
}
export enum TerminalKind {
@@ -373,30 +370,38 @@ export enum TerminalKind {
export interface DataExplorerInputsFrame {
databaseAccount: any;
subscriptionId: string;
resourceGroup: string;
masterKey: string;
hasWriteAccess: boolean;
authorizationToken: string;
features: any;
csmEndpoint: string;
dnsSuffix: string;
serverId: string;
extensionEndpoint: string;
subscriptionType: SubscriptionType;
quotaId: string;
addCollectionDefaultFlight: string;
isTryCosmosDBSubscription: boolean;
subscriptionId?: string;
resourceGroup?: string;
masterKey?: string;
hasWriteAccess?: boolean;
authorizationToken?: string;
csmEndpoint?: string;
dnsSuffix?: string;
serverId?: string;
extensionEndpoint?: string;
subscriptionType?: SubscriptionType;
quotaId?: string;
addCollectionDefaultFlight?: string;
isTryCosmosDBSubscription?: boolean;
loadDatabaseAccountTimestamp?: number;
sharedThroughputMinimum?: number;
sharedThroughputMaximum?: number;
sharedThroughputDefault?: number;
dataExplorerVersion?: string;
isAuthWithresourceToken?: boolean;
defaultCollectionThroughput?: CollectionCreationDefaults;
flights?: readonly string[];
}
export interface SelfServeFrameInputs {
selfServeType: SelfServeType;
databaseAccount: any;
subscriptionId: string;
resourceGroup: string;
authorizationToken: string;
csmEndpoint: string;
flights?: readonly string[];
}
export interface CollectionCreationDefaults {
storage: string;
throughput: ThroughputDefaults;

View File

@@ -1,5 +1,10 @@
import * as Plotly from "plotly.js-cartesian-dist-min";
import dayjs from "dayjs";
import * as Plotly from "plotly.js-cartesian-dist-min";
import { StyleConstants } from "../../Common/Constants";
import { sendCachedDataMessage, sendReadyMessage } from "../../Common/MessageHandler";
import { MessageTypes } from "../../Contracts/ExplorerContracts";
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
import "./Heatmap.less";
import {
ChartSettings,
DataPayload,
@@ -11,11 +16,6 @@ import {
PartitionTimeStampToData,
PortalTheme,
} from "./HeatmapDatatypes";
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
import { sendCachedDataMessage, sendMessage } from "../../Common/MessageHandler";
import { MessageTypes } from "../../Contracts/ExplorerContracts";
import { StyleConstants } from "../../Common/Constants";
import "./Heatmap.less";
export class Heatmap {
public static readonly elementId: string = "heatmap";
@@ -266,4 +266,4 @@ export function handleMessage(event: MessageEvent) {
}
window.addEventListener("message", handleMessage, false);
sendMessage("ready");
sendReadyMessage();

View File

@@ -1,383 +0,0 @@
// Type definitions for adal-angular 1.0.1.1
// Project: https://github.com/AzureAD/azure-activedirectory-library-for-js#readme
// Definitions by: Daniel Perez Alvarez <https://github.com/unindented>
// Anthony Ciccarello <https://github.com/aciccarello>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
//This is a customized version of adal on top of version 1.0.1 which does not support multi tenant
// Customized version add tenantId to stored tokens so when tenant change, adal will refetch instead of read from sessionStorage
// In module contexts the class constructor function is the exported object
// export = AuthenticationContext;
// This class is defined globally in not in a module context
declare class AuthenticationContext {
instance: string;
config: AuthenticationContext.Options;
callback: AuthenticationContext.TokenCallback;
popUp: boolean;
isAngular: boolean;
/**
* Enum for request type
*/
REQUEST_TYPE: AuthenticationContext.RequestType;
RESPONSE_TYPE: AuthenticationContext.ResponseType;
CONSTANTS: AuthenticationContext.Constants;
constructor(options: AuthenticationContext.Options);
/**
* Initiates the login process by redirecting the user to Azure AD authorization endpoint.
*/
login(): void;
/**
* Returns whether a login is in progress.
*/
loginInProgress(): boolean;
/**
* Gets token for the specified resource from the cache.
* @param resource A URI that identifies the resource for which the token is requested.
* @param tenantId tenant Id.
*/
getCachedToken(resource: string, tenantId: string): string;
/**
* If user object exists, returns it. Else creates a new user object by decoding `id_token` from the cache.
*/
getCachedUser(): AuthenticationContext.UserInfo;
/**
* Adds the passed callback to the array of callbacks for the specified resource.
* @param resource A URI that identifies the resource for which the token is requested.
* @param expectedState A unique identifier (guid).
* @param callback The callback provided by the caller. It will be called with token or error.
*/
registerCallback(
expectedState: string,
resource: string,
callback: AuthenticationContext.TokenCallback,
tenantId: string
): void;
/**
* Acquires token from the cache if it is not expired. Otherwise sends request to AAD to obtain a new token.
* @param resource Resource URI identifying the target resource.
* @param callback The callback provided by the caller. It will be called with token or error.
*/
acquireToken(resource: string, tenantId: string, callback: AuthenticationContext.TokenCallback): void;
/**
* Acquires token (interactive flow using a popup window) by sending request to AAD to obtain a new token.
* @param resource Resource URI identifying the target resource.
* @param extraQueryParameters Query parameters to add to the authentication request.
* @param claims Claims to add to the authentication request.
* @param callback The callback provided by the caller. It will be called with token or error.
*/
acquireTokenPopup(
resource: string,
tenantId: string,
extraQueryParameters: string | null | undefined,
claims: string | null | undefined,
callback: AuthenticationContext.TokenCallback
): void;
/**
* Acquires token (interactive flow using a redirect) by sending request to AAD to obtain a new token. In this case the callback passed in the authentication request constructor will be called.
* @param resource Resource URI identifying the target resource.
* @param extraQueryParameters Query parameters to add to the authentication request.
* @param claims Claims to add to the authentication request.
*/
acquireTokenRedirect(
resource: string,
tenantId: string,
extraQueryParameters?: string | null,
claims?: string | null
): void;
/**
* Redirects the browser to Azure AD authorization endpoint.
* @param urlNavigate URL of the authorization endpoint.
*/
promptUser(urlNavigate: string): void;
/**
* Clears cache items.
*/
clearCache(): void;
/**
* Clears cache items for a given resource.
* @param resource Resource URI identifying the target resource.
*/
clearCacheForResource(resource: string): void;
/**
* Redirects user to logout endpoint. After logout, it will redirect to `postLogoutRedirectUri` if added as a property on the config object.
*/
logOut(): void;
/**
* Calls the passed in callback with the user object or error message related to the user.
* @param callback The callback provided by the caller. It will be called with user or error.
*/
getUser(callback: AuthenticationContext.UserCallback): void;
/**
* Checks if the URL fragment contains access token, id token or error description.
* @param hash Hash passed from redirect page.
*/
isCallback(hash: string): boolean;
/**
* Gets login error.
*/
getLoginError(): string;
/**
* Creates a request info object from the URL fragment and returns it.
*/
getRequestInfo(hash: string): AuthenticationContext.RequestInfo;
/**
* Saves token or error received in the response from AAD in the cache. In case of `id_token`, it also creates the user object.
*/
saveTokenFromHash(requestInfo: AuthenticationContext.RequestInfo): void;
/**
* Gets resource for given endpoint if mapping is provided with config.
* @param endpoint Resource URI identifying the target resource.
*/
getResourceForEndpoint(resource: string): string;
/**
* This method must be called for processing the response received from AAD. It extracts the hash, processes the token or error, saves it in the cache and calls the callbacks with the result.
* @param hash Hash fragment of URL. Defaults to `window.location.hash`.
*/
handleWindowCallback(hash?: string): void;
/**
* Checks the logging Level, constructs the log message and logs it. Users need to implement/override this method to turn on logging.
* @param level Level can be set 0, 1, 2 and 3 which turns on 'error', 'warning', 'info' or 'verbose' level logging respectively.
* @param message Message to log.
* @param error Error to log.
*/
log(level: AuthenticationContext.LoggingLevel, message: string, error: any): void;
/**
* Logs messages when logging level is set to 0.
* @param message Message to log.
* @param error Error to log.
*/
error(message: string, error: any): void;
/**
* Logs messages when logging level is set to 1.
* @param message Message to log.
*/
warn(message: string): void;
/**
* Logs messages when logging level is set to 2.
* @param message Message to log.
*/
info(message: string): void;
/**
* Logs messages when logging level is set to 3.
* @param message Message to log.
*/
verbose(message: string): void;
/**
* Logs Pii messages when Logging Level is set to 0 and window.piiLoggingEnabled is set to true.
* @param message Message to log.
* @param error Error to log.
*/
errorPii(message: string, error: any): void;
/**
* Logs Pii messages when Logging Level is set to 1 and window.piiLoggingEnabled is set to true.
* @param message Message to log.
*/
warnPii(message: string): void;
/**
* Logs messages when Logging Level is set to 2 and window.piiLoggingEnabled is set to true.
* @param message Message to log.
*/
infoPii(message: string): void;
/**
* Logs messages when Logging Level is set to 3 and window.piiLoggingEnabled is set to true.
* @param message Message to log.
*/
verbosePii(message: string): void;
}
declare namespace AuthenticationContext {
function inject(config: Options): AuthenticationContext;
type LoggingLevel = 0 | 1 | 2 | 3;
type RequestType = "LOGIN" | "RENEW_TOKEN" | "UNKNOWN";
type ResponseType = "id_token token" | "token";
interface RequestInfo {
/**
* Object comprising of fields such as id_token/error, session_state, state, e.t.c.
*/
parameters: any;
/**
* Request type.
*/
requestType: RequestType;
/**
* Whether state is valid.
*/
stateMatch: boolean;
/**
* Unique guid used to match the response with the request.
*/
stateResponse: string;
/**
* Whether `requestType` contains `id_token`, `access_token` or error.
*/
valid: boolean;
}
interface UserInfo {
/**
* Username assigned from UPN or email.
*/
userName: string;
/**
* Properties parsed from `id_token`.
*/
profile: any;
}
type TokenCallback = (errorDesc: string | null, token: string | null, error: any) => void;
type UserCallback = (errorDesc: string | null, user: UserInfo | null) => void;
/**
* Configuration options for Authentication Context
*/
interface Options {
/**
* Client ID assigned to your app by Azure Active Directory.
*/
clientId: string;
/**
* Endpoint at which you expect to receive tokens.Defaults to `window.location.href`.
*/
redirectUri?: string;
/**
* Azure Active Directory instance. Defaults to `https://login.microsoftonline.com/`.
*/
instance?: string;
/**
* Your target tenant. Defaults to `common`.
*/
tenant?: string;
/**
* Query parameters to add to the authentication request.
*/
extraQueryParameter?: string;
/**
* Unique identifier used to map the request with the response. Defaults to RFC4122 version 4 guid (128 bits).
*/
correlationId?: string;
/**
* User defined function of handling the navigation to Azure AD authorization endpoint in case of login.
*/
displayCall?: (url: string) => void;
/**
* Set this to true to enable login in a popup winodow instead of a full redirect. Defaults to `false`.
*/
popUp?: boolean;
/**
* Set this to the resource to request on login. Defaults to `clientId`.
*/
loginResource?: string;
/**
* Set this to redirect the user to a custom login page.
*/
localLoginUrl?: string;
/**
* Redirects to start page after login. Defaults to `true`.
*/
navigateToLoginRequestUrl?: boolean;
/**
* Set this to redirect the user to a custom logout page.
*/
logOutUri?: string;
/**
* Redirects the user to postLogoutRedirectUri after logout. Defaults to `redirectUri`.
*/
postLogoutRedirectUri?: string;
/**
* Sets browser storage to either 'localStorage' or sessionStorage'. Defaults to `sessionStorage`.
*/
cacheLocation?: "localStorage" | "sessionStorage";
/**
* Array of keywords or URIs. Adal will attach a token to outgoing requests that have these keywords or URIs.
*/
endpoints?: { [resource: string]: string };
/**
* Array of keywords or URIs. Adal will not attach a token to outgoing requests that have these keywords or URIs.
*/
anonymousEndpoints?: string[];
/**
* If the cached token is about to be expired in the expireOffsetSeconds (in seconds), Adal will renew the token instead of using the cached token. Defaults to 300 seconds.
*/
expireOffsetSeconds?: number;
/**
* The number of milliseconds of inactivity before a token renewal response from AAD should be considered timed out. Defaults to 6 seconds.
*/
loadFrameTimeout?: number;
/**
* Callback to be invoked when a token is acquired.
*/
callback?: TokenCallback;
}
interface LoggingConfig {
level: LoggingLevel;
log: (message: string) => void;
piiLoggingEnabled: boolean;
}
/**
* Enum for storage constants
*/
interface Constants {
ACCESS_TOKEN: "access_token";
EXPIRES_IN: "expires_in";
ID_TOKEN: "id_token";
ERROR_DESCRIPTION: "error_description";
SESSION_STATE: "session_state";
STORAGE: {
TOKEN_KEYS: "adal.token.keys";
ACCESS_TOKEN_KEY: "adal.access.token.key";
EXPIRATION_KEY: "adal.expiration.key";
STATE_LOGIN: "adal.state.login";
STATE_RENEW: "adal.state.renew";
NONCE_IDTOKEN: "adal.nonce.idtoken";
SESSION_STATE: "adal.session.state";
USERNAME: "adal.username";
IDTOKEN: "adal.idtoken";
ERROR: "adal.error";
ERROR_DESCRIPTION: "adal.error.description";
LOGIN_REQUEST: "adal.login.request";
LOGIN_ERROR: "adal.login.error";
RENEW_STATUS: "adal.token.renew.status";
};
RESOURCE_DELIMETER: "|";
LOADFRAME_TIMEOUT: "6000";
TOKEN_RENEW_STATUS_CANCELED: "Canceled";
TOKEN_RENEW_STATUS_COMPLETED: "Completed";
TOKEN_RENEW_STATUS_IN_PROGRESS: "In Progress";
LOGGING_LEVEL: {
ERROR: 0;
WARN: 1;
INFO: 2;
VERBOSE: 3;
};
LEVEL_STRING_MAP: {
0: "ERROR:";
1: "WARNING:";
2: "INFO:";
3: "VERBOSE:";
};
POPUP_WIDTH: 483;
POPUP_HEIGHT: 600;
}
}
// declare global {
// interface Window {
// Logging: AuthenticationContext.LoggingConfig;
// }
// }

View File

@@ -20,10 +20,6 @@ describe("Component Registerer", () => {
expect(ko.components.isRegistered("graph-style")).toBe(true);
});
it("should register collapsible-panel component", () => {
expect(ko.components.isRegistered("collapsible-panel")).toBe(true);
});
it("should register json-editor component", () => {
expect(ko.components.isRegistered("json-editor")).toBe(true);
});
@@ -45,7 +41,8 @@ describe("Component Registerer", () => {
});
it("should register settings-tab-v2 component", () => {
expect(ko.components.isRegistered("settings-tab-v2")).toBe(true);
expect(ko.components.isRegistered("database-settings-tab-v2")).toBe(true);
expect(ko.components.isRegistered("collection-settings-tab-v2")).toBe(true);
});
it("should register query-tab component", () => {
@@ -68,10 +65,6 @@ describe("Component Registerer", () => {
expect(ko.components.isRegistered("terminal-tab")).toBe(true);
});
it("should register spark-master-tab component", () => {
expect(ko.components.isRegistered("spark-master-tab")).toBe(true);
});
it("should register mongo-shell-tab component", () => {
expect(ko.components.isRegistered("mongo-shell-tab")).toBe(true);
});
@@ -84,10 +77,6 @@ describe("Component Registerer", () => {
expect(ko.components.isRegistered("delete-collection-confirmation-pane")).toBe(true);
});
it("should register delete-database-confirmation-pane component", () => {
expect(ko.components.isRegistered("delete-database-confirmation-pane")).toBe(true);
});
it("should register save-query-pane component", () => {
expect(ko.components.isRegistered("save-query-pane")).toBe(true);
});
@@ -104,10 +93,6 @@ describe("Component Registerer", () => {
expect(ko.components.isRegistered("graph-styling-pane")).toBe(true);
});
it("should register upload-file-pane component", () => {
expect(ko.components.isRegistered("upload-file-pane")).toBe(true);
});
it("should register string-input-pane component", () => {
expect(ko.components.isRegistered("string-input-pane")).toBe(true);
});

View File

@@ -1,50 +1,61 @@
import * as ko from "knockout";
import * as PaneComponents from "./Panes/PaneComponents";
import * as TabComponents from "./Tabs/TabComponents";
import { CollapsiblePanelComponent } from "./Controls/CollapsiblePanel/CollapsiblePanelComponent";
import { DiffEditorComponent } from "./Controls/DiffEditor/DiffEditorComponent";
import { DynamicListComponent } from "./Controls/DynamicList/DynamicListComponent";
import { EditorComponent } from "./Controls/Editor/EditorComponent";
import { ErrorDisplayComponent } from "./Controls/ErrorDisplayComponent/ErrorDisplayComponent";
import { GraphStyleComponent } from "./Graph/GraphStyleComponent/GraphStyleComponent";
import { InputTypeaheadComponent } from "./Controls/InputTypeahead/InputTypeahead";
import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponent";
import { TabsManagerKOComponent } from "./Tabs/TabsManager";
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
import { GraphStyleComponent } from "./Graph/GraphStyleComponent/GraphStyleComponent";
import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponent";
import * as PaneComponents from "./Panes/PaneComponents";
import ConflictsTab from "./Tabs/ConflictsTab";
import DatabaseSettingsTab from "./Tabs/DatabaseSettingsTab";
import DocumentsTab from "./Tabs/DocumentsTab";
import GalleryTab from "./Tabs/GalleryTab";
import GraphTab from "./Tabs/GraphTab";
import MongoShellTab from "./Tabs/MongoShellTab";
import NotebookTabV2 from "./Tabs/NotebookV2Tab";
import NotebookViewerTab from "./Tabs/NotebookViewerTab";
import QueryTab from "./Tabs/QueryTab";
import QueryTablesTab from "./Tabs/QueryTablesTab";
import { DatabaseSettingsTabV2, SettingsTabV2 } from "./Tabs/SettingsTabV2";
import StoredProcedureTab from "./Tabs/StoredProcedureTab";
import TabsManagerTemplate from "./Tabs/TabsManager.html";
import TerminalTab from "./Tabs/TerminalTab";
import TriggerTab from "./Tabs/TriggerTab";
import UserDefinedFunctionTab from "./Tabs/UserDefinedFunctionTab";
ko.components.register("input-typeahead", new InputTypeaheadComponent());
ko.components.register("new-vertex-form", NewVertexComponent);
ko.components.register("error-display", new ErrorDisplayComponent());
ko.components.register("graph-style", GraphStyleComponent);
ko.components.register("collapsible-panel", new CollapsiblePanelComponent());
ko.components.register("editor", new EditorComponent());
ko.components.register("json-editor", new JsonEditorComponent());
ko.components.register("diff-editor", new DiffEditorComponent());
ko.components.register("dynamic-list", DynamicListComponent);
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
ko.components.register("tabs-manager", TabsManagerKOComponent());
ko.components.register("tabs-manager", { template: TabsManagerTemplate });
// Collection Tabs
ko.components.register("documents-tab", new TabComponents.DocumentsTab());
ko.components.register("mongo-documents-tab", new TabComponents.MongoDocumentsTab());
ko.components.register("stored-procedure-tab", new TabComponents.StoredProcedureTab());
ko.components.register("trigger-tab", new TabComponents.TriggerTab());
ko.components.register("user-defined-function-tab", new TabComponents.UserDefinedFunctionTab());
ko.components.register("settings-tab-v2", new TabComponents.SettingsTabV2());
ko.components.register("query-tab", new TabComponents.QueryTab());
ko.components.register("tables-query-tab", new TabComponents.QueryTablesTab());
ko.components.register("graph-tab", new TabComponents.GraphTab());
ko.components.register("mongo-shell-tab", new TabComponents.MongoShellTab());
ko.components.register("conflicts-tab", new TabComponents.ConflictsTab());
ko.components.register("notebookv2-tab", new TabComponents.NotebookV2Tab());
ko.components.register("terminal-tab", new TabComponents.TerminalTab());
ko.components.register("spark-master-tab", new TabComponents.SparkMasterTab());
ko.components.register("gallery-tab", new TabComponents.GalleryTab());
ko.components.register("notebook-viewer-tab", new TabComponents.NotebookViewerTab());
// Database Tabs
ko.components.register("database-settings-tab", new TabComponents.DatabaseSettingsTab());
[
DocumentsTab,
StoredProcedureTab,
TriggerTab,
UserDefinedFunctionTab,
SettingsTabV2,
QueryTab,
QueryTablesTab,
GraphTab,
MongoShellTab,
ConflictsTab,
NotebookTabV2,
TerminalTab,
GalleryTab,
NotebookViewerTab,
DatabaseSettingsTab,
DatabaseSettingsTabV2,
].forEach(({ component: { name, template } }) => ko.components.register(name, { template }));
// Panes
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
@@ -53,10 +64,7 @@ ko.components.register(
"delete-collection-confirmation-pane",
new PaneComponents.DeleteCollectionConfirmationPaneComponent()
);
ko.components.register(
"delete-database-confirmation-pane",
new PaneComponents.DeleteDatabaseConfirmationPaneComponent()
);
ko.components.register("graph-new-vertex-pane", new PaneComponents.GraphNewVertexPaneComponent());
ko.components.register("graph-styling-pane", new PaneComponents.GraphStylingPaneComponent());
ko.components.register("table-add-entity-pane", new PaneComponents.TableAddEntityPaneComponent());
@@ -64,14 +72,9 @@ ko.components.register("table-edit-entity-pane", new PaneComponents.TableEditEnt
ko.components.register("table-column-options-pane", new PaneComponents.TableColumnOptionsPaneComponent());
ko.components.register("table-query-select-pane", new PaneComponents.TableQuerySelectPaneComponent());
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
ko.components.register("settings-pane", new PaneComponents.SettingsPaneComponent());
ko.components.register("execute-sproc-params-pane", new PaneComponents.ExecuteSprocParamsComponent());
ko.components.register("renew-adhoc-access-pane", new PaneComponents.RenewAdHocAccessPane());
ko.components.register("upload-items-pane", new PaneComponents.UploadItemsPaneComponent());
ko.components.register("load-query-pane", new PaneComponents.LoadQueryPaneComponent());
ko.components.register("save-query-pane", new PaneComponents.SaveQueryPaneComponent());
ko.components.register("browse-queries-pane", new PaneComponents.BrowseQueriesPaneComponent());
ko.components.register("upload-file-pane", new PaneComponents.UploadFilePaneComponent());
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());

View File

@@ -1,23 +1,22 @@
import * as ko from "knockout";
import * as ViewModels from "../Contracts/ViewModels";
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
import AddCollectionIcon from "../../images/AddCollection.svg";
import AddSqlQueryIcon from "../../images/AddSqlQuery_16x16.svg";
import HostedTerminalIcon from "../../images/Hosted-Terminal.svg";
import AddStoredProcedureIcon from "../../images/AddStoredProcedure.svg";
import AddTriggerIcon from "../../images/AddTrigger.svg";
import AddUdfIcon from "../../images/AddUdf.svg";
import DeleteCollectionIcon from "../../images/DeleteCollection.svg";
import DeleteDatabaseIcon from "../../images/DeleteDatabase.svg";
import AddUdfIcon from "../../images/AddUdf.svg";
import AddTriggerIcon from "../../images/AddTrigger.svg";
import DeleteSprocIcon from "../../images/DeleteSproc.svg";
import DeleteTriggerIcon from "../../images/DeleteTrigger.svg";
import DeleteUDFIcon from "../../images/DeleteUDF.svg";
import DeleteSprocIcon from "../../images/DeleteSproc.svg";
import HostedTerminalIcon from "../../images/Hosted-Terminal.svg";
import * as ViewModels from "../Contracts/ViewModels";
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
import { userContext } from "../UserContext";
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
import Explorer from "./Explorer";
import UserDefinedFunction from "./Tree/UserDefinedFunction";
import StoredProcedure from "./Tree/StoredProcedure";
import Trigger from "./Tree/Trigger";
import { userContext } from "../UserContext";
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
import UserDefinedFunction from "./Tree/UserDefinedFunction";
export interface CollectionContextMenuButtonParams {
databaseId: string;
@@ -43,7 +42,7 @@ export class ResourceTreeContextMenuButtonFactory {
if (userContext.defaultExperience !== DefaultAccountExperienceType.Table) {
items.push({
iconSrc: DeleteDatabaseIcon,
onClick: () => container.deleteDatabaseConfirmationPane.open(),
onClick: () => container.openDeleteDatabaseConfirmationPane(),
label: container.deleteDatabaseText(),
styleClass: "deleteDatabaseMenuItem",
});
@@ -112,10 +111,7 @@ export class ResourceTreeContextMenuButtonFactory {
items.push({
iconSrc: DeleteCollectionIcon,
onClick: () => {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
},
onClick: () => container.openDeleteCollectionConfirmationPane(),
label: container.deleteCollectionText(),
styleClass: "deleteCollectionMenuItem",
});

View File

@@ -1,159 +0,0 @@
import React from "react";
import { shallow, mount } from "enzyme";
import { AccountSwitchComponent, AccountSwitchComponentProps } from "./AccountSwitchComponent";
import { AuthType } from "../../../AuthType";
import { DatabaseAccount, Subscription } from "../../../Contracts/DataModels";
import { AccountKind } from "../../../Common/Constants";
const createBlankProps = (): AccountSwitchComponentProps => {
return {
authType: null,
displayText: "",
accounts: [],
selectedAccountName: null,
isLoadingAccounts: false,
onAccountChange: jest.fn(),
subscriptions: [],
selectedSubscriptionId: null,
isLoadingSubscriptions: false,
onSubscriptionChange: jest.fn(),
};
};
const createBlankAccount = (): DatabaseAccount => {
return {
id: "",
kind: AccountKind.Default,
name: "",
properties: null,
location: "",
tags: null,
type: "",
};
};
const createBlankSubscription = (): Subscription => {
return {
subscriptionId: "",
displayName: "",
authorizationSource: "",
state: "",
subscriptionPolicies: null,
tenantId: "",
uniqueDisplayName: "",
};
};
const createFullProps = (): AccountSwitchComponentProps => {
const props = createBlankProps();
props.authType = AuthType.AAD;
const account1 = createBlankAccount();
account1.name = "account1";
const account2 = createBlankAccount();
account2.name = "account2";
const account3 = createBlankAccount();
account3.name = "superlongaccountnamestringtest";
props.accounts = [account1, account2, account3];
props.selectedAccountName = "account2";
const sub1 = createBlankSubscription();
sub1.displayName = "sub1";
sub1.subscriptionId = "a6062a74-5d53-4b20-9545-000b95f22297";
const sub2 = createBlankSubscription();
sub2.displayName = "subsubsubsubsubsubsub2";
sub2.subscriptionId = "b20b3e93-0185-4326-8a9c-d44bac276b6b";
props.subscriptions = [sub1, sub2];
props.selectedSubscriptionId = "a6062a74-5d53-4b20-9545-000b95f22297";
return props;
};
describe("test render", () => {
it("renders no auth type -> handle error in code", () => {
const props = createBlankProps();
const wrapper = shallow(<AccountSwitchComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
// Encrypted Token
it("renders auth security token, with selected account name", () => {
const props = createBlankProps();
props.authType = AuthType.EncryptedToken;
props.selectedAccountName = "testaccount";
const wrapper = shallow(<AccountSwitchComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
// AAD
it("renders auth aad, with all information", () => {
const props = createFullProps();
const wrapper = shallow(<AccountSwitchComponent {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("renders auth aad all dropdown menus", () => {
const props = createFullProps();
const wrapper = mount(<AccountSwitchComponent {...props} />);
expect(wrapper.exists("div.accountSwitchContextualMenu")).toBe(false);
wrapper.find("button.accountSwitchButton").simulate("click");
expect(wrapper.exists("div.accountSwitchContextualMenu")).toBe(true);
expect(wrapper.exists("div.accountSwitchSubscriptionDropdown")).toBe(true);
wrapper.find("DropdownBase.accountSwitchSubscriptionDropdown").simulate("click");
// Click will dismiss the first contextual menu in enzyme. Need to dig deeper to achieve below test
// expect(wrapper.exists("div.accountSwitchSubscriptionDropdownMenu")).toBe(true);
// expect(wrapper.find("button.ms-Dropdown-item").length).toBe(2);
// wrapper.find("div.accountSwitchSubscriptionDropdown").simulate("click");
// expect(wrapper.exists("div.accountSwitchSubscriptionDropdownMenu")).toBe(false);
// expect(wrapper.exists("div.accountSwitchAccountDropdown")).toBe(true);
// wrapper.find("div.accountSwitchAccountDropdown").simulate("click");
// expect(wrapper.exists("div.accountSwitchAccountDropdownMenu")).toBe(true);
// expect(wrapper.find("button.ms-Dropdown-item").length).toBe(3);
// wrapper.find("div.accountSwitchAccountDropdown").simulate("click");
// expect(wrapper.exists("div.accountSwitchAccountDropdownMenu")).toBe(false);
// wrapper.find("button.accountSwitchButton").simulate("click");
// expect(wrapper.exists("div.accountSwitchContextualMenu")).toBe(false);
wrapper.unmount();
});
});
// describe("test function", () => {
// it("switch subscription function", () => {
// const props = createFullProps();
// const wrapper = mount(<AccountSwitchComponent {...props} />);
// wrapper.find("button.accountSwitchButton").simulate("click");
// wrapper.find("div.accountSwitchSubscriptionDropdown").simulate("click");
// wrapper
// .find("button.ms-Dropdown-item")
// .at(1)
// .simulate("click");
// expect(props.onSubscriptionChange).toBeCalled();
// expect(props.onSubscriptionChange).toHaveBeenCalled();
// wrapper.unmount();
// });
// it("switch account", () => {
// const props = createFullProps();
// const wrapper = mount(<AccountSwitchComponent {...props} />);
// wrapper.find("button.accountSwitchButton").simulate("click");
// wrapper.find("div.accountSwitchAccountDropdown").simulate("click");
// wrapper
// .find("button.ms-Dropdown-item")
// .at(0)
// .simulate("click");
// expect(props.onAccountChange).toBeCalled();
// expect(props.onAccountChange).toHaveBeenCalled();
// wrapper.unmount();
// });
// });

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