Compare commits
82 Commits
user/matre
...
users/v-da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88b1265ce9 | ||
|
|
b1a434c011 | ||
|
|
bc951b4e13 | ||
|
|
4617fa9364 | ||
|
|
a282ad9242 | ||
|
|
b954b14f56 | ||
|
|
ed9c7dbb6b | ||
|
|
aadbb50e7d | ||
|
|
abff435e88 | ||
|
|
d1c4059320 | ||
|
|
9b214a3171 | ||
|
|
ba66aa939b | ||
|
|
f9af595eee | ||
|
|
7ce8c7a5a1 | ||
|
|
9ad3f589cc | ||
|
|
9267b2cc18 | ||
|
|
c7d1b2dcdb | ||
|
|
a526410e44 | ||
|
|
58cb85156c | ||
|
|
dd5ccade2b | ||
|
|
38ebef6c02 | ||
|
|
7ad5862e8f | ||
|
|
755b732532 | ||
|
|
1b5a9b83ff | ||
|
|
fb8871cfbf | ||
|
|
874cec26fc | ||
|
|
9d2d0e4754 | ||
|
|
c7c894d6d9 | ||
|
|
547954c3dc | ||
|
|
7f220bf8be | ||
|
|
1ee6abf890 | ||
|
|
72c3605dbe | ||
|
|
a7a38807df | ||
|
|
1285ffc440 | ||
|
|
b4bca3d41a | ||
|
|
2a47e4311c | ||
|
|
49d9f489d8 | ||
|
|
31cc129aa7 | ||
|
|
99af4acca4 | ||
|
|
80dd161a6f | ||
|
|
850f1dfb97 | ||
|
|
a827e79317 | ||
|
|
7dbccff41d | ||
|
|
9184684e75 | ||
|
|
701f486d8f | ||
|
|
5059917edf | ||
|
|
ab1409efb1 | ||
|
|
5de9e682ba | ||
|
|
b2ab979360 | ||
|
|
2f32a676d0 | ||
|
|
950c8ee470 | ||
|
|
b0eaac5b84 | ||
|
|
952491a3ad | ||
|
|
5dde66b032 | ||
|
|
5b1db2778c | ||
|
|
1213788f9c | ||
|
|
1ce3adff0f | ||
|
|
00eb07da11 | ||
|
|
afe59c1589 | ||
|
|
53b5ebd39c | ||
|
|
5b365e642f | ||
|
|
333b3de587 | ||
|
|
e909ac43f4 | ||
|
|
8433a027ad | ||
|
|
81dfd76198 | ||
|
|
7c77ffda6c | ||
|
|
a34d3bb000 | ||
|
|
42731d1aae | ||
|
|
3abbb63adc | ||
|
|
2e618cb3c4 | ||
|
|
beca0d6608 | ||
|
|
d54c896d74 | ||
|
|
d6a58fd45f | ||
|
|
a851f78b1c | ||
|
|
a4061a96b1 | ||
|
|
06c3b6fe94 | ||
|
|
89c7ebdd20 | ||
|
|
d0c2f72ed3 | ||
|
|
c2673ec707 | ||
|
|
759a4ca5cf | ||
|
|
9078878f44 | ||
|
|
139a9cb22c |
8
.github/workflows/ci.yml
vendored
@@ -92,11 +92,11 @@ jobs:
|
||||
name: dist
|
||||
path: dist/
|
||||
- name: Upload build to preview blob storage
|
||||
run: az storage blob upload-batch -d '$web' -s 'dist' --account-name cosmosexplorerpreview --destination-path "${{github.event.pull_request.head.sha || github.sha}}" --account-key="${PREVIEW_STORAGE_KEY}"
|
||||
run: az storage blob upload-batch -d '$web' -s 'dist' --account-name cosmosexplorerpreview --destination-path "${{github.event.pull_request.head.sha || github.sha}}" --account-key="${PREVIEW_STORAGE_KEY}" --overwrite true
|
||||
env:
|
||||
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
||||
- name: Upload preview config to blob storage
|
||||
run: az storage blob upload -c '$web' -f ./preview/config.json --account-name cosmosexplorerpreview --name "${{github.event.pull_request.head.sha || github.sha}}/config.json" --account-key="${PREVIEW_STORAGE_KEY}"
|
||||
run: az storage blob upload -c '$web' -f ./preview/config.json --account-name cosmosexplorerpreview --name "${{github.event.pull_request.head.sha || github.sha}}/config.json" --account-key="${PREVIEW_STORAGE_KEY}" --overwrite true
|
||||
env:
|
||||
PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }}
|
||||
endtoendemulator:
|
||||
@@ -182,7 +182,7 @@ jobs:
|
||||
with:
|
||||
name: dist
|
||||
- run: cp ./configs/prod.json config.json
|
||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
|
||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "jawelton@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
|
||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||
- uses: actions/upload-artifact@v2
|
||||
@@ -207,7 +207,7 @@ jobs:
|
||||
name: dist
|
||||
- run: cp ./configs/mpac.json config.json
|
||||
- run: sed -i 's/Azure.Cosmos.DB.Data.Explorer/Azure.Cosmos.DB.Data.Explorer.MPAC/g' DataExplorer.nuspec
|
||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
|
||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "jawelton@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
|
||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||
- uses: actions/upload-artifact@v2
|
||||
|
||||
41
SECURITY.md
Normal file
@@ -0,0 +1,41 @@
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->
|
||||
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
@@ -1,5 +1,6 @@
|
||||
<!doctype html>
|
||||
<html class="default no-js">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
@@ -9,256 +10,292 @@
|
||||
<link rel="stylesheet" href="../assets/css/main.css">
|
||||
<script async src="../assets/js/search.js" id="search-script"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div class="tsd-page-toolbar">
|
||||
<div class="container">
|
||||
<div class="table-wrap">
|
||||
<div class="table-cell" id="tsd-search" data-index="../assets/js/search.json" data-base="..">
|
||||
<div class="field">
|
||||
<label for="tsd-search-field" class="tsd-widget search no-caption">Search</label>
|
||||
<input id="tsd-search-field" type="text" />
|
||||
</div>
|
||||
<ul class="results">
|
||||
<li class="state loading">Preparing search index...</li>
|
||||
<li class="state failure">The search index is not available</li>
|
||||
</ul>
|
||||
<a href="../index.html" class="title">cosmos-explorer</a>
|
||||
</div>
|
||||
<div class="table-cell" id="tsd-widgets">
|
||||
<div id="tsd-filter">
|
||||
<a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a>
|
||||
<div class="tsd-filter-group">
|
||||
<div class="tsd-select" id="tsd-filter-visibility">
|
||||
<span class="tsd-select-label">All</span>
|
||||
<ul class="tsd-select-list">
|
||||
<li data-value="public">Public</li>
|
||||
<li data-value="protected">Public/Protected</li>
|
||||
<li data-value="private" class="selected">All</li>
|
||||
</ul>
|
||||
</div>
|
||||
<input type="checkbox" id="tsd-filter-inherited" checked />
|
||||
<label class="tsd-widget" for="tsd-filter-inherited">Inherited</label>
|
||||
<input type="checkbox" id="tsd-filter-externals" checked />
|
||||
<label class="tsd-widget" for="tsd-filter-externals">Externals</label>
|
||||
<header>
|
||||
<div class="tsd-page-toolbar">
|
||||
<div class="container">
|
||||
<div class="table-wrap">
|
||||
<div class="table-cell" id="tsd-search" data-index="../assets/js/search.json" data-base="..">
|
||||
<div class="field">
|
||||
<label for="tsd-search-field" class="tsd-widget search no-caption">Search</label>
|
||||
<input id="tsd-search-field" type="text" />
|
||||
</div>
|
||||
<ul class="results">
|
||||
<li class="state loading">Preparing search index...</li>
|
||||
<li class="state failure">The search index is not available</li>
|
||||
</ul>
|
||||
<a href="../index.html" class="title">cosmos-explorer</a>
|
||||
</div>
|
||||
<div class="table-cell" id="tsd-widgets">
|
||||
<div id="tsd-filter">
|
||||
<a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a>
|
||||
<div class="tsd-filter-group">
|
||||
<div class="tsd-select" id="tsd-filter-visibility">
|
||||
<span class="tsd-select-label">All</span>
|
||||
<ul class="tsd-select-list">
|
||||
<li data-value="public">Public</li>
|
||||
<li data-value="protected">Public/Protected</li>
|
||||
<li data-value="private" class="selected">All</li>
|
||||
</ul>
|
||||
</div>
|
||||
<input type="checkbox" id="tsd-filter-inherited" checked />
|
||||
<label class="tsd-widget" for="tsd-filter-inherited">Inherited</label>
|
||||
<input type="checkbox" id="tsd-filter-externals" checked />
|
||||
<label class="tsd-widget" for="tsd-filter-externals">Externals</label>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a>
|
||||
</div>
|
||||
<a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tsd-page-title">
|
||||
<div class="container">
|
||||
<ul class="tsd-breadcrumb">
|
||||
<li>
|
||||
<a href="../modules.html">cosmos-explorer</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../modules/selfserve_selfserveutils.html">SelfServe/SelfServeUtils</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="selfserve_selfserveutils.bladetype.html">BladeType</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h1>Enumeration BladeType</h1>
|
||||
<div class="tsd-page-title">
|
||||
<div class="container">
|
||||
<ul class="tsd-breadcrumb">
|
||||
<li>
|
||||
<a href="../modules.html">cosmos-explorer</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../modules/selfserve_selfserveutils.html">SelfServe/SelfServeUtils</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="selfserve_selfserveutils.bladetype.html">BladeType</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h1>Enumeration BladeType</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container container-main">
|
||||
<div class="row">
|
||||
<div class="col-8 col-content">
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Portal Blade types</p>
|
||||
</header>
|
||||
<div class="container container-main">
|
||||
<div class="row">
|
||||
<div class="col-8 col-content">
|
||||
<section class="tsd-panel tsd-comment">
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Portal Blade types</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<h2>Index</h2>
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<div class="tsd-index-content">
|
||||
<section class="tsd-index-section ">
|
||||
<h3>Enumeration members</h3>
|
||||
<ul class="tsd-index-list">
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="selfserve_selfserveutils.bladetype.html#cassandrakeys" class="tsd-kind-icon">Cassandra<wbr>Keys</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="selfserve_selfserveutils.bladetype.html#gremlinkeys" class="tsd-kind-icon">Gremlin<wbr>Keys</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="selfserve_selfserveutils.bladetype.html#metrics" class="tsd-kind-icon">Metrics</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="selfserve_selfserveutils.bladetype.html#mongokeys" class="tsd-kind-icon">Mongo<wbr>Keys</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="selfserve_selfserveutils.bladetype.html#sqlkeys" class="tsd-kind-icon">Sql<wbr>Keys</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="selfserve_selfserveutils.bladetype.html#tablekeys" class="tsd-kind-icon">Table<wbr>Keys</a></li>
|
||||
</section>
|
||||
<section class="tsd-panel-group tsd-index-group">
|
||||
<h2>Index</h2>
|
||||
<section class="tsd-panel tsd-index-panel">
|
||||
<div class="tsd-index-content">
|
||||
<section class="tsd-index-section ">
|
||||
<h3>Enumeration members</h3>
|
||||
<ul class="tsd-index-list">
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a
|
||||
href="selfserve_selfserveutils.bladetype.html#cassandrakeys"
|
||||
class="tsd-kind-icon">Cassandra<wbr>Keys</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a
|
||||
href="selfserve_selfserveutils.bladetype.html#gremlinkeys"
|
||||
class="tsd-kind-icon">Gremlin<wbr>Keys</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a
|
||||
href="selfserve_selfserveutils.bladetype.html#metrics"
|
||||
class="tsd-kind-icon">Metrics</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a
|
||||
href="selfserve_selfserveutils.bladetype.html#mongokeys"
|
||||
class="tsd-kind-icon">Mongo<wbr>Keys</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a
|
||||
href="selfserve_selfserveutils.bladetype.html#sqlkeys"
|
||||
class="tsd-kind-icon">Sql<wbr>Keys</a></li>
|
||||
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a
|
||||
href="selfserve_selfserveutils.bladetype.html#tablekeys"
|
||||
class="tsd-kind-icon">Table<wbr>Keys</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section class="tsd-panel-group tsd-member-group ">
|
||||
<h2>Enumeration members</h2>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="cassandrakeys" class="tsd-anchor"></a>
|
||||
<h3>Cassandra<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Cassandra<wbr>Keys<span
|
||||
class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> =
|
||||
"cassandraDbKeys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a Azure Cosmos DB for Apache Cassandra account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="gremlinkeys" class="tsd-anchor"></a>
|
||||
<h3>Gremlin<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Gremlin<wbr>Keys<span
|
||||
class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> =
|
||||
"keys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a Azure Cosmos DB for Apache Gremlin account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="metrics" class="tsd-anchor"></a>
|
||||
<h3>Metrics</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Metrics<span class="tsd-signature-symbol">:</span>
|
||||
<span class="tsd-signature-symbol"> = "metrics"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Metrics blade of an Azure Cosmos DB account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="mongokeys" class="tsd-anchor"></a>
|
||||
<h3>Mongo<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Mongo<wbr>Keys<span
|
||||
class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> =
|
||||
"mongoDbKeys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a Azure Cosmos DB for MongoDB account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="sqlkeys" class="tsd-anchor"></a>
|
||||
<h3>Sql<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Sql<wbr>Keys<span class="tsd-signature-symbol">:</span>
|
||||
<span class="tsd-signature-symbol"> = "keys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a Azure Cosmos DB for NoSQL account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="tablekeys" class="tsd-anchor"></a>
|
||||
<h3>Table<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Table<wbr>Keys<span
|
||||
class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> =
|
||||
"tableKeys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a Azure Cosmos DB for Table account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-4 col-menu menu-sticky-wrap menu-highlight">
|
||||
<nav class="tsd-navigation primary">
|
||||
<ul>
|
||||
<li class=" ">
|
||||
<a href="../modules.html">Modules</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a href="../modules/selfserve.html">Self<wbr>Serve</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a href="../modules/selfserve___what_is_currently_supported_.html">Self<wbr>Serve -<wbr>
|
||||
<wbr>What is currently supported?</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a href="../modules/selfserve_decorators.html">Self<wbr>Serve/<wbr>Decorators</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a
|
||||
href="../modules/selfserve_selfservetelemetryprocessor.html">Self<wbr>Serve/<wbr>Self<wbr>Serve<wbr>Telemetry<wbr>Processor</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a
|
||||
href="../modules/selfserve_selfservetypes.html">Self<wbr>Serve/<wbr>Self<wbr>Serve<wbr>Types</a>
|
||||
</li>
|
||||
<li class="current tsd-kind-module">
|
||||
<a
|
||||
href="../modules/selfserve_selfserveutils.html">Self<wbr>Serve/<wbr>Self<wbr>Serve<wbr>Utils</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class="tsd-navigation secondary menu-sticky">
|
||||
<ul class="before-current">
|
||||
</ul>
|
||||
<ul class="current">
|
||||
<li class="current tsd-kind-enum tsd-parent-kind-module">
|
||||
<a href="selfserve_selfserveutils.bladetype.html" class="tsd-kind-icon">Blade<wbr>Type</a>
|
||||
<ul>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#cassandrakeys"
|
||||
class="tsd-kind-icon">Cassandra<wbr>Keys</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#gremlinkeys"
|
||||
class="tsd-kind-icon">Gremlin<wbr>Keys</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#metrics"
|
||||
class="tsd-kind-icon">Metrics</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#mongokeys"
|
||||
class="tsd-kind-icon">Mongo<wbr>Keys</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#sqlkeys"
|
||||
class="tsd-kind-icon">Sql<wbr>Keys</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#tablekeys"
|
||||
class="tsd-kind-icon">Table<wbr>Keys</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<section class="tsd-panel-group tsd-member-group ">
|
||||
<h2>Enumeration members</h2>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="cassandrakeys" class="tsd-anchor"></a>
|
||||
<h3>Cassandra<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Cassandra<wbr>Keys<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> = "cassandraDbKeys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a Cassandra API account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="gremlinkeys" class="tsd-anchor"></a>
|
||||
<h3>Gremlin<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Gremlin<wbr>Keys<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> = "keys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a Gremlin API account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="metrics" class="tsd-anchor"></a>
|
||||
<h3>Metrics</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Metrics<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> = "metrics"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Metrics blade of an Azure Cosmos DB account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="mongokeys" class="tsd-anchor"></a>
|
||||
<h3>Mongo<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Mongo<wbr>Keys<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> = "mongoDbKeys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a Mongo API account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="sqlkeys" class="tsd-anchor"></a>
|
||||
<h3>Sql<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Sql<wbr>Keys<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> = "keys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a SQL API account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a name="tablekeys" class="tsd-anchor"></a>
|
||||
<h3>Table<wbr>Keys</h3>
|
||||
<div class="tsd-signature tsd-kind-icon">Table<wbr>Keys<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> = "tableKeys"</span></div>
|
||||
<aside class="tsd-sources">
|
||||
</aside>
|
||||
<div class="tsd-comment tsd-typography">
|
||||
<div class="lead">
|
||||
<p>Keys blade of a Table API account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-4 col-menu menu-sticky-wrap menu-highlight">
|
||||
<nav class="tsd-navigation primary">
|
||||
<ul>
|
||||
<li class=" ">
|
||||
<a href="../modules.html">Modules</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a href="../modules/selfserve.html">Self<wbr>Serve</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a href="../modules/selfserve___what_is_currently_supported_.html">Self<wbr>Serve -<wbr> <wbr>What is currently supported?</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a href="../modules/selfserve_decorators.html">Self<wbr>Serve/<wbr>Decorators</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a href="../modules/selfserve_selfservetelemetryprocessor.html">Self<wbr>Serve/<wbr>Self<wbr>Serve<wbr>Telemetry<wbr>Processor</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-module">
|
||||
<a href="../modules/selfserve_selfservetypes.html">Self<wbr>Serve/<wbr>Self<wbr>Serve<wbr>Types</a>
|
||||
</li>
|
||||
<li class="current tsd-kind-module">
|
||||
<a href="../modules/selfserve_selfserveutils.html">Self<wbr>Serve/<wbr>Self<wbr>Serve<wbr>Utils</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav class="tsd-navigation secondary menu-sticky">
|
||||
<ul class="before-current">
|
||||
</ul>
|
||||
<ul class="current">
|
||||
<li class="current tsd-kind-enum tsd-parent-kind-module">
|
||||
<a href="selfserve_selfserveutils.bladetype.html" class="tsd-kind-icon">Blade<wbr>Type</a>
|
||||
<ul>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#cassandrakeys" class="tsd-kind-icon">Cassandra<wbr>Keys</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#gremlinkeys" class="tsd-kind-icon">Gremlin<wbr>Keys</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#metrics" class="tsd-kind-icon">Metrics</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#mongokeys" class="tsd-kind-icon">Mongo<wbr>Keys</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#sqlkeys" class="tsd-kind-icon">Sql<wbr>Keys</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
|
||||
<a href="selfserve_selfserveutils.bladetype.html#tablekeys" class="tsd-kind-icon">Table<wbr>Keys</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="after-current">
|
||||
<li class=" tsd-kind-enum tsd-parent-kind-module">
|
||||
<a href="selfserve_selfserveutils.selfservetype.html" class="tsd-kind-icon">Self<wbr>Serve<wbr>Type</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-function tsd-parent-kind-module">
|
||||
<a href="../modules/selfserve_selfserveutils.html#generatebladelink" class="tsd-kind-icon">generate<wbr>Blade<wbr>Link</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="after-current">
|
||||
<li class=" tsd-kind-enum tsd-parent-kind-module">
|
||||
<a href="selfserve_selfserveutils.selfservetype.html"
|
||||
class="tsd-kind-icon">Self<wbr>Serve<wbr>Type</a>
|
||||
</li>
|
||||
<li class=" tsd-kind-function tsd-parent-kind-module">
|
||||
<a href="../modules/selfserve_selfserveutils.html#generatebladelink"
|
||||
class="tsd-kind-icon">generate<wbr>Blade<wbr>Link</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="with-border-bottom">
|
||||
<div class="container">
|
||||
<h2>Legend</h2>
|
||||
<div class="tsd-legend-group">
|
||||
<ul class="tsd-legend">
|
||||
<li class="tsd-kind-function"><span class="tsd-kind-icon">Function</span></li>
|
||||
<li class="tsd-kind-type-alias"><span class="tsd-kind-icon">Type alias</span></li>
|
||||
</ul>
|
||||
<ul class="tsd-legend">
|
||||
<li class="tsd-kind-enum"><span class="tsd-kind-icon">Enumeration</span></li>
|
||||
</ul>
|
||||
<ul class="tsd-legend">
|
||||
<li class="tsd-kind-interface"><span class="tsd-kind-icon">Interface</span></li>
|
||||
</ul>
|
||||
<ul class="tsd-legend">
|
||||
<li class="tsd-kind-class"><span class="tsd-kind-icon">Class</span></li>
|
||||
</ul>
|
||||
<footer class="with-border-bottom">
|
||||
<div class="container">
|
||||
<h2>Legend</h2>
|
||||
<div class="tsd-legend-group">
|
||||
<ul class="tsd-legend">
|
||||
<li class="tsd-kind-function"><span class="tsd-kind-icon">Function</span></li>
|
||||
<li class="tsd-kind-type-alias"><span class="tsd-kind-icon">Type alias</span></li>
|
||||
</ul>
|
||||
<ul class="tsd-legend">
|
||||
<li class="tsd-kind-enum"><span class="tsd-kind-icon">Enumeration</span></li>
|
||||
</ul>
|
||||
<ul class="tsd-legend">
|
||||
<li class="tsd-kind-interface"><span class="tsd-kind-icon">Interface</span></li>
|
||||
</ul>
|
||||
<ul class="tsd-legend">
|
||||
<li class="tsd-kind-class"><span class="tsd-kind-icon">Class</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<div class="container tsd-generator">
|
||||
<p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p>
|
||||
</div>
|
||||
</footer>
|
||||
<div class="container tsd-generator">
|
||||
<p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p>
|
||||
</div>
|
||||
<div class="overlay"></div>
|
||||
<script src="../assets/js/main.js"></script>
|
||||
<div class="overlay"></div>
|
||||
<script src="../assets/js/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
35
images/Copilot.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="40" height="40" fill="white"/>
|
||||
<path d="M17 3.73926L20 4.04436V12.6862L15.2 13.8013L13.6 15.9967V19.4479C13.6 21.7669 14.8545 23.9045 16.879 25.0353L21.4036 27.5626L13.6 31.69L10.3559 32.1523L7.27902 30.4337C5.25445 29.3028 4 27.1652 4 24.8462V14.7591C4 12.4395 5.25512 10.3015 7.28055 9.17085L16.3174 4.12639L16.3121 4.13012L17 3.73926Z" fill="url(#paint0_radial_420_101763)"/>
|
||||
<path d="M17 3.73926L20 4.04436V12.6862L15.2 13.8013L13.6 15.9967V19.4479C13.6 21.7669 14.8545 23.9045 16.879 25.0353L21.4036 27.5626L13.6 31.69L10.3559 32.1523L7.27902 30.4337C5.25445 29.3028 4 27.1652 4 24.8462V14.7591C4 12.4395 5.25512 10.3015 7.28055 9.17085L16.3174 4.12639L16.3121 4.13012L17 3.73926Z" fill="url(#paint1_linear_420_101763)"/>
|
||||
<path d="M26.399 15.001L34.399 19.801L35.999 21.401V24.8452C35.999 27.1642 34.7446 29.3018 32.72 30.4327L23.12 35.7949C21.1804 36.8784 18.8177 36.8784 16.878 35.7949L7.27804 30.4327C7.09146 30.3284 6.91141 30.2157 6.73828 30.095L7.278 30.3965C9.21762 31.4799 11.5803 31.4799 13.52 30.3965L23.12 25.0342C25.1445 23.9033 26.399 21.7657 26.399 19.4467V15.001Z" fill="url(#paint2_radial_420_101763)"/>
|
||||
<path d="M26.399 15.001L34.399 19.801L35.999 21.401V24.8452C35.999 27.1642 34.7446 29.3018 32.72 30.4327L23.12 35.7949C21.1804 36.8784 18.8177 36.8784 16.878 35.7949L7.27804 30.4327C7.09146 30.3284 6.91141 30.2157 6.73828 30.095L7.278 30.3965C9.21762 31.4799 11.5803 31.4799 13.52 30.3965L23.12 25.0342C25.1445 23.9033 26.399 21.7657 26.399 19.4467V15.001Z" fill="url(#paint3_linear_420_101763)"/>
|
||||
<path d="M32.7191 9.17053L23.119 3.8117C21.1802 2.72943 18.819 2.72943 16.8802 3.8117L16.3151 4.1271C14.6239 5.31737 13.5996 7.26472 13.5996 9.36025V16.0461L16.8802 14.2149C18.819 13.1326 21.1802 13.1326 23.119 14.2149L32.7191 19.5737C34.6984 20.6786 35.9421 22.7456 35.9977 25.0039C35.999 24.9514 35.9996 24.8987 35.9996 24.8459V14.7588C35.9996 12.4392 34.7445 10.3011 32.7191 9.17053Z" fill="url(#paint4_radial_420_101763)"/>
|
||||
<path d="M32.7191 9.17053L23.119 3.8117C21.1802 2.72943 18.819 2.72943 16.8802 3.8117L16.3151 4.1271C14.6239 5.31737 13.5996 7.26472 13.5996 9.36025V16.0461L16.8802 14.2149C18.819 13.1326 21.1802 13.1326 23.119 14.2149L32.7191 19.5737C34.6984 20.6786 35.9421 22.7456 35.9977 25.0039C35.999 24.9514 35.9996 24.8987 35.9996 24.8459V14.7588C35.9996 12.4392 34.7445 10.3011 32.7191 9.17053Z" fill="url(#paint5_linear_420_101763)"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_420_101763" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.5054 12.8837) rotate(112.544) scale(23.4519 19.3067)">
|
||||
<stop offset="0.206732" stop-color="#45D586"/>
|
||||
<stop offset="0.875628" stop-color="#128245"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint1_linear_420_101763" x1="15.0625" y1="29.6174" x2="13.787" y2="27.2436" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.9999" stop-color="#0F773F"/>
|
||||
<stop offset="1" stop-color="#0078D4" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint2_radial_420_101763" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(10.899 27.5214) rotate(-3.9995) scale(24.6197 15.4594)">
|
||||
<stop offset="0.140029" stop-color="#FBFF47"/>
|
||||
<stop offset="0.952721" stop-color="#54B228"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint3_linear_420_101763" x1="30.8932" y1="18.5508" x2="29.5491" y2="20.8921" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.9999" stop-color="#27770B"/>
|
||||
<stop offset="1" stop-color="#8C66BA" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint4_radial_420_101763" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(30.632 19.3878) rotate(-138.33) scale(22.8015 13.0047)">
|
||||
<stop stop-color="#95FEA0"/>
|
||||
<stop offset="0.839255" stop-color="#10B7B7"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint5_linear_420_101763" x1="13.5996" y1="11.7134" x2="16.2131" y2="11.7134" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.9999" stop-color="#0A7B7B"/>
|
||||
<stop offset="1" stop-color="#436DCD" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
3
images/CopilotQueryTablesTab.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="22" viewBox="0 0 20 29" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5849 1.28251C11.0125 0.262601 8.98746 0.2626 7.41509 1.28251L2.16509 4.68792C0.8149 5.56372 0 7.06362 0 8.67298V13.3266C0 14.9359 0.814898 16.4358 2.16509 17.3116L7.41509 20.717C8.98746 21.737 11.0125 21.737 12.5849 20.717L17.8349 17.3116C19.1851 16.4358 20 14.9359 20 13.3266V8.67298C20 7.06362 19.1851 5.56372 17.8349 4.68792L12.5849 1.28251ZM9.50097 2.05606C10.2759 1.93579 11.0848 2.09742 11.7686 2.54095L17.0186 5.94636C17.9424 6.54559 18.5 7.57184 18.5 8.67298V11.3953C18.287 11.1924 18.0465 11.0115 17.7802 10.8583L11.8647 7.45689C10.7104 6.79321 9.29086 6.79131 8.13486 7.45187L7.5 7.81465V4.41921C7.5 3.54159 8.01028 2.74407 8.80712 2.3763L9.50097 2.05606ZM17.8644 15.2255L17.7932 15.3501C17.5776 15.6212 17.3172 15.8595 17.0186 16.0532L11.7686 19.4586C10.6928 20.1564 9.30721 20.1564 8.23138 19.4586L5.86807 17.9256C6.11557 17.8358 6.35595 17.719 6.58487 17.5752L12.2457 14.0169C13.3374 13.3307 14 12.1316 14 10.8421V10.415L17.0324 12.1587C18.1077 12.7769 18.4798 14.1486 17.8644 15.2255ZM12.5 9.55251V10.8421C12.5 11.6158 12.1025 12.3352 11.4474 12.747L10.0303 13.6377L8.57078 12.7396C7.90535 12.3301 7.5 11.6047 7.5 10.8233V9.54228L8.87907 8.75424C9.57267 8.3579 10.4244 8.35904 11.1169 8.75725L12.5 9.55251ZM8.61445 14.5277L5.78662 16.3052C5.02045 16.7868 4.04031 16.7625 3.29894 16.2435L2.5521 15.7207C1.88767 15.1109 1.5 14.2448 1.5 13.3266V8.67298C1.5 7.57184 2.05756 6.54559 2.98138 5.94636L6.0268 3.97095C6.00907 4.11852 6 4.26815 6 4.41921V10.8233C6 12.1256 6.67558 13.3345 7.78463 14.017L8.61445 14.5277Z" fill="#0078D4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
4
images/Pivot1_selected.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="8" fill="#0078D4"/>
|
||||
<path d="M9.18652 4.8418V12H7.64844V6.58008C7.5638 6.65495 7.46289 6.72656 7.3457 6.79492C7.23177 6.86003 7.1097 6.92025 6.97949 6.97559C6.84928 7.02767 6.71419 7.07324 6.57422 7.1123C6.43424 7.14811 6.2959 7.17415 6.15918 7.19043V5.8916C6.55957 5.77441 6.93717 5.62467 7.29199 5.44238C7.64681 5.26009 7.96745 5.0599 8.25391 4.8418H9.18652Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 505 B |
4
images/Pivot2.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="7.5" stroke="#949494"/>
|
||||
<path d="M7.21875 10.7207H10.1875V12H5.5293V11.4727C5.5293 11.1146 5.58952 10.7939 5.70996 10.5107C5.8304 10.2243 5.98177 9.96875 6.16406 9.74414C6.34635 9.51628 6.54492 9.31608 6.75977 9.14355C6.97786 8.96777 7.18457 8.8099 7.37988 8.66992C7.58496 8.52344 7.764 8.38346 7.91699 8.25C8.07324 8.11654 8.20345 7.9847 8.30762 7.85449C8.41504 7.72103 8.49479 7.58757 8.54688 7.4541C8.59896 7.31738 8.625 7.17253 8.625 7.01953C8.625 6.72005 8.54036 6.49382 8.37109 6.34082C8.20182 6.18783 7.94303 6.11133 7.59473 6.11133C6.99251 6.11133 6.41634 6.35059 5.86621 6.8291V5.47168C6.47493 5.0778 7.16178 4.88086 7.92676 4.88086C8.28158 4.88086 8.59896 4.92806 8.87891 5.02246C9.16211 5.11361 9.40137 5.24544 9.59668 5.41797C9.79199 5.59049 9.9401 5.80046 10.041 6.04785C10.1452 6.29199 10.1973 6.56543 10.1973 6.86816C10.1973 7.19043 10.1468 7.47689 10.0459 7.72754C9.94824 7.97819 9.81641 8.20605 9.65039 8.41113C9.48763 8.61621 9.29883 8.80501 9.08398 8.97754C8.86914 9.14681 8.64616 9.3112 8.41504 9.4707C8.25879 9.58138 8.10742 9.69206 7.96094 9.80273C7.81771 9.91016 7.69076 10.0176 7.58008 10.125C7.4694 10.2292 7.38151 10.3317 7.31641 10.4326C7.2513 10.5335 7.21875 10.6296 7.21875 10.7207Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
4
images/Pivot2_selected.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="8" fill="#0078D4"/>
|
||||
<path d="M7.21875 10.7207H10.1875V12H5.5293V11.4727C5.5293 11.1146 5.58952 10.7939 5.70996 10.5107C5.8304 10.2243 5.98177 9.96875 6.16406 9.74414C6.34635 9.51628 6.54492 9.31608 6.75977 9.14355C6.97786 8.96777 7.18457 8.8099 7.37988 8.66992C7.58496 8.52344 7.764 8.38346 7.91699 8.25C8.07324 8.11654 8.20345 7.9847 8.30762 7.85449C8.41504 7.72103 8.49479 7.58757 8.54688 7.4541C8.59896 7.31738 8.625 7.17253 8.625 7.01953C8.625 6.72005 8.54036 6.49382 8.37109 6.34082C8.20182 6.18783 7.94303 6.11133 7.59473 6.11133C6.99251 6.11133 6.41634 6.35059 5.86621 6.8291V5.47168C6.47493 5.0778 7.16178 4.88086 7.92676 4.88086C8.28158 4.88086 8.59896 4.92806 8.87891 5.02246C9.16211 5.11361 9.40137 5.24544 9.59668 5.41797C9.79199 5.59049 9.9401 5.80046 10.041 6.04785C10.1452 6.29199 10.1973 6.56543 10.1973 6.86816C10.1973 7.19043 10.1468 7.47689 10.0459 7.72754C9.94824 7.97819 9.81641 8.20605 9.65039 8.41113C9.48763 8.61621 9.29883 8.80501 9.08398 8.97754C8.86914 9.14681 8.64616 9.3112 8.41504 9.4707C8.25879 9.58138 8.10742 9.69206 7.96094 9.80273C7.81771 9.91016 7.69076 10.0176 7.58008 10.125C7.4694 10.2292 7.38151 10.3317 7.31641 10.4326C7.2513 10.5335 7.21875 10.6296 7.21875 10.7207Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
4
images/Pivot3.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="7.5" stroke="#949494"/>
|
||||
<path d="M5.69531 11.7705V10.4277C6.16406 10.7695 6.71094 10.9404 7.33594 10.9404C7.72982 10.9404 8.03581 10.8558 8.25391 10.6865C8.47526 10.5173 8.58594 10.2812 8.58594 9.97852C8.58594 9.66602 8.44922 9.42513 8.17578 9.25586C7.9056 9.08659 7.53288 9.00195 7.05762 9.00195H6.4082V7.82031H7.00879C7.92025 7.82031 8.37598 7.51758 8.37598 6.91211C8.37598 6.34245 8.02604 6.05762 7.32617 6.05762C6.85742 6.05762 6.40169 6.20898 5.95898 6.51172V5.25195C6.45052 5.00456 7.02344 4.88086 7.67773 4.88086C8.39388 4.88086 8.95052 5.04199 9.34766 5.36426C9.74805 5.68652 9.94824 6.10482 9.94824 6.61914C9.94824 7.53385 9.48438 8.10677 8.55664 8.33789V8.3623C9.05143 8.42415 9.44206 8.60482 9.72852 8.9043C10.015 9.20052 10.1582 9.5651 10.1582 9.99805C10.1582 10.6523 9.91895 11.1699 9.44043 11.5508C8.96191 11.9316 8.30111 12.1221 7.45801 12.1221C6.73535 12.1221 6.14779 12.0049 5.69531 11.7705Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
4
images/Pivot3_selected.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="8" fill="#0078D4"/>
|
||||
<path d="M5.69531 11.7705V10.4277C6.16406 10.7695 6.71094 10.9404 7.33594 10.9404C7.72982 10.9404 8.03581 10.8558 8.25391 10.6865C8.47526 10.5173 8.58594 10.2812 8.58594 9.97852C8.58594 9.66602 8.44922 9.42513 8.17578 9.25586C7.9056 9.08659 7.53288 9.00195 7.05762 9.00195H6.4082V7.82031H7.00879C7.92025 7.82031 8.37598 7.51758 8.37598 6.91211C8.37598 6.34245 8.02604 6.05762 7.32617 6.05762C6.85742 6.05762 6.40169 6.20898 5.95898 6.51172V5.25195C6.45052 5.00456 7.02344 4.88086 7.67773 4.88086C8.39388 4.88086 8.95052 5.04199 9.34766 5.36426C9.74805 5.68652 9.94824 6.10482 9.94824 6.61914C9.94824 7.53385 9.48438 8.10677 8.55664 8.33789V8.3623C9.05143 8.42415 9.44206 8.60482 9.72852 8.9043C10.015 9.20052 10.1582 9.5651 10.1582 9.99805C10.1582 10.6523 9.91895 11.1699 9.44043 11.5508C8.96191 11.9316 8.30111 12.1221 7.45801 12.1221C6.73535 12.1221 6.14779 12.0049 5.69531 11.7705Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
4
images/Pivot4.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="7.5" stroke="#949494"/>
|
||||
<path d="M9.77246 4.99805V9.41211H10.6123V10.5645H9.77246V12H8.36621V10.5645H5.31445V9.3584C5.58464 9.05566 5.86458 8.72526 6.1543 8.36719C6.44401 8.00586 6.72396 7.63477 6.99414 7.25391C7.26432 6.87305 7.51497 6.49056 7.74609 6.10645C7.98047 5.71908 8.17904 5.34961 8.3418 4.99805H9.77246ZM6.69629 9.41211H8.36621V6.96582C8.25228 7.17741 8.12858 7.39225 7.99512 7.61035C7.86165 7.8252 7.72168 8.03841 7.5752 8.25C7.42871 8.45833 7.2806 8.66178 7.13086 8.86035C6.98112 9.05566 6.83626 9.23958 6.69629 9.41211Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 680 B |
4
images/Pivot4_selected.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="8" fill="#0078D4"/>
|
||||
<path d="M9.77246 4.99805V9.41211H10.6123V10.5645H9.77246V12H8.36621V10.5645H5.31445V9.3584C5.58464 9.05566 5.86458 8.72526 6.1543 8.36719C6.44401 8.00586 6.72396 7.63477 6.99414 7.25391C7.26432 6.87305 7.51497 6.49056 7.74609 6.10645C7.98047 5.71908 8.17904 5.34961 8.3418 4.99805H9.77246ZM6.69629 9.41211H8.36621V6.96582C8.25228 7.17741 8.12858 7.39225 7.99512 7.61035C7.86165 7.8252 7.72168 8.03841 7.5752 8.25C7.42871 8.45833 7.2806 8.66178 7.13086 8.86035C6.98112 9.05566 6.83626 9.23958 6.69629 9.41211Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 674 B |
4
images/Pivot5.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="7.5" stroke="#949494"/>
|
||||
<path d="M5.81738 11.8193V10.501C6.2959 10.7939 6.80534 10.9404 7.3457 10.9404C7.7526 10.9404 8.06999 10.8444 8.29785 10.6523C8.52897 10.457 8.64453 10.1934 8.64453 9.86133C8.64453 9.16797 8.15462 8.82129 7.1748 8.82129C6.81348 8.82129 6.39681 8.84896 5.9248 8.9043L6.18848 4.99805H9.89453V6.25781H7.36523L7.26758 7.65918C7.51823 7.63965 7.7347 7.62988 7.91699 7.62988C8.63639 7.62988 9.19954 7.81868 9.60645 8.19629C10.0133 8.57389 10.2168 9.08171 10.2168 9.71973C10.2168 10.4261 9.97428 11.0039 9.48926 11.4531C9.00423 11.8991 8.34668 12.1221 7.5166 12.1221C6.84277 12.1221 6.27637 12.0212 5.81738 11.8193Z" fill="#949494"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 779 B |
4
images/Pivot5_selected.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="8" fill="#0078D4"/>
|
||||
<path d="M5.81738 11.8193V10.501C6.2959 10.7939 6.80534 10.9404 7.3457 10.9404C7.7526 10.9404 8.06999 10.8444 8.29785 10.6523C8.52897 10.457 8.64453 10.1934 8.64453 9.86133C8.64453 9.16797 8.15462 8.82129 7.1748 8.82129C6.81348 8.82129 6.39681 8.84896 5.9248 8.9043L6.18848 4.99805H9.89453V6.25781H7.36523L7.26758 7.65918C7.51823 7.63965 7.7347 7.62988 7.91699 7.62988C8.63639 7.62988 9.19954 7.81868 9.60645 8.19629C10.0133 8.57389 10.2168 9.08171 10.2168 9.71973C10.2168 10.4261 9.97428 11.0039 9.48926 11.4531C9.00423 11.8991 8.34668 12.1221 7.5166 12.1221C6.84277 12.1221 6.27637 12.0212 5.81738 11.8193Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 773 B |
3
images/PostgreConnect.svg
Normal 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="M2.9 10.4C2.86142 9.92426 2.92976 9.44591 3.1 9C3.2722 8.57315 3.54698 8.19534 3.9 7.9L5.6 6.1L10.6 11.1L8.8 12.8C8.50466 13.153 8.12685 13.4278 7.7 13.6C7.29358 13.7932 6.84997 13.8955 6.4 13.9L5.2 13.7L4.3 13.2L1.7 15.7L1 15L3.5 12.4C3.29618 12.1222 3.12819 11.8198 3 11.5C2.92162 11.1388 2.88804 10.7694 2.9 10.4ZM6.4 12.9L7.3 12.7C7.60584 12.5584 7.87842 12.354 8.1 12.1L9.2 11.1L5.6 7.5L4.6 8.6L4 9.4C3.9125 9.72567 3.87872 10.0634 3.9 10.4C3.88428 10.7034 3.91806 11.0074 4 11.3L4.6 12.1L5.4 12.7L6.4 12.9ZM13.2 4.3C13.4038 4.5778 13.5718 4.88018 13.7 5.2C13.8153 5.59037 13.8824 5.99335 13.9 6.4C13.8955 6.84997 13.7932 7.29358 13.6 7.7C13.4278 8.12685 13.153 8.50466 12.8 8.8L11.1 10.6L6.1 5.6L7.9 3.9C8.19534 3.54698 8.57315 3.2722 9 3.1C9.44591 2.92976 9.92426 2.86142 10.4 2.9H11.5L12.4 3.4L15 1L15.7 1.7L13.2 4.3ZM12.1 8.1C12.354 7.87842 12.5584 7.60584 12.7 7.3C12.8142 7.01258 12.8818 6.70875 12.9 6.4C12.8909 6.0577 12.8232 5.71948 12.7 5.4L12.1 4.6L11.3 4.1C11.0284 3.9405 10.7136 3.87053 10.4 3.9H9.4L8.6 4.5L7.5 5.5L11.1 9.1L12.1 8.1Z" fill="#0078D4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
15
images/PowerShell.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.944458 10.9331H33.0556V28.836C33.0556 29.1205 32.9425 29.3934 32.7413 29.5946C32.5401 29.7958 32.2672 29.9089 31.9827 29.9089H2.01735C1.7328 29.9089 1.45991 29.7958 1.2587 29.5946C1.05749 29.3934 0.944458 29.1205 0.944458 28.836V10.9331Z" fill="url(#paint0_linear_1497_136679)"/>
|
||||
<path d="M2.02301 4.09132H31.977C32.1179 4.09132 32.2574 4.11907 32.3876 4.17299C32.5178 4.22691 32.636 4.30594 32.7357 4.40557C32.8353 4.50519 32.9143 4.62347 32.9682 4.75364C33.0222 4.8838 33.0499 5.02332 33.0499 5.16421V10.9329H0.944458V5.16421C0.944456 5.02284 0.972394 4.88286 1.02667 4.75232C1.08094 4.62178 1.16047 4.50326 1.2607 4.40356C1.36093 4.30385 1.47987 4.22494 1.6107 4.17136C1.74152 4.11778 1.88164 4.09058 2.02301 4.09132Z" fill="#0078D4"/>
|
||||
<path d="M5.33553 15.0706L6.03309 14.371C6.09216 14.3118 6.17235 14.2785 6.25601 14.2783C6.33967 14.2782 6.41995 14.3113 6.47919 14.3704L11.6394 19.5161C11.6982 19.5747 11.7449 19.6444 11.7769 19.7211C11.8088 19.7979 11.8252 19.8801 11.8254 19.9632C11.8255 20.0463 11.8092 20.1286 11.7775 20.2055C11.7458 20.2823 11.6993 20.3521 11.6407 20.4109L10.9444 21.1091L5.3375 15.518C5.27826 15.4589 5.24491 15.3788 5.24479 15.2951C5.24467 15.2114 5.27779 15.1312 5.33687 15.0719L5.33553 15.0706Z" fill="#F2F2F2"/>
|
||||
<path d="M6.1367 25.5068L5.43717 24.8092C5.37793 24.7501 5.34459 24.67 5.34447 24.5863C5.34435 24.5026 5.37747 24.4224 5.43654 24.3631L10.9463 18.8378L11.6445 19.534C11.7633 19.6525 11.8302 19.8133 11.8305 19.9812C11.8307 20.149 11.7643 20.31 11.6457 20.4289L6.57747 25.5115C6.5184 25.5707 6.43821 25.6041 6.35455 25.6042C6.27089 25.6043 6.19061 25.5712 6.13137 25.5121L6.1367 25.5068Z" fill="#E6E6E6"/>
|
||||
<path d="M22.1019 23.8755H14.1893C13.8857 23.8755 13.6396 24.1216 13.6396 24.4252V25.2355C13.6396 25.5391 13.8857 25.7852 14.1893 25.7852H22.1019C22.4054 25.7852 22.6515 25.5391 22.6515 25.2355V24.4252C22.6515 24.1216 22.4054 23.8755 22.1019 23.8755Z" fill="#F2F2F2"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1497_136679" x1="17" y1="29.9089" x2="17" y2="10.9331" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#32BEDD"/>
|
||||
<stop offset="0.175" stop-color="#32CAEA"/>
|
||||
<stop offset="0.41" stop-color="#32D2F2"/>
|
||||
<stop offset="0.775" stop-color="#32D4F5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
127
images/QuickstartComplete.svg
Normal file
@@ -0,0 +1,127 @@
|
||||
<svg width="68" height="72" viewBox="0 0 68 72" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<circle cx="34" cy="35" r="32" fill="#BFBFBF"/>
|
||||
<g style="mix-blend-mode:multiply" filter="url(#filter0_ddd_1497_137613)">
|
||||
<path d="M18 21.3999C18 19.1908 19.7909 17.3999 22 17.3999H43.6L50 23.7999V48.5999C50 50.809 48.2091 52.5999 46 52.5999H22C19.7909 52.5999 18 50.809 18 48.5999V21.3999Z" fill="white"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_b_1497_137613)">
|
||||
<path d="M18 21.3999C18 19.1908 19.7909 17.3999 22 17.3999H43.6L50 23.7999V48.5999C50 50.809 48.2091 52.5999 46 52.5999H22C19.7909 52.5999 18 50.809 18 48.5999V21.3999Z" fill="url(#paint0_linear_1497_137613)"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:hard-light" filter="url(#filter2_ii_1497_137613)">
|
||||
<path d="M18 21.3999C18 19.1908 19.7909 17.3999 22 17.3999H43.6L50 23.7999V48.5999C50 50.809 48.2091 52.5999 46 52.5999H22C19.7909 52.5999 18 50.809 18 48.5999V21.3999Z" fill="#808080"/>
|
||||
<path d="M18 21.3999C18 19.1908 19.7909 17.3999 22 17.3999H43.6L50 23.7999V48.5999C50 50.809 48.2091 52.5999 46 52.5999H22C19.7909 52.5999 18 50.809 18 48.5999V21.3999Z" fill="url(#pattern0)" fill-opacity="0.02"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:multiply" filter="url(#filter3_ddd_1497_137613)">
|
||||
<path d="M43.6006 17.3999L50.0006 23.7999H47.6006C45.3914 23.7999 43.6006 22.009 43.6006 19.7999V17.3999Z" fill="white"/>
|
||||
</g>
|
||||
<g filter="url(#filter4_b_1497_137613)">
|
||||
<path d="M43.6006 17.3999L50.0006 23.7999H47.6006C45.3914 23.7999 43.6006 22.009 43.6006 19.7999V17.3999Z" fill="url(#paint1_linear_1497_137613)"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:hard-light" filter="url(#filter5_ii_1497_137613)">
|
||||
<path d="M43.6006 17.3999L50.0006 23.7999H47.6006C45.3914 23.7999 43.6006 22.009 43.6006 19.7999V17.3999Z" fill="#808080"/>
|
||||
<path d="M43.6006 17.3999L50.0006 23.7999H47.6006C45.3914 23.7999 43.6006 22.009 43.6006 19.7999V17.3999Z" fill="url(#pattern1)" fill-opacity="0.02"/>
|
||||
</g>
|
||||
<circle cx="33.9994" cy="34.9999" r="9.6" fill="#BFBFBF"/>
|
||||
<path d="M30.7998 34.9998L33.3598 37.5598L37.8398 32.7598" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M41.6793 34.9998C41.6793 39.2414 38.2409 42.6798 33.9993 42.6798C29.7578 42.6798 26.3193 39.2414 26.3193 34.9998C26.3193 30.7583 29.7578 27.3198 33.9993 27.3198C38.2409 27.3198 41.6793 30.7583 41.6793 34.9998ZM33.9993 42.0398C37.8874 42.0398 41.0393 38.8879 41.0393 34.9998C41.0393 31.1117 37.8874 27.9598 33.9993 27.9598C30.1113 27.9598 26.9593 31.1117 26.9593 34.9998C26.9593 38.8879 30.1113 42.0398 33.9993 42.0398Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.3899 22.3199C30.0451 22.3769 29.7748 22.6471 29.7179 22.9919C29.661 22.6471 29.3907 22.3769 29.0459 22.3199C29.3907 22.263 29.661 21.9928 29.7179 21.6479C29.7748 21.9928 30.0451 22.263 30.3899 22.3199Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.5508 19.4641C30.2404 19.5779 30.781 20.1185 30.8948 20.8081C31.0086 20.1185 31.5491 19.5779 32.2388 19.4641C31.5491 19.3503 31.0086 18.8098 30.8948 18.1201C30.781 18.8098 30.2404 19.3503 29.5508 19.4641Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.20776 22.3199C7.55259 22.3769 7.82285 22.6471 7.87976 22.9919C7.93667 22.6471 8.20693 22.3769 8.55176 22.3199C8.20693 22.263 7.93667 21.9928 7.87976 21.6479C7.82285 21.9928 7.55259 22.263 7.20776 22.3199Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.04688 19.4641C7.35721 19.5779 6.81669 20.1185 6.70287 20.8081C6.58906 20.1185 6.04854 19.5779 5.35887 19.4641C6.04854 19.3503 6.58906 18.8098 6.70287 18.1201C6.81669 18.8098 7.35721 19.3503 8.04688 19.4641Z" fill="white"/>
|
||||
<defs>
|
||||
<filter id="filter0_ddd_1497_137613" x="0" y="0.399902" width="68" height="71.2002" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="3"/>
|
||||
<feGaussianBlur stdDeviation="2.5"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0.16 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1497_137613"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="1"/>
|
||||
<feGaussianBlur stdDeviation="9"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0.12 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow_1497_137613" result="effect2_dropShadow_1497_137613"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="6"/>
|
||||
<feGaussianBlur stdDeviation="5"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0.14 0"/>
|
||||
<feBlend mode="normal" in2="effect2_dropShadow_1497_137613" result="effect3_dropShadow_1497_137613"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect3_dropShadow_1497_137613" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter1_b_1497_137613" x="16" y="15.3999" width="36" height="39.2002" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feGaussianBlur in="BackgroundImage" stdDeviation="1"/>
|
||||
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_1497_137613"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_1497_137613" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter2_ii_1497_137613" x="18" y="17.3999" width="32" height="35.2002" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="1"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 0.5 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1497_137613"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="effect1_innerShadow_1497_137613" result="effect2_innerShadow_1497_137613"/>
|
||||
</filter>
|
||||
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1.875" height="1.70455">
|
||||
<use xlink:href="#image0_1497_137613" transform="scale(0.03125 0.0284091)"/>
|
||||
</pattern>
|
||||
<filter id="filter3_ddd_1497_137613" x="25.6006" y="0.399902" width="42.4004" height="42.3999" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="3"/>
|
||||
<feGaussianBlur stdDeviation="2.5"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0.16 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1497_137613"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="1"/>
|
||||
<feGaussianBlur stdDeviation="9"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0.12 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow_1497_137613" result="effect2_dropShadow_1497_137613"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="6"/>
|
||||
<feGaussianBlur stdDeviation="5"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0 0.25098 0 0 0 0.14 0"/>
|
||||
<feBlend mode="normal" in2="effect2_dropShadow_1497_137613" result="effect3_dropShadow_1497_137613"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect3_dropShadow_1497_137613" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter4_b_1497_137613" x="41.6006" y="15.3999" width="10.4004" height="10.3999" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feGaussianBlur in="BackgroundImage" stdDeviation="1"/>
|
||||
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_1497_137613"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_backgroundBlur_1497_137613" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter5_ii_1497_137613" x="43.6006" y="17.3999" width="6.40039" height="6.3999" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="1"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 0.5 0"/>
|
||||
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1497_137613"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="3"/>
|
||||
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 0 0.94902 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="effect1_innerShadow_1497_137613" result="effect2_innerShadow_1497_137613"/>
|
||||
</filter>
|
||||
<pattern id="pattern1" patternContentUnits="objectBoundingBox" width="9.375" height="9.375">
|
||||
<use xlink:href="#image0_1497_137613" transform="scale(0.15625)"/>
|
||||
</pattern>
|
||||
<linearGradient id="paint0_linear_1497_137613" x1="34" y1="52.5999" x2="34" y2="17.3999" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F2F2F2" stop-opacity="0.5"/>
|
||||
<stop offset="1" stop-color="#F2F2F2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1497_137613" x1="46.8006" y1="23.7999" x2="46.8006" y2="17.3999" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F2F2F2" stop-opacity="0.5"/>
|
||||
<stop offset="1" stop-color="#F2F2F2"/>
|
||||
</linearGradient>
|
||||
<image id="image0_1497_137613" width="60" height="60" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAM9ElEQVRoQ+WbS6iO3RvG1zv/HCcbiRhR2mJESlJbjBwmGwOH0laUHAaEJLXFwCG1FROHgcPEYbRFSaJtRKQYyCltTBxi/v77rc/v+e53efic+fo/k733+z7Peta67+u+7uu+19qNlFKz2Wwmr/fv36e//vorNRqN1NbWll68eJFWrlyZDh06lG/h856enrRq1ap0//79NH78eAbIn3d1deX7+HzcuHH5uSFDhqRdu3alhQsXpkGDBqXDhw/ncXzG927cuDHfV16My707d+5MW7Zsyb9znTx5Mi1atCiPe/r06Wo8xzl48GBavXp1y3Dt7e2pwWp56NSpU3nSXEz8xo0bqbOzM23evDlNnDgxL2Tq1Km1k+3u7k7Lly9PT548SW/evEkPHz7MBtFAdQu8d+9eNsqwYcOyUb2YC9/duXOneteFCxfS3r1708WLFz8a0+eYI89gEO5//fp1unLlSlq3bl3lFNbECrPJuPH58+dpxIgRlRVnzZqVX6K19LjeZIA4MV56/fr1NGDAgDyPjo6O/LyoiBbnfZs2baq8yj19fX1pypQpGRkgQRREBOqciB4dVaKD50Rhb29vdkS1YLyJl7HO4MGD0+XLl7N3gc6RI0eqiTsJ4CECNIgTixPw/hMnTqTFixe3IMTvWKjo0fjRc7t3706zZ8/OhovjAF++8xkd5LOEAWuIKMqQjhN89+5dOn78eMZ/tKyxVOcVJwu0jTPuZyy97SQcUzRhOIwsspxLNKjvNl4/CvQPCPVzx+jv70/Dhw9vCYNGT09Pk/iM+MeaWkUIAV/gFi8GBrY8Sxz698uXL/NnkNvYsWPTnDlzsvEw1s2bN9OBAwfSjBkzEvdJSBDP7du3M19w8TtXJCU9Vs5BD4O0JUuWZNLlAs4gFT7h3aAhQxpPMAm8Ey9wD2yYjGzM9yys9KYLEmJ4yEn7TLzH+OY7Gdh3C1vev2zZsvwxxnFhn+Iax/kc1+QF/2or8z5IZ/To0Wny5MkVAliQ6UYYx3DDyDDvvHnzsievXbuWn/UyhFywiAJtR48e/ZsHOjo6mmPGjKlYkcBnEuREXs53+/fvzy/CyqQMyArvleQUYyjGP+GBxy9dupQhbAqJKOFdkhqxt3Tp0gx7UEdoyCm+M44vWfEew6TUCDq1ysPANl7fwtpYG0P9yazdaGtra0I4JaUbd8ZNhBjW4n48Rkp59OhRTl2ID7xk/kNY4LlJkyZlDnCsFst++IPxZVXfheqD5UuOQRjhfREBhPWsOX/Hjh3p1q1bmf3hAuAMshpdXV1NYgnoMLCyMnpYliXutm/f3pJCmKQpRQka0wKLZTJOKhIPKcN7CZWRI0fmBZaCg3skQaD6+PHjKi8TFuRaWRjCO3/+fItyjA7KkMZbeoeBo3pSUzPpmFejAPF+BUQUIMZsjPlSoGDcuXPn5nDwvihY8CYeYg56TSaPjMwzIMpxTGOm1oxaIU3cQVaICAYDopIMi1aUlyJe65vHhSaeNTfrXYsOoMXCIJlt27alq1evZo/UhZEe5DsLG3OzaUsGVpJqDBELKrhAUKO3t7eJGIg52LSBIGEBwK2MQVHBQMat8GSBa9asqcaMOdkcavEQ41mmLlFmLEeol4a36MCQzI3Uxe979uypNEaultrb23PxwJfKRqSecatVzZHCxXtdJN6bOXNmjnEGJq6ix/gdj1DFYCCIJ5aKpqpoABdYR3LRKHXP+CyG3bdvX34XKM0xzAIV58aXrI3VYWEKCiSaLEjiVyczKESiCHAMIAaBRJXE5Bj72LFj+Z1CG/hj3GfPnuWxuCwzZW0NGHMwnxmjhMi5c+dyWMYFi04MnmNYSieOgTE5mZTiBQxhVLwKgWApjIQ3WZD1rF7XWLEQIMYhp1isx5RXpiyeFQVMHi2P0QkVuCVmB9DBfCLrR/hjCMIshygeVpXwUpnWuDWnxs4GL4OUtCZw9mJQvGbXIzYXuA+CAmJldeQETYexaGBBFC5kEn7yN8al42HBEtWi8tQCyLAk7lsaAN9DDixUIiG+5AVhKHNKiBb4kSPwEuixzuZZ5aIGjfk8xi7jYwAFEQw9bdq0KsXqzJbi4Uf0jEACsBUhpiI8YUcjavA6whGuos1c+yNSZwXpsm78nRUNRmBx9sVMV8Z0LCa4j0ahuiFygWQnpxBm2cNfIypUW8bcv4kK4jl2PCNRxU7ljxAVymLegVGePn2aVVds8LW0eBQIsVBwgU6uLM+IMYp0Cn+L+s/lVJQT+dq2kMwd+1r+zngwMhe/K3+j+lL6RqET05akRjMRovuItMpAjypJQrADSI0aCw7jMXqfOpq45hl+37p1a0ufq+w78Tf3WQkxpjFMakJBlXmdhSAvyRpohvisi1fONvr6+ppQvbm27DQYy7wYZo1dQxaB2ICg4IC6Ij0igglHUVAaKMZfbBZGweF4zENBRNjQf47awWf46TzhgLxgJ+EEzJ0xl5aCwclBGvPnz88vfPXqVe6QELOOESsWPGdBUZaAsSNZVx6a+33euRKrvA+nvX37NlddpCdJr1SROYZLC1owx8/5HQOws/A1iJBRo6W/BxGxHPwWRDQ6OzubeMaghtUspmU7oYq36G4SQxqlbpcA6FL2xfISOFG52EK1X4bxREFdj8wQI+Toczkmc1MolbkctHBNnz49x3VUYRVLl5UJZGWngoeBBpDEILZto4Uj9TMRmvkWG/G+qKycaCwO+MzOSUvh/mFTLcZmWcbyN+Nv2LAh801s8ltdNbq7u5t4jdiQVFRHDG4slOmonAxQw6vuH1nvWgTQRsJQFBEyP0bCqLGJT3HCFcvLWChopG/e5Sy3S7+m5YkUZaEQhdqYCdmWHTp0aO5kfGp30i7nr9ydrCAdC3IW8eDBg5wv3fON8EMEGBdCRZEgOohRvEXJJnKMe4wqm4ucKGwUO3W7FzFemSc9LutquIcGg8qOe4n9s2fP/pMygTS9rP9yjUu4xFqYharWYmcEhZZZOm6GS14xNUWp6HYHrB7jKJZ9piB+ooCsnhAGqrUyLgkD5WHMwyV3MKbziUWEmoH7ESWIEeSuY4FUmhUteVg2jenhd+3UI4YsTGKDXr2u4YR42fZhDeh0LhxaScv+/v4m3YefUXtq3T+pwqo8LDt/yZYjC1iwYEE6c+bMFx9viHu7tn49+CLD1533wDvferzB8tBeeY5ztlp+V7sFlWWeBnolu4K8UkUZbsDVwy8gydZyeX/LcYdG45827Zee6/gv7BCW5zpYNBekWLG0RxrqzlEQ3x5y4UFyLIwH+7rHq6jXwpAEn61fvz5L0oEDB2b1pGwsO6XUudbOP7NTmhsAdvyiTnXikfoVEDAlKooFlydwGAtRYUfDFMGChHCZDWTYsnjwb8VPhG3U/vwuCzM2AmTUqFGV2LAnlp+J57Qc0O5ibNvieSQkXqDqiduacQEMyguoj2NnIhYIHj7hyAIqiIY+hGLlxXimn5hf7a7EZoVHl2JruGxiMJ7FUNXEQ5LZ6I6BXx4g0YJUTbH7EDfUypzIMxhvxYoVLbuEThYDwyE06Lk88KZKgpwwCoxtk93QiOInGj4WL7G533LkAauycz5hwoTKg1iYM5Juf7pgVExdM436k8nTxyIc3IJVKztBJgxSPJjiuwkFDMD35Sm/eEAtanwQw44GIeYxKreP4rZPhjQdj7iJ/L27guXpuF+1K+hxKZzgxh/OscVDSK5du/bvBf/JBbvQjMeZWEhsJrYk63BulMOuHqYDBaCvti8tW8cDYm6nxj5yPJknUaCosLCNANnUOEVdyfagQfaWiHy3pWHcW2IsOcV4jWmQgsH2EqGk17mn2vLhBMDXHu5igK/ZCuF+YyxOtG4rpDzxV9bjEiC9sXgmWiRgEC7LRQmUWCau88E0zyHHDWmMYDzQqfQsZIQPEy4bAHomniLQa/HoBN7lqEXZCCh3DWKOLnNv2QtzbrFvFhsMZKKqeNBysCaTQSXFk3PAxdMAtHacvAornmP2KIMT+JMOuVULLvdpPeMRC3D3kfAMxohNAr3IzygzSWvm0CgIyi5p3PyK7zS2eTfNBOYljyhr4wGZKHvrjlll4VHqYHIg2xj0pNC/ETplH9rOhd60Vfon7lFlp9DT+n84NmwN39LTiv0qvBoLAzwXd+Bl2zqCiN6O8pM9H686eMc+VzyHZQUHlBEP36MEqyMPTORndz3Kfx8w9n7mP3WUXY+PtlpIK7+jyHdLE8N/yz9vxCLffz3yDBhc5GG5iqXVvPG0zZe2Pr/3+DGsj+z7FcePq+KBOtcNKCxTV2zD5ljLPraiwONGQNYyEU/ZuOMnwgUpat/KDkss+n/Ff9RUaclyirxHcX/37t3ctRBeLJZ6lQXZIKh6vR/+oy32gSWzWC19TkXVFQCQFedHovS1Vx1P6cUT+BrwU+dV/gf5nFrpDQ6oIQAAAABJRU5ErkJggg=="/>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
3
images/YoutubePlaceholder.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="334" height="154" viewBox="0 0 334 154" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="333" height="153" fill="#F3F2F1" stroke="#E1DFDD"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 188 B |
BIN
images/firewallRule.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
|
||||
<svg enable-background="new 0 0 256 256" height="256px" id="Layer_1" version="1.1" viewBox="0 0 256 256" width="256px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path stroke="white" stroke-width="0.5" fill="#b5a3a3" d="M179.199,38.399c0,1.637-0.625,3.274-1.875,4.524l-85.076,85.075l85.076,85.075c2.5,2.5,2.5,6.55,0,9.05s-6.55,2.5-9.05,0 l-89.601-89.6c-2.5-2.5-2.5-6.551,0-9.051l89.601-89.6c2.5-2.5,6.55-2.5,9.05,0C178.574,35.124,179.199,36.762,179.199,38.399z"/>
|
||||
<path stroke="white" stroke-width="0.5" fill="#000" d="M179.199,38.399c0,1.637-0.625,3.274-1.875,4.524l-85.076,85.075l85.076,85.075c2.5,2.5,2.5,6.55,0,9.05s-6.55,2.5-9.05,0 l-89.601-89.6c-2.5-2.5-2.5-6.551,0-9.051l89.601-89.6c2.5-2.5,6.55-2.5,9.05,0C178.574,35.124,179.199,36.762,179.199,38.399z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 652 B After Width: | Height: | Size: 649 B |
@@ -18,7 +18,8 @@ module.exports = {
|
||||
// clearMocks: false,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
collectCoverage: true,
|
||||
|
||||
collectCoverage: process.env.skipCodeCoverage === "true" ? false : true,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
collectCoverageFrom: ["src/**/*.{js,jsx,ts,tsx}"],
|
||||
@@ -36,9 +37,9 @@ module.exports = {
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 25,
|
||||
functions: 25,
|
||||
lines: 29,
|
||||
statements: 29,
|
||||
functions: 24,
|
||||
lines: 28,
|
||||
statements: 28,
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -3,337 +3,337 @@
|
||||
@import "../Common/Constants";
|
||||
|
||||
.query-panel {
|
||||
display: table;
|
||||
display: none;
|
||||
width: 100%;
|
||||
border-top: 1px solid #DDDDDD;
|
||||
/*[{environment-commandbar-toolbar-separator}]*/
|
||||
background-color: #ffffff;
|
||||
/*[{plugin-background-color}]*/
|
||||
padding: 2px 0px 0px 2px;
|
||||
resize: vertical;
|
||||
display: table;
|
||||
display: none;
|
||||
width: 100%;
|
||||
border-top: 1px solid #dddddd;
|
||||
/*[{environment-commandbar-toolbar-separator}]*/
|
||||
background-color: #ffffff;
|
||||
/*[{plugin-background-color}]*/
|
||||
padding: 2px 0px 0px 2px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.query-panel .row {
|
||||
display: table-row;
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.query-panel .row .cell {
|
||||
display: table-cell;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.query-panel.transition-in {
|
||||
display: table;
|
||||
top: 0px;
|
||||
-webkit-transition: top 2s linear;
|
||||
-ms-transition: top 2s linear;
|
||||
-moz-transition: top 2s linear;
|
||||
-khtml-transition: top 2s linear;
|
||||
-o-transition: top 2s linear;
|
||||
transition: top 2s linear;
|
||||
display: table;
|
||||
top: 0px;
|
||||
-webkit-transition: top 2s linear;
|
||||
-ms-transition: top 2s linear;
|
||||
-moz-transition: top 2s linear;
|
||||
-khtml-transition: top 2s linear;
|
||||
-o-transition: top 2s linear;
|
||||
transition: top 2s linear;
|
||||
}
|
||||
|
||||
.query-builder {
|
||||
width:100%;
|
||||
padding-right: @DefaultSpace;
|
||||
border-bottom: 1px solid @BaseMedium;
|
||||
margin-bottom: @DefaultSpace;
|
||||
width: 100%;
|
||||
padding-right: @DefaultSpace;
|
||||
border-bottom: 1px solid @BaseMedium;
|
||||
margin-bottom: @DefaultSpace;
|
||||
}
|
||||
|
||||
.query-builder-toolbar {
|
||||
background-color: #ffffff;
|
||||
/*[{plugin-background-color}]*/
|
||||
min-width: 600px;
|
||||
height: 30px;
|
||||
border-bottom: 1px solid #DDDDDD;
|
||||
/*[1px solid {environment-commandbar-toolbar-separator}]*/
|
||||
background-color: #ffffff;
|
||||
/*[{plugin-background-color}]*/
|
||||
min-width: 600px;
|
||||
height: 30px;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
/*[1px solid {environment-commandbar-toolbar-separator}]*/
|
||||
}
|
||||
|
||||
.query-builder-toolbar .query-toolbar-group {
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
margin: 2px 0px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
margin: 2px 0px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button {
|
||||
min-width: 0px;
|
||||
padding: 0px;
|
||||
margin-left: 2px;
|
||||
background-color: transparent;
|
||||
border: solid transparent;
|
||||
min-width: 0px;
|
||||
padding: 0px;
|
||||
margin-left: 2px;
|
||||
background-color: transparent;
|
||||
border: solid transparent;
|
||||
}
|
||||
|
||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button:active {
|
||||
outline: 2px solid dodgerblue;
|
||||
/*[2px solid {common-common-controls-button-border-hover}]*/
|
||||
outline: 2px solid dodgerblue;
|
||||
/*[2px solid {common-common-controls-button-border-hover}]*/
|
||||
}
|
||||
|
||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button:hover {
|
||||
background-color: #CCCEDB;
|
||||
/*[{common-controls-button-hover-background}]*/
|
||||
background-color: #cccedb;
|
||||
/*[{common-controls-button-hover-background}]*/
|
||||
}
|
||||
|
||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button.active {
|
||||
background-color: #E6E7ED;
|
||||
/*[{common-controls-inner-tab-active-background}]*/
|
||||
outline: none
|
||||
background-color: #e6e7ed;
|
||||
/*[{common-controls-inner-tab-active-background}]*/
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button:disabled,
|
||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button.disabled {
|
||||
background-color: #ffffff;
|
||||
/*[{common-controls-button-disabled-background}]*/
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
outline: none;
|
||||
opacity: 0.4;
|
||||
background-color: #ffffff;
|
||||
/*[{common-controls-button-disabled-background}]*/
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
outline: none;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.tableContainer {
|
||||
overflow: hidden;
|
||||
.flex-display();
|
||||
.flex-direction();
|
||||
overflow: hidden;
|
||||
.flex-display();
|
||||
.flex-direction();
|
||||
}
|
||||
|
||||
.tablesQueryTab{
|
||||
padding-left: @MediumSpace;
|
||||
width: 100%;
|
||||
margin-bottom:@LargeSpace;
|
||||
.tablesQueryTab {
|
||||
padding-left: @MediumSpace;
|
||||
width: 100%;
|
||||
margin-bottom: @LargeSpace;
|
||||
}
|
||||
|
||||
.entity-error-Img {
|
||||
width: @WarningErrorIconSize;
|
||||
height: @WarningErrorIconSize;
|
||||
margin: @DefaultSpace 0px 0px @SmallSpace;
|
||||
width: @WarningErrorIconSize;
|
||||
height: @WarningErrorIconSize;
|
||||
margin: @DefaultSpace 0px 0px @SmallSpace;
|
||||
}
|
||||
|
||||
.query-editor-panel {
|
||||
margin-right: 16px;
|
||||
margin-left: 16px;
|
||||
margin-top: 25px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
cursor: default;
|
||||
margin-right: 16px;
|
||||
margin-left: 16px;
|
||||
margin-top: 25px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.query-editor-text {
|
||||
width: 100%;
|
||||
margin: 2px;
|
||||
border: solid 1px #A9ACB3;
|
||||
/*[{plugin-textbox-disabled-color}]*/
|
||||
resize: none;
|
||||
margin-top: -39px;
|
||||
background-color: #ddd;
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
margin: 2px;
|
||||
border: solid 1px #a9acb3;
|
||||
/*[{plugin-textbox-disabled-color}]*/
|
||||
resize: none;
|
||||
margin-top: -39px;
|
||||
background-color: #ddd;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.error-bar {
|
||||
padding: @LargeSpace 34px @MediumSpace 24px;
|
||||
padding: @LargeSpace 34px @MediumSpace 24px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background-color: @BaseLow;
|
||||
padding: @DefaultSpace;
|
||||
display: inline-flex;
|
||||
background-color: @BaseLow;
|
||||
padding: @DefaultSpace;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
padding-left: @MediumSpace;
|
||||
padding-left: @MediumSpace;
|
||||
}
|
||||
|
||||
.query-editor-text-invalid {
|
||||
width: 100%;
|
||||
margin: 2px;
|
||||
border: 1px solid #e51400;
|
||||
resize: none;
|
||||
margin-top: -30px;
|
||||
width: 100%;
|
||||
margin: 2px;
|
||||
border: 1px solid #e51400;
|
||||
resize: none;
|
||||
margin-top: -30px;
|
||||
}
|
||||
|
||||
.query-editor-panel .warning-bar {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background-color: #ffffff;
|
||||
/*[{plugin-background-color}]*/
|
||||
position: absolute;
|
||||
top: -24px;
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
background-color: #ffffff;
|
||||
/*[{plugin-background-color}]*/
|
||||
position: absolute;
|
||||
top: -24px;
|
||||
}
|
||||
|
||||
.query-editor-panel .warning-bar .warning-message {
|
||||
display: inline-flex;
|
||||
padding-top: 2px;
|
||||
vertical-align: middle;
|
||||
display: inline-flex;
|
||||
padding-top: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.query-editor-panel .warning-bar .warning-message .warning-text {
|
||||
margin-left: 2px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.advanced-options-panel{
|
||||
margin-bottom: @DefaultSpace;
|
||||
.advanced-options-panel {
|
||||
margin-bottom: @DefaultSpace;
|
||||
}
|
||||
|
||||
.advanced-options-panel .advanced-heading .advanced-title {
|
||||
display: inline-flex;
|
||||
margin-left: 27px;
|
||||
margin-top: 10px;
|
||||
cursor: default;
|
||||
display: inline-flex;
|
||||
margin-left: 27px;
|
||||
margin-top: 10px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.advanced-options-panel .advanced-options {
|
||||
margin-left: 32px;
|
||||
margin-top: 5px;
|
||||
border: 1px solid transparent;
|
||||
margin-left: 32px;
|
||||
margin-top: 5px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 12px;
|
||||
border: 0;
|
||||
border-top: 1px solid #ccc;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 12px;
|
||||
border: 0;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.advanced-options-panel .advanced-options .top .top-input {
|
||||
width: 100px;
|
||||
word-spacing: normal;
|
||||
color: #1E1E1E;
|
||||
/*[{common-controls-button-foreground}]*/
|
||||
border: 1px solid #CCCEDB;
|
||||
/*[1px solid {plugin-textbox-border-color}]*/
|
||||
height: 20px;
|
||||
margin-left: 8px;
|
||||
width: 100px;
|
||||
word-spacing: normal;
|
||||
color: #1e1e1e;
|
||||
/*[{common-controls-button-foreground}]*/
|
||||
border: 1px solid #cccedb;
|
||||
/*[1px solid {plugin-textbox-border-color}]*/
|
||||
height: 20px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.advanced-options-panel .advanced-options .top .invalid-top {
|
||||
color: red;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.advanced-options-panel .advanced-options .select {
|
||||
margin-top: 18px;
|
||||
display: inline-flex;
|
||||
margin-top: 18px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.advanced-options-icon {
|
||||
margin-left: 2px;
|
||||
vertical-align: sub;
|
||||
margin-left: 2px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
.advanced-options-panel .advanced-options .select .select-options-text {
|
||||
margin-left: 4px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.advanced-options-panel .advanced-options .select .select-options-link {
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
margin-left: 4px;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.query-panel .row .column-headers .Field {
|
||||
padding-left: 95px;
|
||||
padding-right: 0px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: 95px;
|
||||
padding-right: 0px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.clause-table {
|
||||
border-spacing: 0px;
|
||||
display: table;
|
||||
width: 100%;
|
||||
margin-top: -3px;
|
||||
border-spacing: 0px;
|
||||
display: table;
|
||||
width: 100%;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
.clause-table-row {
|
||||
display: row;
|
||||
margin-bottom: 10px;
|
||||
display: row;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.clause-table-cell {
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.action-column>button,
|
||||
.group-control-header>button,
|
||||
.group-indicator-column>button {
|
||||
min-width: 20px;
|
||||
width: 20px;
|
||||
padding: 0px;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
cursor: pointer;
|
||||
.action-column > button,
|
||||
.group-control-header > button,
|
||||
.group-indicator-column > button {
|
||||
min-width: 20px;
|
||||
width: 20px;
|
||||
padding: 0px;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.group-control-header>button:disabled {
|
||||
min-width: 20px;
|
||||
width: 20px;
|
||||
padding: 0px;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
outline: none;
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
.group-control-header > button:disabled {
|
||||
min-width: 20px;
|
||||
width: 20px;
|
||||
padding: 0px;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
outline: none;
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.clause-table-field {
|
||||
width: 100%;
|
||||
border: 1px solid #bbbbbb;
|
||||
width: 100%;
|
||||
border: 1px solid #bbbbbb;
|
||||
}
|
||||
|
||||
.clause-table-cell button {
|
||||
height: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.clause-table-cell input[type="checkbox"] {
|
||||
padding: 0px;
|
||||
margin-bottom: 12px;
|
||||
padding: 0px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.and-or-svg {
|
||||
margin-top: -8px;
|
||||
margin-right: -5px;
|
||||
margin-top: -8px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
|
||||
.scroll-box {
|
||||
border-bottom: 1px transparent #DDD;
|
||||
/*[1px solid {plugin-table-border-color}]*/
|
||||
border-top: 1px transparent #DDD;
|
||||
/*[1px solid {plugin-table-border-color}]*/
|
||||
max-height: 20vh;
|
||||
width: 100%;
|
||||
border-bottom: 1px transparent #ddd;
|
||||
/*[1px solid {plugin-table-border-color}]*/
|
||||
border-top: 1px transparent #ddd;
|
||||
/*[1px solid {plugin-table-border-color}]*/
|
||||
max-height: 20vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.and-or-column,
|
||||
.and-or-header {
|
||||
min-width: 65px;
|
||||
padding-right: 10px;
|
||||
padding-left: 5px;
|
||||
min-width: 65px;
|
||||
padding-right: 10px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.operator-column,
|
||||
.operator-header {
|
||||
min-width: 65px;
|
||||
padding-right: 10px;
|
||||
min-width: 65px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.field-header,
|
||||
.field-column {
|
||||
min-width: 125px;
|
||||
padding-right: 10px;
|
||||
min-width: 125px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.type-header,
|
||||
.type-column {
|
||||
min-width: 85px;
|
||||
min-width: 85px;
|
||||
}
|
||||
|
||||
.and-or-column,
|
||||
@@ -345,41 +345,41 @@ input::-webkit-inner-spin-button {
|
||||
.type-header,
|
||||
.type-column,
|
||||
.action-header {
|
||||
padding-right: 10px;
|
||||
margin-bottom: 8px;
|
||||
padding-right: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.value-header,
|
||||
.value-column,
|
||||
.time-column {
|
||||
min-width: 230px;
|
||||
padding: 0px 4px 0px 0px;
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
min-width: 230px;
|
||||
padding: 0px 4px 0px 0px;
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.group-control-header,
|
||||
.group-control-column {
|
||||
min-width: 25px;
|
||||
text-align: right;
|
||||
min-width: 25px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.group-indicator-table {
|
||||
border-spacing: 0px;
|
||||
min-height: 24px
|
||||
border-spacing: 0px;
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.group-indicator-column {
|
||||
min-width: 21px;
|
||||
padding: 0px;
|
||||
border-style: none;
|
||||
height: 29px;
|
||||
min-width: 21px;
|
||||
padding: 0px;
|
||||
border-style: none;
|
||||
height: 29px;
|
||||
}
|
||||
|
||||
.clause-table-cell.action-column,
|
||||
.clause-table-cell.action-column,
|
||||
.clause-table-cell.action-header {
|
||||
min-width: 60px;
|
||||
padding-left: @SmallSpace;
|
||||
min-width: 60px;
|
||||
padding-left: @SmallSpace;
|
||||
}
|
||||
|
||||
.action-header,
|
||||
@@ -388,15 +388,14 @@ input::-webkit-inner-spin-button {
|
||||
.operator-header,
|
||||
.value-header,
|
||||
.and-or-header {
|
||||
padding-right: 4px;
|
||||
padding-bottom: 5px;
|
||||
padding-right: 4px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.header-background {
|
||||
background-color: #ffffff;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
|
||||
/*.type-header {
|
||||
padding-right: 4px;
|
||||
}
|
||||
@@ -410,112 +409,111 @@ input::-webkit-inner-spin-button {
|
||||
}*/
|
||||
|
||||
.clause-table-field[readonly] {
|
||||
background-color: #EEEEF2;
|
||||
/*[{plugin-table-header-background-color}]*/
|
||||
border: 1px solid #CCCEDB;
|
||||
/*[{plugin-table-border-color}]*/
|
||||
background-color: #eeeef2;
|
||||
/*[{plugin-table-header-background-color}]*/
|
||||
border: 1px solid #cccedb;
|
||||
/*[{plugin-table-border-color}]*/
|
||||
}
|
||||
|
||||
.addClause-title {
|
||||
/*[{common-common-controls-button-border-hover}]*/
|
||||
cursor: pointer;
|
||||
margin-left: -5px;
|
||||
/*[{common-common-controls-button-border-hover}]*/
|
||||
cursor: pointer;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.addClause {
|
||||
width: 125px;
|
||||
padding: 8px 0px 5px 5px;
|
||||
border: 1px solid #fff;
|
||||
margin-left: 5px;
|
||||
width: 125px;
|
||||
padding: 8px 0px 5px 5px;
|
||||
border: 1px solid #fff;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.addClause:hover {
|
||||
.hover();
|
||||
.hover();
|
||||
}
|
||||
|
||||
.addClause:active {
|
||||
.active();
|
||||
border: 1px dashed @AccentMedium;
|
||||
.active();
|
||||
border: 1px dashed @AccentMedium;
|
||||
}
|
||||
|
||||
.clause-table-row {
|
||||
min-width: 550px;
|
||||
width: 100%;
|
||||
min-width: 550px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.clause-table-field field-column {
|
||||
min-width: 75px;
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
min-width: 75px;
|
||||
height: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.clause-table-field field-input {
|
||||
min-width: 54px;
|
||||
margin-left: -78px;
|
||||
height: 25px;
|
||||
border: none;
|
||||
min-width: 54px;
|
||||
margin-left: -78px;
|
||||
height: 25px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.query-panel .row .spacing {
|
||||
padding-bottom: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.query-panel .divider.horizontal {
|
||||
height: 10px;
|
||||
width: 100%
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inline-div {
|
||||
display: inline
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.querybuilder-addpropertyImg,
|
||||
.querybuilder-cancelImg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-left: 3px;
|
||||
margin-bottom: 8px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-left: 3px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.addclauseProperty-Img {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 12px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.entity-Add-Cancel {
|
||||
padding: @DefaultSpace @SmallSpace @SmallSpace;
|
||||
cursor: pointer;
|
||||
// padding: @DefaultSpace @SmallSpace @SmallSpace;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.entity-Add-Cancel:hover {
|
||||
.hover();
|
||||
.hover();
|
||||
}
|
||||
|
||||
.entity-Add-Cancel:active {
|
||||
.active();
|
||||
.active();
|
||||
}
|
||||
|
||||
.query-builder-isDisabled {
|
||||
border: 1px solid #CCCEDB;
|
||||
color: #ccc;
|
||||
border: 1px solid #cccedb;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.edit-value-text {
|
||||
padding-left: @DefaultSpace;
|
||||
padding-left: @DefaultSpace;
|
||||
}
|
||||
|
||||
.expand-triangle {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.expand-triangle-right {
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.clause-table {
|
||||
@@ -524,4 +522,4 @@ input::-webkit-inner-spin-button {
|
||||
width: 100%;
|
||||
padding-top: 10px;
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
|
||||
@@ -2576,6 +2576,10 @@ a:link {
|
||||
.querydropdown.placeholderVisible {
|
||||
font-style: italic;
|
||||
}
|
||||
.querydropdown.placeholderVisible::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: #767474;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.querydropdown:hover {
|
||||
background-color: @AccentLow;
|
||||
@@ -3087,3 +3091,4 @@ a:link {
|
||||
background: white;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,17 @@
|
||||
overflow: auto;
|
||||
|
||||
.databaseHeader {
|
||||
padding: 1px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.collectionHeader {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.loadMoreHeader {
|
||||
color: RGB(5, 99, 193);
|
||||
}
|
||||
}
|
||||
|
||||
.notebookResourceTree {
|
||||
@@ -23,6 +28,4 @@
|
||||
.clickDisabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
21
package-lock.json
generated
@@ -402,6 +402,7 @@
|
||||
"dependencies": {
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-rest-pipeline": "^1.2.0",
|
||||
"@azure/core-tracing": "^1.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"jsbi": "^3.1.3",
|
||||
@@ -441,6 +442,17 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/cosmos/node_modules/@azure/core-tracing": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
|
||||
"integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/cosmos/node_modules/tslib": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
@@ -31464,6 +31476,7 @@
|
||||
"requires": {
|
||||
"@azure/core-auth": "^1.3.0",
|
||||
"@azure/core-rest-pipeline": "^1.2.0",
|
||||
"@azure/core-tracing": "^1.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"jsbi": "^3.1.3",
|
||||
@@ -31475,6 +31488,14 @@
|
||||
"uuid": "^8.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/core-tracing": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz",
|
||||
"integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==",
|
||||
"requires": {
|
||||
"tslib": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
"strict:find": "node ./strict-null-checks/find.js",
|
||||
"strict:add": "node ./strict-null-checks/auto-add.js",
|
||||
"compile:fullStrict": "tsc -p ./tsconfig.json --strictNullChecks",
|
||||
"generateARMClients": "npx ts-node --compiler-options '{\"module\":\"commonjs\"}' utils/armClientGenerator/generator.ts"
|
||||
"generateARMClients": "npx ts-node utils/armClientGenerator/generator.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -232,4 +232,4 @@
|
||||
"prettier": {
|
||||
"printWidth": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
11484
sampleData/queryCopilotSampleData.json
Normal file
@@ -1,6 +1,6 @@
|
||||
import React, { FunctionComponent, MutableRefObject, useEffect, useRef } from "react";
|
||||
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
|
||||
import { NormalizedEventKey } from "./Constants";
|
||||
|
||||
export interface CollapsedResourceTreeProps {
|
||||
@@ -36,7 +36,7 @@ export const CollapsedResourceTree: FunctionComponent<CollapsedResourceTreeProps
|
||||
id="collapseToggleLeftPaneButton"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="Expand Tree"
|
||||
aria-label={getApiShortDisplayName() + `Expand tree`}
|
||||
onClick={toggleLeftPaneExpanded}
|
||||
onKeyPress={onKeyPressToggleLeftPaneExpanded}
|
||||
ref={focusButton}
|
||||
@@ -45,7 +45,7 @@ export const CollapsedResourceTree: FunctionComponent<CollapsedResourceTreeProps
|
||||
<img className="arrowCollapsed" src={arrowLeftImg} alt="Expand" />
|
||||
</span>
|
||||
<span className="collectionCollapsed">
|
||||
<span>{userContext.apiType} API</span>
|
||||
<span>{getApiShortDisplayName()}</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -45,6 +45,8 @@ export class ArmResourceTypes {
|
||||
|
||||
export class BackendDefaults {
|
||||
public static partitionKeyKind = "Hash";
|
||||
public static partitionKeyMultiHash = "MultiHash";
|
||||
public static maxNumMultiHashPartition = 2;
|
||||
public static singlePartitionStorageInGb: string = "10";
|
||||
public static multiPartitionStorageInGb: string = "100";
|
||||
public static maxChangeFeedRetentionDuration: number = 10;
|
||||
@@ -139,7 +141,7 @@ export class Queries {
|
||||
public static UnlimitedPageOption: string = "unlimited";
|
||||
public static itemsPerPage: number = 100;
|
||||
public static unlimitedItemsPerPage: number = 100; // TODO: Figure out appropriate value so it works for accounts with a large number of partitions
|
||||
|
||||
public static containersPerPage: number = 50;
|
||||
public static QueryEditorMinHeightRatio: number = 0.1;
|
||||
public static QueryEditorMaxHeightRatio: number = 0.4;
|
||||
public static readonly DefaultMaxDegreeOfParallelism = 6;
|
||||
@@ -426,3 +428,91 @@ export class JunoEndpoints {
|
||||
public static readonly Prod = "https://tools.cosmos.azure.com";
|
||||
public static readonly Stage = "https://tools-staging.cosmos.azure.com";
|
||||
}
|
||||
|
||||
export const QueryCopilotSampleDatabaseId = "CopilotSampleDb";
|
||||
export const QueryCopilotSampleContainerId = "SampleContainer";
|
||||
|
||||
export const QueryCopilotSampleContainerSchema = {
|
||||
product: {
|
||||
sampleData: {
|
||||
id: "de6fadec-0384-43c8-93ea-16c0170b5460",
|
||||
name: "Premium Phone Mini (Red)",
|
||||
price: 652.04,
|
||||
category: "Electronics",
|
||||
description:
|
||||
"This Premium Phone Mini (Red) is designed by the company under agreement with the FCC, so we'd give it a pass in either direction, but no one should be using this handset without a compatible handset. All in all, this phone is one of the most affordable Android handsets out there at $100. Check them out.\n\n9. HTC One M9 4",
|
||||
stock: 74,
|
||||
countryOfOrigin: "Mexico",
|
||||
firstAvailable: "2018-07-07 17:42:28",
|
||||
priceHistory: [592.81],
|
||||
customerRatings: [
|
||||
{ username: "dannyhowell", stars: 1, date: "2022-03-12 17:01:23", verifiedUser: true },
|
||||
{ username: "lindsay26", stars: 1, date: "2022-12-29 07:18:20", verifiedUser: false },
|
||||
{ username: "smithmiguel", stars: 3, date: "2022-09-08 11:43:27", verifiedUser: false },
|
||||
{ username: "julie07", stars: 3, date: "2021-03-14 23:54:10", verifiedUser: true },
|
||||
{ username: "kelly93", stars: 3, date: "2022-11-05 12:45:43", verifiedUser: false },
|
||||
{ username: "katherinereynolds", stars: 2, date: "2022-09-14 11:49:36", verifiedUser: false },
|
||||
{ username: "chandlerkenneth", stars: 1, date: "2022-01-14 12:14:43", verifiedUser: true },
|
||||
],
|
||||
rareProperty: true,
|
||||
},
|
||||
schema: {
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
},
|
||||
name: {
|
||||
type: "string",
|
||||
},
|
||||
price: {
|
||||
type: "number",
|
||||
},
|
||||
category: {
|
||||
type: "string",
|
||||
},
|
||||
description: {
|
||||
type: "string",
|
||||
},
|
||||
stock: {
|
||||
type: "number",
|
||||
},
|
||||
countryOfOrigin: {
|
||||
type: "string",
|
||||
},
|
||||
firstAvailable: {
|
||||
type: "string",
|
||||
},
|
||||
priceHistory: {
|
||||
items: {
|
||||
type: "number",
|
||||
},
|
||||
type: "array",
|
||||
},
|
||||
customerRatings: {
|
||||
items: {
|
||||
properties: {
|
||||
username: {
|
||||
type: "string",
|
||||
},
|
||||
stars: {
|
||||
type: "number",
|
||||
},
|
||||
date: {
|
||||
type: "string",
|
||||
},
|
||||
verifiedUser: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
type: "object",
|
||||
},
|
||||
type: "array",
|
||||
},
|
||||
rareProperty: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
type: "object",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -55,7 +55,6 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
|
||||
label={entityValueLabel && entityValueLabel}
|
||||
className="addEntityTextField"
|
||||
id="entityValueId"
|
||||
autoFocus
|
||||
disabled={isEntityValueDisable}
|
||||
type={entityValueType}
|
||||
placeholder={entityValuePlaceholder}
|
||||
|
||||
14
src/Common/EnvironmentUtility.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import * as EnvironmentUtility from "./EnvironmentUtility";
|
||||
|
||||
describe("Environment Utility Test", () => {
|
||||
it("Test sample URI with /", () => {
|
||||
const uri = "test/";
|
||||
expect(EnvironmentUtility.normalizeArmEndpoint(uri)).toEqual(uri);
|
||||
});
|
||||
|
||||
it("Test sample URI without /", () => {
|
||||
const uri = "test";
|
||||
const expectedResult = "test/";
|
||||
expect(EnvironmentUtility.normalizeArmEndpoint(uri)).toEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
@@ -299,7 +299,7 @@ export function createMongoCollectionWithProxy(
|
||||
db: params.databaseId,
|
||||
coll: params.collectionId,
|
||||
pk: shardKey,
|
||||
offerThroughput: params.offerThroughput,
|
||||
offerThroughput: params.autoPilotMaxThroughput || params.offerThroughput,
|
||||
cd: params.createNewDatabase,
|
||||
st: params.databaseLevelThroughput,
|
||||
is: !!shardKey,
|
||||
@@ -309,7 +309,6 @@ export function createMongoCollectionWithProxy(
|
||||
rg: userContext.resourceGroup,
|
||||
dba: databaseAccount.name,
|
||||
isAutoPilot: !!params.autoPilotMaxThroughput,
|
||||
autoPilotThroughput: params.autoPilotMaxThroughput?.toString(),
|
||||
};
|
||||
|
||||
const endpoint = getFeatureEndpointOrDefault("createCollectionWithProxy");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as OfferUtility from "./OfferUtility";
|
||||
import { SDKOfferDefinition, Offer } from "../Contracts/DataModels";
|
||||
import { OfferResponse } from "@azure/cosmos";
|
||||
import { Offer, SDKOfferDefinition } from "../Contracts/DataModels";
|
||||
import * as OfferUtility from "./OfferUtility";
|
||||
|
||||
describe("parseSDKOfferResponse", () => {
|
||||
it("manual throughput", () => {
|
||||
@@ -31,6 +31,26 @@ describe("parseSDKOfferResponse", () => {
|
||||
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it("offerContent not defined", () => {
|
||||
const mockOfferDefinition = {
|
||||
id: "test",
|
||||
} as SDKOfferDefinition;
|
||||
|
||||
const mockResponse = {
|
||||
resource: mockOfferDefinition,
|
||||
} as OfferResponse;
|
||||
|
||||
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("offerDefinition is null", () => {
|
||||
const mockResponse = {
|
||||
resource: undefined,
|
||||
} as OfferResponse;
|
||||
|
||||
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("autoscale throughput", () => {
|
||||
const mockOfferDefinition = {
|
||||
content: {
|
||||
|
||||
@@ -6,6 +6,7 @@ import Explorer from "../Explorer/Explorer";
|
||||
import { ResourceTokenTree } from "../Explorer/Tree/ResourceTokenTree";
|
||||
import { ResourceTree } from "../Explorer/Tree/ResourceTree";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
|
||||
import { NormalizedEventKey } from "./Constants";
|
||||
|
||||
export interface ResourceTreeContainerProps {
|
||||
@@ -42,7 +43,7 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
|
||||
{/* Collections Window Title/Command Bar - Start */}
|
||||
<div className="collectiontitle">
|
||||
<div className="coltitle">
|
||||
<span className="titlepadcol">{userContext.apiType} API</span>
|
||||
<span className="titlepadcol">{getApiShortDisplayName()}</span>
|
||||
<div className="float-right">
|
||||
<span
|
||||
className="padimgcolrefresh"
|
||||
@@ -50,7 +51,7 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
|
||||
role="button"
|
||||
data-bind="click: onRefreshResourcesClick, clickBubble: false, event: { keypress: onRefreshDatabasesKeyPress }"
|
||||
tabIndex={0}
|
||||
aria-label="Refresh tree"
|
||||
aria-label={getApiShortDisplayName() + `Refresh tree`}
|
||||
title="Refresh tree"
|
||||
>
|
||||
<img className="refreshcol" src={refreshImg} alt="Refresh Tree" />
|
||||
@@ -62,7 +63,7 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
|
||||
onClick={toggleLeftPaneExpanded}
|
||||
onKeyPress={onKeyPressToggleLeftPaneExpanded}
|
||||
tabIndex={0}
|
||||
aria-label="Collapse Tree"
|
||||
aria-label={getApiShortDisplayName() + `Collapse Tree`}
|
||||
title="Collapse Tree"
|
||||
ref={focusButton}
|
||||
>
|
||||
|
||||
@@ -73,6 +73,17 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
|
||||
const sectionStackTokens: IStackTokens = { childrenGap: 12 };
|
||||
|
||||
const handleKeyPress = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||
if (event.key === "Enter" || event.key === "Space") {
|
||||
onEditEntity();
|
||||
}
|
||||
};
|
||||
const handleKeyPressdelete = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||
if (event.key === "Enter" || event.key === "Space") {
|
||||
onDeleteEntity();
|
||||
}
|
||||
};
|
||||
|
||||
const getEntityValueType = (): string => {
|
||||
const { Int, Smallint, Tinyint } = CassandraType;
|
||||
const { Double, Int32, Int64 } = TableType;
|
||||
@@ -126,12 +137,29 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
/>
|
||||
{!isEntityValueDisable && (
|
||||
<TooltipHost content="Edit property" id="editTooltip">
|
||||
<Image {...imageProps} src={EditIcon} alt="editEntity" id="editEntity" onClick={onEditEntity} />
|
||||
<div tabIndex={0}>
|
||||
<Image
|
||||
{...imageProps}
|
||||
src={EditIcon}
|
||||
alt="editEntity"
|
||||
onClick={onEditEntity}
|
||||
tabIndex={0}
|
||||
onKeyPress={handleKeyPress}
|
||||
/>
|
||||
</div>
|
||||
</TooltipHost>
|
||||
)}
|
||||
{isDeleteOptionVisible && userContext.apiType !== "Cassandra" && (
|
||||
<TooltipHost content="Delete property" id="deleteTooltip">
|
||||
<Image {...imageProps} src={DeleteIcon} alt="delete entity" id="deleteEntity" onClick={onDeleteEntity} />
|
||||
<Image
|
||||
{...imageProps}
|
||||
src={DeleteIcon}
|
||||
alt="delete entity"
|
||||
id="deleteEntity"
|
||||
onClick={onDeleteEntity}
|
||||
tabIndex={0}
|
||||
onKeyPress={handleKeyPressdelete}
|
||||
/>
|
||||
</TooltipHost>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -9,7 +9,7 @@ export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ children }:
|
||||
return (
|
||||
<span>
|
||||
<TooltipHost content={children}>
|
||||
<Icon iconName="Info" ariaLabel="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon iconName="Info" ariaLabel={children} className="panelInfoIcon" tabIndex={0} />
|
||||
</TooltipHost>
|
||||
</span>
|
||||
);
|
||||
|
||||
49
src/Common/UrlUtility.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import * as UrlUtility from "./UrlUtility";
|
||||
|
||||
describe("parseDocumentsPath", () => {
|
||||
it("empty resource path", () => {
|
||||
const resourcePath = "";
|
||||
|
||||
expect(UrlUtility.parseDocumentsPath(resourcePath)).toEqual({});
|
||||
});
|
||||
|
||||
it("resourcePath does not begin or end with /", () => {
|
||||
const resourcePath = "localhost/portal/home";
|
||||
const expectedResult = {
|
||||
type: "home",
|
||||
objectBody: {
|
||||
id: "portal",
|
||||
self: "/localhost/portal/home/",
|
||||
},
|
||||
};
|
||||
|
||||
expect(UrlUtility.parseDocumentsPath(resourcePath)).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it("resourcePath length is even", () => {
|
||||
const resourcePath = "/localhost/portal/src/home/";
|
||||
const expectedResult = {
|
||||
type: "src",
|
||||
objectBody: {
|
||||
id: "home",
|
||||
self: resourcePath,
|
||||
},
|
||||
};
|
||||
|
||||
expect(UrlUtility.parseDocumentsPath(resourcePath)).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it("createUri", () => {
|
||||
const baseUri = "http://foo.com/bar/";
|
||||
const relativeUri = "/index.html";
|
||||
const expectedUri = "http://foo.com/bar/index.html";
|
||||
|
||||
expect(UrlUtility.createUri(baseUri, relativeUri)).toEqual(expectedUri);
|
||||
});
|
||||
|
||||
it("should throw an error if baseUri is empty", () => {
|
||||
expect(() => {
|
||||
UrlUtility.createUri("", "/home");
|
||||
}).toThrow("baseUri is null or empty");
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,7 @@ import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationCons
|
||||
import { client } from "../CosmosClient";
|
||||
import { getEntityName } from "../DocumentUtility";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { getPartitionKeyValue } from "./getPartitionKeyValue";
|
||||
|
||||
export const deleteDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<void> => {
|
||||
const entityName: string = getEntityName();
|
||||
@@ -13,7 +14,7 @@ export const deleteDocument = async (collection: CollectionBase, documentId: Doc
|
||||
await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
|
||||
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||
.delete();
|
||||
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
|
||||
} catch (error) {
|
||||
|
||||
12
src/Common/dataAccess/getPartitionKeyValue.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { userContext } from "UserContext";
|
||||
import DocumentId from "../../Explorer/Tree/DocumentId";
|
||||
|
||||
export const getPartitionKeyValue = (documentId: DocumentId) => {
|
||||
if (userContext.apiType === "Tables" && documentId.partitionKeyValue?.length === 0) {
|
||||
return "";
|
||||
}
|
||||
if (documentId.partitionKeyValue?.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return documentId.partitionKeyValue;
|
||||
};
|
||||
@@ -96,6 +96,14 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
? parseInt(resource.minimumThroughput)
|
||||
: resource.minimumThroughput;
|
||||
const autoscaleSettings = resource.autoscaleSettings;
|
||||
const instantMaximumThroughput: number =
|
||||
typeof resource.instantMaximumThroughput === "string"
|
||||
? parseInt(resource.instantMaximumThroughput)
|
||||
: resource.instantMaximumThroughput;
|
||||
const softAllowedMaximumThroughput: number =
|
||||
typeof resource.softAllowedMaximumThroughput === "string"
|
||||
? parseInt(resource.softAllowedMaximumThroughput)
|
||||
: resource.softAllowedMaximumThroughput;
|
||||
|
||||
if (autoscaleSettings) {
|
||||
return {
|
||||
@@ -104,6 +112,8 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
manualThroughput: undefined,
|
||||
minimumThroughput,
|
||||
offerReplacePending: resource.offerReplacePending === "true",
|
||||
instantMaximumThroughput,
|
||||
softAllowedMaximumThroughput,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,6 +123,8 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
manualThroughput: resource.throughput,
|
||||
minimumThroughput,
|
||||
offerReplacePending: resource.offerReplacePending === "true",
|
||||
instantMaximumThroughput,
|
||||
softAllowedMaximumThroughput,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Queries } from "Common/Constants";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
@@ -31,6 +32,35 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
|
||||
}
|
||||
}
|
||||
|
||||
export async function readCollectionsWithPagination(
|
||||
databaseId: string,
|
||||
continuationToken?: string
|
||||
): Promise<DataModels.CollectionsWithPagination> {
|
||||
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
|
||||
try {
|
||||
const sdkResponse = await client()
|
||||
.database(databaseId)
|
||||
.containers.query(
|
||||
{ query: "SELECT * FROM c" },
|
||||
{
|
||||
continuationToken,
|
||||
maxItemCount: Queries.containersPerPage,
|
||||
}
|
||||
)
|
||||
.fetchNext();
|
||||
const collectionsWithPagination: DataModels.CollectionsWithPagination = {
|
||||
collections: sdkResponse.resources as DataModels.Collection[],
|
||||
continuationToken: sdkResponse.continuationToken,
|
||||
};
|
||||
return collectionsWithPagination;
|
||||
} catch (error) {
|
||||
handleError(error, "ReadCollections", `Error while querying containers for database ${databaseId}`);
|
||||
throw error;
|
||||
} finally {
|
||||
clearMessage();
|
||||
}
|
||||
}
|
||||
|
||||
async function readCollectionsWithARM(databaseId: string): Promise<DataModels.Collection[]> {
|
||||
let rpResponse;
|
||||
|
||||
|
||||
@@ -68,6 +68,14 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
|
||||
? parseInt(resource.minimumThroughput)
|
||||
: resource.minimumThroughput;
|
||||
const autoscaleSettings = resource.autoscaleSettings;
|
||||
const instantMaximumThroughput: number =
|
||||
typeof resource.instantMaximumThroughput === "string"
|
||||
? parseInt(resource.instantMaximumThroughput)
|
||||
: resource.instantMaximumThroughput;
|
||||
const softAllowedMaximumThroughput: number =
|
||||
typeof resource.softAllowedMaximumThroughput === "string"
|
||||
? parseInt(resource.softAllowedMaximumThroughput)
|
||||
: resource.softAllowedMaximumThroughput;
|
||||
|
||||
if (autoscaleSettings) {
|
||||
return {
|
||||
@@ -76,6 +84,8 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
|
||||
manualThroughput: undefined,
|
||||
minimumThroughput,
|
||||
offerReplacePending: resource.offerReplacePending === "true",
|
||||
instantMaximumThroughput,
|
||||
softAllowedMaximumThroughput,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -85,6 +95,8 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
|
||||
manualThroughput: resource.throughput,
|
||||
minimumThroughput,
|
||||
offerReplacePending: resource.offerReplacePending === "true",
|
||||
instantMaximumThroughput,
|
||||
softAllowedMaximumThroughput,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { HttpHeaders } from "../Constants";
|
||||
import { client } from "../CosmosClient";
|
||||
import { getEntityName } from "../DocumentUtility";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { getPartitionKeyValue } from "./getPartitionKeyValue";
|
||||
|
||||
export const readDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<Item> => {
|
||||
const entityName = getEntityName();
|
||||
@@ -21,8 +22,7 @@ export const readDocument = async (collection: CollectionBase, documentId: Docum
|
||||
const response = await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
// use undefined if the partitionKeyValue is empty
|
||||
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
|
||||
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||
.read(options);
|
||||
|
||||
return response?.resource;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||
import { CloudError, SqlStoredProcedureListResult } from "Utils/arm/generatedClients/cosmos/types";
|
||||
import { AuthType } from "../../AuthType";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { listSqlStoredProcedures } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
import { listSqlStoredProcedures } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
|
||||
import { client } from "../CosmosClient";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
|
||||
@@ -24,7 +25,13 @@ export async function readStoredProcedures(
|
||||
databaseId,
|
||||
collectionId
|
||||
);
|
||||
return rpResponse?.value?.map((sproc) => sproc.properties?.resource as StoredProcedureDefinition & Resource);
|
||||
const listResult = rpResponse as SqlStoredProcedureListResult;
|
||||
if (listResult) {
|
||||
return listResult?.value?.map((sproc) => sproc.properties?.resource as StoredProcedureDefinition & Resource);
|
||||
}
|
||||
|
||||
const cloudError = rpResponse as CloudError;
|
||||
throw new Error(cloudError?.error?.message);
|
||||
}
|
||||
|
||||
const response = await client()
|
||||
|
||||
@@ -6,6 +6,7 @@ import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationCons
|
||||
import { client } from "../CosmosClient";
|
||||
import { getEntityName } from "../DocumentUtility";
|
||||
import { handleError } from "../ErrorHandlingUtils";
|
||||
import { getPartitionKeyValue } from "./getPartitionKeyValue";
|
||||
|
||||
export const updateDocument = async (
|
||||
collection: CollectionBase,
|
||||
@@ -25,7 +26,7 @@ export const updateDocument = async (
|
||||
const response = await client()
|
||||
.database(collection.databaseId)
|
||||
.container(collection.id())
|
||||
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
|
||||
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||
.replace(newDocument, options);
|
||||
|
||||
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
|
||||
|
||||
@@ -33,6 +33,8 @@ export interface DatabaseAccountExtendedProperties {
|
||||
privateEndpointConnections?: unknown[];
|
||||
capacity?: { totalThroughputLimit: number };
|
||||
locations?: DatabaseAccountResponseLocation[];
|
||||
postgresqlEndpoint?: string;
|
||||
publicNetworkAccess?: string;
|
||||
}
|
||||
|
||||
export interface DatabaseAccountResponseLocation {
|
||||
@@ -154,6 +156,11 @@ export interface Collection extends Resource {
|
||||
requestSchema?: () => void;
|
||||
}
|
||||
|
||||
export interface CollectionsWithPagination {
|
||||
continuationToken?: string;
|
||||
collections?: Collection[];
|
||||
}
|
||||
|
||||
export interface Database extends Resource {
|
||||
collections?: Collection[];
|
||||
}
|
||||
@@ -235,6 +242,8 @@ export interface Offer {
|
||||
minimumThroughput: number | undefined;
|
||||
offerDefinition?: SDKOfferDefinition;
|
||||
offerReplacePending: boolean;
|
||||
instantMaximumThroughput?: number;
|
||||
softAllowedMaximumThroughput?: number;
|
||||
}
|
||||
|
||||
export interface SDKOfferDefinition extends Resource {
|
||||
@@ -481,6 +490,10 @@ export interface IMaxUsersPerDbAccountExceeded extends IPhoenixError {
|
||||
}
|
||||
|
||||
export interface IPhoenixConnectionInfoResult {
|
||||
readonly phoenixServiceInfo?: IPhoenixServiceInfo;
|
||||
}
|
||||
|
||||
export interface IPhoenixServiceInfo {
|
||||
readonly authToken?: string;
|
||||
readonly phoenixServiceUrl?: string;
|
||||
readonly forwardingId?: string;
|
||||
@@ -562,8 +575,17 @@ export interface ContainerConnectionInfo {
|
||||
//need to add ram and rom info
|
||||
}
|
||||
|
||||
export interface PostgresFirewallRule {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
properties: {
|
||||
startIpAddress: string;
|
||||
endIpAddress: string;
|
||||
};
|
||||
}
|
||||
|
||||
export enum PhoenixErrorType {
|
||||
UserMissingPermissionsError = "UserMissingPermissionsError",
|
||||
MaxAllocationTimeExceeded = "MaxAllocationTimeExceeded",
|
||||
MaxDbAccountsPerUserExceeded = "MaxDbAccountsPerUserExceeded",
|
||||
MaxUsersPerDbAccountExceeded = "MaxUsersPerDbAccountExceeded",
|
||||
@@ -572,4 +594,5 @@ export enum PhoenixErrorType {
|
||||
SubscriptionNotAllowed = "SubscriptionNotAllowed",
|
||||
UnknownError = "UnknownError",
|
||||
PhoenixFlightFallback = "PhoenixFlightFallback",
|
||||
UserMissingPermissionsError = "UserMissingPermissionsError",
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ export enum MessageTypes {
|
||||
RefreshDatabaseAccount,
|
||||
CloseTab,
|
||||
OpenQuickstartBlade,
|
||||
OpenPostgreSQLPasswordReset,
|
||||
OpenPostgresNetworkingBlade,
|
||||
OpenCosmosDBNetworkingBlade,
|
||||
}
|
||||
|
||||
export { Versions, ActionContracts, Diagnostics };
|
||||
|
||||
@@ -87,13 +87,13 @@ export interface Database extends TreeNode {
|
||||
isDatabaseExpanded: ko.Observable<boolean>;
|
||||
isDatabaseShared: ko.Computed<boolean>;
|
||||
isSampleDB?: boolean;
|
||||
|
||||
collectionsContinuationToken?: string;
|
||||
selectedSubnodeKind: ko.Observable<CollectionTabKind>;
|
||||
|
||||
expandDatabase(): Promise<void>;
|
||||
collapseDatabase(): void;
|
||||
|
||||
loadCollections(): Promise<void>;
|
||||
loadCollections(restart?: boolean): Promise<void>;
|
||||
findCollectionWithId(collectionId: string): Collection;
|
||||
openAddCollection(database: Database, event: MouseEvent): void;
|
||||
onSettingsClick: () => void;
|
||||
@@ -186,7 +186,6 @@ export interface Collection extends CollectionBase {
|
||||
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
|
||||
uploadFiles(fileList: FileList): Promise<{ data: UploadDetailsRecord[] }>;
|
||||
|
||||
getLabel(): string;
|
||||
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
|
||||
}
|
||||
|
||||
@@ -372,6 +371,7 @@ export enum TerminalKind {
|
||||
Default = 0,
|
||||
Mongo = 1,
|
||||
Cassandra = 2,
|
||||
Postgres = 3,
|
||||
}
|
||||
|
||||
export interface DataExplorerInputsFrame {
|
||||
@@ -395,6 +395,11 @@ export interface DataExplorerInputsFrame {
|
||||
sharedThroughputDefault?: number;
|
||||
dataExplorerVersion?: string;
|
||||
defaultCollectionThroughput?: CollectionCreationDefaults;
|
||||
isPostgresAccount?: boolean;
|
||||
isReplica?: boolean;
|
||||
clientIpAddress?: string;
|
||||
// TODO: Update this param in the OSS extension to remove isFreeTier, isMarlinServerGroup, and make nodes a flat array instead of an nested array
|
||||
connectionStringParams?: any;
|
||||
flights?: readonly string[];
|
||||
features?: {
|
||||
[key: string]: string;
|
||||
|
||||
@@ -11,9 +11,9 @@ import DeleteTriggerIcon from "../../images/DeleteTrigger.svg";
|
||||
import DeleteUDFIcon from "../../images/DeleteUDF.svg";
|
||||
import HostedTerminalIcon from "../../images/Hosted-Terminal.svg";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { useSidePanel } from "../hooks/useSidePanel";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
||||
import { useSidePanel } from "../hooks/useSidePanel";
|
||||
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
||||
import Explorer from "./Explorer";
|
||||
import { useNotebook } from "./Notebook/useNotebook";
|
||||
@@ -152,7 +152,7 @@ export const createStoreProcedureContextMenuItems = (
|
||||
{
|
||||
iconSrc: DeleteSprocIcon,
|
||||
onClick: () => storedProcedure.delete(),
|
||||
label: "Delete Store Procedure",
|
||||
label: "Delete Stored Procedure",
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
|
||||
<img
|
||||
className="expandCollapseIcon"
|
||||
src={this.state.isExpanded ? TriangleDownIcon : TriangleRightIcon}
|
||||
alt="Hide"
|
||||
alt={this.state.isExpanded ? `${this.props.title} hide` : `${this.props.title} expand`}
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
/>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Wrapper around Notebook server terminal
|
||||
*/
|
||||
|
||||
import { useTerminal } from "hooks/useTerminal";
|
||||
import postRobot from "post-robot";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
@@ -40,6 +41,7 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
||||
|
||||
handleFrameLoad(event: React.SyntheticEvent<HTMLIFrameElement, Event>): void {
|
||||
this.terminalWindow = (event.target as HTMLIFrameElement).contentWindow;
|
||||
useTerminal.getState().setTerminal(this.terminalWindow);
|
||||
this.sendPropsToTerminalFrame();
|
||||
}
|
||||
|
||||
@@ -74,6 +76,8 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
||||
this.props.databaseAccount?.properties.mongoEndpoint || this.props.databaseAccount?.properties.documentEndpoint;
|
||||
} else if (StringUtils.endsWith(notebookServerEndpoint, "cassandra")) {
|
||||
terminalEndpoint = this.props.databaseAccount?.properties.cassandraEndpoint;
|
||||
} else if (StringUtils.endsWith(notebookServerEndpoint, "postgresql")) {
|
||||
return this.props.databaseAccount?.properties.postgresqlEndpoint;
|
||||
}
|
||||
|
||||
if (terminalEndpoint) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HoverCard, HoverCardType, Icon, Label, Link, Stack } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { Icon, Label, Stack, HoverCard, HoverCardType, Link } from "@fluentui/react";
|
||||
import { CodeOfConductEndpoints } from "../../../../Common/Constants";
|
||||
import "./InfoComponent.less";
|
||||
|
||||
@@ -41,7 +41,7 @@ export class InfoComponent extends React.Component<InfoComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<HoverCard plainCardProps={{ onRenderPlainCard: this.onHover }} instantOpenOnClick type={HoverCardType.plain}>
|
||||
<div className="infoPanelMain">
|
||||
<div className="infoPanelMain" tabIndex={0}>
|
||||
<Icon className="infoIconMain" iconName="Help" styles={{ root: { verticalAlign: "middle" } }} />
|
||||
<Label className="infoLabelMain">Help</Label>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@ exports[`InfoComponent renders 1`] = `
|
||||
>
|
||||
<div
|
||||
className="infoPanelMain"
|
||||
tabIndex={0}
|
||||
>
|
||||
<Icon
|
||||
className="infoIconMain"
|
||||
|
||||
@@ -1,46 +1,29 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { IColumn, Text } from "@fluentui/react";
|
||||
import {
|
||||
getAutoPilotV3SpendElement,
|
||||
getEstimatedSpendingElement,
|
||||
manualToAutoscaleDisclaimerElement,
|
||||
ttlWarning,
|
||||
indexingPolicynUnsavedWarningMessage,
|
||||
updateThroughputBeyondLimitWarningMessage,
|
||||
updateThroughputDelayedApplyWarningMessage,
|
||||
getThroughputApplyDelayedMessage,
|
||||
getThroughputApplyShortDelayMessage,
|
||||
getThroughputApplyLongDelayMessage,
|
||||
getToolTipContainer,
|
||||
conflictResolutionCustomToolTip,
|
||||
changeFeedPolicyToolTip,
|
||||
conflictResolutionLwwTooltip,
|
||||
mongoIndexingPolicyDisclaimer,
|
||||
mongoIndexingPolicyAADError,
|
||||
mongoIndexTransformationRefreshingMessage,
|
||||
renderMongoIndexTransformationRefreshMessage,
|
||||
ManualEstimatedSpendingDisplayProps,
|
||||
PriceBreakdown,
|
||||
changeFeedPolicyToolTip,
|
||||
conflictResolutionCustomToolTip,
|
||||
conflictResolutionLwwTooltip,
|
||||
getEstimatedSpendingElement,
|
||||
getRuPriceBreakdown,
|
||||
getThroughputApplyDelayedMessage,
|
||||
getThroughputApplyLongDelayMessage,
|
||||
getThroughputApplyShortDelayMessage,
|
||||
getToolTipContainer,
|
||||
indexingPolicynUnsavedWarningMessage,
|
||||
manualToAutoscaleDisclaimerElement,
|
||||
mongoIndexTransformationRefreshingMessage,
|
||||
mongoIndexingPolicyAADError,
|
||||
mongoIndexingPolicyDisclaimer,
|
||||
renderMongoIndexTransformationRefreshMessage,
|
||||
ttlWarning,
|
||||
updateThroughputDelayedApplyWarningMessage,
|
||||
} from "./SettingsRenderUtils";
|
||||
|
||||
class SettingsRenderUtilsTestComponent extends React.Component {
|
||||
public render(): JSX.Element {
|
||||
const estimatedSpendingColumns: IColumn[] = [
|
||||
{ key: "costType", name: "", fieldName: "costType", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{ key: "hourly", name: "Hourly", fieldName: "hourly", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{ key: "daily", name: "Daily", fieldName: "daily", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{ key: "monthly", name: "Monthly", fieldName: "monthly", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
];
|
||||
const estimatedSpendingItems: ManualEstimatedSpendingDisplayProps[] = [
|
||||
{
|
||||
costType: <Text>Current Cost</Text>,
|
||||
hourly: <Text>$ 1.02</Text>,
|
||||
daily: <Text>$ 24.48</Text>,
|
||||
monthly: <Text>$ 744.6</Text>,
|
||||
},
|
||||
];
|
||||
const costElement: JSX.Element = <></>;
|
||||
const priceBreakdown: PriceBreakdown = {
|
||||
hourlyPrice: 1.02,
|
||||
dailyPrice: 24.48,
|
||||
@@ -52,17 +35,11 @@ class SettingsRenderUtilsTestComponent extends React.Component {
|
||||
|
||||
return (
|
||||
<>
|
||||
{getAutoPilotV3SpendElement(1000, false)}
|
||||
{getAutoPilotV3SpendElement(undefined, false)}
|
||||
{getAutoPilotV3SpendElement(1000, true)}
|
||||
{getAutoPilotV3SpendElement(undefined, true)}
|
||||
|
||||
{getEstimatedSpendingElement(estimatedSpendingColumns, estimatedSpendingItems, 1000, 2, priceBreakdown, false)}
|
||||
{getEstimatedSpendingElement(costElement, 1000, 2, priceBreakdown, false)}
|
||||
|
||||
{manualToAutoscaleDisclaimerElement}
|
||||
{ttlWarning}
|
||||
{indexingPolicynUnsavedWarningMessage}
|
||||
{updateThroughputBeyondLimitWarningMessage}
|
||||
{updateThroughputDelayedApplyWarningMessage}
|
||||
|
||||
{getThroughputApplyDelayedMessage(false, 1000, "RU/s", "sampleDb", "sampleCollection", 2000)}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import {
|
||||
DetailsList,
|
||||
DetailsListLayoutMode,
|
||||
DetailsRow,
|
||||
ICheckboxStyles,
|
||||
IChoiceGroupStyles,
|
||||
IColumn,
|
||||
IDetailsColumnStyles,
|
||||
IDetailsListStyles,
|
||||
IDetailsRowProps,
|
||||
@@ -20,7 +17,6 @@ import {
|
||||
Link,
|
||||
MessageBar,
|
||||
MessageBarType,
|
||||
SelectionMode,
|
||||
Spinner,
|
||||
SpinnerSize,
|
||||
Stack,
|
||||
@@ -28,8 +24,7 @@ import {
|
||||
} from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { StyleConstants, Urls } from "../../../Common/Constants";
|
||||
import { AutopilotDocumentation, hoursInAMonth } from "../../../Shared/Constants";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import { hoursInAMonth } from "../../../Shared/Constants";
|
||||
import {
|
||||
computeRUUsagePriceHourly,
|
||||
estimatedCostDisclaimer,
|
||||
@@ -103,6 +98,10 @@ export const checkBoxAndInputStackProps: Partial<IStackProps> = {
|
||||
tokens: { childrenGap: 10 },
|
||||
};
|
||||
|
||||
export const relaxedSpacingStackProps: Partial<IStackProps> = {
|
||||
tokens: { childrenGap: 20 },
|
||||
};
|
||||
|
||||
export const toolTipLabelStackTokens: IStackTokens = {
|
||||
childrenGap: 6,
|
||||
};
|
||||
@@ -174,41 +173,6 @@ export function onRenderRow(props: IDetailsRowProps): JSX.Element {
|
||||
return <DetailsRow {...props} styles={transparentDetailsRowStyles} />;
|
||||
}
|
||||
|
||||
export const getAutoPilotV3SpendElement = (
|
||||
maxAutoPilotThroughputSet: number,
|
||||
isDatabaseThroughput: boolean,
|
||||
requestUnitsUsageCostElement?: JSX.Element
|
||||
): JSX.Element => {
|
||||
if (!maxAutoPilotThroughputSet) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const resource: string = isDatabaseThroughput ? "database" : "container";
|
||||
return (
|
||||
<>
|
||||
<Text>
|
||||
Your {resource} throughput will automatically scale from{" "}
|
||||
<b>
|
||||
{AutoPilotUtils.getMinRUsBasedOnUserInput(maxAutoPilotThroughputSet)} RU/s (10% of max RU/s) -{" "}
|
||||
{maxAutoPilotThroughputSet} RU/s
|
||||
</b>{" "}
|
||||
based on usage.
|
||||
<br />
|
||||
</Text>
|
||||
{requestUnitsUsageCostElement}
|
||||
<Text>
|
||||
After the first {AutoPilotUtils.getStorageBasedOnUserInput(maxAutoPilotThroughputSet)} GB of data stored, the
|
||||
max RU/s will be automatically upgraded based on the new storage value.
|
||||
<Link href={AutopilotDocumentation.Url} target="_blank">
|
||||
{" "}
|
||||
Learn more
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getRuPriceBreakdown = (
|
||||
throughput: number,
|
||||
serverId: string,
|
||||
@@ -238,8 +202,7 @@ export const getRuPriceBreakdown = (
|
||||
};
|
||||
|
||||
export const getEstimatedSpendingElement = (
|
||||
estimatedSpendingColumns: IColumn[],
|
||||
estimatedSpendingItems: EstimatedSpendingDisplayProps[],
|
||||
costElement: JSX.Element,
|
||||
throughput: number,
|
||||
numberOfRegions: number,
|
||||
priceBreakdown: PriceBreakdown,
|
||||
@@ -247,22 +210,25 @@ export const getEstimatedSpendingElement = (
|
||||
): JSX.Element => {
|
||||
const ruRange: string = isAutoscale ? throughput / 10 + " RU/s - " : "";
|
||||
return (
|
||||
<Stack {...addMongoIndexStackProps} styles={mediumWidthStackStyles}>
|
||||
<DetailsList
|
||||
disableSelectionZone
|
||||
items={estimatedSpendingItems}
|
||||
columns={estimatedSpendingColumns}
|
||||
selectionMode={SelectionMode.none}
|
||||
layoutMode={DetailsListLayoutMode.justified}
|
||||
onRenderRow={onRenderRow}
|
||||
/>
|
||||
<Text id="throughputSpendElement">
|
||||
({"regions: "} {numberOfRegions}, {ruRange}
|
||||
{throughput} RU/s, {priceBreakdown.currencySign}
|
||||
{priceBreakdown.pricePerRu}/RU)
|
||||
</Text>
|
||||
<Text>
|
||||
<em>{estimatedCostDisclaimer}</em>
|
||||
<Stack>
|
||||
<Text style={{ fontWeight: 600 }}>Cost estimate*</Text>
|
||||
{costElement}
|
||||
<Text style={{ fontWeight: 600, marginTop: 15 }}>How we calculate this</Text>
|
||||
<Stack id="throughputSpendElement" style={{ marginTop: 5 }}>
|
||||
<span>
|
||||
{numberOfRegions} region{numberOfRegions > 1 && <span>s</span>}
|
||||
</span>
|
||||
<span>
|
||||
{ruRange}
|
||||
{throughput} RU/s
|
||||
</span>
|
||||
<span>
|
||||
{priceBreakdown.currencySign}
|
||||
{priceBreakdown.pricePerRu}/RU
|
||||
</span>
|
||||
</Stack>
|
||||
<Text style={{ marginTop: 15 }}>
|
||||
<em>*{estimatedCostDisclaimer}</em>
|
||||
</Text>
|
||||
</Stack>
|
||||
);
|
||||
@@ -293,14 +259,6 @@ export const indexingPolicynUnsavedWarningMessage: JSX.Element = (
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const updateThroughputBeyondLimitWarningMessage: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle} id="updateThroughputBeyondLimitWarningMessage">
|
||||
You are about to request an increase in throughput beyond the pre-allocated capacity. The service will scale out and
|
||||
increase throughput for the selected container. This operation will take 1-3 business days to complete. You can
|
||||
track the status of this request in Notifications.
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const updateThroughputDelayedApplyWarningMessage: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle} id="updateThroughputDelayedApplyWarningMessage">
|
||||
You are about to request an increase in throughput beyond the pre-allocated capacity. This operation will take some
|
||||
@@ -308,6 +266,61 @@ export const updateThroughputDelayedApplyWarningMessage: JSX.Element = (
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const getUpdateThroughputBeyondInstantLimitMessage = (instantMaximumThroughput: number): JSX.Element => {
|
||||
return (
|
||||
<Text styles={infoAndToolTipTextStyle} id="updateThroughputDelayedApplyWarningMessage">
|
||||
Scaling up will take 4-6 hours as it exceeds what Azure Cosmos DB can instantly support currently based on your
|
||||
number of physical partitions. You can increase your throughput to {instantMaximumThroughput} instantly or proceed
|
||||
with this value and wait until the scale-up is completed.
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export const getUpdateThroughputBeyondSupportLimitMessage = (
|
||||
instantMaximumThroughput: number,
|
||||
maximumThroughput: number
|
||||
): JSX.Element => {
|
||||
return (
|
||||
<>
|
||||
<Text styles={infoAndToolTipTextStyle} id="updateThroughputDelayedApplyWarningMessage">
|
||||
Your request to increase throughput exceeds the pre-allocated capacity which may take longer than expected.
|
||||
There are three options you can choose from to proceed:
|
||||
</Text>
|
||||
<ol style={{ fontSize: 14, color: "windowtext", marginTop: "5px" }}>
|
||||
<li>You can instantly scale up to {instantMaximumThroughput} RU/s.</li>
|
||||
{instantMaximumThroughput < maximumThroughput && (
|
||||
<li>You can asynchronously scale up to any value under {maximumThroughput} RU/s in 4-6 hours.</li>
|
||||
)}
|
||||
<li>
|
||||
Your current quota max is {maximumThroughput} RU/s. To go over this limit, you must request a quota increase
|
||||
and the Azure Cosmos DB team will review.
|
||||
<Link
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/create-support-request-quota-increase"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</Link>
|
||||
</li>
|
||||
</ol>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getUpdateThroughputBelowMinimumMessage = (minimum: number): JSX.Element => {
|
||||
return (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
You are not able to lower throughput below your current minimum of {minimum} RU/s. For more information on this
|
||||
limit, please refer to our service quote documentation.
|
||||
<Link
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/concepts-limits#minimum-throughput-limits"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
</Link>
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export const saveThroughputWarningMessage: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
Your bill will be affected as you update your throughput settings. Please review the updated cost estimate below
|
||||
@@ -499,7 +512,11 @@ export const getTextFieldStyles = (current: isDirtyTypes, baseline: isDirtyTypes
|
||||
},
|
||||
});
|
||||
|
||||
export const getChoiceGroupStyles = (current: isDirtyTypes, baseline: isDirtyTypes): Partial<IChoiceGroupStyles> => ({
|
||||
export const getChoiceGroupStyles = (
|
||||
current: isDirtyTypes,
|
||||
baseline: isDirtyTypes,
|
||||
isHorizontal?: boolean
|
||||
): Partial<IChoiceGroupStyles> => ({
|
||||
flexContainer: [
|
||||
{
|
||||
selectors: {
|
||||
@@ -516,6 +533,8 @@ export const getChoiceGroupStyles = (current: isDirtyTypes, baseline: isDirtyTyp
|
||||
padding: "2px 5px",
|
||||
},
|
||||
},
|
||||
display: isHorizontal ? "inline-flex" : "default",
|
||||
columnGap: isHorizontal ? "30px" : "default",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@ import ko from "knockout";
|
||||
import React from "react";
|
||||
import * as Constants from "../../../../Common/Constants";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import * as SharedConstants from "../../../../Shared/Constants";
|
||||
import { updateUserContext } from "../../../../UserContext";
|
||||
import Explorer from "../../../Explorer";
|
||||
import { throughputUnit } from "../SettingsRenderUtils";
|
||||
@@ -12,7 +11,6 @@ import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
|
||||
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
|
||||
|
||||
describe("ScaleComponent", () => {
|
||||
const nonNationalCloudContainer = new Explorer();
|
||||
const targetThroughput = 6000;
|
||||
|
||||
const baseProps: ScaleComponentProps = {
|
||||
@@ -125,11 +123,4 @@ describe("ScaleComponent", () => {
|
||||
scaleComponent = new ScaleComponent(newProps);
|
||||
expect(scaleComponent.canThroughputExceedMaximumValue()).toEqual(true);
|
||||
});
|
||||
|
||||
it("getThroughputWarningMessage", () => {
|
||||
const throughputBeyondLimit = SharedConstants.CollectionCreation.DefaultCollectionRUs1Million + 1000;
|
||||
const newProps = { ...baseProps, container: nonNationalCloudContainer, throughput: throughputBeyondLimit };
|
||||
const scaleComponent = new ScaleComponent(newProps);
|
||||
expect(scaleComponent.getThroughputWarningMessage().props.id).toEqual("updateThroughputBeyondLimitWarningMessage");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Label, Link, MessageBar, MessageBarType, Stack, Text, TextField } from "@fluentui/react";
|
||||
import { Link, MessageBar, MessageBarType, Stack, Text, TextField } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import * as Constants from "../../../../Common/Constants";
|
||||
import { configContext, Platform } from "../../../../ConfigContext";
|
||||
import { Platform, configContext } from "../../../../ConfigContext";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import * as SharedConstants from "../../../../Shared/Constants";
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
subComponentStackProps,
|
||||
throughputUnit,
|
||||
titleAndInputStackProps,
|
||||
updateThroughputBeyondLimitWarningMessage,
|
||||
} from "../SettingsRenderUtils";
|
||||
import { hasDatabaseSharedThroughput } from "../SettingsUtils";
|
||||
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
|
||||
@@ -68,16 +67,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
return !!enableAutoScaleCapability;
|
||||
};
|
||||
|
||||
private getStorageCapacityTitle = (): JSX.Element => {
|
||||
const capacity: string = this.props.isFixedContainer ? "Fixed" : "Unlimited";
|
||||
return (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<Label>Storage capacity</Label>
|
||||
<Text>{capacity}</Text>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
public getMaxRUs = (): number => {
|
||||
if (userContext.isTryCosmosDBSubscription) {
|
||||
return Constants.TryCosmosExperience.maxRU;
|
||||
@@ -131,18 +120,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
public getThroughputWarningMessage = (): JSX.Element => {
|
||||
const throughputExceedsBackendLimits: boolean =
|
||||
this.canThroughputExceedMaximumValue() &&
|
||||
this.props.throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
|
||||
|
||||
if (throughputExceedsBackendLimits && !this.props.isFixedContainer) {
|
||||
return updateThroughputBeyondLimitWarningMessage;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
public getLongDelayMessage = (): JSX.Element => {
|
||||
const matches: string[] = this.props.initialNotification?.description.match(
|
||||
`Throughput update for (.*) ${throughputUnit}`
|
||||
@@ -188,9 +165,10 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
spendAckChecked={false}
|
||||
onScaleSaveableChange={this.props.onScaleSaveableChange}
|
||||
onScaleDiscardableChange={this.props.onScaleDiscardableChange}
|
||||
getThroughputWarningMessage={this.getThroughputWarningMessage}
|
||||
usageSizeInKB={this.props.collection?.usageSizeInKB()}
|
||||
throughputError={this.props.throughputError}
|
||||
instantMaximumThroughput={this.offer?.instantMaximumThroughput}
|
||||
softAllowedMaximumThroughput={this.offer?.softAllowedMaximumThroughput}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -229,12 +207,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
{this.getInitialNotificationElement() && (
|
||||
<MessageBar messageBarType={MessageBarType.warning}>{this.getInitialNotificationElement()}</MessageBar>
|
||||
)}
|
||||
{!this.isAutoScaleEnabled() && (
|
||||
<Stack {...subComponentStackProps}>
|
||||
{this.getThroughputInputComponent()}
|
||||
{!this.props.database && this.getStorageCapacityTitle()}
|
||||
</Stack>
|
||||
)}
|
||||
{!this.isAutoScaleEnabled() && <Stack {...subComponentStackProps}>{this.getThroughputInputComponent()}</Stack>}
|
||||
|
||||
{/* TODO: Replace link with call to the Azure Support blade */}
|
||||
{this.isAutoScaleEnabled() && (
|
||||
|
||||
@@ -310,7 +310,16 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
/>
|
||||
)}
|
||||
|
||||
{this.isLargePartitionKeyEnabled() && <Text>Large {this.partitionKeyName.toLowerCase()} has been enabled</Text>}
|
||||
{userContext.apiType === "SQL" && this.isLargePartitionKeyEnabled() && (
|
||||
<Text>Large {this.partitionKeyName.toLowerCase()} has been enabled.</Text>
|
||||
)}
|
||||
|
||||
{userContext.apiType === "SQL" &&
|
||||
(this.isHierarchicalPartitionedContainer() ? (
|
||||
<Text>Hierarchically partitioned container.</Text>
|
||||
) : (
|
||||
<Text>Non-hierarchically partitioned container.</Text>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -328,6 +337,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
};
|
||||
|
||||
public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2;
|
||||
public isHierarchicalPartitionedContainer = (): boolean => this.props.collection.partitionKey?.kind === "MultiHash";
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
|
||||
@@ -42,7 +42,8 @@ describe("ThroughputInputAutoPilotV3Component", () => {
|
||||
onScaleDiscardableChange: () => {
|
||||
return;
|
||||
},
|
||||
getThroughputWarningMessage: () => undefined,
|
||||
instantMaximumThroughput: 5000,
|
||||
softAllowedMaximumThroughput: 1000000,
|
||||
};
|
||||
|
||||
it("throughput input visible", () => {
|
||||
|
||||
@@ -3,10 +3,14 @@ import {
|
||||
ChoiceGroup,
|
||||
FontIcon,
|
||||
IChoiceGroupOption,
|
||||
IColumn,
|
||||
IProgressIndicatorStyles,
|
||||
ISeparatorStyles,
|
||||
Label,
|
||||
Link,
|
||||
MessageBar,
|
||||
MessageBarType,
|
||||
ProgressIndicator,
|
||||
Separator,
|
||||
Stack,
|
||||
Text,
|
||||
TextField,
|
||||
@@ -23,24 +27,24 @@ import { autoPilotThroughput1K } from "../../../../../Utils/AutoPilotUtils";
|
||||
import { calculateEstimateNumber, usageInGB } from "../../../../../Utils/PricingUtils";
|
||||
import { Int32 } from "../../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
|
||||
import {
|
||||
AutoscaleEstimatedSpendingDisplayProps,
|
||||
PriceBreakdown,
|
||||
checkBoxAndInputStackProps,
|
||||
getAutoPilotV3SpendElement,
|
||||
getChoiceGroupStyles,
|
||||
getEstimatedSpendingElement,
|
||||
getRuPriceBreakdown,
|
||||
getTextFieldStyles,
|
||||
getToolTipContainer,
|
||||
ManualEstimatedSpendingDisplayProps,
|
||||
getUpdateThroughputBelowMinimumMessage,
|
||||
getUpdateThroughputBeyondInstantLimitMessage,
|
||||
getUpdateThroughputBeyondSupportLimitMessage,
|
||||
manualToAutoscaleDisclaimerElement,
|
||||
messageBarStyles,
|
||||
noLeftPaddingCheckBoxStyle,
|
||||
PriceBreakdown,
|
||||
relaxedSpacingStackProps,
|
||||
saveThroughputWarningMessage,
|
||||
titleAndInputStackProps,
|
||||
transparentDetailsHeaderStyle,
|
||||
} from "../../SettingsRenderUtils";
|
||||
import { getSanitizedInputValue, IsComponentDirtyResult, isDirty } from "../../SettingsUtils";
|
||||
import { IsComponentDirtyResult, getSanitizedInputValue, isDirty } from "../../SettingsUtils";
|
||||
import { ToolTipLabelComponent } from "../ToolTipLabelComponent";
|
||||
|
||||
export interface ThroughputInputAutoPilotV3Props {
|
||||
@@ -73,16 +77,16 @@ export interface ThroughputInputAutoPilotV3Props {
|
||||
onMaxAutoPilotThroughputChange: (newThroughput: number) => void;
|
||||
onScaleSaveableChange: (isScaleSaveable: boolean) => void;
|
||||
onScaleDiscardableChange: (isScaleDiscardable: boolean) => void;
|
||||
getThroughputWarningMessage: () => JSX.Element;
|
||||
usageSizeInKB: number;
|
||||
throughputError?: string;
|
||||
instantMaximumThroughput: number;
|
||||
softAllowedMaximumThroughput: number;
|
||||
}
|
||||
|
||||
interface ThroughputInputAutoPilotV3State {
|
||||
spendAckChecked: boolean;
|
||||
exceedFreeTierThroughput: boolean;
|
||||
}
|
||||
|
||||
export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
ThroughputInputAutoPilotV3Props,
|
||||
ThroughputInputAutoPilotV3State
|
||||
@@ -128,7 +132,10 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
} else if (this.props.isAutoPilotSelected) {
|
||||
if (isDirty(this.props.maxAutoPilotThroughput, this.props.maxAutoPilotThroughputBaseline)) {
|
||||
isDiscardable = true;
|
||||
if (AutoPilotUtils.isValidAutoPilotThroughput(this.props.maxAutoPilotThroughput)) {
|
||||
if (
|
||||
this.props.maxAutoPilotThroughput <= this.props.softAllowedMaximumThroughput &&
|
||||
AutoPilotUtils.isValidAutoPilotThroughput(this.props.maxAutoPilotThroughput)
|
||||
) {
|
||||
isSaveable = true;
|
||||
}
|
||||
}
|
||||
@@ -187,7 +194,15 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
|
||||
let estimatedSpend: JSX.Element;
|
||||
|
||||
if (!this.props.isAutoPilotSelected) {
|
||||
if (this.props.isAutoPilotSelected) {
|
||||
estimatedSpend = this.getEstimatedAutoscaleSpendElement(
|
||||
this.props.maxAutoPilotThroughputBaseline,
|
||||
userContext.portalEnv,
|
||||
regions,
|
||||
multimaster,
|
||||
isDirty ? this.props.maxAutoPilotThroughput : undefined
|
||||
);
|
||||
} else {
|
||||
estimatedSpend = this.getEstimatedManualSpendElement(
|
||||
// if migrating from autoscale to manual, we use the autoscale RUs value as that is what will be set...
|
||||
this.overrideWithAutoPilotSettings() ? this.props.maxAutoPilotThroughput : this.props.throughputBaseline,
|
||||
@@ -196,14 +211,6 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
multimaster,
|
||||
isDirty ? this.props.throughput : undefined
|
||||
);
|
||||
} else {
|
||||
estimatedSpend = this.getEstimatedAutoscaleSpendElement(
|
||||
this.props.maxAutoPilotThroughputBaseline,
|
||||
userContext.portalEnv,
|
||||
regions,
|
||||
multimaster,
|
||||
isDirty ? this.props.maxAutoPilotThroughput : undefined
|
||||
);
|
||||
}
|
||||
return estimatedSpend;
|
||||
};
|
||||
@@ -216,52 +223,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
newThroughput?: number
|
||||
): JSX.Element => {
|
||||
const prices: PriceBreakdown = getRuPriceBreakdown(throughput, serverId, numberOfRegions, isMultimaster, true);
|
||||
const estimatedSpendingColumns: IColumn[] = [
|
||||
{
|
||||
key: "costType",
|
||||
name: "",
|
||||
fieldName: "costType",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
styles: transparentDetailsHeaderStyle,
|
||||
},
|
||||
{
|
||||
key: "minPerMonth",
|
||||
name: "Min Per Month",
|
||||
fieldName: "minPerMonth",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
styles: transparentDetailsHeaderStyle,
|
||||
},
|
||||
{
|
||||
key: "maxPerMonth",
|
||||
name: "Max Per Month",
|
||||
fieldName: "maxPerMonth",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
styles: transparentDetailsHeaderStyle,
|
||||
},
|
||||
];
|
||||
const estimatedSpendingItems: AutoscaleEstimatedSpendingDisplayProps[] = [
|
||||
{
|
||||
costType: <Text>Current Cost</Text>,
|
||||
minPerMonth: (
|
||||
<Text>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice / 10)}
|
||||
</Text>
|
||||
),
|
||||
maxPerMonth: (
|
||||
<Text>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (newThroughput) {
|
||||
const newThroughputCostElement = (): JSX.Element => {
|
||||
const newPrices: PriceBreakdown = getRuPriceBreakdown(
|
||||
newThroughput,
|
||||
serverId,
|
||||
@@ -269,37 +232,40 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
isMultimaster,
|
||||
true
|
||||
);
|
||||
estimatedSpendingItems.unshift({
|
||||
costType: (
|
||||
<Text>
|
||||
<b>Updated Cost</b>
|
||||
</Text>
|
||||
),
|
||||
minPerMonth: (
|
||||
<Text>
|
||||
<b>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice / 10)}
|
||||
</b>
|
||||
</Text>
|
||||
),
|
||||
maxPerMonth: (
|
||||
<Text>
|
||||
<b>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)}
|
||||
</b>
|
||||
</Text>
|
||||
),
|
||||
});
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Text style={{ fontWeight: 600 }}>Updated cost per month</Text>
|
||||
<Stack horizontal style={{ marginTop: 5, marginBottom: 10 }}>
|
||||
<Text style={{ width: "50%" }}>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice / 10)} min
|
||||
</Text>
|
||||
<Text style={{ width: "50%" }}>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)} max
|
||||
</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return getEstimatedSpendingElement(
|
||||
estimatedSpendingColumns,
|
||||
estimatedSpendingItems,
|
||||
newThroughput ?? throughput,
|
||||
numberOfRegions,
|
||||
prices,
|
||||
true
|
||||
);
|
||||
const costElement = (): JSX.Element => {
|
||||
const prices: PriceBreakdown = getRuPriceBreakdown(throughput, serverId, numberOfRegions, isMultimaster, true);
|
||||
return (
|
||||
<Stack {...checkBoxAndInputStackProps} style={{ marginTop: 15 }}>
|
||||
{newThroughput && newThroughputCostElement()}
|
||||
<Text style={{ fontWeight: 600 }}>Current cost per month</Text>
|
||||
<Stack horizontal style={{ marginTop: 5 }}>
|
||||
<Text style={{ width: "50%" }}>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice / 10)} min
|
||||
</Text>
|
||||
<Text style={{ width: "50%" }}>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)} max
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
return getEstimatedSpendingElement(costElement(), newThroughput ?? throughput, numberOfRegions, prices, true);
|
||||
};
|
||||
|
||||
private getEstimatedManualSpendElement = (
|
||||
@@ -310,122 +276,55 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
newThroughput?: number
|
||||
): JSX.Element => {
|
||||
const prices: PriceBreakdown = getRuPriceBreakdown(throughput, serverId, numberOfRegions, isMultimaster, false);
|
||||
const estimatedSpendingColumns: IColumn[] = [
|
||||
{
|
||||
key: "costType",
|
||||
name: "",
|
||||
fieldName: "costType",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
styles: transparentDetailsHeaderStyle,
|
||||
},
|
||||
{
|
||||
key: "hourly",
|
||||
name: "Hourly",
|
||||
fieldName: "hourly",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
styles: transparentDetailsHeaderStyle,
|
||||
},
|
||||
{
|
||||
key: "daily",
|
||||
name: "Daily",
|
||||
fieldName: "daily",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
styles: transparentDetailsHeaderStyle,
|
||||
},
|
||||
{
|
||||
key: "monthly",
|
||||
name: "Monthly",
|
||||
fieldName: "monthly",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
styles: transparentDetailsHeaderStyle,
|
||||
},
|
||||
];
|
||||
const estimatedSpendingItems: ManualEstimatedSpendingDisplayProps[] = [
|
||||
{
|
||||
costType: <Text>Current Cost</Text>,
|
||||
hourly: (
|
||||
<Text>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.hourlyPrice)}
|
||||
</Text>
|
||||
),
|
||||
daily: (
|
||||
<Text>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.dailyPrice)}
|
||||
</Text>
|
||||
),
|
||||
monthly: (
|
||||
<Text>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (newThroughput) {
|
||||
const newThroughputCostElement = (): JSX.Element => {
|
||||
const newPrices: PriceBreakdown = getRuPriceBreakdown(
|
||||
newThroughput,
|
||||
serverId,
|
||||
numberOfRegions,
|
||||
isMultimaster,
|
||||
false
|
||||
true
|
||||
);
|
||||
estimatedSpendingItems.unshift({
|
||||
costType: (
|
||||
<Text>
|
||||
<b>Updated Cost</b>
|
||||
</Text>
|
||||
),
|
||||
hourly: (
|
||||
<Text>
|
||||
<b>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.hourlyPrice)}
|
||||
</b>
|
||||
</Text>
|
||||
),
|
||||
daily: (
|
||||
<Text>
|
||||
<b>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.dailyPrice)}
|
||||
</b>
|
||||
</Text>
|
||||
),
|
||||
monthly: (
|
||||
<Text>
|
||||
<b>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)}
|
||||
</b>
|
||||
</Text>
|
||||
),
|
||||
});
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Text style={{ fontWeight: 600 }}>Updated cost per month</Text>
|
||||
<Stack horizontal style={{ marginTop: 5, marginBottom: 10 }}>
|
||||
<Text style={{ width: "33%" }}>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.hourlyPrice)}/hr
|
||||
</Text>
|
||||
<Text style={{ width: "33%" }}>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.dailyPrice)}/day
|
||||
</Text>
|
||||
<Text style={{ width: "33%" }}>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)}/mo
|
||||
</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return getEstimatedSpendingElement(
|
||||
estimatedSpendingColumns,
|
||||
estimatedSpendingItems,
|
||||
newThroughput ?? throughput,
|
||||
numberOfRegions,
|
||||
prices,
|
||||
false
|
||||
);
|
||||
};
|
||||
const costElement = (): JSX.Element => {
|
||||
const prices: PriceBreakdown = getRuPriceBreakdown(throughput, serverId, numberOfRegions, isMultimaster, true);
|
||||
return (
|
||||
<Stack {...checkBoxAndInputStackProps} style={{ marginTop: 15 }}>
|
||||
{newThroughput && newThroughputCostElement()}
|
||||
<Text style={{ fontWeight: 600 }}>Current cost per month</Text>
|
||||
<Stack horizontal style={{ marginTop: 5 }}>
|
||||
<Text style={{ width: "33%" }}>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.hourlyPrice)}/hr
|
||||
</Text>
|
||||
<Text style={{ width: "33%" }}>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.dailyPrice)}/day
|
||||
</Text>
|
||||
<Text style={{ width: "33%" }}>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)}/mo
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
private getAutoPilotUsageCost = (): JSX.Element => {
|
||||
if (!this.props.maxAutoPilotThroughput) {
|
||||
return <></>;
|
||||
}
|
||||
return getAutoPilotV3SpendElement(
|
||||
this.props.maxAutoPilotThroughput,
|
||||
false /* isDatabaseThroughput */,
|
||||
!this.props.isEmulator ? this.getRequestUnitsUsageCost() : <></>
|
||||
);
|
||||
return getEstimatedSpendingElement(costElement(), newThroughput ?? throughput, numberOfRegions, prices, false);
|
||||
};
|
||||
|
||||
private onAutoPilotThroughputChange = (
|
||||
@@ -512,7 +411,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
onChange={this.onChoiceGroupChange}
|
||||
required={this.props.showAsMandatory}
|
||||
ariaLabelledBy={labelId}
|
||||
styles={getChoiceGroupStyles(this.props.wasAutopilotOriginallySet, this.props.isAutoPilotSelected)}
|
||||
styles={getChoiceGroupStyles(this.props.wasAutopilotOriginallySet, this.props.isAutoPilotSelected, true)}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
@@ -521,97 +420,266 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
private onSpendAckChecked = (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean): void =>
|
||||
this.setState({ spendAckChecked: checked });
|
||||
|
||||
private renderAutoPilotInput = (): JSX.Element => (
|
||||
<>
|
||||
<Text>
|
||||
Provision maximum RU/s required by this resource. Estimate your required RU/s with
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/">
|
||||
{` capacity calculator`}
|
||||
</Link>
|
||||
</Text>
|
||||
<TextField
|
||||
label="Max RU/s"
|
||||
required
|
||||
type="number"
|
||||
id="autopilotInput"
|
||||
key="auto pilot throughput input"
|
||||
styles={getTextFieldStyles(this.props.maxAutoPilotThroughput, this.props.maxAutoPilotThroughputBaseline)}
|
||||
disabled={this.overrideWithProvisionedThroughputSettings()}
|
||||
step={AutoPilotUtils.autoPilotIncrementStep}
|
||||
value={this.overrideWithProvisionedThroughputSettings() ? "" : this.props.maxAutoPilotThroughput?.toString()}
|
||||
onChange={this.onAutoPilotThroughputChange}
|
||||
min={autoPilotThroughput1K}
|
||||
errorMessage={this.props.throughputError}
|
||||
private getStorageCapacityTitle = (): JSX.Element => {
|
||||
const capacity: string = this.props.isFixed ? "Fixed" : "Unlimited";
|
||||
return (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<Label>Storage capacity</Label>
|
||||
<Text>{capacity}</Text>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
private thoughputRangeSeparatorStyles: Partial<ISeparatorStyles> = {
|
||||
root: [
|
||||
{
|
||||
selectors: {
|
||||
"::before": {
|
||||
backgroundColor: "rgb(200, 200, 200)",
|
||||
height: "3px",
|
||||
marginTop: "-1px",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
private currentThroughputValue = (): number => {
|
||||
return this.props.isAutoPilotSelected
|
||||
? this.props.maxAutoPilotThroughput
|
||||
: this.overrideWithAutoPilotSettings()
|
||||
? this.props.maxAutoPilotThroughputBaseline
|
||||
: this.props.throughput;
|
||||
};
|
||||
|
||||
private getCurrentRuRange = (): "below" | "instant" | "delayed" | "requireSupport" => {
|
||||
if (this.currentThroughputValue() < this.props.minimum) {
|
||||
return "below";
|
||||
}
|
||||
if (
|
||||
this.currentThroughputValue() >= this.props.minimum &&
|
||||
this.currentThroughputValue() <= this.props.instantMaximumThroughput
|
||||
) {
|
||||
return "instant";
|
||||
}
|
||||
if (this.currentThroughputValue() > this.props.softAllowedMaximumThroughput) {
|
||||
return "requireSupport";
|
||||
}
|
||||
|
||||
return "delayed";
|
||||
};
|
||||
|
||||
private getRuThermometerStyles = (): Partial<IProgressIndicatorStyles> => ({
|
||||
progressBar: [
|
||||
{
|
||||
backgroundColor:
|
||||
this.getCurrentRuRange() === "instant"
|
||||
? "rgb(0, 120, 212)"
|
||||
: this.getCurrentRuRange() === "delayed"
|
||||
? "rgb(255 216 109)"
|
||||
: "rgb(251, 217, 203)",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
private getRuThermometerPercentValue = (): number => {
|
||||
let percentValue: number;
|
||||
const currentRus = this.currentThroughputValue();
|
||||
|
||||
switch (this.getCurrentRuRange()) {
|
||||
case "below":
|
||||
percentValue = 0;
|
||||
break;
|
||||
case "instant": {
|
||||
const percentOfInstantRange: number = currentRus / this.props.instantMaximumThroughput;
|
||||
percentValue = percentOfInstantRange * 0.34;
|
||||
break;
|
||||
}
|
||||
case "delayed": {
|
||||
const adjustedMax = this.props.softAllowedMaximumThroughput - this.props.instantMaximumThroughput;
|
||||
const adjustedRus = currentRus - this.props.instantMaximumThroughput;
|
||||
const percentOfDelayedRange = adjustedRus / adjustedMax;
|
||||
const adjustedPercent = percentOfDelayedRange * 0.66;
|
||||
percentValue = adjustedPercent + 0.34;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// over maximum
|
||||
percentValue = 1;
|
||||
}
|
||||
return percentValue;
|
||||
};
|
||||
|
||||
private getRUThermometer = (): JSX.Element => (
|
||||
<Stack>
|
||||
<Stack horizontal>
|
||||
<Stack.Item style={{ width: "34%" }}>
|
||||
<span>{this.props.minimum.toLocaleString()}</span>
|
||||
</Stack.Item>
|
||||
<Stack.Item style={{ width: "66%" }}>
|
||||
<span style={{ float: "left", transform: "translateX(-50%)" }}>
|
||||
{this.props.instantMaximumThroughput.toLocaleString()}
|
||||
</span>
|
||||
<span style={{ float: "right" }}>{this.props.softAllowedMaximumThroughput.toLocaleString()}</span>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
<ProgressIndicator
|
||||
barHeight={20}
|
||||
percentComplete={this.getRuThermometerPercentValue()}
|
||||
styles={this.getRuThermometerStyles()}
|
||||
/>
|
||||
{!this.overrideWithProvisionedThroughputSettings() && this.getAutoPilotUsageCost()}
|
||||
{this.minRUperGBSurvey()}
|
||||
{this.props.spendAckVisible && (
|
||||
<Checkbox
|
||||
id="spendAckCheckBox"
|
||||
styles={noLeftPaddingCheckBoxStyle}
|
||||
label={this.props.spendAckText}
|
||||
checked={this.state.spendAckChecked}
|
||||
onChange={this.onSpendAckChecked}
|
||||
<Stack horizontal>
|
||||
<Stack.Item style={{ width: "34%", paddingRight: "5px" }}>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>Instant</Separator>
|
||||
</Stack.Item>
|
||||
<Stack.Item style={{ width: "66%", paddingLeft: "5px" }}>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>4-6 hrs</Separator>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
private showThroughputWarning = (): boolean => {
|
||||
return (
|
||||
this.currentThroughputValue() > this.props.instantMaximumThroughput ||
|
||||
this.currentThroughputValue() < this.props.minimum
|
||||
);
|
||||
};
|
||||
|
||||
private getThroughputWarningMessageText = (): JSX.Element => {
|
||||
switch (this.getCurrentRuRange()) {
|
||||
case "below":
|
||||
return getUpdateThroughputBelowMinimumMessage(this.props.minimum);
|
||||
case "delayed":
|
||||
return getUpdateThroughputBeyondInstantLimitMessage(this.props.instantMaximumThroughput);
|
||||
case "requireSupport":
|
||||
return getUpdateThroughputBeyondSupportLimitMessage(
|
||||
this.props.instantMaximumThroughput,
|
||||
this.props.softAllowedMaximumThroughput
|
||||
);
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
|
||||
private getThroughputWarningMessageBar = (): JSX.Element => {
|
||||
const isSevereWarning: boolean =
|
||||
this.currentThroughputValue() > this.props.softAllowedMaximumThroughput ||
|
||||
this.currentThroughputValue() < this.props.minimum;
|
||||
return (
|
||||
<MessageBar messageBarType={isSevereWarning ? MessageBarType.severeWarning : MessageBarType.warning}>
|
||||
{this.getThroughputWarningMessageText()}
|
||||
</MessageBar>
|
||||
);
|
||||
};
|
||||
|
||||
private getThroughputTextField = (): JSX.Element => (
|
||||
<>
|
||||
{this.props.isAutoPilotSelected ? (
|
||||
<TextField
|
||||
label="Maximum RU/s required by this resource"
|
||||
required
|
||||
type="number"
|
||||
id="autopilotInput"
|
||||
key="auto pilot throughput input"
|
||||
styles={getTextFieldStyles(this.props.maxAutoPilotThroughput, this.props.maxAutoPilotThroughputBaseline)}
|
||||
disabled={this.overrideWithProvisionedThroughputSettings()}
|
||||
step={AutoPilotUtils.autoPilotIncrementStep}
|
||||
value={this.overrideWithProvisionedThroughputSettings() ? "" : this.props.maxAutoPilotThroughput?.toString()}
|
||||
onChange={this.onAutoPilotThroughputChange}
|
||||
min={autoPilotThroughput1K}
|
||||
onGetErrorMessage={(value: string) => {
|
||||
const sanitizedValue = getSanitizedInputValue(value);
|
||||
return sanitizedValue % 1000
|
||||
? "Throughput value must be in increments of 1000"
|
||||
: this.props.throughputError;
|
||||
}}
|
||||
validateOnLoad={false}
|
||||
/>
|
||||
) : (
|
||||
<TextField
|
||||
required
|
||||
type="number"
|
||||
id="throughputInput"
|
||||
key="provisioned throughput input"
|
||||
styles={getTextFieldStyles(this.props.throughput, this.props.throughputBaseline)}
|
||||
disabled={this.overrideWithAutoPilotSettings()}
|
||||
step={this.step}
|
||||
value={
|
||||
this.overrideWithAutoPilotSettings()
|
||||
? this.props.maxAutoPilotThroughputBaseline?.toString()
|
||||
: this.props.throughput?.toString()
|
||||
}
|
||||
onChange={this.onThroughputChange}
|
||||
min={this.props.minimum}
|
||||
errorMessage={this.props.throughputError}
|
||||
/>
|
||||
)}
|
||||
{this.props.isFixed && <p>When using a collection with fixed storage capacity, you can set up to 10,000 RU/s.</p>}
|
||||
</>
|
||||
);
|
||||
|
||||
private renderThroughputInput = (): JSX.Element => (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<Text>
|
||||
Estimate your required throughput with
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/">
|
||||
{` capacity calculator`} <FontIcon iconName="NavigateExternalInline" />
|
||||
</Link>
|
||||
</Text>
|
||||
<TextField
|
||||
required
|
||||
type="number"
|
||||
id="throughputInput"
|
||||
key="provisioned throughput input"
|
||||
styles={getTextFieldStyles(this.props.throughput, this.props.throughputBaseline)}
|
||||
disabled={this.overrideWithAutoPilotSettings()}
|
||||
step={this.step}
|
||||
value={
|
||||
this.overrideWithAutoPilotSettings()
|
||||
? this.props.maxAutoPilotThroughputBaseline?.toString()
|
||||
: this.props.throughput?.toString()
|
||||
}
|
||||
onChange={this.onThroughputChange}
|
||||
min={this.props.minimum}
|
||||
errorMessage={this.props.throughputError}
|
||||
/>
|
||||
{this.state.exceedFreeTierThroughput && (
|
||||
<MessageBar
|
||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||
styles={messageBarStyles}
|
||||
>
|
||||
{`Billing will apply if you provision more than ${SharedConstants.FreeTierLimits.RU} RU/s of manual throughput, or if the resource scales beyond ${SharedConstants.FreeTierLimits.RU} RU/s with autoscale.`}
|
||||
</MessageBar>
|
||||
)}
|
||||
{this.props.getThroughputWarningMessage() && (
|
||||
<MessageBar
|
||||
messageBarIconProps={{ iconName: "InfoSolid", className: "messageBarInfoIcon" }}
|
||||
styles={messageBarStyles}
|
||||
>
|
||||
{this.props.getThroughputWarningMessage()}
|
||||
</MessageBar>
|
||||
)}
|
||||
{!this.props.isEmulator && this.getRequestUnitsUsageCost()}
|
||||
{this.minRUperGBSurvey()}
|
||||
{this.props.spendAckVisible && (
|
||||
<Checkbox
|
||||
id="spendAckCheckBox"
|
||||
styles={noLeftPaddingCheckBoxStyle}
|
||||
label={this.props.spendAckText}
|
||||
checked={this.state.spendAckChecked}
|
||||
onChange={this.onSpendAckChecked}
|
||||
/>
|
||||
)}
|
||||
<br />
|
||||
{this.props.isFixed && <p>When using a collection with fixed storage capacity, you can set up to 10,000 RU/s.</p>}
|
||||
private renderThroughputComponent = (): JSX.Element => (
|
||||
<Stack horizontal>
|
||||
<Stack.Item style={{ width: "70%", maxWidth: "700px" }}>
|
||||
<Stack {...relaxedSpacingStackProps} style={{ paddingRight: "50px" }}>
|
||||
{this.getThroughputTextField()}
|
||||
{this.props.instantMaximumThroughput && (
|
||||
<Stack>
|
||||
{this.getRUThermometer()}
|
||||
{this.showThroughputWarning() && this.getThroughputWarningMessageBar()}
|
||||
</Stack>
|
||||
)}
|
||||
{this.props.isAutoPilotSelected ? (
|
||||
<Text style={{ marginTop: "40px" }}>
|
||||
Based on usage, your {this.props.collectionName ? "container" : "database"} throughput will scale from{" "}
|
||||
<b>
|
||||
{AutoPilotUtils.getMinRUsBasedOnUserInput(this.props.maxAutoPilotThroughput)} RU/s (10% of max RU/s) -{" "}
|
||||
{this.props.maxAutoPilotThroughput} RU/s
|
||||
</b>
|
||||
<br />
|
||||
</Text>
|
||||
) : (
|
||||
<>
|
||||
{this.state.exceedFreeTierThroughput && (
|
||||
<MessageBar
|
||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||
styles={messageBarStyles}
|
||||
style={{ marginTop: "40px" }}
|
||||
>
|
||||
{`Billing will apply if you provision more than ${SharedConstants.FreeTierLimits.RU} RU/s of manual throughput, or if the resource scales beyond ${SharedConstants.FreeTierLimits.RU} RU/s with autoscale.`}
|
||||
</MessageBar>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!this.overrideWithProvisionedThroughputSettings() && (
|
||||
<Text>
|
||||
Estimate your required RU/s with
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/">
|
||||
{` capacity calculator`} <FontIcon iconName="NavigateExternalInline" />
|
||||
</Link>
|
||||
</Text>
|
||||
)}
|
||||
{this.minRUperGBSurvey()}
|
||||
{this.props.spendAckVisible && (
|
||||
<Checkbox
|
||||
id="spendAckCheckBox"
|
||||
styles={noLeftPaddingCheckBoxStyle}
|
||||
label={this.props.spendAckText}
|
||||
checked={this.state.spendAckChecked}
|
||||
onChange={this.onSpendAckChecked}
|
||||
/>
|
||||
)}
|
||||
{this.props.isFixed && (
|
||||
<p>When using a collection with fixed storage capacity, you can set up to 10,000 RU/s.</p>
|
||||
)}
|
||||
{this.props.collectionName && (
|
||||
<Stack.Item style={{ marginTop: "40px" }}>{this.getStorageCapacityTitle()}</Stack.Item>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item style={{ width: "30%", maxWidth: "300px" }}>
|
||||
{!this.props.isEmulator ? this.getRequestUnitsUsageCost() : <></>}
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -624,7 +692,10 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
return (
|
||||
<>
|
||||
{warningMessage && (
|
||||
<MessageBar messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}>
|
||||
<MessageBar
|
||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||
role="alert"
|
||||
>
|
||||
{warningMessage}
|
||||
</MessageBar>
|
||||
)}
|
||||
@@ -638,7 +709,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
{this.renderWarningMessage()}
|
||||
{this.renderThroughputModeChoices()}
|
||||
|
||||
{this.props.isAutoPilotSelected ? this.renderAutoPilotInput() : this.renderThroughputInput()}
|
||||
{this.renderThroughputComponent()}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ exports[`ConflictResolutionComponent Path text field displayed 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": undefined,
|
||||
@@ -100,6 +102,8 @@ exports[`ConflictResolutionComponent Sproc text field displayed 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
|
||||
@@ -39,7 +39,6 @@ exports[`ScaleComponent renders with correct initial notification 1`] = `
|
||||
canExceedMaximumValue={true}
|
||||
collectionName="test"
|
||||
databaseName="test"
|
||||
getThroughputWarningMessage={[Function]}
|
||||
isAutoPilotSelected={false}
|
||||
isEmulator={false}
|
||||
isEnabled={true}
|
||||
@@ -60,20 +59,6 @@ exports[`ScaleComponent renders with correct initial notification 1`] = `
|
||||
usageSizeInKB={100}
|
||||
wasAutopilotOriginallySet={true}
|
||||
/>
|
||||
<Stack
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<StyledLabelBase>
|
||||
Storage capacity
|
||||
</StyledLabelBase>
|
||||
<Text>
|
||||
Unlimited
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
`;
|
||||
|
||||
@@ -40,6 +40,8 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -106,6 +108,8 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -168,6 +172,8 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -218,7 +224,10 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
||||
<Text>
|
||||
Large
|
||||
partition key
|
||||
has been enabled
|
||||
has been enabled.
|
||||
</Text>
|
||||
<Text>
|
||||
Non-hierarchically partitioned container.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -264,6 +273,8 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -330,6 +341,8 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -382,6 +395,8 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": undefined,
|
||||
@@ -445,6 +460,8 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -495,7 +512,10 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
||||
<Text>
|
||||
Large
|
||||
partition key
|
||||
has been enabled
|
||||
has been enabled.
|
||||
</Text>
|
||||
<Text>
|
||||
Non-hierarchically partitioned container.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -541,6 +561,8 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -607,6 +629,8 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -659,6 +683,8 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -734,7 +760,10 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
|
||||
<Text>
|
||||
Large
|
||||
partition key
|
||||
has been enabled
|
||||
has been enabled.
|
||||
</Text>
|
||||
<Text>
|
||||
Non-hierarchically partitioned container.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -780,6 +809,8 @@ exports[`SubSettingsComponent renders 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -846,6 +877,8 @@ exports[`SubSettingsComponent renders 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -898,6 +931,8 @@ exports[`SubSettingsComponent renders 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -986,6 +1021,8 @@ exports[`SubSettingsComponent renders 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -1036,7 +1073,10 @@ exports[`SubSettingsComponent renders 1`] = `
|
||||
<Text>
|
||||
Large
|
||||
partition key
|
||||
has been enabled
|
||||
has been enabled.
|
||||
</Text>
|
||||
<Text>
|
||||
Non-hierarchically partitioned container.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -1082,6 +1122,8 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": undefined,
|
||||
@@ -1123,6 +1165,8 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -1175,6 +1219,8 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -1263,6 +1309,8 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
||||
Object {
|
||||
"flexContainer": Array [
|
||||
Object {
|
||||
"columnGap": "default",
|
||||
"display": "default",
|
||||
"selectors": Object {
|
||||
".ms-ChoiceField-field.is-checked::after": Object {
|
||||
"borderColor": "",
|
||||
@@ -1313,7 +1361,10 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
||||
<Text>
|
||||
Large
|
||||
partition key
|
||||
has been enabled
|
||||
has been enabled.
|
||||
</Text>
|
||||
<Text>
|
||||
Non-hierarchically partitioned container.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -35,6 +35,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
"onRefreshResourcesClick": [Function],
|
||||
"phoenixClient": PhoenixClient {
|
||||
"armResourceId": undefined,
|
||||
"retryOptions": Object {
|
||||
"maxTimeout": 5000,
|
||||
"minTimeout": 5000,
|
||||
@@ -111,6 +112,7 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
"onRefreshResourcesClick": [Function],
|
||||
"phoenixClient": PhoenixClient {
|
||||
"armResourceId": undefined,
|
||||
"retryOptions": Object {
|
||||
"maxTimeout": 5000,
|
||||
"minTimeout": 5000,
|
||||
|
||||
@@ -2,154 +2,60 @@
|
||||
|
||||
exports[`SettingsUtils functions render 1`] = `
|
||||
<Fragment>
|
||||
<Text>
|
||||
Your
|
||||
container
|
||||
throughput will automatically scale from
|
||||
|
||||
<b>
|
||||
100
|
||||
RU/s (10% of max RU/s) -
|
||||
|
||||
1000
|
||||
RU/s
|
||||
</b>
|
||||
|
||||
based on usage.
|
||||
<br />
|
||||
</Text>
|
||||
<Text>
|
||||
After the first
|
||||
10
|
||||
GB of data stored, the max RU/s will be automatically upgraded based on the new storage value.
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cosmos-autoscale-info"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
.
|
||||
</Text>
|
||||
<Text>
|
||||
Your
|
||||
database
|
||||
throughput will automatically scale from
|
||||
|
||||
<b>
|
||||
100
|
||||
RU/s (10% of max RU/s) -
|
||||
|
||||
1000
|
||||
RU/s
|
||||
</b>
|
||||
|
||||
based on usage.
|
||||
<br />
|
||||
</Text>
|
||||
<Text>
|
||||
After the first
|
||||
10
|
||||
GB of data stored, the max RU/s will be automatically upgraded based on the new storage value.
|
||||
<StyledLinkBase
|
||||
href="https://aka.ms/cosmos-autoscale-info"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
.
|
||||
</Text>
|
||||
<Stack
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"width": 600,
|
||||
},
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<StyledWithViewportComponent
|
||||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"fieldName": "costType",
|
||||
"isResizable": true,
|
||||
"key": "costType",
|
||||
"maxWidth": 200,
|
||||
"minWidth": 100,
|
||||
"name": "",
|
||||
},
|
||||
Object {
|
||||
"fieldName": "hourly",
|
||||
"isResizable": true,
|
||||
"key": "hourly",
|
||||
"maxWidth": 200,
|
||||
"minWidth": 100,
|
||||
"name": "Hourly",
|
||||
},
|
||||
Object {
|
||||
"fieldName": "daily",
|
||||
"isResizable": true,
|
||||
"key": "daily",
|
||||
"maxWidth": 200,
|
||||
"minWidth": 100,
|
||||
"name": "Daily",
|
||||
},
|
||||
Object {
|
||||
"fieldName": "monthly",
|
||||
"isResizable": true,
|
||||
"key": "monthly",
|
||||
"maxWidth": 200,
|
||||
"minWidth": 100,
|
||||
"name": "Monthly",
|
||||
},
|
||||
]
|
||||
}
|
||||
disableSelectionZone={true}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"costType": <Text>
|
||||
Current Cost
|
||||
</Text>,
|
||||
"daily": <Text>
|
||||
$ 24.48
|
||||
</Text>,
|
||||
"hourly": <Text>
|
||||
$ 1.02
|
||||
</Text>,
|
||||
"monthly": <Text>
|
||||
$ 744.6
|
||||
</Text>,
|
||||
},
|
||||
]
|
||||
}
|
||||
layoutMode={1}
|
||||
onRenderRow={[Function]}
|
||||
selectionMode={0}
|
||||
/>
|
||||
<Stack>
|
||||
<Text
|
||||
id="throughputSpendElement"
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": 600,
|
||||
}
|
||||
}
|
||||
>
|
||||
(
|
||||
regions:
|
||||
|
||||
2
|
||||
,
|
||||
1000
|
||||
RU/s,
|
||||
¥
|
||||
0.00051
|
||||
/RU)
|
||||
Cost estimate*
|
||||
</Text>
|
||||
<Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": 600,
|
||||
"marginTop": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
How we calculate this
|
||||
</Text>
|
||||
<Stack
|
||||
id="throughputSpendElement"
|
||||
style={
|
||||
Object {
|
||||
"marginTop": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<span>
|
||||
2
|
||||
region
|
||||
<span>
|
||||
s
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
1000
|
||||
RU/s
|
||||
</span>
|
||||
<span>
|
||||
¥
|
||||
0.00051
|
||||
/RU
|
||||
</span>
|
||||
</Stack>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"marginTop": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<em>
|
||||
*
|
||||
This cost is an estimate and may vary based on the regions where your account is deployed and potential discounts applied to your account
|
||||
</em>
|
||||
</Text>
|
||||
@@ -205,19 +111,6 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
>
|
||||
You have not saved the latest changes made to your indexing policy. Please click save to confirm the changes.
|
||||
</Text>
|
||||
<Text
|
||||
id="updateThroughputBeyondLimitWarningMessage"
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "windowtext",
|
||||
"fontSize": 14,
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
You are about to request an increase in throughput beyond the pre-allocated capacity. The service will scale out and increase throughput for the selected container. This operation will take 1-3 business days to complete. You can track the status of this request in Notifications.
|
||||
</Text>
|
||||
<Text
|
||||
id="updateThroughputDelayedApplyWarningMessage"
|
||||
styles={
|
||||
|
||||
@@ -46,10 +46,13 @@ export class TabComponent extends React.Component<TabComponentProps> {
|
||||
}
|
||||
|
||||
let className = "toggleSwitch";
|
||||
let ariaselected;
|
||||
if (index === this.props.currentTabIndex) {
|
||||
className += " selectedToggle";
|
||||
ariaselected = true;
|
||||
} else {
|
||||
className += " unselectedToggle";
|
||||
ariaselected = false;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -57,9 +60,10 @@ export class TabComponent extends React.Component<TabComponentProps> {
|
||||
<AccessibleElement
|
||||
as="span"
|
||||
className={className}
|
||||
role="presentation"
|
||||
role="tab"
|
||||
onActivated={() => this.setActiveTab(index)}
|
||||
aria-label={`Select tab: ${tab.title}`}
|
||||
aria-selected={ariaselected}
|
||||
>
|
||||
{tab.title}
|
||||
</AccessibleElement>
|
||||
@@ -77,7 +81,11 @@ export class TabComponent extends React.Component<TabComponentProps> {
|
||||
|
||||
return (
|
||||
<div className="tabComponentContainer">
|
||||
{!this.props.hideHeader && <div className="tabs tabSwitch">{this.renderTabTitles()}</div>}
|
||||
{!this.props.hideHeader && (
|
||||
<div className="tabs tabSwitch" role="tablist">
|
||||
{this.renderTabTitles()}
|
||||
</div>
|
||||
)}
|
||||
<div className={className}>{currentTabContent.render()}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -23,12 +23,12 @@ describe("ThroughputInput Pane", () => {
|
||||
});
|
||||
|
||||
it("should switch mode properly", () => {
|
||||
wrapper.find('[aria-label="Manual mode"]').simulate("change");
|
||||
wrapper.find('[aria-label="Manual database throughput"]').simulate("change");
|
||||
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe(
|
||||
"Container throughput (400 - unlimited RU/s)"
|
||||
);
|
||||
|
||||
wrapper.find('[aria-label="Autoscale mode"]').simulate("change");
|
||||
wrapper.find('[aria-label="Autoscale database throughput"]').simulate("change");
|
||||
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe("Container throughput (autoscale)");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,6 +28,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
isSharded,
|
||||
isFreeTier,
|
||||
showFreeTierExceedThroughputTooltip,
|
||||
isQuickstart,
|
||||
setThroughputValue,
|
||||
setIsAutoscale,
|
||||
setIsThroughputCapExceeded,
|
||||
@@ -35,7 +36,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
}: ThroughputInputProps) => {
|
||||
const [isAutoscaleSelected, setIsAutoScaleSelected] = useState<boolean>(true);
|
||||
const [throughput, setThroughput] = useState<number>(
|
||||
isFreeTier ? AutoPilotUtils.autoPilotThroughput1K : AutoPilotUtils.autoPilotThroughput4K
|
||||
isFreeTier || isQuickstart ? AutoPilotUtils.autoPilotThroughput1K : AutoPilotUtils.autoPilotThroughput4K
|
||||
);
|
||||
const [isCostAcknowledged, setIsCostAcknowledged] = useState<boolean>(false);
|
||||
const [throughputError, setThroughputError] = useState<string>("");
|
||||
@@ -184,36 +185,48 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
</Stack>
|
||||
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<input
|
||||
className="throughputInputRadioBtn"
|
||||
aria-label="Autoscale mode"
|
||||
aria-required={true}
|
||||
checked={isAutoscaleSelected}
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={(e) => handleOnChangeMode(e, "Autoscale")}
|
||||
/>
|
||||
<span className="throughputInputRadioBtnLabel">Autoscale</span>
|
||||
<div role="radiogroup">
|
||||
<input
|
||||
id="Autoscale-input"
|
||||
className="throughputInputRadioBtn"
|
||||
aria-label="Autoscale database throughput"
|
||||
aria-required={true}
|
||||
checked={isAutoscaleSelected}
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={(e) => handleOnChangeMode(e, "Autoscale")}
|
||||
/>
|
||||
<label htmlFor="Autoscale-input" className="throughputInputRadioBtnLabel">
|
||||
Autoscale
|
||||
</label>
|
||||
|
||||
<input
|
||||
className="throughputInputRadioBtn"
|
||||
aria-label="Manual mode"
|
||||
checked={!isAutoscaleSelected}
|
||||
type="radio"
|
||||
aria-required={true}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={(e) => handleOnChangeMode(e, "Manual")}
|
||||
/>
|
||||
<span className="throughputInputRadioBtnLabel">Manual</span>
|
||||
<input
|
||||
id="Manual-input"
|
||||
className="throughputInputRadioBtn"
|
||||
aria-label="Manual database throughput"
|
||||
checked={!isAutoscaleSelected}
|
||||
type="radio"
|
||||
aria-required={true}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={(e) => handleOnChangeMode(e, "Manual")}
|
||||
/>
|
||||
<label className="throughputInputRadioBtnLabel" htmlFor="Manual-input">
|
||||
Manual
|
||||
</label>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
{isAutoscaleSelected && (
|
||||
<Stack className="throughputInputSpacing">
|
||||
<Text variant="small" aria-label="ruDescription">
|
||||
<Text variant="small" aria-label="capacity calculator of azure cosmos db">
|
||||
Estimate your required RU/s with{" "}
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/" aria-label="ruDescription">
|
||||
<Link
|
||||
target="_blank"
|
||||
href="https://cosmos.azure.com/capacitycalculator/"
|
||||
aria-label="capacity calculator of azure cosmos db"
|
||||
>
|
||||
capacity calculator
|
||||
</Link>
|
||||
.
|
||||
@@ -237,7 +250,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
||||
step={AutoPilotUtils.autoPilotIncrementStep}
|
||||
min={AutoPilotUtils.autoPilotThroughput1K}
|
||||
value={throughput.toString()}
|
||||
aria-label="Max request units per second"
|
||||
ariaLabel={`${isDatabase ? "Database" : getCollectionName()} max RU/s`}
|
||||
required={true}
|
||||
errorMessage={throughputError}
|
||||
/>
|
||||
|
||||
@@ -344,13 +344,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<StyledIconBase
|
||||
ariaLabel="Info"
|
||||
ariaLabel="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
>
|
||||
<IconBase
|
||||
ariaLabel="Info"
|
||||
ariaLabel="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
styles={[Function]}
|
||||
@@ -630,7 +630,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
}
|
||||
>
|
||||
<i
|
||||
aria-label="Info"
|
||||
aria-label="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
||||
className="panelInfoIcon root-57"
|
||||
data-icon-name="Info"
|
||||
role="img"
|
||||
@@ -654,40 +654,45 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
<div
|
||||
className="ms-Stack css-58"
|
||||
>
|
||||
<input
|
||||
aria-label="Autoscale mode"
|
||||
aria-required={true}
|
||||
checked={true}
|
||||
className="throughputInputRadioBtn"
|
||||
<div
|
||||
key=".0:$.0"
|
||||
onChange={[Function]}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
type="radio"
|
||||
/>
|
||||
<span
|
||||
className="throughputInputRadioBtnLabel"
|
||||
key=".0:$.1"
|
||||
role="radiogroup"
|
||||
>
|
||||
Autoscale
|
||||
</span>
|
||||
<input
|
||||
aria-label="Manual mode"
|
||||
aria-required={true}
|
||||
checked={false}
|
||||
className="throughputInputRadioBtn"
|
||||
key=".0:$.2"
|
||||
onChange={[Function]}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
type="radio"
|
||||
/>
|
||||
<span
|
||||
className="throughputInputRadioBtnLabel"
|
||||
key=".0:$.3"
|
||||
>
|
||||
Manual
|
||||
</span>
|
||||
<input
|
||||
aria-label="Autoscale database throughput"
|
||||
aria-required={true}
|
||||
checked={true}
|
||||
className="throughputInputRadioBtn"
|
||||
id="Autoscale-input"
|
||||
onChange={[Function]}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
type="radio"
|
||||
/>
|
||||
<label
|
||||
className="throughputInputRadioBtnLabel"
|
||||
htmlFor="Autoscale-input"
|
||||
>
|
||||
Autoscale
|
||||
</label>
|
||||
<input
|
||||
aria-label="Manual database throughput"
|
||||
aria-required={true}
|
||||
checked={false}
|
||||
className="throughputInputRadioBtn"
|
||||
id="Manual-input"
|
||||
onChange={[Function]}
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
type="radio"
|
||||
/>
|
||||
<label
|
||||
className="throughputInputRadioBtnLabel"
|
||||
htmlFor="Manual-input"
|
||||
>
|
||||
Manual
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
<Stack
|
||||
@@ -697,23 +702,23 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
className="ms-Stack throughputInputSpacing css-59"
|
||||
>
|
||||
<Text
|
||||
aria-label="ruDescription"
|
||||
aria-label="capacity calculator of azure cosmos db"
|
||||
key=".0:$.0"
|
||||
variant="small"
|
||||
>
|
||||
<span
|
||||
aria-label="ruDescription"
|
||||
aria-label="capacity calculator of azure cosmos db"
|
||||
className="css-54"
|
||||
>
|
||||
Estimate your required RU/s with
|
||||
|
||||
<StyledLinkBase
|
||||
aria-label="ruDescription"
|
||||
aria-label="capacity calculator of azure cosmos db"
|
||||
href="https://cosmos.azure.com/capacitycalculator/"
|
||||
target="_blank"
|
||||
>
|
||||
<LinkBase
|
||||
aria-label="ruDescription"
|
||||
aria-label="capacity calculator of azure cosmos db"
|
||||
href="https://cosmos.azure.com/capacitycalculator/"
|
||||
styles={[Function]}
|
||||
target="_blank"
|
||||
@@ -992,7 +997,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
}
|
||||
>
|
||||
<a
|
||||
aria-label="ruDescription"
|
||||
aria-label="capacity calculator of azure cosmos db"
|
||||
className="ms-Link root-60"
|
||||
href="https://cosmos.azure.com/capacitycalculator/"
|
||||
onClick={[Function]}
|
||||
@@ -1331,13 +1336,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<StyledIconBase
|
||||
ariaLabel="Info"
|
||||
ariaLabel="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
tabIndex={0}
|
||||
>
|
||||
<IconBase
|
||||
ariaLabel="Info"
|
||||
ariaLabel="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
||||
className="panelInfoIcon"
|
||||
iconName="Info"
|
||||
styles={[Function]}
|
||||
@@ -1617,7 +1622,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
}
|
||||
>
|
||||
<i
|
||||
aria-label="Info"
|
||||
aria-label="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
||||
className="panelInfoIcon root-57"
|
||||
data-icon-name="Info"
|
||||
role="img"
|
||||
@@ -1635,7 +1640,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
</div>
|
||||
</Stack>
|
||||
<StyledTextFieldBase
|
||||
aria-label="Max request units per second"
|
||||
ariaLabel="Container max RU/s"
|
||||
errorMessage=""
|
||||
id="autoscaleRUValueField"
|
||||
key=".0:$.2"
|
||||
@@ -1658,7 +1663,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
value="4000"
|
||||
>
|
||||
<TextFieldBase
|
||||
aria-label="Max request units per second"
|
||||
ariaLabel="Container max RU/s"
|
||||
deferredValidationTime={200}
|
||||
errorMessage=""
|
||||
id="autoscaleRUValueField"
|
||||
@@ -1956,6 +1961,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
||||
>
|
||||
<input
|
||||
aria-invalid={false}
|
||||
aria-label="Container max RU/s"
|
||||
className="ms-TextField-field field-64"
|
||||
id="autoscaleRUValueField"
|
||||
min={1000}
|
||||
|
||||
@@ -195,7 +195,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
|
||||
</div>
|
||||
{node.children && (
|
||||
<AnimateHeight duration={TreeNodeComponent.transitionDurationMS} height={this.state.isExpanded ? "auto" : 0}>
|
||||
<div className="nodeChildren" data-test={node.label}>
|
||||
<div className="nodeChildren" data-test={node.label} role="group">
|
||||
{TreeNodeComponent.getSortedChildren(node).map((childNode: TreeNode) => (
|
||||
<TreeNodeComponent
|
||||
key={`${childNode.label}-${generation + 1}-${childNode.timestamp}`}
|
||||
|
||||
@@ -100,6 +100,7 @@ exports[`TreeNodeComponent does not render children by default 1`] = `
|
||||
<div
|
||||
className="nodeChildren"
|
||||
data-test="label"
|
||||
role="group"
|
||||
>
|
||||
<TreeNodeComponent
|
||||
generation={3}
|
||||
@@ -251,6 +252,7 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
|
||||
<div
|
||||
className="nodeChildren"
|
||||
data-test="label"
|
||||
role="group"
|
||||
>
|
||||
<TreeNodeComponent
|
||||
generation={13}
|
||||
@@ -352,6 +354,7 @@ exports[`TreeNodeComponent renders loading icon 1`] = `
|
||||
<div
|
||||
className="nodeChildren"
|
||||
data-test="label"
|
||||
role="group"
|
||||
/>
|
||||
</AnimateHeight>
|
||||
</div>
|
||||
@@ -465,6 +468,7 @@ exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents
|
||||
<div
|
||||
className="nodeChildren"
|
||||
data-test="label"
|
||||
role="group"
|
||||
>
|
||||
<TreeNodeComponent
|
||||
generation={13}
|
||||
@@ -594,6 +598,7 @@ exports[`TreeNodeComponent renders unsorted children by default 1`] = `
|
||||
<div
|
||||
className="nodeChildren"
|
||||
data-test="label"
|
||||
role="group"
|
||||
>
|
||||
<TreeNodeComponent
|
||||
generation={3}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
.treeComponent {
|
||||
.nodeItem {
|
||||
&:focus {
|
||||
outline: 1px dashed @AccentMedium;
|
||||
outline: 2px @AccentMedium;
|
||||
}
|
||||
|
||||
.treeNodeHeader {
|
||||
|
||||
@@ -133,7 +133,6 @@ describe("ContainerSampleGenerator", () => {
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
// Rejects with error that contains experience
|
||||
expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createCollection } from "../../Common/dataAccess/createCollection";
|
||||
import { createDocument } from "../../Common/dataAccess/createDocument";
|
||||
import { createDocument as createMongoDocument } from "../../Common/MongoProxyClient";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
@@ -21,14 +22,21 @@ export class ContainerSampleGenerator {
|
||||
/**
|
||||
* Factory function to load the json data file
|
||||
*/
|
||||
public static async createSampleGeneratorAsync(container: Explorer): Promise<ContainerSampleGenerator> {
|
||||
public static async createSampleGeneratorAsync(
|
||||
container: Explorer,
|
||||
isCopilot?: boolean
|
||||
): Promise<ContainerSampleGenerator> {
|
||||
const generator = new ContainerSampleGenerator(container);
|
||||
let dataFileContent: any;
|
||||
if (userContext.apiType === "Gremlin") {
|
||||
if (isCopilot) {
|
||||
dataFileContent = await import(
|
||||
/* webpackChunkName: "queryCopilotSampleData" */ "../../../sampleData/queryCopilotSampleData.json"
|
||||
);
|
||||
} else if (userContext.apiType === "Gremlin") {
|
||||
dataFileContent = await import(
|
||||
/* webpackChunkName: "gremlinSampleJsonData" */ "../../../sampleData/gremlinSampleData.json"
|
||||
);
|
||||
} else if (userContext.apiType === "SQL") {
|
||||
} else if (userContext.apiType === "SQL" || userContext.apiType === "Mongo") {
|
||||
dataFileContent = await import(
|
||||
/* webpackChunkName: "sqlSampleJsonData" */ "../../../sampleData/sqlSampleData.json"
|
||||
);
|
||||
@@ -68,7 +76,7 @@ export class ContainerSampleGenerator {
|
||||
return database.findCollectionWithId(this.sampleDataFile.collectionId);
|
||||
}
|
||||
|
||||
public async populateContainerAsync(collection: ViewModels.Collection): Promise<void> {
|
||||
public async populateContainerAsync(collection: ViewModels.Collection, shardKey?: string): Promise<void> {
|
||||
if (!collection) {
|
||||
throw new Error("No container to populate");
|
||||
}
|
||||
@@ -99,7 +107,9 @@ export class ContainerSampleGenerator {
|
||||
await Promise.all(
|
||||
this.sampleDataFile.data.map(async (doc) => {
|
||||
try {
|
||||
await createDocument(collection, doc);
|
||||
userContext.apiType === "Mongo"
|
||||
? await createMongoDocument(collection.databaseId, collection, shardKey, doc)
|
||||
: await createDocument(collection, doc);
|
||||
} catch (error) {
|
||||
NotificationConsoleUtils.logConsoleError(error);
|
||||
}
|
||||
|
||||
@@ -16,12 +16,7 @@ import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHand
|
||||
import * as Logger from "../Common/Logger";
|
||||
import { QueriesClient } from "../Common/QueriesClient";
|
||||
import * as DataModels from "../Contracts/DataModels";
|
||||
import {
|
||||
ContainerConnectionInfo,
|
||||
IPhoenixConnectionInfoResult,
|
||||
IProvisionData,
|
||||
IResponse,
|
||||
} from "../Contracts/DataModels";
|
||||
import { ContainerConnectionInfo, IPhoenixServiceInfo, IProvisionData, IResponse } from "../Contracts/DataModels";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
|
||||
import { useSidePanel } from "../hooks/useSidePanel";
|
||||
@@ -98,7 +93,8 @@ export default class Explorer {
|
||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||
});
|
||||
this._isInitializingNotebooks = false;
|
||||
this.phoenixClient = new PhoenixClient();
|
||||
|
||||
this.phoenixClient = new PhoenixClient(userContext?.databaseAccount?.id);
|
||||
useNotebook.subscribe(
|
||||
() => this.refreshCommandBarButtons(),
|
||||
(state) => state.isNotebooksEnabledForAccount
|
||||
@@ -356,7 +352,7 @@ export default class Explorer {
|
||||
(notebookServerInfo && notebookServerInfo.notebookServerEndpoint === undefined))
|
||||
) {
|
||||
const provisionData: IProvisionData = {
|
||||
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
|
||||
cosmosEndpoint: userContext?.databaseAccount?.properties?.documentEndpoint,
|
||||
poolId: PoolIdType.DefaultPoolId,
|
||||
};
|
||||
const connectionStatus: ContainerConnectionInfo = {
|
||||
@@ -407,7 +403,7 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
private async setNotebookInfo(
|
||||
connectionInfo: IResponse<IPhoenixConnectionInfoResult>,
|
||||
connectionInfo: IResponse<IPhoenixServiceInfo>,
|
||||
connectionStatus: DataModels.ContainerConnectionInfo
|
||||
) {
|
||||
const containerData = {
|
||||
@@ -581,7 +577,7 @@ export default class Explorer {
|
||||
try {
|
||||
await Promise.all(
|
||||
databasesToLoad.map(async (database: ViewModels.Database) => {
|
||||
await database.loadCollections();
|
||||
await database.loadCollections(true);
|
||||
const isNewDatabase: boolean = _.some(newDatabases, (db: ViewModels.Database) => db.id() === database.id());
|
||||
if (isNewDatabase) {
|
||||
database.expandDatabase();
|
||||
@@ -1061,6 +1057,10 @@ export default class Explorer {
|
||||
title = "Cassandra Shell";
|
||||
break;
|
||||
|
||||
case ViewModels.TerminalKind.Postgres:
|
||||
title = "PSQL Shell";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Terminal kind: ${kind} not supported");
|
||||
}
|
||||
@@ -1120,7 +1120,7 @@ export default class Explorer {
|
||||
account: userContext.databaseAccount,
|
||||
container: this,
|
||||
junoClient: this.notebookManager?.junoClient,
|
||||
selectedTab: selectedTab || GalleryTabKind.PublicGallery,
|
||||
selectedTab: selectedTab || GalleryTabKind.OfficialSamples,
|
||||
notebookUrl,
|
||||
galleryItem,
|
||||
isFavorite,
|
||||
@@ -1247,9 +1247,11 @@ export default class Explorer {
|
||||
}
|
||||
|
||||
public async refreshExplorer(): Promise<void> {
|
||||
userContext.authType === AuthType.ResourceToken
|
||||
? this.refreshDatabaseForResourceToken()
|
||||
: this.refreshAllDatabases();
|
||||
if (userContext.apiType !== "Postgres") {
|
||||
userContext.authType === AuthType.ResourceToken
|
||||
? this.refreshDatabaseForResourceToken()
|
||||
: this.refreshAllDatabases();
|
||||
}
|
||||
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
|
||||
|
||||
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
|
||||
|
||||
@@ -729,6 +729,7 @@ export class D3ForceGraph implements GraphRenderer {
|
||||
var iconGroup = newNodes
|
||||
.append("g")
|
||||
.attr("class", "iconContainer")
|
||||
.attr("role", "group")
|
||||
.attr("tabindex", 0)
|
||||
.attr("aria-label", (d: D3Node) => {
|
||||
return this.retrieveNodeCaption(d);
|
||||
|
||||
@@ -6,9 +6,9 @@ import * as React from "react";
|
||||
import LoadGraphIcon from "../../../../images/LoadGraph.png";
|
||||
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
|
||||
import { queryDocuments } from "../../../Common/dataAccess/queryDocuments";
|
||||
import { queryDocumentsPage } from "../../../Common/dataAccess/queryDocumentsPage";
|
||||
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { InputProperty } from "../../../Contracts/ViewModels";
|
||||
@@ -182,7 +182,8 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
||||
public static readonly WITHOUT_STEP_ARGS_MAX_CHARS = 10000; // maximums char size of the without() step parameter
|
||||
public static readonly ROOT_LIST_PAGE_SIZE = 100;
|
||||
private static readonly MAX_LATEST_QUERIES = 10;
|
||||
private static readonly MAX_RESULT_SIZE = 10000;
|
||||
// TODO This prevents loading too much data in GraphExplorer and d3. A better is to cap the size of the result, rather than the number of items in the array.
|
||||
private static readonly MAX_RESULT_SIZE = 100_000;
|
||||
|
||||
private static readonly TAB_INDEX_JSON = 0;
|
||||
private static readonly TAB_INDEX_GRAPH = 1;
|
||||
|
||||
@@ -40,6 +40,7 @@ export class GraphVizComponent extends React.Component<GraphVizComponentProps> {
|
||||
{/* svg load more icon inlined as-is here: remove the style="fill:#374649;" so we can override it */}
|
||||
<svg
|
||||
role="img"
|
||||
aria-label="graph"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
@@ -135,6 +136,7 @@ export class GraphVizComponent extends React.Component<GraphVizComponentProps> {
|
||||
<g id="triangleRight">
|
||||
<svg
|
||||
role="img"
|
||||
aria-label="graph"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -31,7 +31,7 @@ export class GremlinClient {
|
||||
public client: GremlinSimpleClient;
|
||||
public pendingResults: Map<string, PendingResultData>; // public for testing purposes
|
||||
private maxResultSize: number;
|
||||
private static readonly PENDING_REQUEST_TIMEOUT_MS = 6 /* minutes */ * 60 /* seconds */ * 1000 /* ms */;
|
||||
private static readonly PENDING_REQUEST_TIMEOUT_MS = 1 /* hour */ * 3_600 /* seconds */ * 1_000 /* ms */;
|
||||
private static readonly TIMEOUT_ERROR_MSG = `Pending request timed out (${GremlinClient.PENDING_REQUEST_TIMEOUT_MS} ms)`;
|
||||
private static readonly LOG_AREA = "GremlinClient";
|
||||
|
||||
|
||||
@@ -17,19 +17,21 @@ export class MiddlePaneComponent extends React.Component<MiddlePaneComponentProp
|
||||
<div className="middlePane">
|
||||
<div className="graphTitle">
|
||||
<span className="paneTitle">Graph</span>
|
||||
<span
|
||||
<button
|
||||
style={{ border: "none", background: "none" }}
|
||||
className="graphExpandCollapseBtn pull-right"
|
||||
onClick={this.props.toggleExpandGraph}
|
||||
role="button"
|
||||
aria-expanded={this.props.isTabsContentExpanded}
|
||||
aria-name="View graph in full screen"
|
||||
tabIndex={0}
|
||||
aria-label={
|
||||
this.props.isTabsContentExpanded ? "Collapse graph to minimized view" : "View graph in full screen"
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={this.props.isTabsContentExpanded ? CollapseArrowIcon : ExpandIcon}
|
||||
alt={this.props.isTabsContentExpanded ? "collapse graph content" : "expand graph content"}
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="maingraphContainer">
|
||||
<GraphVizComponent forceGraphParams={this.props.graphVizProps.forceGraphParams} />
|
||||
|
||||
@@ -107,11 +107,11 @@ describe("New Vertex Component", () => {
|
||||
it("should call onTypeChange method on type dropdown change", () => {
|
||||
const DOWN_ARROW = { keyCode: 40 };
|
||||
const onTypeChange = jest.fn();
|
||||
const dropdown = screen.getByRole("listbox");
|
||||
const dropdown = screen.getByRole("combobox");
|
||||
dropdown.onclick = onTypeChange();
|
||||
dropdown.onkeydown = onTypeChange();
|
||||
|
||||
fireEvent.keyDown(screen.getByRole("listbox"), DOWN_ARROW);
|
||||
fireEvent.keyDown(screen.getByRole("combobox"), DOWN_ARROW);
|
||||
fireEvent.click(screen.getByText(/number/));
|
||||
expect(onTypeChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -145,6 +145,7 @@ export const NewVertexComponent: FunctionComponent<INewVertexComponentProps> = (
|
||||
type="text"
|
||||
id="propertyKeyNewVertexPane"
|
||||
componentRef={input}
|
||||
aria-required="true"
|
||||
placeholder="Key"
|
||||
autoComplete="off"
|
||||
aria-label={`Enter value for propery ${index + 1}`}
|
||||
@@ -152,6 +153,8 @@ export const NewVertexComponent: FunctionComponent<INewVertexComponentProps> = (
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => onKeyChange(event, index)}
|
||||
/>
|
||||
</div>
|
||||
<span className="mandatoryStar">* </span>
|
||||
|
||||
<div className="valueCol">
|
||||
<TextField
|
||||
className="edgeInput"
|
||||
@@ -165,7 +168,7 @@ export const NewVertexComponent: FunctionComponent<INewVertexComponentProps> = (
|
||||
</div>
|
||||
<div>
|
||||
<Dropdown
|
||||
role="listbox"
|
||||
role="combobox"
|
||||
placeholder="Select an option"
|
||||
defaultSelectedKey={data.values[0].type}
|
||||
style={{ width: 100 }}
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
|
||||
import { useNotebook } from "Explorer/Notebook/useNotebook";
|
||||
import * as React from "react";
|
||||
import { userContext } from "UserContext";
|
||||
import create, { UseStore } from "zustand";
|
||||
import { StyleConstants } from "../../../Common/Constants";
|
||||
import { ConnectionStatusType, StyleConstants } from "../../../Common/Constants";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../../Explorer";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
@@ -33,6 +34,22 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||
const buttons = useCommandBar((state) => state.contextButtons);
|
||||
const backgroundColor = StyleConstants.BaseLight;
|
||||
|
||||
if (userContext.apiType === "Postgres") {
|
||||
const buttons = CommandBarComponentButtonFactory.createPostgreButtons(container);
|
||||
return (
|
||||
<div className="commandBarContainer">
|
||||
<FluentCommandBar
|
||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||
items={CommandBarUtil.convertButton(buttons, backgroundColor)}
|
||||
styles={{
|
||||
root: { backgroundColor: backgroundColor },
|
||||
}}
|
||||
overflowButtonProps={{ ariaLabel: "More commands" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const staticButtons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(container, selectedNodeState);
|
||||
const contextButtons = (buttons || []).concat(
|
||||
CommandBarComponentButtonFactory.createContextCommandBarButtons(container, selectedNodeState)
|
||||
@@ -53,7 +70,12 @@ export const CommandBar: React.FC<Props> = ({ container }: Props) => {
|
||||
const uiFabricControlButtons = CommandBarUtil.convertButton(controlButtons, backgroundColor);
|
||||
uiFabricControlButtons.forEach((btn: ICommandBarItemProps) => (btn.iconOnly = true));
|
||||
|
||||
if (useNotebook.getState().isPhoenixNotebooks || useNotebook.getState().isPhoenixFeatures) {
|
||||
const connectionInfo = useNotebook((state) => state.connectionInfo);
|
||||
|
||||
if (
|
||||
(useNotebook.getState().isPhoenixNotebooks || useNotebook.getState().isPhoenixFeatures) &&
|
||||
connectionInfo?.status !== ConnectionStatusType.Connect
|
||||
) {
|
||||
uiFabricControlButtons.unshift(CommandBarUtil.createConnectionStatus(container, "connectionStatus"));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: "EnableTable" }],
|
||||
capabilities: [{ name: "EnableMongo" }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
@@ -38,6 +38,38 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
||||
);
|
||||
expect(enableAzureSynapseLinkBtn).toBeDefined();
|
||||
});
|
||||
|
||||
it("Button should not be visible for Tables API", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: "EnableTable" }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const enableAzureSynapseLinkBtn = buttons.find(
|
||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||
);
|
||||
expect(enableAzureSynapseLinkBtn).toBeUndefined();
|
||||
});
|
||||
|
||||
it("Button should not be visible for Cassandra API", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: "EnableCassandra" }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer, selectedNodeState);
|
||||
const enableAzureSynapseLinkBtn = buttons.find(
|
||||
(button) => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||
);
|
||||
expect(enableAzureSynapseLinkBtn).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Enable notebook button", () => {
|
||||
|
||||
@@ -51,11 +51,13 @@ export function createStaticCommandBarButtons(
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
|
||||
buttons.push(newCollectionBtn);
|
||||
if (userContext.apiType !== "Tables" && userContext.apiType !== "Cassandra") {
|
||||
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
|
||||
|
||||
const addSynapseLink = createOpenSynapseLinkDialogButton(container);
|
||||
if (addSynapseLink) {
|
||||
buttons.push(createDivider());
|
||||
buttons.push(addSynapseLink);
|
||||
if (addSynapseLink) {
|
||||
buttons.push(createDivider());
|
||||
buttons.push(addSynapseLink);
|
||||
}
|
||||
}
|
||||
|
||||
if (userContext.apiType !== "Tables") {
|
||||
@@ -104,6 +106,10 @@ export function createStaticCommandBarButtons(
|
||||
if (!useNotebook.getState().isPhoenixFeatures) {
|
||||
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.mongoShellTemporarilyDownMsg);
|
||||
}
|
||||
} else if (btn.commandButtonLabel.indexOf("Open Terminal") !== -1) {
|
||||
if (!useNotebook.getState().isPhoenixFeatures) {
|
||||
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg);
|
||||
}
|
||||
} else if (!useNotebook.getState().isPhoenixNotebooks) {
|
||||
applyNotebooksTemporarilyDownStyle(btn, Constants.Notebook.temporarilyDownMsg);
|
||||
}
|
||||
@@ -198,6 +204,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
|
||||
if (showOpenFullScreen) {
|
||||
const label = "Open Full Screen";
|
||||
const fullScreenButton: CommandButtonComponentProps = {
|
||||
id: "openFullScreenBtn",
|
||||
iconSrc: OpenInTabIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
@@ -519,6 +526,28 @@ function createOpenCassandraTerminalButton(container: Explorer): CommandButtonCo
|
||||
};
|
||||
}
|
||||
|
||||
function createOpenPsqlTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||
const label = "Open PSQL Shell";
|
||||
const disableButton =
|
||||
!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled;
|
||||
return {
|
||||
iconSrc: HostedTerminalIcon,
|
||||
iconAlt: label,
|
||||
onCommandClick: () => {
|
||||
if (useNotebook.getState().isNotebookEnabled) {
|
||||
container.openNotebookTerminal(ViewModels.TerminalKind.Postgres);
|
||||
}
|
||||
},
|
||||
commandButtonLabel: label,
|
||||
hasPopup: false,
|
||||
disabled: disableButton,
|
||||
ariaLabel: label,
|
||||
tooltipText: !disableButton
|
||||
? ""
|
||||
: "This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.",
|
||||
};
|
||||
}
|
||||
|
||||
function createNotebookWorkspaceResetButton(container: Explorer): CommandButtonComponentProps {
|
||||
const label = "Reset Workspace";
|
||||
return {
|
||||
@@ -581,3 +610,9 @@ function createStaticCommandBarButtonsForResourceToken(
|
||||
|
||||
return [newSqlQueryBtn, openQueryBtn];
|
||||
}
|
||||
|
||||
export function createPostgreButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||
const openPostgreShellBtn = createOpenPsqlTerminalButton(container);
|
||||
|
||||
return [openPostgreShellBtn];
|
||||
}
|
||||
|
||||
@@ -147,12 +147,9 @@ export class NotificationConsoleComponent extends React.Component<
|
||||
<div className="notificationConsoleControls">
|
||||
<Dropdown
|
||||
label="Filter:"
|
||||
role="combobox"
|
||||
selectedKey={this.state.selectedFilter}
|
||||
options={NotificationConsoleComponent.FilterOptions}
|
||||
onChange={this.onFilterSelected.bind(this)}
|
||||
aria-labelledby="consoleFilterLabel"
|
||||
aria-label={this.state.selectedFilter}
|
||||
/>
|
||||
<span className="consoleSplitter" />
|
||||
<span
|
||||
|
||||
@@ -112,8 +112,6 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
||||
className="notificationConsoleControls"
|
||||
>
|
||||
<Dropdown
|
||||
aria-label="All"
|
||||
aria-labelledby="consoleFilterLabel"
|
||||
label="Filter:"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
@@ -136,7 +134,6 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
|
||||
},
|
||||
]
|
||||
}
|
||||
role="combobox"
|
||||
selectedKey="All"
|
||||
/>
|
||||
<span
|
||||
@@ -278,8 +275,6 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
||||
className="notificationConsoleControls"
|
||||
>
|
||||
<Dropdown
|
||||
aria-label="All"
|
||||
aria-labelledby="consoleFilterLabel"
|
||||
label="Filter:"
|
||||
onChange={[Function]}
|
||||
options={
|
||||
@@ -302,7 +297,6 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
|
||||
},
|
||||
]
|
||||
}
|
||||
role="combobox"
|
||||
selectedKey="All"
|
||||
/>
|
||||
<span
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as InMemoryContentProviderUtils from "./ContentProviders/InMemoryContentProviderUtils";
|
||||
|
||||
describe("fromContentUri", () => {
|
||||
it("fromContentUri should return valid result", () => {
|
||||
const contentUri = "memory://resource/path";
|
||||
const result = "resource";
|
||||
|
||||
expect(InMemoryContentProviderUtils.fromContentUri(contentUri)).toEqual(result);
|
||||
});
|
||||
|
||||
it("fromContentUri should return undefined on invalid input", () => {
|
||||
const contentUri = "invalid";
|
||||
|
||||
expect(InMemoryContentProviderUtils.fromContentUri(contentUri)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("toContentUri should return valid result", () => {
|
||||
const path = "resource/path";
|
||||
const result = "memory://resource/path";
|
||||
|
||||
expect(InMemoryContentProviderUtils.toContentUri(path)).toEqual(result);
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@ import { ConnectionStatusType, HttpHeaders, HttpStatusCodes, Notebook, PoolIdTyp
|
||||
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||
import * as Logger from "../../Common/Logger";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import { IPhoenixConnectionInfoResult, IProvisionData, IResponse } from "../../Contracts/DataModels";
|
||||
import { IPhoenixServiceInfo, IProvisionData, IResponse } from "../../Contracts/DataModels";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
|
||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||
@@ -23,7 +23,7 @@ export class NotebookContainerClient {
|
||||
private scheduleTimerId: NodeJS.Timeout;
|
||||
|
||||
constructor(private onConnectionLost: () => void) {
|
||||
this.phoenixClient = new PhoenixClient();
|
||||
this.phoenixClient = new PhoenixClient(userContext?.databaseAccount?.id);
|
||||
this.retryOptions = {
|
||||
retries: Notebook.retryAttempts,
|
||||
maxTimeout: Notebook.retryAttemptDelayMs,
|
||||
@@ -129,9 +129,9 @@ export class NotebookContainerClient {
|
||||
);
|
||||
}
|
||||
|
||||
public async resetWorkspace(): Promise<IResponse<IPhoenixConnectionInfoResult>> {
|
||||
public async resetWorkspace(): Promise<IResponse<IPhoenixServiceInfo>> {
|
||||
this.isResettingWorkspace = true;
|
||||
let response: IResponse<IPhoenixConnectionInfoResult>;
|
||||
let response: IResponse<IPhoenixServiceInfo>;
|
||||
try {
|
||||
response = await this._resetWorkspace();
|
||||
} catch (error) {
|
||||
@@ -142,7 +142,7 @@ export class NotebookContainerClient {
|
||||
return response;
|
||||
}
|
||||
|
||||
private async _resetWorkspace(): Promise<IResponse<IPhoenixConnectionInfoResult>> {
|
||||
private async _resetWorkspace(): Promise<IResponse<IPhoenixServiceInfo>> {
|
||||
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
|
||||
if (!notebookServerInfo || !notebookServerInfo.notebookServerEndpoint) {
|
||||
const error = "No server endpoint detected";
|
||||
|
||||
@@ -124,8 +124,9 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
}
|
||||
|
||||
const firstWriteLocation =
|
||||
databaseAccount?.properties?.writeLocations &&
|
||||
databaseAccount?.properties?.writeLocations[0]?.locationName.toLowerCase();
|
||||
userContext.apiType === "Postgres"
|
||||
? databaseAccount?.location
|
||||
: databaseAccount?.properties?.writeLocations?.[0]?.locationName.toLowerCase();
|
||||
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
|
||||
const authorizationHeader = getAuthorizationHeader();
|
||||
try {
|
||||
@@ -307,13 +308,16 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||
let isPhoenixFeatures = false;
|
||||
|
||||
const isPublicInternetAllowed = isPublicInternetAccessAllowed();
|
||||
const phoenixClient = new PhoenixClient();
|
||||
const phoenixClient = new PhoenixClient(userContext?.databaseAccount?.id);
|
||||
const dbAccountAllowedInfo = await phoenixClient.getDbAccountAllowedStatus();
|
||||
|
||||
if (dbAccountAllowedInfo.status === HttpStatusCodes.OK) {
|
||||
if (dbAccountAllowedInfo?.type === PhoenixErrorType.PhoenixFlightFallback) {
|
||||
isPhoenixNotebooks = isPublicInternetAllowed && userContext.features.phoenixNotebooks;
|
||||
isPhoenixFeatures = isPublicInternetAllowed && userContext.features.phoenixFeatures;
|
||||
isPhoenixNotebooks = isPublicInternetAllowed && userContext.features.phoenixNotebooks === true;
|
||||
isPhoenixFeatures =
|
||||
isPublicInternetAllowed &&
|
||||
// phoenix needs to be enabled for Postgres accounts since the PSQL shell requires phoenix containers
|
||||
(userContext.features.phoenixFeatures === true || userContext.apiType === "Postgres");
|
||||
} else {
|
||||
isPhoenixNotebooks = isPhoenixFeatures = isPublicInternetAllowed;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
jest.mock("../hooks/useFullScreenURLs");
|
||||
import "@testing-library/jest-dom";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import { useFullScreenURLs } from "../hooks/useFullScreenURLs";
|
||||
import { OpenFullScreen } from "./OpenFullScreen";
|
||||
|
||||
it("renders the correct URLs", () => {
|
||||
(useFullScreenURLs as jest.Mock).mockReturnValue({
|
||||
readWrite: "read and write url",
|
||||
read: "read only url",
|
||||
});
|
||||
|
||||
render(<OpenFullScreen />);
|
||||
expect(screen.getByLabelText("Read and Write")).toHaveValue("https://cosmos.azure.com/?key=read and write url");
|
||||
expect(screen.getByLabelText("Read Only")).toHaveValue("https://cosmos.azure.com/?key=read only url");
|
||||
expect(screen.getByText("Open")).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -1,66 +1,26 @@
|
||||
import { DefaultButton, PrimaryButton, Spinner, Stack, Text, TextField } from "@fluentui/react";
|
||||
import copyToClipboard from "clipboard-copy";
|
||||
import { PrimaryButton, Stack, Text } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { useFullScreenURLs } from "../hooks/useFullScreenURLs";
|
||||
|
||||
export const OpenFullScreen: React.FunctionComponent = () => {
|
||||
const [isReadUrlCopy, setIsReadUrlCopy] = React.useState<boolean>(false);
|
||||
const [isReadWriteUrlCopy, setIsReadWriteUrlCopy] = React.useState<boolean>(false);
|
||||
const result = useFullScreenURLs();
|
||||
if (!result) {
|
||||
return <Spinner label="Generating URLs..." ariaLive="assertive" labelPosition="right" />;
|
||||
}
|
||||
|
||||
const readWriteUrl = `https://cosmos.azure.com/?key=${result.readWrite}`;
|
||||
const readUrl = `https://cosmos.azure.com/?key=${result.read}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack tokens={{ childrenGap: 10 }}>
|
||||
<Text>
|
||||
Open this database account in a new browser tab with Cosmos DB Explorer. Or copy the read-write or read only
|
||||
access urls below to share with others. For security purposes, the URLs grant time-bound access to the
|
||||
account. When access expires, you can reconnect, using a valid connection string for the account.
|
||||
</Text>
|
||||
<TextField label="Read and Write" readOnly defaultValue={readWriteUrl} />
|
||||
<Stack horizontal tokens={{ childrenGap: 10 }}>
|
||||
<DefaultButton
|
||||
ariaLabel={isReadWriteUrlCopy ? "Copied url" : "Copy"}
|
||||
onClick={() => {
|
||||
copyToClipboard(readWriteUrl);
|
||||
setIsReadWriteUrlCopy(true);
|
||||
}}
|
||||
text={isReadWriteUrlCopy ? "Copied" : "Copy"}
|
||||
iconProps={{ iconName: "Copy" }}
|
||||
/>
|
||||
<PrimaryButton
|
||||
onClick={() => {
|
||||
window.open(readWriteUrl, "_blank");
|
||||
}}
|
||||
text="Open"
|
||||
iconProps={{ iconName: "OpenInNewWindow" }}
|
||||
/>
|
||||
<div style={{ padding: "34px" }}>
|
||||
<Stack tokens={{ childrenGap: 10 }}>
|
||||
<Text>
|
||||
Open this database account in a new browser tab with Cosmos DB Explorer. You can connect using your
|
||||
Microsoft account or a connection string.
|
||||
</Text>
|
||||
<Stack horizontal tokens={{ childrenGap: 10 }}>
|
||||
<PrimaryButton
|
||||
onClick={() => {
|
||||
window.open("https://cosmos.azure.com/", "_blank");
|
||||
}}
|
||||
text="Open"
|
||||
iconProps={{ iconName: "OpenInNewWindow" }}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<TextField label="Read Only" readOnly defaultValue={readUrl} />
|
||||
<Stack horizontal tokens={{ childrenGap: 10 }}>
|
||||
<DefaultButton
|
||||
ariaLabel={isReadUrlCopy ? "Copied url" : "Copy"}
|
||||
onClick={() => {
|
||||
setIsReadUrlCopy(true);
|
||||
copyToClipboard(readUrl);
|
||||
}}
|
||||
text={isReadUrlCopy ? "Copied" : "Copy"}
|
||||
iconProps={{ iconName: "Copy" }}
|
||||
/>
|
||||
<PrimaryButton
|
||||
onClick={() => {
|
||||
window.open(readUrl, "_blank");
|
||||
}}
|
||||
text="Open"
|
||||
iconProps={{ iconName: "OpenInNewWindow" }}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ const SharedDatabaseDefault: DataModels.IndexingPolicy = {
|
||||
],
|
||||
};
|
||||
|
||||
const AllPropertiesIndexed: DataModels.IndexingPolicy = {
|
||||
export const AllPropertiesIndexed: DataModels.IndexingPolicy = {
|
||||
indexingMode: "consistent",
|
||||
automatic: true,
|
||||
includedPaths: [
|
||||
@@ -89,9 +89,10 @@ export interface AddCollectionPanelState {
|
||||
enableIndexing: boolean;
|
||||
isSharded: boolean;
|
||||
partitionKey: string;
|
||||
subPartitionKeys: string[];
|
||||
enableDedicatedThroughput: boolean;
|
||||
createMongoWildCardIndex: boolean;
|
||||
useHashV2: boolean;
|
||||
useHashV1: boolean;
|
||||
enableAnalyticalStore: boolean;
|
||||
uniqueKeys: string[];
|
||||
errorMessage: string;
|
||||
@@ -121,9 +122,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
enableIndexing: true,
|
||||
isSharded: userContext.apiType !== "Tables",
|
||||
partitionKey: this.getPartitionKey(),
|
||||
subPartitionKeys: [],
|
||||
enableDedicatedThroughput: false,
|
||||
createMongoWildCardIndex: isCapabilityEnabled("EnableMongo"),
|
||||
useHashV2: false,
|
||||
createMongoWildCardIndex:
|
||||
isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport"),
|
||||
useHashV1: false,
|
||||
enableAnalyticalStore: false,
|
||||
uniqueKeys: [],
|
||||
errorMessage: "",
|
||||
@@ -259,37 +262,46 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
true
|
||||
).toLocaleLowerCase()}.`}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel={`A database is analogous to a namespace. It is the unit of management for a set of ${getCollectionName(
|
||||
true
|
||||
).toLocaleLowerCase()}.`}
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={this.state.createNewDatabase}
|
||||
aria-label="Create new database"
|
||||
aria-checked={this.state.createNewDatabase}
|
||||
name="databaseType"
|
||||
type="radio"
|
||||
role="radio"
|
||||
id="databaseCreateNew"
|
||||
tabIndex={0}
|
||||
onChange={this.onCreateNewDatabaseRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="panelRadioBtnLabel">Create new</span>
|
||||
<div role="radiogroup">
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={this.state.createNewDatabase}
|
||||
aria-label="Create new database"
|
||||
aria-checked={this.state.createNewDatabase}
|
||||
name="databaseType"
|
||||
type="radio"
|
||||
role="radio"
|
||||
id="databaseCreateNew"
|
||||
tabIndex={0}
|
||||
onChange={this.onCreateNewDatabaseRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="panelRadioBtnLabel">Create new</span>
|
||||
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={!this.state.createNewDatabase}
|
||||
aria-label="Use existing database"
|
||||
aria-checked={!this.state.createNewDatabase}
|
||||
name="databaseType"
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={this.onUseExistingDatabaseRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="panelRadioBtnLabel">Use existing</span>
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={!this.state.createNewDatabase}
|
||||
aria-label="Use existing database"
|
||||
aria-checked={!this.state.createNewDatabase}
|
||||
name="databaseType"
|
||||
type="radio"
|
||||
role="radio"
|
||||
tabIndex={0}
|
||||
onChange={this.onUseExistingDatabaseRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="panelRadioBtnLabel">Use existing</span>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
{this.state.createNewDatabase && (
|
||||
@@ -335,7 +347,14 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
true
|
||||
).toLocaleLowerCase()} within the database.`}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel={`Throughput configured at the database level will be shared across all ${getCollectionName(
|
||||
true
|
||||
).toLocaleLowerCase()} within the database.`}
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -346,6 +365,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
isDatabase={true}
|
||||
isSharded={this.state.isSharded}
|
||||
isFreeTier={this.isFreeTierAccount()}
|
||||
isQuickstart={this.props.isQuickstart}
|
||||
setThroughputValue={(throughput: number) => (this.newDatabaseThroughput = throughput)}
|
||||
setIsAutoscale={(isAutoscale: boolean) => (this.isNewDatabaseAutoscale = isAutoscale)}
|
||||
setIsThroughputCapExceeded={(isThroughputCapExceeded: boolean) =>
|
||||
@@ -382,7 +402,13 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon
|
||||
role="button"
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -465,7 +491,14 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
"Sharded collections split your data across many replica sets (shards) to achieve unlimited scalability. Sharded collections require choosing a shard key (field) to evenly distribute your data."
|
||||
}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel={
|
||||
"Sharded collections split your data across many replica sets (shards) to achieve unlimited scalability. Sharded collections require choosing a shard key (field) to evenly distribute your data."
|
||||
}
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -512,7 +545,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content={this.getPartitionKeyTooltipText()}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel={this.getPartitionKeyTooltipText()}
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -544,6 +582,77 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{userContext.apiType === "SQL" &&
|
||||
this.state.subPartitionKeys.map((subPartitionKey: string, index: number) => {
|
||||
return (
|
||||
<Stack style={{ marginBottom: 8 }} key={`uniqueKey${index}`} horizontal>
|
||||
<div
|
||||
style={{
|
||||
width: "20px",
|
||||
border: "solid",
|
||||
borderWidth: "0px 0px 1px 1px",
|
||||
marginRight: "5px",
|
||||
}}
|
||||
></div>
|
||||
<input
|
||||
type="text"
|
||||
id="addCollection-partitionKeyValue"
|
||||
key={`addCollection-partitionKeyValue_${index}`}
|
||||
aria-required
|
||||
required
|
||||
size={40}
|
||||
tabIndex={index > 0 ? 1 : 0}
|
||||
className="panelTextField"
|
||||
autoComplete="off"
|
||||
placeholder={this.getPartitionKeyPlaceHolder(index)}
|
||||
aria-label={this.getPartitionKeyName()}
|
||||
pattern={".*"}
|
||||
title={""}
|
||||
value={subPartitionKey}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const subPartitionKeys = [...this.state.subPartitionKeys];
|
||||
if (!this.state.subPartitionKeys[index] && !event.target.value.startsWith("/")) {
|
||||
subPartitionKeys[index] = "/" + event.target.value.trim();
|
||||
this.setState({ subPartitionKeys });
|
||||
} else {
|
||||
subPartitionKeys[index] = event.target.value.trim();
|
||||
this.setState({ subPartitionKeys });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
iconProps={{ iconName: "Delete" }}
|
||||
style={{ height: 27 }}
|
||||
onClick={() => {
|
||||
const subPartitionKeys = this.state.subPartitionKeys.filter((uniqueKey, j) => index !== j);
|
||||
this.setState({ subPartitionKeys });
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
{userContext.apiType === "SQL" && (
|
||||
<Stack className="panelGroupSpacing">
|
||||
<DefaultButton
|
||||
styles={{ root: { padding: 0, width: 200, height: 30 }, label: { fontSize: 12 } }}
|
||||
hidden={this.state.useHashV1}
|
||||
disabled={this.state.subPartitionKeys.length >= Constants.BackendDefaults.maxNumMultiHashPartition}
|
||||
onClick={() => this.setState({ subPartitionKeys: [...this.state.subPartitionKeys, ""] })}
|
||||
>
|
||||
Add hierarchical partition key
|
||||
</DefaultButton>
|
||||
{this.state.subPartitionKeys.length > 0 && (
|
||||
<Text variant="small">
|
||||
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} /> This feature allows you to
|
||||
partition your data with up to three levels of keys for better data distribution. Requires .NET
|
||||
V3, Java V4 SDK, or preview JavaScript V3 SDK.{" "}
|
||||
<Link href="https://aka.ms/cosmos-hierarchical-partitioning" target="_blank">
|
||||
Learn more
|
||||
</Link>
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
@@ -570,7 +679,17 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
does not count towards the throughput you provisioned for the database. This throughput amount will be
|
||||
billed in addition to the throughput amount you provisioned at the database level.`}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel={`You can optionally provision dedicated throughput for a ${getCollectionName().toLocaleLowerCase()} within a database that has throughput
|
||||
provisioned. This dedicated throughput amount will not be shared with other ${getCollectionName(
|
||||
true
|
||||
).toLocaleLowerCase()} in the database and
|
||||
does not count towards the throughput you provisioned for the database. This throughput amount will be
|
||||
billed in addition to the throughput amount you provisioned at the database level.`}
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -581,6 +700,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
isDatabase={false}
|
||||
isSharded={this.state.isSharded}
|
||||
isFreeTier={this.isFreeTierAccount()}
|
||||
isQuickstart={this.props.isQuickstart}
|
||||
setThroughputValue={(throughput: number) => (this.collectionThroughput = throughput)}
|
||||
setIsAutoscale={(isAutoscale: boolean) => (this.isCollectionAutoscale = isAutoscale)}
|
||||
setIsThroughputCapExceeded={(isThroughputCapExceeded: boolean) =>
|
||||
@@ -600,11 +720,18 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
</Text>
|
||||
<TooltipHost
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content="Unique keys provide developers with the ability to add a layer of data integrity to their database. By
|
||||
creating a unique key policy when a container is created, you ensure the uniqueness of one or more values
|
||||
per partition key."
|
||||
content={
|
||||
"Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key."
|
||||
}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel={
|
||||
"Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key."
|
||||
}
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -667,40 +794,48 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content={this.getAnalyticalStorageTooltipContent()}
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel="Enable analytical store capability to perform near real-time analytics on your operational data, without
|
||||
impacting the performance of transactional workloads."
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={this.state.enableAnalyticalStore}
|
||||
disabled={!this.isSynapseLinkEnabled()}
|
||||
aria-label="Enable analytical store"
|
||||
aria-checked={this.state.enableAnalyticalStore}
|
||||
name="analyticalStore"
|
||||
type="radio"
|
||||
role="radio"
|
||||
id="enableAnalyticalStoreBtn"
|
||||
tabIndex={0}
|
||||
onChange={this.onEnableAnalyticalStoreRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="panelRadioBtnLabel">On</span>
|
||||
<div role="radiogroup">
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={this.state.enableAnalyticalStore}
|
||||
disabled={!this.isSynapseLinkEnabled()}
|
||||
aria-label="Enable analytical store"
|
||||
aria-checked={this.state.enableAnalyticalStore}
|
||||
name="analyticalStore"
|
||||
type="radio"
|
||||
role="radio"
|
||||
id="enableAnalyticalStoreBtn"
|
||||
tabIndex={0}
|
||||
onChange={this.onEnableAnalyticalStoreRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="panelRadioBtnLabel">On</span>
|
||||
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={!this.state.enableAnalyticalStore}
|
||||
disabled={!this.isSynapseLinkEnabled()}
|
||||
aria-label="Disable analytical store"
|
||||
aria-checked={!this.state.enableAnalyticalStore}
|
||||
name="analyticalStore"
|
||||
type="radio"
|
||||
role="radio"
|
||||
id="disableAnalyticalStoreBtn"
|
||||
tabIndex={0}
|
||||
onChange={this.onDisableAnalyticalStoreRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="panelRadioBtnLabel">Off</span>
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={!this.state.enableAnalyticalStore}
|
||||
disabled={!this.isSynapseLinkEnabled()}
|
||||
aria-label="Disable analytical store"
|
||||
aria-checked={!this.state.enableAnalyticalStore}
|
||||
name="analyticalStore"
|
||||
type="radio"
|
||||
role="radio"
|
||||
id="disableAnalyticalStoreBtn"
|
||||
tabIndex={0}
|
||||
onChange={this.onDisableAnalyticalStoreRadioBtnChange.bind(this)}
|
||||
/>
|
||||
<span className="panelRadioBtnLabel">Off</span>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
{!this.isSynapseLinkEnabled() && (
|
||||
@@ -733,7 +868,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
}}
|
||||
>
|
||||
<Stack className="panelGroupSpacing" id="collapsibleSectionContent">
|
||||
{isCapabilityEnabled("EnableMongo") && (
|
||||
{isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport") && (
|
||||
<Stack className="panelGroupSpacing">
|
||||
<Stack horizontal>
|
||||
<span className="mandatoryStar">* </span>
|
||||
@@ -744,7 +879,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
content="The _id field is indexed by default. Creating a wildcard index for all fields will optimize queries and is recommended for development."
|
||||
>
|
||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||
<Icon
|
||||
iconName="Info"
|
||||
className="panelInfoIcon"
|
||||
tabIndex={0}
|
||||
ariaLabel="The _id field is indexed by default. Creating a wildcard index for all fields will optimize queries and is recommended for development."
|
||||
/>
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
|
||||
@@ -764,18 +904,29 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
)}
|
||||
|
||||
{userContext.apiType === "SQL" && (
|
||||
<Checkbox
|
||||
label="My partition key is larger than 101 bytes"
|
||||
checked={this.state.useHashV2}
|
||||
styles={{
|
||||
text: { fontSize: 12 },
|
||||
checkbox: { width: 12, height: 12 },
|
||||
label: { padding: 0, alignItems: "center" },
|
||||
}}
|
||||
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) =>
|
||||
this.setState({ useHashV2: isChecked })
|
||||
}
|
||||
/>
|
||||
<Stack className="panelGroupSpacing">
|
||||
<Checkbox
|
||||
label="My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)"
|
||||
checked={this.state.useHashV1}
|
||||
styles={{
|
||||
text: { fontSize: 12 },
|
||||
checkbox: { width: 12, height: 12 },
|
||||
label: { padding: 0, alignItems: "center", wordWrap: "break-word", whiteSpace: "break-spaces" },
|
||||
}}
|
||||
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) =>
|
||||
this.setState({ useHashV1: isChecked, subPartitionKeys: [] })
|
||||
}
|
||||
/>
|
||||
<Text variant="small">
|
||||
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} /> To ensure compatibility with
|
||||
older SDKs, the created container will use a legacy partitioning scheme that supports partition
|
||||
key values of size only up to 101 bytes. If this is enabled, you will not be able to use
|
||||
hierarchical partition keys.{" "}
|
||||
<Link href="https://aka.ms/cosmos-large-pk" target="_blank">
|
||||
Learn more
|
||||
</Link>
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</CollapsibleSectionComponent>
|
||||
@@ -830,12 +981,20 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName;
|
||||
}
|
||||
|
||||
private getPartitionKeyPlaceHolder(): string {
|
||||
private getPartitionKeyPlaceHolder(index?: number): string {
|
||||
switch (userContext.apiType) {
|
||||
case "Mongo":
|
||||
return "e.g., address.zipCode";
|
||||
return "e.g., categoryId";
|
||||
case "Gremlin":
|
||||
return "e.g., /address";
|
||||
case "SQL":
|
||||
return `${
|
||||
index === undefined
|
||||
? "Required - first partition key e.g., /TenantId"
|
||||
: index === 0
|
||||
? "second partition key e.g., /UserId"
|
||||
: "third partition key e.g., /SessionId"
|
||||
}`;
|
||||
default:
|
||||
return "e.g., /address/zipCode";
|
||||
}
|
||||
@@ -957,7 +1116,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
return userContext.apiType === "SQL" ? "/pk" : "pk";
|
||||
}
|
||||
if (this.props.isQuickstart) {
|
||||
return userContext.apiType === "SQL" ? "/address" : "address";
|
||||
return userContext.apiType === "SQL" ? "/categoryId" : "categoryId";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -1161,11 +1320,16 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
}
|
||||
|
||||
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = this.parseUniqueKeys();
|
||||
const partitionKeyVersion = this.state.useHashV2 ? 2 : undefined;
|
||||
const partitionKeyVersion = this.state.useHashV1 ? undefined : 2;
|
||||
const partitionKey: DataModels.PartitionKey = partitionKeyString
|
||||
? {
|
||||
paths: [partitionKeyString],
|
||||
kind: "Hash",
|
||||
paths: [
|
||||
partitionKeyString,
|
||||
...(userContext.apiType === "SQL" && this.state.subPartitionKeys.length > 0
|
||||
? this.state.subPartitionKeys
|
||||
: []),
|
||||
],
|
||||
kind: userContext.apiType === "SQL" && this.state.subPartitionKeys.length > 0 ? "MultiHash" : "Hash",
|
||||
version: partitionKeyVersion,
|
||||
}
|
||||
: undefined;
|
||||
@@ -1249,7 +1413,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
collection.isSampleCollection = true;
|
||||
useTeachingBubble.getState().setSampleCollection(collection);
|
||||
const sampleGenerator = await ContainerSampleGenerator.createSampleGeneratorAsync(this.props.explorer);
|
||||
await sampleGenerator.populateContainerAsync(collection);
|
||||
await sampleGenerator.populateContainerAsync(collection, partitionKeyString);
|
||||
// auto-expand sample database + container and show teaching bubble
|
||||
await database.expandDatabase();
|
||||
collection.expandCollection();
|
||||
|
||||
@@ -89,8 +89,8 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
||||
}
|
||||
} catch (error) {
|
||||
setLoadingFalse();
|
||||
setFormError(error);
|
||||
const errorMessage = getErrorMessage(error);
|
||||
setFormError(errorMessage);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.DeleteDatabase,
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
|
||||
"onRefreshDatabasesKeyPress": [Function],
|
||||
"onRefreshResourcesClick": [Function],
|
||||
"phoenixClient": PhoenixClient {
|
||||
"armResourceId": undefined,
|
||||
"retryOptions": Object {
|
||||
"maxTimeout": 5000,
|
||||
"minTimeout": 5000,
|
||||
|
||||
@@ -48,7 +48,7 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
|
||||
)}
|
||||
</Text>
|
||||
{showErrorDetails && (
|
||||
<a className="paneErrorLink" role="link" onClick={expandConsole}>
|
||||
<a className="paneErrorLink" role="button" onClick={expandConsole} tabIndex={0} onKeyPress={expandConsole}>
|
||||
More details
|
||||
</a>
|
||||
)}
|
||||
|
||||
@@ -4,11 +4,11 @@ import React, { FunctionComponent, useState } from "react";
|
||||
import { Areas, SavedQueries } from "../../../Common/Constants";
|
||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||
import { Query } from "../../../Contracts/DataModels";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { useTabs } from "../../../hooks/useTabs";
|
||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { useTabs } from "../../../hooks/useTabs";
|
||||
import Explorer from "../../Explorer";
|
||||
import { NewQueryTab } from "../../Tabs/QueryTab/QueryTab";
|
||||
import { useDatabases } from "../../useDatabases";
|
||||
@@ -16,9 +16,13 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor
|
||||
|
||||
interface SaveQueryPaneProps {
|
||||
explorer: Explorer;
|
||||
queryToSave?: string;
|
||||
}
|
||||
|
||||
export const SaveQueryPane: FunctionComponent<SaveQueryPaneProps> = ({ explorer }: SaveQueryPaneProps): JSX.Element => {
|
||||
export const SaveQueryPane: FunctionComponent<SaveQueryPaneProps> = ({
|
||||
explorer,
|
||||
queryToSave,
|
||||
}: SaveQueryPaneProps): JSX.Element => {
|
||||
const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
|
||||
const [isLoading, { setTrue: setLoadingTrue, setFalse: setLoadingFalse }] = useBoolean(false);
|
||||
const [formError, setFormError] = useState<string>("");
|
||||
@@ -36,7 +40,7 @@ export const SaveQueryPane: FunctionComponent<SaveQueryPaneProps> = ({ explorer
|
||||
}
|
||||
|
||||
const queryTab = useTabs.getState().activeTab as NewQueryTab;
|
||||
const query: string = queryTab && queryTab.iTabAccessor.onSaveClickEvent();
|
||||
const query: string = queryToSave || queryTab?.iTabAccessor.onSaveClickEvent();
|
||||
|
||||
if (!queryName || queryName.length === 0) {
|
||||
setFormError("No query name specified");
|
||||
@@ -62,8 +66,8 @@ export const SaveQueryPane: FunctionComponent<SaveQueryPaneProps> = ({ explorer
|
||||
try {
|
||||
await explorer.queriesClient.saveQuery(queryParam);
|
||||
setLoadingFalse();
|
||||
queryTab.tabTitle(queryParam.queryName);
|
||||
queryTab.tabPath(`${queryTab.collection.databaseId}>${queryTab.collection.id()}>${queryParam.queryName}`);
|
||||
queryTab?.tabTitle(queryParam.queryName);
|
||||
queryTab?.tabPath(`${queryTab.collection.databaseId}>${queryTab.collection.id()}>${queryParam.queryName}`);
|
||||
traceSuccess(
|
||||
Action.SaveQuery,
|
||||
{
|
||||
|
||||
@@ -21,6 +21,11 @@ export const SettingsPane: FunctionComponent = () => {
|
||||
const [customItemPerPage, setCustomItemPerPage] = useState<number>(
|
||||
LocalStorageUtility.getEntryNumber(StorageKey.CustomItemPerPage) || 0
|
||||
);
|
||||
const [containerPaginationEnabled, setContainerPaginationEnabled] = useState<boolean>(
|
||||
LocalStorageUtility.hasItem(StorageKey.ContainerPaginationEnabled)
|
||||
? LocalStorageUtility.getEntryString(StorageKey.ContainerPaginationEnabled) === "true"
|
||||
: false
|
||||
);
|
||||
const [crossPartitionQueryEnabled, setCrossPartitionQueryEnabled] = useState<boolean>(
|
||||
LocalStorageUtility.hasItem(StorageKey.IsCrossPartitionQueryEnabled)
|
||||
? LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
|
||||
@@ -50,6 +55,7 @@ export const SettingsPane: FunctionComponent = () => {
|
||||
isCustomPageOptionSelected() ? customItemPerPage : Constants.Queries.unlimitedItemsPerPage
|
||||
);
|
||||
LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage);
|
||||
LocalStorageUtility.setEntryString(StorageKey.ContainerPaginationEnabled, containerPaginationEnabled.toString());
|
||||
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, crossPartitionQueryEnabled.toString());
|
||||
LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism);
|
||||
|
||||
@@ -185,6 +191,25 @@ export const SettingsPane: FunctionComponent = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="settingsSection">
|
||||
<div className="settingsSectionPart">
|
||||
<div className="settingsSectionLabel">
|
||||
Enable container pagination
|
||||
<InfoTooltip>
|
||||
Load 50 containers at a time. Currently, containers are not pulled in alphanumeric order.
|
||||
</InfoTooltip>
|
||||
</div>
|
||||
<Checkbox
|
||||
styles={{
|
||||
label: { padding: 0 },
|
||||
}}
|
||||
className="padding"
|
||||
ariaLabel="Enable container pagination"
|
||||
checked={containerPaginationEnabled}
|
||||
onChange={() => setContainerPaginationEnabled(!containerPaginationEnabled)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{shouldShowCrossPartitionOption && (
|
||||
<div className="settingsSection">
|
||||
<div className="settingsSectionPart">
|
||||
|
||||