Compare commits

..

3 Commits

Author SHA1 Message Date
Victor Meng
af5fd699f9 Increase the video width 2022-09-16 18:25:02 -07:00
Victor Meng
d78610cd33 Remove feature flag 2022-09-14 18:27:09 -07:00
Victor Meng
bc99ca372b Hardcode API type to Postgres to test Postgres quick start 2022-09-14 17:21:26 -07:00
60 changed files with 890 additions and 1575 deletions

View File

@@ -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 "vimeng@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
- run: nuget 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 "vimeng@microsoft.com" -Password "$AZURE_DEVOPS_PAT"
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
- run: nuget push -SkipDuplicate -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
- uses: actions/upload-artifact@v2

View File

@@ -1,6 +1,5 @@
<!doctype html>
<html class="default no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -10,292 +9,256 @@
<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>
<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>
<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>
<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>
<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 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 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>
</header>
<div class="container container-main">
<div class="row">
<div class="col-8 col-content">
<section class="tsd-panel tsd-comment">
</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>
</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>
</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"> = &quot;cassandraDbKeys&quot;</span></div>
<aside class="tsd-sources">
</aside>
<div class="tsd-comment tsd-typography">
<div class="lead">
<p>Portal Blade types</p>
<p>Keys blade of a Cassandra API account.</p>
</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>
</ul>
</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"> = &quot;keys&quot;</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>
</section>
</div>
</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"> =
&quot;cassandraDbKeys&quot;</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>
<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"> = &quot;metrics&quot;</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>
</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"> =
&quot;keys&quot;</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"> = &quot;metrics&quot;</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"> =
&quot;mongoDbKeys&quot;</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"> = &quot;keys&quot;</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"> =
&quot;tableKeys&quot;</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>
</div>
</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>
</div>
<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"> = &quot;mongoDbKeys&quot;</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"> = &quot;keys&quot;</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"> = &quot;tableKeys&quot;</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>
</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>
</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>
</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>
</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>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -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,14 +388,15 @@ 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;
}
@@ -409,111 +410,112 @@ 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 {
@@ -522,4 +524,4 @@ input::-webkit-inner-spin-button {
width: 100%;
padding-top: 10px;
}
}*/
}*/

View File

@@ -1,6 +1,6 @@
import React, { FunctionComponent, MutableRefObject, useEffect, useRef } from "react";
import arrowLeftImg from "../../images/imgarrowlefticon.svg";
import { getApiShortDisplayName } from "../Utils/APITypeUtils";
import { userContext } from "../UserContext";
import { NormalizedEventKey } from "./Constants";
export interface CollapsedResourceTreeProps {
@@ -45,7 +45,7 @@ export const CollapsedResourceTree: FunctionComponent<CollapsedResourceTreeProps
<img className="arrowCollapsed" src={arrowLeftImg} alt="Expand" />
</span>
<span className="collectionCollapsed">
<span>{getApiShortDisplayName()}</span>
<span>{userContext.apiType} API</span>
</span>
</li>
</ul>

View File

@@ -6,7 +6,6 @@ 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 {
@@ -43,7 +42,7 @@ export const ResourceTreeContainer: FunctionComponent<ResourceTreeContainerProps
{/* Collections Window Title/Command Bar - Start */}
<div className="collectiontitle">
<div className="coltitle">
<span className="titlepadcol">{getApiShortDisplayName()}</span>
<span className="titlepadcol">{userContext.apiType} API</span>
<div className="float-right">
<span
className="padimgcolrefresh"

View File

@@ -73,17 +73,6 @@ 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;
@@ -137,28 +126,12 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
/>
{!isEntityValueDisable && (
<TooltipHost content="Edit property" id="editTooltip">
<Image
{...imageProps}
src={EditIcon}
alt="editEntity"
id="editEntity"
onClick={onEditEntity}
tabIndex={0}
onKeyPress={handleKeyPress}
/>
<Image {...imageProps} src={EditIcon} alt="editEntity" id="editEntity" onClick={onEditEntity} />
</TooltipHost>
)}
{isDeleteOptionVisible && userContext.apiType !== "Cassandra" && (
<TooltipHost content="Delete property" id="deleteTooltip">
<Image
{...imageProps}
src={DeleteIcon}
alt="delete entity"
id="deleteEntity"
onClick={onDeleteEntity}
tabIndex={0}
onKeyPress={handleKeyPressdelete}
/>
<Image {...imageProps} src={DeleteIcon} alt="delete entity" id="deleteEntity" onClick={onDeleteEntity} />
</TooltipHost>
)}
</Stack>

View File

@@ -9,7 +9,7 @@ export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ children }:
return (
<span>
<TooltipHost content={children}>
<Icon iconName="Info" ariaLabel={children} className="panelInfoIcon" tabIndex={0} />
<Icon iconName="Info" ariaLabel="Info" className="panelInfoIcon" tabIndex={0} />
</TooltipHost>
</span>
);

View File

@@ -4,7 +4,6 @@ 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();
@@ -14,7 +13,7 @@ export const deleteDocument = async (collection: CollectionBase, documentId: Doc
await client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), getPartitionKeyValue(documentId))
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
.delete();
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
} catch (error) {

View File

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

View File

@@ -6,7 +6,6 @@ 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();
@@ -22,7 +21,8 @@ export const readDocument = async (collection: CollectionBase, documentId: Docum
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), getPartitionKeyValue(documentId))
// use undefined if the partitionKeyValue is empty
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
.read(options);
return response?.resource;

View File

@@ -6,7 +6,6 @@ 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,
@@ -26,7 +25,7 @@ export const updateDocument = async (
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), getPartitionKeyValue(documentId))
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
.replace(newDocument, options);
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);

View File

@@ -33,8 +33,6 @@ export interface DatabaseAccountExtendedProperties {
privateEndpointConnections?: unknown[];
capacity?: { totalThroughputLimit: number };
locations?: DatabaseAccountResponseLocation[];
postgresqlEndpoint?: string;
publicNetworkAccess?: string;
}
export interface DatabaseAccountResponseLocation {
@@ -568,16 +566,6 @@ 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 {
MaxAllocationTimeExceeded = "MaxAllocationTimeExceeded",
MaxDbAccountsPerUserExceeded = "MaxDbAccountsPerUserExceeded",

View File

@@ -35,9 +35,6 @@ export enum MessageTypes {
RefreshDatabaseAccount,
CloseTab,
OpenQuickstartBlade,
OpenPostgreSQLPasswordReset,
OpenPostgresNetworkingBlade,
OpenCosmosDBNetworkingBlade,
}
export { Versions, ActionContracts, Diagnostics };

View File

@@ -186,6 +186,7 @@ export interface Collection extends CollectionBase {
onDrop(source: Collection, event: { originalEvent: DragEvent }): void;
uploadFiles(fileList: FileList): Promise<{ data: UploadDetailsRecord[] }>;
getLabel(): string;
getPendingThroughputSplitNotification(): Promise<DataModels.Notification>;
}
@@ -371,7 +372,6 @@ export enum TerminalKind {
Default = 0,
Mongo = 1,
Cassandra = 2,
Postgres = 3,
}
export interface DataExplorerInputsFrame {
@@ -395,10 +395,6 @@ export interface DataExplorerInputsFrame {
sharedThroughputDefault?: number;
dataExplorerVersion?: string;
defaultCollectionThroughput?: CollectionCreationDefaults;
isPostgresAccount?: boolean;
isReplica?: boolean;
// 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;

View File

@@ -2,7 +2,6 @@
* 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";
@@ -41,7 +40,6 @@ 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();
}
@@ -76,8 +74,6 @@ 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) {

View File

@@ -35,7 +35,6 @@ exports[`SettingsComponent renders 1`] = `
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,
@@ -112,7 +111,6 @@ exports[`SettingsComponent renders 1`] = `
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,

View File

@@ -344,13 +344,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
onMouseLeave={[Function]}
>
<StyledIconBase
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."
ariaLabel="Info"
className="panelInfoIcon"
iconName="Info"
tabIndex={0}
>
<IconBase
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."
ariaLabel="Info"
className="panelInfoIcon"
iconName="Info"
styles={[Function]}
@@ -630,7 +630,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
}
>
<i
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."
aria-label="Info"
className="panelInfoIcon root-57"
data-icon-name="Info"
role="img"
@@ -1331,13 +1331,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
onMouseLeave={[Function]}
>
<StyledIconBase
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."
ariaLabel="Info"
className="panelInfoIcon"
iconName="Info"
tabIndex={0}
>
<IconBase
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."
ariaLabel="Info"
className="panelInfoIcon"
iconName="Info"
styles={[Function]}
@@ -1617,7 +1617,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
}
>
<i
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."
aria-label="Info"
className="panelInfoIcon root-57"
data-icon-name="Info"
role="img"

View File

@@ -93,8 +93,7 @@ export default class Explorer {
dataExplorerArea: Constants.Areas.ResourceTree,
});
this._isInitializingNotebooks = false;
this.phoenixClient = new PhoenixClient(userContext?.databaseAccount?.id);
this.phoenixClient = new PhoenixClient();
useNotebook.subscribe(
() => this.refreshCommandBarButtons(),
(state) => state.isNotebooksEnabledForAccount
@@ -186,7 +185,9 @@ export default class Explorer {
useNotebook.getState().setNotebookBasePath(userContext.features.notebookBasePath);
}
this.refreshExplorer();
if (userContext.apiType !== "Postgres") {
this.refreshExplorer();
}
}
public async initiateAndRefreshNotebookList(): Promise<void> {
@@ -352,7 +353,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 = {
@@ -1057,10 +1058,6 @@ 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");
}
@@ -1247,11 +1244,9 @@ export default class Explorer {
}
public async refreshExplorer(): Promise<void> {
if (userContext.apiType !== "Postgres") {
userContext.authType === AuthType.ResourceToken
? this.refreshDatabaseForResourceToken()
: this.refreshAllDatabases();
}
userContext.authType === AuthType.ResourceToken
? this.refreshDatabaseForResourceToken()
: this.refreshAllDatabases();
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount

View File

@@ -523,28 +523,6 @@ 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 {
@@ -609,7 +587,16 @@ function createStaticCommandBarButtonsForResourceToken(
}
export function createPostgreButtons(container: Explorer): CommandButtonComponentProps[] {
const openPostgreShellBtn = createOpenPsqlTerminalButton(container);
const postgreShellLabel = "Open PostgreSQL Shell";
const openPostgreShellBtn = {
iconSrc: HostedTerminalIcon,
iconAlt: postgreShellLabel,
onCommandClick: () => container.openNotebookTerminal(ViewModels.TerminalKind.Mongo),
commandButtonLabel: postgreShellLabel,
hasPopup: false,
disabled: false,
ariaLabel: postgreShellLabel,
};
return [openPostgreShellBtn];
}

View File

@@ -23,7 +23,7 @@ export class NotebookContainerClient {
private scheduleTimerId: NodeJS.Timeout;
constructor(private onConnectionLost: () => void) {
this.phoenixClient = new PhoenixClient(userContext?.databaseAccount?.id);
this.phoenixClient = new PhoenixClient();
this.retryOptions = {
retries: Notebook.retryAttempts,
maxTimeout: Notebook.retryAttemptDelayMs,

View File

@@ -124,9 +124,8 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
}
const firstWriteLocation =
userContext.apiType === "Postgres"
? databaseAccount?.location
: databaseAccount?.properties?.writeLocations?.[0]?.locationName.toLowerCase();
databaseAccount?.properties?.writeLocations &&
databaseAccount?.properties?.writeLocations[0]?.locationName.toLowerCase();
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
const authorizationHeader = getAuthorizationHeader();
try {
@@ -308,16 +307,13 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
let isPhoenixFeatures = false;
const isPublicInternetAllowed = isPublicInternetAccessAllowed();
const phoenixClient = new PhoenixClient(userContext?.databaseAccount?.id);
const phoenixClient = new PhoenixClient();
const dbAccountAllowedInfo = await phoenixClient.getDbAccountAllowedStatus();
if (dbAccountAllowedInfo.status === HttpStatusCodes.OK) {
if (dbAccountAllowedInfo?.type === PhoenixErrorType.PhoenixFlightFallback) {
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");
isPhoenixFeatures = isPublicInternetAllowed && userContext.features.phoenixFeatures === true;
} else {
isPhoenixNotebooks = isPhoenixFeatures = isPublicInternetAllowed;
}

View File

@@ -1,9 +1,17 @@
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.getByText("Open")).toBeDefined();
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");
});

View File

@@ -1,26 +1,66 @@
import { PrimaryButton, Stack, Text } from "@fluentui/react";
import { DefaultButton, PrimaryButton, Spinner, Stack, Text, TextField } from "@fluentui/react";
import copyToClipboard from "clipboard-copy";
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 (
<>
<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 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" }}
/>
</Stack>
</div>
<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>
</>
);
};

View File

@@ -122,8 +122,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
isSharded: userContext.apiType !== "Tables",
partitionKey: this.getPartitionKey(),
enableDedicatedThroughput: false,
createMongoWildCardIndex:
isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport"),
createMongoWildCardIndex: isCapabilityEnabled("EnableMongo"),
useHashV2: false,
enableAnalyticalStore: false,
uniqueKeys: [],
@@ -736,7 +735,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
}}
>
<Stack className="panelGroupSpacing" id="collapsibleSectionContent">
{isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport") && (
{isCapabilityEnabled("EnableMongo") && (
<Stack className="panelGroupSpacing">
<Stack horizontal>
<span className="mandatoryStar">*&nbsp;</span>

View File

@@ -24,7 +24,6 @@ exports[`GitHub Repos Panel should render Default properly 1`] = `
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,

View File

@@ -14,7 +14,6 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"phoenixClient": PhoenixClient {
"armResourceId": undefined,
"retryOptions": Object {
"maxTimeout": 5000,
"minTimeout": 5000,

View File

@@ -242,11 +242,6 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
submitButtonText: getButtonLabel(userContext.apiType),
onSubmit,
};
const handlekeypressaddentity = (event: React.KeyboardEvent<HTMLElement>) => {
if (event.key === "Enter" || event.key === "Space") {
addNewEntity();
}
};
return (
<RightPaneForm {...props}>
@@ -289,13 +284,7 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
);
})}
{userContext.apiType !== "Cassandra" && (
<Stack
horizontal
onClick={addNewEntity}
className="addButtonEntiy"
tabIndex={0}
onKeyPress={handlekeypressaddentity}
>
<Stack horizontal onClick={addNewEntity} className="addButtonEntiy">
<Image {...imageProps} src={AddPropertyIcon} alt="Add Entity" />
<Text className="addNewParamStyle">{getAddButtonLabel(userContext.apiType)}</Text>
</Stack>

View File

@@ -29,14 +29,10 @@ exports[`Excute Add Table Entity Pane should render Default properly 1`] = `
className="addButtonEntiy"
horizontal={true}
onClick={[Function]}
onKeyPress={[Function]}
tabIndex={0}
>
<div
className="ms-Stack addButtonEntiy css-53"
onClick={[Function]}
onKeyPress={[Function]}
tabIndex={0}
>
<StyledImageBase
alt="Add Entity"

View File

@@ -1,105 +0,0 @@
export const newTableCommand = `DROP SCHEMA IF EXISTS cosmosdb_tutorial CASCADE;
CREATE SCHEMA cosmosdb_tutorial;
SET search_path to cosmosdb_tutorial;
CREATE TABLE github_users
(
user_id bigint,
url text,
login text,
avatar_url text,
gravatar_id text,
display_login text
);
CREATE TABLE github_events
(
event_id bigint,
event_type text,
event_public boolean,
repo_id bigint,
payload jsonb,
repo jsonb,
user_id bigint,
org jsonb,
created_at timestamp
);
CREATE INDEX event_type_index ON github_events (event_type);
CREATE INDEX payload_index ON github_events USING GIN (payload jsonb_path_ops);
`;
export const newTableCommandForDisplay = `DROP SCHEMA IF EXISTS cosmosdb_tutorial CASCADE;
CREATE SCHEMA cosmosdb_tutorial;
-- Using schema created for tutorial
SET search_path to cosmosdb_tutorial;
CREATE TABLE github_users
(
user_id bigint,
url text,
login text,
avatar_url text,
gravatar_id text,
display_login text
);
CREATE TABLE github_events
(
event_id bigint,
event_type text,
event_public boolean,
repo_id bigint,
payload jsonb,
repo jsonb,
user_id bigint,
org jsonb,
created_at timestamp
);
--Create indexes on events table
CREATE INDEX event_type_index ON github_events (event_type);
CREATE INDEX payload_index ON github_events USING GIN (payload jsonb_path_ops);`;
export const distributeTableCommand = `SET search_path to cosmosdb_tutorial;
SELECT create_distributed_table('github_users', 'user_id');
SELECT create_distributed_table('github_events', 'user_id');
`;
export const distributeTableCommandForDisplay = `-- Using schema created for the tutorial
SET search_path to cosmosdb_tutorial;
SELECT create_distributed_table('github_users', 'user_id');
SELECT create_distributed_table('github_events', 'user_id');`;
export const loadDataCommand = `SET search_path to cosmosdb_tutorial;
\\COPY github_users FROM PROGRAM 'wget -q -O - "$@" "https://examples.citusdata.com/users.csv"' WITH (FORMAT CSV);
\\COPY github_events FROM PROGRAM 'wget -q -O - "$@" "https://examples.citusdata.com/events.csv"' WITH (FORMAT CSV);
`;
export const loadDataCommandForDisplay = `-- Using schema created for the tutorial
SET search_path to cosmosdb_tutorial;
-- download users and store in table
\\COPY github_users FROM PROGRAM 'wget -q -O - "$@" "https://examples.citusdata.com/users.csv"' WITH (FORMAT CSV);
\\COPY github_events FROM PROGRAM 'wget -q -O - "$@" "https://examples.citusdata.com/events.csv"' WITH (FORMAT CSV);`;
export const queryCommand = `SET search_path to cosmosdb_tutorial;
SELECT count(*) FROM github_users;
SELECT created_at, event_type, repo->>'name' AS repo_name
FROM github_events
WHERE user_id = 3861633;
SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::int) AS num_commits FROM github_events WHERE event_type = 'PushEvent' AND payload @> '{"ref":"refs/heads/master"}' GROUP BY hour ORDER BY hour;
`;
export const queryCommandForDisplay = `-- Using schema created for the tutorial
SET search_path to cosmosdb_tutorial;
-- count all rows (across shards)
SELECT count(*) FROM github_users;
-- Find all events for a single user.
SELECT created_at, event_type, repo->>'name' AS repo_name
FROM github_events
WHERE user_id = 3861633;
-- Find the number of commits on the master branch per hour
SELECT date_trunc('hour', created_at) AS hour, sum((payload->>'distinct_size')::int) AS num_commits FROM github_events WHERE event_type = 'PushEvent' AND payload @> '{"ref":"refs/heads/master"}' GROUP BY hour ORDER BY hour;`;

View File

@@ -94,7 +94,7 @@ const getDescriptionText = (page: number): string => {
case 1:
return "Azure Cosmos DB is a fully managed NoSQL database service for modern app development. ";
case 2:
return "Launch the quickstart for a tutorial to learn how to create a database, add sample data, connect to a sample app and more.";
return "Launch the quickstart for a tutotrial to learn how to create a database, add sample data, connect to a sample app and more.";
case 3:
return "Already have an existing app? Connect your database to an app, or tooling of your choice from Data Explorer.";
default:

View File

@@ -1,22 +0,0 @@
import { Image, PrimaryButton, Stack, Text } from "@fluentui/react";
import { sendMessage } from "Common/MessageHandler";
import { MessageTypes } from "Contracts/ExplorerContracts";
import React from "react";
import FirewallRuleScreenshot from "../../../images/firewallRule.png";
export const QuickstartFirewallNotification: React.FC = (): JSX.Element => (
<Stack style={{ padding: "16px 20px" }}>
<Text block>
To use the PostgreSQL shell, you need to add a firewall rule to allow access from all IP addresses
(0.0.0.0-255.255.255).
</Text>
<Text block>We strongly recommend removing this rule once you finish using the PostgreSQL shell.</Text>
<Image style={{ margin: "20px 0" }} src={FirewallRuleScreenshot} />
<PrimaryButton
style={{ width: 150 }}
onClick={() => sendMessage({ type: MessageTypes.OpenPostgresNetworkingBlade })}
>
Add firewall rule
</PrimaryButton>
</Stack>
);

View File

@@ -11,17 +11,6 @@ import {
Text,
TextField,
} from "@fluentui/react";
import {
distributeTableCommand,
distributeTableCommandForDisplay,
loadDataCommand,
loadDataCommandForDisplay,
newTableCommand,
newTableCommandForDisplay,
queryCommand,
queryCommandForDisplay,
} from "Explorer/Quickstart/PostgreQuickstartCommands";
import { useTerminal } from "hooks/useTerminal";
import React, { useState } from "react";
import Youtube from "react-youtube";
import Pivot1SelectedIcon from "../../../images/Pivot1_selected.svg";
@@ -46,6 +35,24 @@ enum GuideSteps {
export const QuickstartGuide: React.FC = (): JSX.Element => {
const [currentStep, setCurrentStep] = useState<number>(0);
const newTableCommand = `CREATE TABLE github_users
(
user_id bigint,
url text,
login text,
.....`;
const distributeTableCommand = `SELECT create_distributed_table('github_users', 'user_id');
SELECT create_distributed_table('github_events', 'user_id');`;
const loadDataCommand = `-- download users and store in table
COPY github_users FROM PROGRAM 'curl https://examples.citusdata.com/
users.csv' WITH (FORMAT CSV)`;
const queryCommand = `-- Find all events for a single user.
-- (A common transactional/operational query)
SELECT created_at, event_type, repo->>'name' AS repo_name
FROM github_events
WHERE user_id = 3861633;`;
const onCopyBtnClicked = (selector: string): void => {
const textfield: HTMLInputElement = document.querySelector(selector);
@@ -95,32 +102,24 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
<Stack style={{ paddingTop: 8, height: "100%", width: "100%" }}>
<Stack style={{ flexGrow: 1, padding: "0 20px", overflow: "auto" }}>
<Text variant="xxLarge">Quick start guide</Text>
<Text variant="medium">Gettings started in Cosmos DB</Text>
{currentStep < 5 && (
<Pivot
style={{ marginTop: 10, width: "100%" }}
selectedKey={GuideSteps[currentStep]}
onLinkClick={(item?: PivotItem) => setCurrentStep(Object.values(GuideSteps).indexOf(item.props.itemKey))}
>
<Pivot style={{ marginTop: 10, width: "100%" }} selectedKey={GuideSteps[currentStep]}>
<PivotItem
headerText="Login"
onRenderItemLink={(props, defaultRenderer) => customPivotHeaderRenderer(props, defaultRenderer, 0)}
itemKey={GuideSteps[0]}
onClick={() => {
setCurrentStep(0);
}}
onClick={() => setCurrentStep(0)}
>
<Stack style={{ marginTop: 20 }}>
<Text>
This tutorial guides you to create and query distributed tables using a sample dataset.
<br />
<br />
To begin, please enter the cluster&apos;s password in the PostgreSQL terminal.
<br />
<br />
Note: If you navigate out of the Quick Start tab (PostgreSQL Shell), the session will be closed and
all ongoing commands might be interrupted.
This tutorial walks you through the most essential Cosmos DB PostgreSQL statements that will be used
in the PostgreSQL shell (on the right). You can also choose to go through this quick start by
connecting to PGAdmin or other tooling of your choice. <br />
<br /> Before you can interact with your data using PGShell, you will need to login - please follow
instructions on the right to enter your password
</Text>
<Youtube videoId="nT64dFSfiUo" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "80%" }} />
</Stack>
</PivotItem>
<PivotItem
@@ -130,28 +129,19 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
onClick={() => setCurrentStep(1)}
>
<Stack style={{ marginTop: 20 }}>
<Text>Let&apos;s create two tables github_users and github_events in cosmosdb_tutorial schema.</Text>
<DefaultButton
style={{ marginTop: 16, width: 150 }}
onClick={() => useTerminal.getState().sendMessage(newTableCommand)}
>
Create new table
</DefaultButton>
<Text>
After logging in, lets create some new tables for storing data. We will start with two sample tables
- one for storing github users and one for storing github events
</Text>
<DefaultButton style={{ marginTop: 16, width: 110 }}>New table</DefaultButton>
<Stack horizontal style={{ marginTop: 16 }}>
<TextField
id="newTableCommand"
multiline
rows={5}
rows={6}
readOnly
defaultValue={newTableCommandForDisplay}
styles={{
root: { width: "90%" },
field: {
backgroundColor: "#EEEEEE",
fontFamily:
"Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New",
},
}}
defaultValue={newTableCommand}
styles={{ root: { width: "90%" } }}
/>
<IconButton
iconProps={{
@@ -160,7 +150,7 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
onClick={() => onCopyBtnClicked("#newTableCommand")}
/>
</Stack>
<Youtube videoId="il_sA6U1WcY" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "60%" }} />
</Stack>
</PivotItem>
<PivotItem
@@ -171,32 +161,21 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
>
<Stack style={{ marginTop: 20 }}>
<Text>
Lets distribute the two tables using the create_distributed_table() function.
Congratulations, you have now created your first 2 tables.
<br />
<br />
We are choosing user_id as the distribution column for our sample dataset.
Your table needs to be sharded on the worker nodes. You need to distribute table before you load any
data or run any queries
</Text>
<DefaultButton
style={{ marginTop: 16, width: 200 }}
onClick={() => useTerminal.getState().sendMessage(distributeTableCommand)}
>
Create distributed table
</DefaultButton>
<DefaultButton style={{ marginTop: 16, width: 150 }}>Distribute table</DefaultButton>
<Stack horizontal style={{ marginTop: 16 }}>
<TextField
id="distributeTableCommand"
multiline
rows={5}
rows={2}
readOnly
defaultValue={distributeTableCommandForDisplay}
styles={{
root: { width: "90%" },
field: {
backgroundColor: "#EEEEEE",
fontFamily:
"Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New",
},
}}
defaultValue={distributeTableCommand}
styles={{ root: { width: "90%" } }}
/>
<IconButton
iconProps={{
@@ -205,7 +184,7 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
onClick={() => onCopyBtnClicked("#distributeTableCommand")}
/>
</Stack>
<Youtube videoId="kCCDRRrN1r0" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "60%" }} />
</Stack>
</PivotItem>
<PivotItem
@@ -215,28 +194,22 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
onClick={() => setCurrentStep(3)}
>
<Stack style={{ marginTop: 20 }}>
<Text>Let&apos;s load the two tables with a sample dataset generated from the GitHub API.</Text>
<DefaultButton
style={{ marginTop: 16, width: 110 }}
onClick={() => useTerminal.getState().sendMessage(loadDataCommand)}
>
Load data
</DefaultButton>
<Text>
We&apos;re ready to fill the tables with sample data.
<br />
<br />
For this quick start, we&apos;ll use a dataset previously captured from the GitHub API. Run the
command below to load the data
</Text>
<DefaultButton style={{ marginTop: 16, width: 110 }}>Load data</DefaultButton>
<Stack horizontal style={{ marginTop: 16 }}>
<TextField
id="loadDataCommand"
multiline
rows={5}
rows={4}
readOnly
defaultValue={loadDataCommandForDisplay}
styles={{
root: { width: "90%" },
field: {
backgroundColor: "#EEEEEE",
fontFamily:
"Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New",
},
}}
defaultValue={loadDataCommand}
styles={{ root: { width: "90%" } }}
/>
<IconButton
iconProps={{
@@ -245,7 +218,7 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
onClick={() => onCopyBtnClicked("#loadDataCommand")}
/>
</Stack>
<Youtube videoId="XSMEE2tujEk" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "60%" }} />
</Stack>
</PivotItem>
<PivotItem
@@ -256,29 +229,19 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
>
<Stack style={{ marginTop: 20 }}>
<Text>
Congratulations on creating and distributing your tables. Now, it&apos;s time to run your first query!
github_users is a distributed table, meaning its data is divided between multiple shards. Hyperscale
(Citus) automatically runs the count on all shards in parallel, and combines the results. Lets try a
query.
</Text>
<DefaultButton
style={{ marginTop: 16, width: 115 }}
onClick={() => useTerminal.getState().sendMessage(queryCommand)}
>
Try queries
</DefaultButton>
<DefaultButton style={{ marginTop: 16, width: 110 }}>Try query</DefaultButton>
<Stack horizontal style={{ marginTop: 16 }}>
<TextField
id="queryCommand"
multiline
rows={5}
rows={6}
readOnly
defaultValue={queryCommandForDisplay}
styles={{
root: { width: "90%" },
field: {
backgroundColor: "#EEEEEE",
fontFamily:
"Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New",
},
}}
defaultValue={queryCommand}
styles={{ root: { width: "90%" } }}
/>
<IconButton
iconProps={{
@@ -287,7 +250,7 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
onClick={() => onCopyBtnClicked("#queryCommand")}
/>
</Stack>
<Youtube videoId="k_EanjMtaPg" style={{ margin: "20px 0" }} opts={{ width: "90%" }} />
<Youtube videoId="Jvgh64rvdXU" style={{ margin: "20px 0" }} opts={{ width: "60%" }} />
</Stack>
</PivotItem>
</Pivot>
@@ -295,7 +258,7 @@ export const QuickstartGuide: React.FC = (): JSX.Element => {
{currentStep === 5 && (
<Stack style={{ margin: "auto" }} horizontalAlign="center">
<Image src={CompleteIcon} />
<Text variant="mediumPlus" style={{ fontWeight: 900, marginTop: 7 }}>
<Text variant="mediumPlus" style={{ fontWeight: 600, marginTop: 7 }}>
You are all set!
</Text>
<Text variant="mediumPlus" style={{ marginTop: 8 }}>

View File

@@ -101,7 +101,7 @@ export const MongoQuickstartTutorial: React.FC = (): JSX.Element => {
onDismiss={() => onDimissTeachingBubble()}
footerContent="Step 4 of 8"
>
Query your data using the filter function. Azure Cosmos DB for MongoDB provides comprehensive support for
Query your data using the filter function. Azure Cosmos DB API for MongoDB provides comprehensive support for
MongoDB query language constructs. You can also use your favorite MongoDB tools and drivers to do so.
</TeachingBubble>
);

View File

@@ -14,6 +14,10 @@
padding-right: 16px;
max-width: 1168px;
> * {
justify-content: space-between;
}
> .title {
position: relative; // To attach FeaturePanelLauncher as absolute
color: @BaseHigh;
@@ -36,7 +40,6 @@
text-align: center;
cursor: pointer;
margin: 40px auto;
width: 84%;
> .mainButton {
min-width: 124px;

View File

@@ -11,8 +11,6 @@ import {
TeachingBubbleContent,
Text,
} from "@fluentui/react";
import { sendMessage } from "Common/MessageHandler";
import { MessageTypes } from "Contracts/ExplorerContracts";
import { TerminalKind } from "Contracts/ViewModels";
import { useCarousel } from "hooks/useCarousel";
import { usePostgres } from "hooks/usePostgres";
@@ -93,12 +91,6 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
() => this.setState({}),
(state) => state.showPostgreTeachingBubble
),
},
{
dispose: usePostgres.subscribe(
() => this.setState({}),
(state) => state.showResetPasswordBubble
),
}
);
}
@@ -117,9 +109,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
<div className="splashScreenContainer">
<div className="splashScreen">
<div className="title">
{userContext.apiType === "Postgres"
? "Welcome to Azure Cosmos DB for PostgreSQL"
: "Welcome to Azure Cosmos DB"}
{userContext.apiType === "Postgres" ? "Welcome to Cosmos DB - PostgreSQL" : "Welcome to Cosmos DB"}
<FeaturePanelLauncher />
</div>
<div className="subtitle">
@@ -128,36 +118,26 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
: "Globally distributed, multi-model database service for any scale"}
</div>
<div className="mainButtonsContainer">
{userContext.apiType === "Postgres" &&
usePostgres.getState().showPostgreTeachingBubble &&
!usePostgres.getState().showResetPasswordBubble && (
<TeachingBubble
headline="New to Cosmos DB PGSQL?"
target={"#mainButton-quickstartDescription"}
hasCloseButton
onDismiss={() => usePostgres.getState().setShowPostgreTeachingBubble(false)}
calloutProps={{
directionalHint: DirectionalHint.rightCenter,
directionalHintFixed: true,
preventDismissOnLostFocus: true,
preventDismissOnResize: true,
preventDismissOnScroll: true,
}}
primaryButtonProps={{
text: "Get started",
onClick: () => {
useTabs.getState().openAndActivateReactTab(ReactTabKind.Quickstart);
usePostgres.getState().setShowPostgreTeachingBubble(false);
},
}}
>
Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you
can find sample data, query.
</TeachingBubble>
)}
{userContext.apiType === "Postgres" && usePostgres.getState().showPostgreTeachingBubble && (
<TeachingBubble
headline="New to Cosmos DB PGSQL?"
target={"#quickstartDescription"}
hasCloseButton
onDismiss={() => usePostgres.getState().setShowPostgreTeachingBubble(false)}
primaryButtonProps={{
text: "Get started",
onClick: () => {
useTabs.getState().openAndActivateReactTab(ReactTabKind.Quickstart);
usePostgres.getState().setShowPostgreTeachingBubble(false);
},
}}
>
Welcome! If you are new to Cosmos DB PGSQL and need help with getting started, here is where you can
find sample data, query.
</TeachingBubble>
)}
{mainItems.map((item) => (
<Stack
id={`mainButton-${item.id}`}
horizontal
className="mainButton focusable"
key={`${item.title}`}
@@ -181,36 +161,6 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
</div>
</Stack>
))}
{userContext.apiType === "Postgres" && usePostgres.getState().showResetPasswordBubble && (
<TeachingBubble
headline="Create your password"
target={"#mainButton-quickstartDescription"}
hasCloseButton
onDismiss={() => {
localStorage.setItem(userContext.databaseAccount.id, "true");
usePostgres.getState().setShowResetPasswordBubble(false);
}}
calloutProps={{
directionalHint: DirectionalHint.bottomRightEdge,
directionalHintFixed: true,
preventDismissOnLostFocus: true,
preventDismissOnResize: true,
preventDismissOnScroll: true,
}}
primaryButtonProps={{
text: "Create",
onClick: () => {
localStorage.setItem(userContext.databaseAccount.id, "true");
sendMessage({
type: MessageTypes.OpenPostgreSQLPasswordReset,
});
usePostgres.getState().setShowResetPasswordBubble(false);
},
}}
>
If you haven&apos;t changed your password yet, change it now.
</TeachingBubble>
)}
</div>
{useCarousel.getState().showCoachMark && (
<Coachmark
@@ -241,8 +191,8 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
</Coachmark>
)}
{userContext.apiType === "Postgres" ? (
<Stack horizontal style={{ margin: "0 auto", width: "84%" }} tokens={{ childrenGap: 32 }}>
<Stack style={{ width: "33%" }}>
<Stack horizontal style={{ margin: "0 auto" }} tokens={{ childrenGap: "15%" }}>
<Stack>
<Text
variant="large"
style={{
@@ -254,7 +204,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
</Text>
{this.getNextStepItems()}
</Stack>
<Stack style={{ width: "33%" }}>
<Stack>
<Text
variant="large"
style={{
@@ -266,7 +216,6 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
</Text>
{this.getTipsAndLearnMoreItems()}
</Stack>
<Stack style={{ width: "33%" }}></Stack>
</Stack>
) : (
<div className="moreStuffContainer">
@@ -304,11 +253,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
public createMainItems(): SplashScreenItem[] {
const heroes: SplashScreenItem[] = [];
if (
userContext.apiType === "SQL" ||
userContext.apiType === "Mongo" ||
(userContext.apiType === "Postgres" && !userContext.isReplica)
) {
if (userContext.apiType === "SQL" || userContext.apiType === "Mongo" || userContext.apiType === "Postgres") {
const launchQuickstartBtn = {
id: "quickstartDescription",
iconSrc: QuickStartIcon,
@@ -337,7 +282,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
iconSrc: PowerShellIcon,
title: "PostgreSQL Shell",
description: "Create table and interact with data using PostgreSQLs shell interface",
onClick: () => this.container.openNotebookTerminal(TerminalKind.Postgres),
onClick: () => this.container.openNotebookTerminal(TerminalKind.Mongo),
};
heroes.push(postgreShellBtn);
} else {
@@ -355,11 +300,11 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
const connectBtn = {
iconSrc: ConnectIcon,
title: userContext.apiType === "Postgres" ? "Connect with pgAdmin" : "Connect",
title: userContext.apiType === "Postgres" ? "Connect with PG Admin" : "Connect",
description:
userContext.apiType === "Postgres"
? "Prefer pgAdmin? Find your connection strings here"
: "Prefer using your own choice of tooling? Find the connection string you need to connect",
? "Prefer using your own choice of tooling? Find the connection string you need to connect"
: "Prefer PGadmin? Find your connection strings here",
onClick: () => useTabs.getState().openAndActivateReactTab(ReactTabKind.Connect),
};
heroes.push(connectBtn);
@@ -443,7 +388,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
{
link: "https://aka.ms/mongodbintro",
title: "What is the MongoDB API?",
description: "Understand Azure Cosmos DB for MongoDB and its features.",
description: "Understand the Cosmos DB API for MongoDB and its features.",
},
{
link: "https://aka.ms/mongodbfeaturesupport",
@@ -500,7 +445,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
{
link: "https://aka.ms/tableintro",
title: "What is the Table API?",
description: "Understand Azure Cosmos DB for Table and its features",
description: "Understand the Table API in Cosmos DB and its features",
},
{
link: "https://aka.ms/tableimport",
@@ -509,8 +454,8 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
},
{
link: "https://aka.ms/tablefaq",
title: "Azure Cosmos DB for Table FAQs",
description: "Common questions about Azure Cosmos DB for Table",
title: "Table API FAQs",
description: "Common questions about the Table API",
},
];
break;
@@ -647,17 +592,17 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
{
link: "https://aka.ms/tabledotnet",
title: "Build a .NET App",
description: "How to access Azure Cosmos DB for Table from a .NET app.",
description: "How to access Table API from a .NET app.",
},
{
link: "https://aka.ms/Tablejava",
title: "Build a Java App",
description: "Create a Azure Cosmos DB for Table app with Java SDK ",
description: "Create a Table API app with Java SDK ",
},
{
link: "https://aka.ms/tablenodejs",
title: "Build a Node.js App",
description: "Create a Azure Cosmos DB for Table app with Node.js SDK",
description: "Create a Table API app with Node.js SDK",
},
];
break;
@@ -689,24 +634,24 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
private getNextStepItems(): JSX.Element {
const items: { link: string; title: string; description: string }[] = [
{
link: "https://go.microsoft.com/fwlink/?linkid=2208312",
title: "Data Modeling",
description: "",
link: "",
title: "Performance tuning",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
},
{
link: " https://go.microsoft.com/fwlink/?linkid=2206941 ",
title: "How to choose a Distribution Column",
description: "",
link: "",
title: "Join Citus community",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
},
{
link: "https://go.microsoft.com/fwlink/?linkid=2207425",
title: "Build Apps with Python/Java/Django",
description: "",
link: "",
title: "Useful diagnostic queries",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
},
];
return (
<Stack style={{ minWidth: 124, maxWidth: 296 }}>
<Stack>
{items.map((item, i) => (
<Stack key={`nextStep${i}`} style={{ marginBottom: 26 }}>
<Stack horizontal verticalAlign="center" style={{ fontSize: 14 }}>
@@ -725,24 +670,24 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
private getTipsAndLearnMoreItems(): JSX.Element {
const items: { link: string; title: string; description: string }[] = [
{
link: "https://go.microsoft.com/fwlink/?linkid=2207226",
title: "Performance Tuning",
description: "",
link: "",
title: "Data modeling",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
},
{
link: "https://go.microsoft.com/fwlink/?linkid=2208037",
title: "Useful Diagnostic Queries",
description: "",
link: "",
title: "How to choose a distribution Column",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
},
{
link: "https://go.microsoft.com/fwlink/?linkid=2205270",
title: "Distributed SQL Reference",
description: "",
link: "",
title: "Build apps with Python/ Java/ Django",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
},
];
return (
<Stack style={{ minWidth: 124, maxWidth: 296 }}>
<Stack>
{items.map((item, i) => (
<Stack key={`tips${i}`} style={{ marginBottom: 26 }}>
<Stack horizontal verticalAlign="center" style={{ fontSize: 14 }}>

View File

@@ -516,7 +516,7 @@ export default class QueryBuilderViewModel {
};
public onAddNewClauseKeyDown = (event: KeyboardEvent): boolean => {
if (event.key === "Enter" || event.key === "Space") {
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.addClauseIndex(this.clauseArray().length - 1);
event.stopPropagation();
return false;

View File

@@ -1,145 +0,0 @@
import {
Checkbox,
Dropdown,
Icon,
IconButton,
IDropdownOption,
ITextFieldStyles,
Label,
Link,
Stack,
Text,
TextField,
TooltipHost,
} from "@fluentui/react";
import React from "react";
import { userContext } from "UserContext";
export const PostgresConnectTab: React.FC = (): JSX.Element => {
const { adminLogin, nodes, enablePublicIpAccess } = userContext.postgresConnectionStrParams;
const [usePgBouncerPort, setUsePgBouncerPort] = React.useState<boolean>(false);
const [selectedNode, setSelectedNode] = React.useState<string>(nodes?.[0]?.value);
const portNumber = usePgBouncerPort ? "6432" : "5432";
const onCopyBtnClicked = (selector: string): void => {
const textfield: HTMLInputElement = document.querySelector(selector);
textfield.select();
document.execCommand("copy");
};
const textfieldStyles: Partial<ITextFieldStyles> = {
root: { width: "100%" },
field: { backgroundColor: "rgb(230, 230, 230)" },
fieldGroup: { borderColor: "rgb(138, 136, 134)" },
subComponentStyles: { label: { fontWeight: 400 } },
description: { fontWeight: 400 },
};
const nodesDropdownOptions: IDropdownOption[] = nodes.map((node) => ({
key: node.value,
text: node.text,
}));
const postgresSQLConnectionURL = `postgres://${adminLogin}:{your_password}@${selectedNode}:${portNumber}/citus?sslmode=require`;
const psql = `psql "host=${selectedNode} port=${portNumber} dbname=citus user=${adminLogin} password={your_password} sslmode=require"`;
const jdbc = `jdbc:postgresql://${selectedNode}:${portNumber}/citus?user=${adminLogin}&password={your_password}&sslmode=require`;
const libpq = `host=${selectedNode} port=${portNumber} dbname=citus user=${adminLogin} password={your_password} sslmode=require`;
const adoDotNet = `Server=${selectedNode};Database=citus;Port=${portNumber};User Id=${adminLogin};Password={your_password};Ssl Mode=Require;`;
return (
<div style={{ width: "100%", padding: 16 }}>
<Stack horizontal verticalAlign="center" style={{ marginBottom: 8 }}>
<Label style={{ marginRight: 8 }}>Public IP addresses on worker nodes:</Label>
<TooltipHost
content="
You can enable or disable public IP addresses on the worker nodes on 'Networking' page of your server group."
>
<Icon style={{ margin: "5px 8px 0 0", cursor: "default" }} iconName="Info" />
</TooltipHost>
<TextField value={enablePublicIpAccess ? "On" : "Off"} readOnly disabled />
</Stack>
<Stack horizontal style={{ marginBottom: 8 }}>
<Label style={{ marginRight: 85 }}>Show connection strings for</Label>
<Dropdown
options={nodesDropdownOptions}
selectedKey={selectedNode}
onChange={(_, option) => {
const selectedNode = option.key as string;
setSelectedNode(selectedNode);
if (!selectedNode.startsWith("c.")) {
setUsePgBouncerPort(false);
}
}}
style={{ width: 200 }}
/>
</Stack>
<Stack horizontal style={{ marginBottom: 8 }}>
<Label style={{ marginRight: 44 }}>PgBouncer connection strings</Label>
<Checkbox
boxSide="end"
checked={usePgBouncerPort}
onChange={(_, checked: boolean) => setUsePgBouncerPort(checked)}
disabled={!selectedNode?.startsWith("c.")}
/>
</Stack>
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
<TextField
label="PostgreSQL connection URL"
id="postgresSQLConnectionURL"
readOnly
value={postgresSQLConnectionURL}
styles={textfieldStyles}
/>
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#postgresSQLConnectionURL")} />
</Stack>
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
<TextField label="psql" id="psql" readOnly value={psql} styles={textfieldStyles} />
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#psql")} />
</Stack>
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
<TextField label="JDBC" id="JDBC" readOnly value={jdbc} styles={textfieldStyles} />
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#JDBC")} />
</Stack>
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
<TextField
label="Node.js, Python, Ruby, PHP, C++ (libpq)"
id="libpq"
readOnly
value={libpq}
styles={textfieldStyles}
/>
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#libpq")} />
</Stack>
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
<TextField label="ADO.NET" id="adoDotNet" readOnly value={adoDotNet} styles={textfieldStyles} />
<IconButton iconProps={{ iconName: "Copy" }} onClick={() => onCopyBtnClicked("#adoDotNet")} />
</Stack>
<Label>Secure connections</Label>
<Text style={{ marginBottom: 8 }}>
Only secure connections are supported. For production use cases, we recommend using the &apos;verify-full&apos;
mode to enforce TLS certificate verification. You will need to download the Hyperscale (Citus) certificate, and
provide it when connecting to the database.{" "}
<Link href="https://go.microsoft.com/fwlink/?linkid=2155061" target="_blank">
Learn more
</Link>
</Text>
<Label>Connect with pgAdmin</Label>
<Text>
Refer to our{" "}
<Link
href="https://learn.microsoft.com/en-us/azure/postgresql/hyperscale/howto-connect?tabs=pgadmin"
target="_blank"
>
guide
</Link>{" "}
to help you connect via pgAdmin.
</Text>
</div>
);
};

View File

@@ -70,19 +70,24 @@
<tbody data-bind="template: { name: 'queryClause-template', foreach: clauseArray, as: 'clause' }"></tbody>
</table>
</div>
<button
data-bind="click: addNewClause, event: { keydown: onAddNewClauseKeyDown }"
style="border: none; background: none"
<div
class="addClause"
role="button"
data-bind="click: addNewClause, event: { keydown: onAddNewClauseKeyDown }, attr: { title: addNewClauseLine }"
tabindex="0"
>
<div class="addClause" data-bind=" ">
<div class="addClause-heading">
<span class="clause-table addClause-title">
<img class="addclauseProperty-Img" style="margin-bottom: 5px" src="/Add-property.svg" />
<span style="margin-left: 5px" data-bind="text: addNewClauseLine"></span>
</span>
</div>
<div class="addClause-heading">
<span class="clause-table addClause-title">
<img
class="addclauseProperty-Img"
style="margin-bottom: 5px"
src="/Add-property.svg"
alt="Add new clause"
/>
<span style="margin-left: 5px" data-bind="text: addNewClauseLine"></span>
</span>
</div>
</button>
</div>
</div>
</div>
<!-- Tables Query Tab Query Helper - End-->
@@ -163,20 +168,22 @@
<script type="text/html" id="queryClause-template">
<tr class="clause-table-row">
<td class="clause-table-cell action-column">
<button
<span
class="entity-Add-Cancel"
role="button"
tabindex="0"
data-bind="click: $parent.addClauseIndex.bind($data, $index()), event: { keydown: $parent.onAddClauseKeyDown.bind($data, $index()) }, attr:{title: $parent.insertNewFilterLine}"
>
<span class="entity-Add-Cancel" role="button">
<img class="querybuilder-addpropertyImg" src="/Add-property.svg" alt="Add clause" />
</span>
</button>
<button
<img class="querybuilder-addpropertyImg" src="/Add-property.svg" alt="Add clause" />
</span>
<span
class="entity-Add-Cancel"
role="button"
tabindex="0"
data-bind="hasFocus: isDeleteButtonFocused, click: $parent.deleteClause.bind($data, $index()), event: { keydown: $parent.onDeleteClauseKeyDown.bind($data, $index()) }, attr:{title: $parent.removeThisFilterLine}"
>
<span class="entity-Add-Cancel" role="button">
<img class="querybuilder-cancelImg" src="/Entity_cancel.svg" alt="Delete clause" />
</span>
</button>
<img class="querybuilder-cancelImg" src="/Entity_cancel.svg" alt="Delete clause" />
</span>
</td>
<td class="clause-table-cell group-control-column">
<input type="checkbox" aria-label="And/Or" data-bind="checked: checkedForGrouping" />

View File

@@ -1,15 +1,11 @@
import { Spinner, SpinnerSize, Stack, Text } from "@fluentui/react";
import { configContext } from "ConfigContext";
import { NotebookWorkspaceConnectionInfo, PostgresFirewallRule } from "Contracts/DataModels";
import { Spinner, SpinnerSize, Stack } from "@fluentui/react";
import { NotebookWorkspaceConnectionInfo } from "Contracts/DataModels";
import { NotebookTerminalComponent } from "Explorer/Controls/Notebook/NotebookTerminalComponent";
import Explorer from "Explorer/Explorer";
import { useNotebook } from "Explorer/Notebook/useNotebook";
import { QuickstartFirewallNotification } from "Explorer/Quickstart/QuickstartFirewallNotification";
import { QuickstartGuide } from "Explorer/Quickstart/QuickstartGuide";
import { ReactTabKind, useTabs } from "hooks/useTabs";
import React, { useEffect, useState } from "react";
import React, { useEffect } from "react";
import { userContext } from "UserContext";
import { armRequest } from "Utils/arm/request";
interface QuickstartTabProps {
explorer: Explorer;
@@ -17,42 +13,14 @@ interface QuickstartTabProps {
export const QuickstartTab: React.FC<QuickstartTabProps> = ({ explorer }: QuickstartTabProps): JSX.Element => {
const notebookServerInfo = useNotebook((state) => state.notebookServerInfo);
const [isAllPublicIPAddressEnabled, setIsAllPublicIPAddressEnabled] = useState<boolean>(true);
const getNotebookServerInfo = (): NotebookWorkspaceConnectionInfo => ({
authToken: notebookServerInfo.authToken,
notebookServerEndpoint: `${notebookServerInfo.notebookServerEndpoint?.replace(/\/+$/, "")}/postgresql`,
forwardingId: notebookServerInfo.forwardingId,
});
const checkFirewallRules = async (): Promise<void> => {
const firewallRulesUri = `${userContext.databaseAccount.id}/firewallRules`;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const response: any = await armRequest({
host: configContext.ARM_ENDPOINT,
path: firewallRulesUri,
method: "GET",
apiVersion: "2020-10-05-privatepreview",
});
const firewallRules: PostgresFirewallRule[] = response?.data?.value || response?.value || [];
const isEnabled = firewallRules.some(
(rule) => rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255"
);
setIsAllPublicIPAddressEnabled(isEnabled);
// If the firewall rule is not added, check every 30 seconds to see if the user has added the rule
if (!isEnabled && useTabs.getState().activeReactTab === ReactTabKind.Quickstart) {
setTimeout(checkFirewallRules, 30000);
}
};
useEffect(() => {
checkFirewallRules();
});
useEffect(() => {
explorer.allocateContainer();
}, []);
const getNotebookServerInfo = (): NotebookWorkspaceConnectionInfo => ({
authToken: notebookServerInfo.authToken,
notebookServerEndpoint: `${notebookServerInfo.notebookServerEndpoint?.replace(/\/+$/, "")}/mongo`,
forwardingId: notebookServerInfo.forwardingId,
});
return (
<Stack style={{ width: "100%" }} horizontal>
@@ -60,24 +28,15 @@ export const QuickstartTab: React.FC<QuickstartTabProps> = ({ explorer }: Quicks
<QuickstartGuide />
</Stack>
<Stack style={{ width: "50%", borderLeft: "black solid 1px" }}>
{!isAllPublicIPAddressEnabled && <QuickstartFirewallNotification />}
{isAllPublicIPAddressEnabled && notebookServerInfo?.notebookServerEndpoint && (
{notebookServerInfo?.notebookServerEndpoint && (
<NotebookTerminalComponent
notebookServerInfo={getNotebookServerInfo()}
databaseAccount={userContext.databaseAccount}
tabId="QuickstartPSQLShell"
tabId="EmbbedTerminal"
/>
)}
{isAllPublicIPAddressEnabled && !notebookServerInfo?.notebookServerEndpoint && (
<Stack style={{ margin: "auto 0" }}>
<Text block style={{ margin: "auto" }}>
Connecting to the PostgreSQL shell.
</Text>
<Text block style={{ margin: "auto" }}>
If the cluster was just created, this could take up to a minute.
</Text>
<Spinner styles={{ root: { marginTop: 16 } }} size={SpinnerSize.large}></Spinner>
</Stack>
{!notebookServerInfo?.notebookServerEndpoint && (
<Spinner styles={{ root: { marginTop: 10 } }} size={SpinnerSize.large}></Spinner>
)}
</Stack>
</Stack>

View File

@@ -1,16 +1,11 @@
import { MessageBar, MessageBarButton, MessageBarType } from "@fluentui/react";
import { sendMessage } from "Common/MessageHandler";
import { MessageTypes } from "Contracts/ExplorerContracts";
import { CollectionTabKind } from "Contracts/ViewModels";
import Explorer from "Explorer/Explorer";
import { SplashScreen } from "Explorer/SplashScreen/SplashScreen";
import { ConnectTab } from "Explorer/Tabs/ConnectTab";
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
import { useTeachingBubble } from "hooks/useTeachingBubble";
import ko from "knockout";
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
import { userContext } from "UserContext";
import loadingIcon from "../../../images/circular_loader_black_16x16.gif";
import errorIcon from "../../../images/close-black.svg";
import { useObservable } from "../../hooks/useObservable";
@@ -24,24 +19,10 @@ interface TabsProps {
}
export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
const { openedTabs, openedReactTabs, activeTab, activeReactTab, showNetworkSettingsWarning } = useTabs();
const { openedTabs, openedReactTabs, activeTab, activeReactTab } = useTabs();
return (
<div className="tabsManagerContainer">
{showNetworkSettingsWarning && (
<MessageBar
messageBarType={MessageBarType.warning}
actions={
<MessageBarButton onClick={() => sendMessage({ type: MessageTypes.OpenPostgresNetworkingBlade })}>
Change network settings
</MessageBarButton>
}
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
>
The Network settings for this account are preventing access from Data Explorer. Please allow access from Azure
Portal to proceed.
</MessageBar>
)}
<div id="content" className="flexContainer hideOverflows">
<div className="nav-tabs-margin">
<ul className="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">
@@ -208,7 +189,7 @@ const onKeyPressReactTab = (e: KeyboardEvent, tabKind: ReactTabKind): void => {
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
switch (activeReactTab) {
case ReactTabKind.Connect:
return userContext.apiType === "Postgres" ? <PostgresConnectTab /> : <ConnectTab />;
return <ConnectTab />;
case ReactTabKind.Home:
return <SplashScreen explorer={explorer} />;
case ReactTabKind.Quickstart:

View File

@@ -1,9 +1,6 @@
import { Spinner, SpinnerSize } from "@fluentui/react";
import { configContext } from "ConfigContext";
import { QuickstartFirewallNotification } from "Explorer/Quickstart/QuickstartFirewallNotification";
import * as ko from "knockout";
import * as React from "react";
import { armRequest } from "Utils/arm/request";
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
@@ -29,15 +26,10 @@ class NotebookTerminalComponentAdapter implements ReactAdapter {
constructor(
private getNotebookServerInfo: () => DataModels.NotebookWorkspaceConnectionInfo,
private getDatabaseAccount: () => DataModels.DatabaseAccount,
private getTabId: () => string,
private isAllPublicIPAddressesEnabled: ko.Observable<boolean>
private getTabId: () => string
) {}
public renderComponent(): JSX.Element {
if (!this.isAllPublicIPAddressesEnabled()) {
return <QuickstartFirewallNotification />;
}
return this.parameters() ? (
<NotebookTerminalComponent
notebookServerInfo={this.getNotebookServerInfo()}
@@ -54,33 +46,25 @@ export default class TerminalTab extends TabsBase {
public readonly html = '<div style="height: 100%" data-bind="react:notebookTerminalComponentAdapter"></div> ';
private container: Explorer;
private notebookTerminalComponentAdapter: NotebookTerminalComponentAdapter;
private isAllPublicIPAddressesEnabled: ko.Observable<boolean>;
constructor(options: TerminalTabOptions) {
super(options);
this.container = options.container;
this.isAllPublicIPAddressesEnabled = ko.observable(true);
this.notebookTerminalComponentAdapter = new NotebookTerminalComponentAdapter(
() => this.getNotebookServerInfo(options),
() => userContext?.databaseAccount,
() => this.tabId,
this.isAllPublicIPAddressesEnabled
() => this.tabId
);
this.notebookTerminalComponentAdapter.parameters = ko.computed<boolean>(() => {
if (
this.isTemplateReady() &&
useNotebook.getState().isNotebookEnabled &&
useNotebook.getState().notebookServerInfo?.notebookServerEndpoint &&
this.isAllPublicIPAddressesEnabled()
useNotebook.getState().notebookServerInfo?.notebookServerEndpoint
) {
return true;
}
return false;
});
if (options.kind === ViewModels.TerminalKind.Postgres) {
this.checkPostgresFirewallRules();
}
}
public getContainer(): Explorer {
@@ -111,10 +95,6 @@ export default class TerminalTab extends TabsBase {
endpointSuffix = "cassandra";
break;
case ViewModels.TerminalKind.Postgres:
endpointSuffix = "postgresql";
break;
default:
throw new Error(`Terminal kind: ${options.kind} not supported`);
}
@@ -126,25 +106,4 @@ export default class TerminalTab extends TabsBase {
forwardingId: info.forwardingId,
};
}
private async checkPostgresFirewallRules(): Promise<void> {
const firewallRulesUri = `${userContext.databaseAccount.id}/firewallRules`;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const response: any = await armRequest({
host: configContext.ARM_ENDPOINT,
path: firewallRulesUri,
method: "GET",
apiVersion: "2020-10-05-privatepreview",
});
const firewallRules: DataModels.PostgresFirewallRule[] = response?.data?.value || response?.value || [];
const isEnabled = firewallRules.some(
(rule) => rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255"
);
this.isAllPublicIPAddressesEnabled(isEnabled);
// If the firewall rule is not added, check every 30 seconds to see if the user has added the rule
if (!isEnabled) {
setTimeout(() => this.checkPostgresFirewallRules(), 30000);
}
}
}

View File

@@ -1160,6 +1160,23 @@ export default class Collection implements ViewModels.Collection {
this.onDocumentDBDocumentsClick();
}
/**
* Get correct collection label depending on account API
*/
public getLabel(): string {
if (userContext.apiType === "Tables") {
return "Entities";
} else if (userContext.apiType === "Cassandra") {
return "Rows";
} else if (userContext.apiType === "Gremlin") {
return "Graph";
} else if (userContext.apiType === "Mongo") {
return "Documents";
}
return "Items";
}
public getDatabase(): ViewModels.Database {
return useDatabases.getState().findDatabaseWithId(this.databaseId);
}

View File

@@ -1,6 +1,5 @@
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
import * as React from "react";
import { getItemName } from "Utils/APITypeUtils";
import shallow from "zustand/shallow";
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
import DeleteIcon from "../../../images/delete.svg";
@@ -498,7 +497,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
const buildCollectionNode = (database: ViewModels.Database, collection: ViewModels.Collection): TreeNode => {
const children: TreeNode[] = [];
children.push({
label: getItemName(),
label: collection.getLabel(),
id: collection.isSampleCollection ? "sampleItems" : "",
onClick: () => {
collection.openTab();

View File

@@ -1,7 +1,6 @@
import { Callout, DirectionalHint, ICalloutProps, ILinkProps, Link, Stack, Text } from "@fluentui/react";
import * as ko from "knockout";
import * as React from "react";
import { getItemName } from "Utils/APITypeUtils";
import CosmosDBIcon from "../../../images/Azure-Cosmos-DB.svg";
import DeleteIcon from "../../../images/delete.svg";
import GalleryIcon from "../../../images/GalleryIcon.svg";
@@ -255,7 +254,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
private buildCollectionNode(database: ViewModels.Database, collection: ViewModels.Collection): TreeNode {
const children: TreeNode[] = [];
children.push({
label: getItemName(),
label: collection.getLabel(),
onClick: () => {
collection.openTab();
// push to most recent

View File

@@ -32,7 +32,6 @@ import { userContext } from "../UserContext";
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
export class PhoenixClient {
private armResourceId: string;
private containerHealthHandler: NodeJS.Timeout;
private retryOptions: promiseRetry.Options = {
retries: Notebook.retryAttempts,
@@ -40,16 +39,8 @@ export class PhoenixClient {
minTimeout: Notebook.retryAttemptDelayMs,
};
constructor(armResourceId: string) {
this.armResourceId = armResourceId;
}
public async allocateContainer(provisionData: IProvisionData): Promise<IResponse<IPhoenixServiceInfo>> {
return promiseRetry(() => this.executeContainerAssignmentOperation(provisionData, "allocate"), {
retries: 4,
maxTimeout: 20000,
minTimeout: 20000,
});
return this.executeContainerAssignmentOperation(provisionData, "allocate");
}
public async resetContainer(provisionData: IProvisionData): Promise<IResponse<IPhoenixServiceInfo>> {
@@ -84,12 +75,9 @@ export class PhoenixClient {
}
const phoenixError = responseJson as IPhoenixError;
if (response.status === HttpStatusCodes.Forbidden) {
if (phoenixError.message === "Sequence contains no elements") {
throw Error("Phoenix container allocation failed, please try again later.");
}
throw new AbortError(this.ConvertToForbiddenErrorString(phoenixError));
throw new Error(this.ConvertToForbiddenErrorString(phoenixError));
}
throw new AbortError(phoenixError.message);
throw new Error(phoenixError.message);
} catch (error) {
error.status = response?.status;
throw error;
@@ -226,21 +214,22 @@ export class PhoenixClient {
}
}
private getPhoenixControlPlanePathPrefix(): string {
if (!this.armResourceId) {
throw new Error("The Phoenix client was not initialized properly: missing ARM resourcce id");
}
const toolsEndpoint =
public static getPhoenixEndpoint(): string {
const phoenixEndpoint =
userContext.features.phoenixEndpoint ?? userContext.features.junoEndpoint ?? configContext.JUNO_ENDPOINT;
if (!validateEndpoint(toolsEndpoint, allowedJunoOrigins)) {
const error = `${toolsEndpoint} not allowed as tools endpoint`;
if (!validateEndpoint(phoenixEndpoint, allowedJunoOrigins)) {
const error = `${phoenixEndpoint} not allowed as juno endpoint`;
console.error(error);
throw new Error(error);
}
return `${toolsEndpoint}/api/controlplane/toolscontainer/cosmosaccounts${this.armResourceId}`;
return phoenixEndpoint;
}
public getPhoenixControlPlanePathPrefix(): string {
return `${PhoenixClient.getPhoenixEndpoint()}/api/controlplane/toolscontainer/cosmosaccounts${
userContext.databaseAccount.id
}`;
}
private static getHeaders(): HeadersInit {

View File

@@ -0,0 +1,31 @@
import * as Constants from "../../Common/Constants";
import { configContext } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import { userContext } from "../../UserContext";
export default class AuthHeadersUtil {
public static async generateEncryptedToken(readOnly: boolean = false): Promise<DataModels.GenerateTokenResponse> {
const url = configContext.BACKEND_ENDPOINT + "/api/tokens/generateToken" + AuthHeadersUtil._generateResourceUrl();
const headers: any = { authorization: userContext.authorizationToken };
headers[Constants.HttpHeaders.getReadOnlyKey] = readOnly;
const response = await fetch(url, { method: "POST", headers });
const result = await response.json();
// This API has a quirk where the response must be parsed to JSON twice
return JSON.parse(result);
}
private static _generateResourceUrl(): string {
const { databaseAccount, resourceGroup, subscriptionId } = userContext;
const apiKind: DataModels.ApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType);
const accountEndpoint = databaseAccount?.properties?.documentEndpoint || "";
const sid = subscriptionId || "";
const rg = resourceGroup || "";
const dba = databaseAccount?.name || "";
const resourceUrl = encodeURIComponent(accountEndpoint);
const rid = "";
const rtype = "";
return `?resourceUrl=${resourceUrl}&rid=${rid}&rtype=${rtype}&sid=${sid}&rg=${rg}&dba=${dba}&api=${apiKind}`;
}
}

View File

@@ -29,6 +29,7 @@ export type Features = {
readonly mongoProxyEndpoint?: string;
readonly mongoProxyAPIs?: string;
readonly enableThroughputCap: boolean;
readonly enablePGQuickstart: boolean;
// can be set via both flight and feature flag
autoscaleDefault: boolean;
@@ -90,6 +91,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
notebooksDownBanner: "true" === get("notebooksDownBanner"),
enableThroughputCap: "true" === get("enablethroughputcap"),
enablePGQuickstart: "true" === get("enablepgquickstart"),
};
}

View File

@@ -40,23 +40,23 @@ export enum SelfServeType {
*/
export enum BladeType {
/**
* Keys blade of a Azure Cosmos DB for NoSQL account.
* Keys blade of a SQL API account.
*/
SqlKeys = "keys",
/**
* Keys blade of a Azure Cosmos DB for MongoDB account.
* Keys blade of a Mongo API account.
*/
MongoKeys = "mongoDbKeys",
/**
* Keys blade of a Azure Cosmos DB for Apache Cassandra account.
* Keys blade of a Cassandra API account.
*/
CassandraKeys = "cassandraDbKeys",
/**
* Keys blade of a Azure Cosmos DB for Apache Gremlin account.
* Keys blade of a Gremlin API account.
*/
GremlinKeys = "keys",
/**
* Keys blade of a Azure Cosmos DB for Table account.
* Keys blade of a Table API account.
*/
TableKeys = "tableKeys",
/**

View File

@@ -2,7 +2,7 @@
* JupyterLab applications based on jupyterLab components
*/
import { ServerConnection, TerminalManager } from "@jupyterlab/services";
import { IMessage, ITerminalConnection } from "@jupyterlab/services/lib/terminal/terminal";
import { IMessage } from "@jupyterlab/services/lib/terminal/terminal";
import { Terminal } from "@jupyterlab/terminal";
import { Panel, Widget } from "@phosphor/widgets";
import { userContext } from "UserContext";
@@ -24,10 +24,6 @@ export class JupyterLabAppFactory {
this.isShellStarted = content?.includes("Connected to") && content?.includes("cqlsh");
}
private isPostgresShellStarted(content: string | undefined) {
this.isShellStarted = content?.includes("citus=>");
}
constructor(closeTab: () => void) {
this.onShellExited = closeTab;
this.isShellStarted = false;
@@ -40,13 +36,10 @@ export class JupyterLabAppFactory {
case "Cassandra":
this.checkShellStarted = this.isCassandraShellStarted;
break;
case "Postgres":
this.checkShellStarted = this.isPostgresShellStarted;
break;
}
}
public async createTerminalApp(serverSettings: ServerConnection.ISettings): Promise<ITerminalConnection | undefined> {
public async createTerminalApp(serverSettings: ServerConnection.ISettings) {
const manager = new TerminalManager({
serverSettings: serverSettings,
});
@@ -68,7 +61,7 @@ export class JupyterLabAppFactory {
if (!term) {
console.error("Failed starting terminal");
return undefined;
return;
}
term.title.closable = false;
@@ -81,9 +74,6 @@ export class JupyterLabAppFactory {
// Attach the widget to the dom.
Widget.attach(panel, document.body);
// Switch focus to the terminal
term.activate();
// Handle resize events.
window.addEventListener("resize", () => {
panel.update();
@@ -93,7 +83,5 @@ export class JupyterLabAppFactory {
window.addEventListener("unload", () => {
panel.dispose();
});
return session;
}
}

View File

@@ -1,5 +1,4 @@
import { ServerConnection } from "@jupyterlab/services";
import { IMessage, ITerminalConnection } from "@jupyterlab/services/lib/terminal/terminal";
import "@jupyterlab/terminal/style/index.css";
import { MessageTypes } from "Contracts/ExplorerContracts";
import postRobot from "post-robot";
@@ -42,7 +41,7 @@ const createServerSettings = (props: TerminalProps): ServerConnection.ISettings
return ServerConnection.makeSettings(options);
};
const initTerminal = async (props: TerminalProps): Promise<ITerminalConnection | undefined> => {
const initTerminal = async (props: TerminalProps) => {
// Initialize userContext (only properties which are needed by TelemetryProcessor)
updateUserContext({
subscriptionId: props.subscriptionId,
@@ -56,12 +55,10 @@ const initTerminal = async (props: TerminalProps): Promise<ITerminalConnection |
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, data);
try {
const session = await new JupyterLabAppFactory(() => closeTab(props.tabId)).createTerminalApp(serverSettings);
await new JupyterLabAppFactory(() => closeTab(props.tabId)).createTerminalApp(serverSettings);
TelemetryProcessor.traceSuccess(Action.OpenTerminal, data, startTime);
return session;
} catch (error) {
TelemetryProcessor.traceFailure(Action.OpenTerminal, data, startTime);
return undefined;
}
};
@@ -73,7 +70,6 @@ const closeTab = (tabId: string): void => {
};
const main = async (): Promise<void> => {
let session: ITerminalConnection | undefined;
postRobot.on(
"props",
{
@@ -84,22 +80,7 @@ const main = async (): Promise<void> => {
// Typescript definition for event is wrong. So read props by casting to <any>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const props = (event as any).data as TerminalProps;
session = await initTerminal(props);
}
);
postRobot.on(
"sendMessage",
{
window: window.parent,
domain: window.location.origin,
},
async (event) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const message = (event as any).data as IMessage;
if (session) {
session.send(message);
}
await initTerminal(props);
}
);
};

View File

@@ -26,20 +26,6 @@ export interface CollectionCreationDefaults {
throughput: ThroughputDefaults;
}
export interface Node {
text: string;
value: string;
ariaLabel: string;
}
export interface PostgresConnectionStrParams {
adminLogin: string;
enablePublicIpAccess: boolean;
nodes: Node[];
isMarlinServerGroup: boolean;
isFreeTier: boolean;
}
interface UserContext {
readonly authType?: AuthType;
readonly masterKey?: string;
@@ -66,8 +52,6 @@ interface UserContext {
collectionId: string;
partitionKey?: string;
};
readonly postgresConnectionStrParams?: PostgresConnectionStrParams;
readonly isReplica?: boolean;
collectionCreationDefaults: CollectionCreationDefaults;
}
@@ -102,7 +86,7 @@ function isAccountNewerThanThresholdInMs(createdAt: string, threshold: number) {
function updateUserContext(newContext: Partial<UserContext>): void {
if (newContext.databaseAccount) {
newContext.apiType = apiType(newContext.databaseAccount);
newContext.apiType = "Postgres";
const isNewAccount = isAccountNewerThanThresholdInMs(
newContext.databaseAccount?.systemData?.createdAt || "",
@@ -110,51 +94,43 @@ function updateUserContext(newContext: Partial<UserContext>): void {
);
if (!localStorage.getItem(newContext.databaseAccount.id)) {
if (newContext.isTryCosmosDBSubscription || isNewAccount) {
if (newContext.apiType === "Postgres" && !newContext.isReplica) {
usePostgres.getState().setShowResetPasswordBubble(true);
usePostgres.getState().setShowPostgreTeachingBubble(true);
} else {
useCarousel.getState().setShouldOpen(true);
localStorage.setItem(newContext.databaseAccount.id, "true");
traceOpen(Action.OpenCarousel);
}
} else if (newContext.apiType === "Postgres") {
if (newContext.apiType === "Postgres") {
usePostgres.getState().setShowPostgreTeachingBubble(true);
localStorage.setItem(newContext.databaseAccount.id, "true");
} else if (userContext.isTryCosmosDBSubscription || isNewAccount) {
useCarousel.getState().setShouldOpen(true);
localStorage.setItem(newContext.databaseAccount.id, "true");
traceOpen(Action.OpenCarousel);
}
}
}
Object.assign(userContext, newContext);
}
function apiType(account: DatabaseAccount | undefined): ApiType {
if (!account) {
return "SQL";
}
// function apiType(account: DatabaseAccount | undefined): ApiType {
// if (!account) {
// return "SQL";
// }
const capabilities = account.properties?.capabilities;
if (capabilities) {
if (capabilities.find((c) => c.name === "EnableCassandra")) {
return "Cassandra";
}
if (capabilities.find((c) => c.name === "EnableGremlin")) {
return "Gremlin";
}
if (capabilities.find((c) => c.name === "EnableMongo")) {
return "Mongo";
}
if (capabilities.find((c) => c.name === "EnableTable")) {
return "Tables";
}
}
if (account.kind === "MongoDB" || account.kind === "Parse") {
return "Mongo";
}
if (account.kind === "Postgres") {
return "Postgres";
}
return "SQL";
}
// const capabilities = account.properties?.capabilities;
// if (capabilities) {
// if (capabilities.find((c) => c.name === "EnableCassandra")) {
// return "Cassandra";
// }
// if (capabilities.find((c) => c.name === "EnableGremlin")) {
// return "Gremlin";
// }
// if (capabilities.find((c) => c.name === "EnableMongo")) {
// return "Mongo";
// }
// if (capabilities.find((c) => c.name === "EnableTable")) {
// return "Tables";
// }
// }
// if (account.kind === "MongoDB" || account.kind === "Parse") {
// return "Mongo";
// }
// return "SQL";
// }
export { userContext, updateUserContext };

View File

@@ -57,35 +57,3 @@ export const getUploadName = (): string => {
return "Items";
}
};
export const getApiShortDisplayName = (): string => {
switch (userContext.apiType) {
case "Cassandra":
return "Apache Cassandra API";
case "Gremlin":
return "Apache Gremlin API";
case "Mongo":
return "MongoDB API";
case "Postgres":
return "PostgreSQL API";
case "SQL":
return "NoSQL API";
case "Tables":
return "Table API";
}
};
export const getItemName = (): string => {
switch (userContext.apiType) {
case "Tables":
return "Entities";
case "Cassandra":
return "Rows";
case "Gremlin":
return "Graph";
case "Mongo":
return "Documents";
default:
return "Items";
}
};

View File

@@ -1,40 +0,0 @@
import { userContext } from "UserContext";
const PortalIPs: { [key: string]: string[] } = {
prod1: ["104.42.195.92", "40.76.54.131"],
prod2: ["104.42.196.69"],
mooncake: ["139.217.8.252"],
blackforest: ["51.4.229.218"],
fairfax: ["52.244.48.71"],
ussec: ["29.26.26.67", "29.26.26.66"],
usnat: ["7.28.202.68"],
};
export const doNetworkSettingsAllowDataExplorerAccess = (): boolean => {
const accountProperties = userContext.databaseAccount?.properties;
if (!accountProperties) {
return false;
}
// public network access is disabled
if (accountProperties.publicNetworkAccess !== "Enabled") {
return false;
}
const ipRules = accountProperties.ipRules;
// public network access is set to "All networks"
if (ipRules.length === 0) {
return true;
}
const portalIPs = PortalIPs[userContext.portalEnv];
let numberOfMatches = 0;
ipRules.forEach((ipRule) => {
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {
numberOfMatches++;
}
});
return numberOfMatches === portalIPs.length;
};

View File

@@ -0,0 +1,18 @@
import { useEffect, useState } from "react";
import { GenerateTokenResponse } from "../Contracts/DataModels";
import AuthHeadersUtil from "../Platform/Hosted/Authorization";
export function useFullScreenURLs(): GenerateTokenResponse | undefined {
const [state, setState] = useState<GenerateTokenResponse>();
useEffect(() => {
Promise.all([AuthHeadersUtil.generateEncryptedToken(), AuthHeadersUtil.generateEncryptedToken(true)]).then(
([readWriteResponse, readOnlyResponse]) =>
setState({
readWrite: readWriteResponse.readWrite,
read: readOnlyResponse.read,
})
);
}, []);
return state;
}

View File

@@ -1,6 +1,5 @@
import { ReactTabKind, useTabs } from "hooks/useTabs";
import { useTabs } from "hooks/useTabs";
import { useEffect, useState } from "react";
import { doNetworkSettingsAllowDataExplorerAccess } from "Utils/NetworkUtility";
import { applyExplorerBindings } from "../applyExplorerBindings";
import { AuthType } from "../AuthType";
import { AccountKind, Flights } from "../Common/Constants";
@@ -29,7 +28,7 @@ import {
} from "../Platform/Hosted/HostedUtils";
import { CollectionCreation } from "../Shared/Constants";
import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
import { Node, PortalEnv, updateUserContext, userContext } from "../UserContext";
import { PortalEnv, updateUserContext, userContext } from "../UserContext";
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";
import { getMsalInstance } from "../Utils/AuthorizationUtils";
@@ -101,11 +100,7 @@ async function configureHosted(): Promise<Explorer> {
}
if (event.data?.type === MessageTypes.CloseTab) {
if (event.data?.data?.tabId === "QuickstartPSQLShell") {
useTabs.getState().closeReactTab(ReactTabKind.Quickstart);
} else {
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
}
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
}
},
false
@@ -295,11 +290,7 @@ async function configurePortal(): Promise<Explorer> {
} else if (shouldForwardMessage(message, event.origin)) {
sendMessage(message);
} else if (event.data?.type === MessageTypes.CloseTab) {
if (event.data?.data?.tabId === "QuickstartPSQLShell") {
useTabs.getState().closeReactTab(ReactTabKind.Quickstart);
} else {
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
}
useTabs.getState().closeTabsByComparator((tab) => tab.tabId === event.data?.data?.tabId);
}
},
false
@@ -363,32 +354,9 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
collectionCreationDefaults: inputs.defaultCollectionThroughput,
isTryCosmosDBSubscription: inputs.isTryCosmosDBSubscription,
});
if (inputs.isPostgresAccount) {
updateUserContext({ apiType: "Postgres", isReplica: !!inputs.isReplica });
if (inputs.connectionStringParams) {
// TODO: Remove after the nodes param has been updated to be a flat array in the OSS extension
let flattenedNodesArray: Node[] = [];
inputs.connectionStringParams.nodes?.forEach((node: Node | Node[]) => {
if (Array.isArray(node)) {
flattenedNodesArray = [...flattenedNodesArray, ...node];
} else {
flattenedNodesArray.push(node);
}
});
inputs.connectionStringParams.nodes = flattenedNodesArray;
updateUserContext({ postgresConnectionStrParams: inputs.connectionStringParams });
}
}
const isDataExplorerAccessAllowed = doNetworkSettingsAllowDataExplorerAccess();
useTabs.getState().setShowNetworkSettingsWarning(!isDataExplorerAccessAllowed);
if (inputs.features) {
Object.assign(userContext.features, extractFeatures(new URLSearchParams(inputs.features)));
}
if (inputs.flights) {
if (inputs.flights.indexOf(Flights.AutoscaleTest) !== -1) {
userContext.features.autoscaleDefault;

View File

@@ -2,14 +2,10 @@ import create, { UseStore } from "zustand";
interface TeachingBubbleState {
showPostgreTeachingBubble: boolean;
showResetPasswordBubble: boolean;
setShowPostgreTeachingBubble: (showPostgreTeachingBubble: boolean) => void;
setShowResetPasswordBubble: (showResetPasswordBubble: boolean) => void;
}
export const usePostgres: UseStore<TeachingBubbleState> = create((set) => ({
showPostgreTeachingBubble: false,
showResetPasswordBubble: false,
setShowPostgreTeachingBubble: (showPostgreTeachingBubble: boolean) => set({ showPostgreTeachingBubble }),
setShowResetPasswordBubble: (showResetPasswordBubble: boolean) => set({ showResetPasswordBubble }),
}));

View File

@@ -9,7 +9,6 @@ interface TabsState {
openedReactTabs: ReactTabKind[];
activeTab: TabsBase | undefined;
activeReactTab: ReactTabKind | undefined;
showNetworkSettingsWarning: boolean;
activateTab: (tab: TabsBase) => void;
activateNewTab: (tab: TabsBase) => void;
activateReactTab: (tabkind: ReactTabKind) => void;
@@ -21,7 +20,6 @@ interface TabsState {
closeAllNotebookTabs: (hardClose: boolean) => void;
openAndActivateReactTab: (tabKind: ReactTabKind) => void;
closeReactTab: (tabKind: ReactTabKind) => void;
setShowNetworkSettingsWarning: (showWarning: boolean) => void;
}
export enum ReactTabKind {
@@ -35,7 +33,6 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
openedReactTabs: [ReactTabKind.Home],
activeTab: undefined,
activeReactTab: ReactTabKind.Home,
showNetworkSettingsWarning: false,
activateTab: (tab: TabsBase): void => {
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
set({ activeTab: tab, activeReactTab: undefined });
@@ -145,5 +142,4 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
set({ openedReactTabs: updatedOpenedReactTabs });
},
setShowNetworkSettingsWarning: (showWarning: boolean) => set({ showNetworkSettingsWarning: showWarning }),
}));

View File

@@ -1,26 +0,0 @@
import postRobot from "post-robot";
import create, { UseStore } from "zustand";
interface TerminalState {
terminalWindow: Window;
setTerminal: (terminalWindow: Window) => void;
sendMessage: (message: string) => void;
}
export const useTerminal: UseStore<TerminalState> = create((set, get) => ({
terminalWindow: undefined,
setTerminal: (terminalWindow: Window) => {
set({ terminalWindow });
},
sendMessage: (message: string) => {
const terminalWindow = get().terminalWindow;
postRobot.send(
terminalWindow,
"sendMessage",
{ type: "stdin", content: [message] },
{
domain: window.location.origin,
}
);
},
}));

View File

@@ -82,6 +82,7 @@
"./src/Explorer/Tree/AccessibleVerticalList.ts",
"./src/GitHub/GitHubConnector.ts",
"./src/HostedExplorerChildFrame.ts",
"./src/Platform/Hosted/Authorization.ts",
"./src/Platform/Hosted/Components/MeControl.test.tsx",
"./src/Platform/Hosted/Components/MeControl.tsx",
"./src/Platform/Hosted/Components/SignInButton.tsx",
@@ -125,6 +126,7 @@
"./src/Utils/WindowUtils.ts",
"./src/hooks/useConfig.ts",
"./src/hooks/useDirectories.tsx",
"./src/hooks/useFullScreenURLs.tsx",
"./src/hooks/useGraphPhoto.tsx",
"./src/hooks/useNotebookSnapshotStore.ts",
"./src/hooks/usePortalAccessToken.tsx",
@@ -163,4 +165,4 @@
"src/Terminal/**/*",
"src/Utils/arm/**/*"
]
}
}