mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-29 22:02:01 +00:00
Compare commits
29 Commits
fix-emulat
...
upgrade-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1636f20978 | ||
|
|
9a95c7d069 | ||
|
|
ec07ff05a4 | ||
|
|
7512b3c1d5 | ||
|
|
dd199e6565 | ||
|
|
8200cc521f | ||
|
|
1d3b672a14 | ||
|
|
e5fc6f2022 | ||
|
|
3bf42b23dd | ||
|
|
d22cb598a9 | ||
|
|
269ea6a349 | ||
|
|
123902e7ee | ||
|
|
bccebaade5 | ||
|
|
9fedf63a77 | ||
|
|
4abfcc5e25 | ||
|
|
3d9256abc6 | ||
|
|
7f1355b1a4 | ||
|
|
3eff440680 | ||
|
|
4da0887e5e | ||
|
|
b783445130 | ||
|
|
73f2c612ed | ||
|
|
d70e30c4fc | ||
|
|
f8f1df4183 | ||
|
|
d427fc729e | ||
|
|
9c36782661 | ||
|
|
d32bd8851e | ||
|
|
23b2d8100f | ||
|
|
1662d20e8a | ||
|
|
582ac865ff |
6
.env.example
Normal file
6
.env.example
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# These options are only needed when if running end to end tests locally
|
||||||
|
PORTAL_RUNNER_USERNAME=
|
||||||
|
PORTAL_RUNNER_PASSWORD=
|
||||||
|
PORTAL_RUNNER_SUBSCRIPTION=
|
||||||
|
PORTAL_RUNNER_RESOURCE_GROUP=
|
||||||
|
PORTAL_RUNNER_DATABASE_ACCOUNT=
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
**/node_modules/
|
**/node_modules/
|
||||||
|
dist/
|
||||||
src/Api/Apis.ts
|
src/Api/Apis.ts
|
||||||
src/AuthType.ts
|
src/AuthType.ts
|
||||||
src/Bindings/BindingHandlersRegisterer.ts
|
src/Bindings/BindingHandlersRegisterer.ts
|
||||||
@@ -25,7 +26,6 @@ src/Common/Logger.test.ts
|
|||||||
src/Common/MessageHandler.test.ts
|
src/Common/MessageHandler.test.ts
|
||||||
src/Common/MessageHandler.ts
|
src/Common/MessageHandler.ts
|
||||||
src/Common/MongoProxyClient.test.ts
|
src/Common/MongoProxyClient.test.ts
|
||||||
src/Common/MongoProxyClient.ts
|
|
||||||
src/Common/MongoUtility.ts
|
src/Common/MongoUtility.ts
|
||||||
src/Common/NotificationsClientBase.ts
|
src/Common/NotificationsClientBase.ts
|
||||||
src/Common/ObjectCache.test.ts
|
src/Common/ObjectCache.test.ts
|
||||||
@@ -202,7 +202,6 @@ src/Explorer/Tabs/GraphTab.ts
|
|||||||
src/Explorer/Tabs/MongoDocumentsTab.ts
|
src/Explorer/Tabs/MongoDocumentsTab.ts
|
||||||
src/Explorer/Tabs/MongoQueryTab.ts
|
src/Explorer/Tabs/MongoQueryTab.ts
|
||||||
src/Explorer/Tabs/MongoShellTab.ts
|
src/Explorer/Tabs/MongoShellTab.ts
|
||||||
src/Explorer/Tabs/NotebookTab.ts
|
|
||||||
src/Explorer/Tabs/NotebookV2Tab.ts
|
src/Explorer/Tabs/NotebookV2Tab.ts
|
||||||
src/Explorer/Tabs/QueryTab.test.ts
|
src/Explorer/Tabs/QueryTab.test.ts
|
||||||
src/Explorer/Tabs/QueryTab.ts
|
src/Explorer/Tabs/QueryTab.ts
|
||||||
@@ -216,7 +215,6 @@ src/Explorer/Tabs/TabComponents.ts
|
|||||||
src/Explorer/Tabs/TabsBase.ts
|
src/Explorer/Tabs/TabsBase.ts
|
||||||
src/Explorer/Tabs/TriggerTab.ts
|
src/Explorer/Tabs/TriggerTab.ts
|
||||||
src/Explorer/Tabs/UserDefinedFunctionTab.ts
|
src/Explorer/Tabs/UserDefinedFunctionTab.ts
|
||||||
src/Explorer/Tabs/__mocks__/NotebookTab.ts
|
|
||||||
src/Explorer/Tree/AccessibleVerticalList.ts
|
src/Explorer/Tree/AccessibleVerticalList.ts
|
||||||
src/Explorer/Tree/Collection.test.ts
|
src/Explorer/Tree/Collection.test.ts
|
||||||
src/Explorer/Tree/Collection.ts
|
src/Explorer/Tree/Collection.ts
|
||||||
|
|||||||
33
.eslintrc.js
33
.eslintrc.js
@@ -1,45 +1,44 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es6: true
|
es6: true,
|
||||||
},
|
},
|
||||||
plugins: ["@typescript-eslint"],
|
plugins: ["@typescript-eslint", "no-null"],
|
||||||
extends: [
|
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/eslint-recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended"
|
|
||||||
],
|
|
||||||
globals: {
|
globals: {
|
||||||
Atomics: "readonly",
|
Atomics: "readonly",
|
||||||
SharedArrayBuffer: "readonly"
|
SharedArrayBuffer: "readonly",
|
||||||
},
|
},
|
||||||
parser: "@typescript-eslint/parser",
|
parser: "@typescript-eslint/parser",
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
jsx: true
|
jsx: true,
|
||||||
},
|
},
|
||||||
ecmaVersion: 2018,
|
ecmaVersion: 2018,
|
||||||
sourceType: "module"
|
sourceType: "module",
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ["**/*.tsx"],
|
files: ["**/*.tsx"],
|
||||||
env: {
|
env: {
|
||||||
jest: true
|
jest: true,
|
||||||
},
|
},
|
||||||
extends: ["plugin:react/recommended"],
|
extends: ["plugin:react/recommended"],
|
||||||
plugins: ["react"]
|
plugins: ["react"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ["**/*.test.{ts,tsx}"],
|
files: ["**/*.test.{ts,tsx}"],
|
||||||
env: {
|
env: {
|
||||||
jest: true
|
jest: true,
|
||||||
},
|
},
|
||||||
extends: ["plugin:jest/recommended"],
|
extends: ["plugin:jest/recommended"],
|
||||||
plugins: ["jest"]
|
plugins: ["jest"],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
curly: "error"
|
curly: "error",
|
||||||
}
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
|
"@typescript-eslint/no-extraneous-class": "error",
|
||||||
|
"no-null/no-null": "error",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
||||||
|
|
||||||
name: CI
|
name: CI
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -8,7 +5,41 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
compile:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: "Compile TypeScript"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js 12.x
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 12.x
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run compile
|
||||||
|
- run: npm run compile:strict
|
||||||
|
format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: "Check Format"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js 12.x
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 12.x
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run format:check
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: "Lint"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js 12.x
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 12.x
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run lint
|
||||||
|
unittest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "Unit Tests"
|
name: "Unit Tests"
|
||||||
steps:
|
steps:
|
||||||
@@ -36,13 +67,14 @@ jobs:
|
|||||||
path: .cache
|
path: .cache
|
||||||
key: ${{ runner.os }}-build-cache
|
key: ${{ runner.os }}-build-cache
|
||||||
- run: npm run pack:prod
|
- run: npm run pack:prod
|
||||||
- run: npm run copyToConsumers
|
- run: cp -r ./Contracts ./dist/contracts
|
||||||
|
- run: cp -r ./configs ./dist/configs
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: dist/
|
path: dist/
|
||||||
endtoend:
|
endtoendemulator:
|
||||||
name: "End to End Tests"
|
name: "End To End Tests | Emulator | SQL"
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -67,9 +99,65 @@ jobs:
|
|||||||
EMULATOR_ENDPOINT: https://0.0.0.0:8081/
|
EMULATOR_ENDPOINT: https://0.0.0.0:8081/
|
||||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||||
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
||||||
|
endtoendsql:
|
||||||
|
name: "End To End Tests | SQL"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js 12.x
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 12.x
|
||||||
|
- name: Restore Cypress Binary Cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cache/Cypress
|
||||||
|
key: ${{ runner.os }}-cypress-binary-cache
|
||||||
|
- run: npm ci
|
||||||
|
- name: End to End Tests
|
||||||
|
run: |
|
||||||
|
npm start &
|
||||||
|
cd cypress
|
||||||
|
npm ci
|
||||||
|
node cleanup.js
|
||||||
|
npm run wait-for-server
|
||||||
|
npx cypress run --browser chrome --headless --spec "./integration/dataexplorer/SQL/*"
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||||
|
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
||||||
|
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
|
||||||
|
endtoendmongo:
|
||||||
|
name: "End To End Tests | Mongo"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js 12.x
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 12.x
|
||||||
|
- name: Restore Cypress Binary Cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cache/Cypress
|
||||||
|
key: ${{ runner.os }}-cypress-binary-cache
|
||||||
|
- name: End to End Tests
|
||||||
|
run: |
|
||||||
|
npm ci
|
||||||
|
npm start &
|
||||||
|
cd cypress
|
||||||
|
npm ci
|
||||||
|
node cleanup.js
|
||||||
|
npm run wait-for-server
|
||||||
|
npx cypress run --browser chrome --headless --spec "./integration/dataexplorer/MONGO/*"
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||||
|
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
||||||
|
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
|
||||||
nuget:
|
nuget:
|
||||||
name: Publish Nuget
|
name: Publish Nuget
|
||||||
needs: [build, test, endtoend]
|
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
|
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
|
||||||
@@ -82,6 +170,7 @@ jobs:
|
|||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
|
- run: cp ./configs/prod.json config.json
|
||||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
|
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
|
||||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||||
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||||
@@ -90,7 +179,7 @@ jobs:
|
|||||||
path: "*.nupkg"
|
path: "*.nupkg"
|
||||||
nugetmpac:
|
nugetmpac:
|
||||||
name: Publish Nuget MPAC
|
name: Publish Nuget MPAC
|
||||||
needs: [build, test, endtoend]
|
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
|
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
|
||||||
@@ -103,6 +192,7 @@ jobs:
|
|||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: dist
|
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: sed -i 's/Azure.Cosmos.DB.Data.Explorer/Azure.Cosmos.DB.Data.Explorer.MPAC/g' DataExplorer.nuspec
|
||||||
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
|
- run: nuget sources add -Name "ADO" -Source "$NUGET_SOURCE" -UserName "GitHub" -Password "$AZURE_DEVOPS_PAT"
|
||||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||||
20
.github/workflows/runners.yml
vendored
Normal file
20
.github/workflows/runners.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Runners
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "*/10 * * * *"
|
||||||
|
jobs:
|
||||||
|
sqlcreatecollection:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: "SQL | Create Collection"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run test:e2e
|
||||||
|
env:
|
||||||
|
PORTAL_RUNNER_APP_INSIGHTS_KEY: ${{ secrets.PORTAL_RUNNER_APP_INSIGHTS_KEY }}
|
||||||
|
PORTAL_RUNNER_USERNAME: ${{ secrets.PORTAL_RUNNER_USERNAME }}
|
||||||
|
PORTAL_RUNNER_PASSWORD: ${{ secrets.PORTAL_RUNNER_PASSWORD }}
|
||||||
|
PORTAL_RUNNER_SUBSCRIPTION: 69e02f2d-f059-4409-9eac-97e8a276ae2c
|
||||||
|
PORTAL_RUNNER_RESOURCE_GROUP: runners
|
||||||
|
PORTAL_RUNNER_DATABASE_ACCOUNT: portal-sql-runner
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,3 +16,4 @@ notebookapp/*
|
|||||||
Contracts/*
|
Contracts/*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.cache/
|
.cache/
|
||||||
|
.env
|
||||||
51
CONTRIBUTING.md
Normal file
51
CONTRIBUTING.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Contribution guidelines to Data Explorer
|
||||||
|
|
||||||
|
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||||
|
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||||
|
the rights to use your contribution. For details, visit https://cla.microsoft.com.
|
||||||
|
|
||||||
|
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
|
||||||
|
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
|
||||||
|
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||||
|
|
||||||
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||||
|
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||||
|
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
|
||||||
|
## Microsoft Open Source Code of Conduct
|
||||||
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
|
||||||
|
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
||||||
|
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||||
|
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
||||||
|
|
||||||
|
## Browser support
|
||||||
|
Please make sure to support all modern browsers as well as Internet Explorer 11.
|
||||||
|
For IE support, polyfill is preferred over new usage of lodash or underscore. We already polyfill almost everything by importing babel-polyfill at the top of entry points.
|
||||||
|
|
||||||
|
|
||||||
|
## Coding guidelines, conventions and recommendations
|
||||||
|
### Typescript
|
||||||
|
* Follow this [typescript style guide](https://github.com/excelmicro/typescript) which is based on [airbnb's style guide](https://github.com/airbnb/javascript).
|
||||||
|
* Conventions speficic to this project:
|
||||||
|
* Use double-quotes for string
|
||||||
|
* Don't use null, use undefined
|
||||||
|
* Pascal case for private static readonly fields
|
||||||
|
* Camel case for classnames in markup
|
||||||
|
* Don't use class unless necessary
|
||||||
|
* Code related to notebooks should be dynamically imported so that it is loaded from a separate bundle only if the account is notebook-enabled. There are already top-level notebook components which are dynamically imported and their dependencies can be statically imported from these files.
|
||||||
|
|
||||||
|
### React
|
||||||
|
* Prefer using React class components over function components and hooks unless you have a simple component and require no nested functions:
|
||||||
|
* Nested functions may be harder to test independently
|
||||||
|
* Switching from function component to class component later mayb be painful
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
Any PR should not decrease testing coverage.
|
||||||
|
|
||||||
|
## Recommended Tools and VS Code extensions
|
||||||
|
* [Bookmarks](https://github.com/alefragnani/vscode-bookmarks)
|
||||||
|
* [Bracket pair colorizer](https://github.com/CoenraadS/Bracket-Pair-Colorizer-2)
|
||||||
|
* [GitHub Pull Requests and Issues](https://github.com/Microsoft/vscode-pull-request-github)
|
||||||
21
README.md
21
README.md
@@ -70,7 +70,7 @@ Unit tests are located adjacent to the code under test and run with [Jest](https
|
|||||||
|
|
||||||
`npm run test`
|
`npm run test`
|
||||||
|
|
||||||
#### End to End Tests
|
#### End to End CI Tests
|
||||||
|
|
||||||
[Cypress](https://www.cypress.io/) is used for end to end tests and are contained in `cypress/`. Currently, it operates as sub project with its own typescript config and dependencies. It also only operates against the emulator. To run cypress tests:
|
[Cypress](https://www.cypress.io/) is used for end to end tests and are contained in `cypress/`. Currently, it operates as sub project with its own typescript config and dependencies. It also only operates against the emulator. To run cypress tests:
|
||||||
|
|
||||||
@@ -80,16 +80,13 @@ Unit tests are located adjacent to the code under test and run with [Jest](https
|
|||||||
4. Install dependencies: `npm install`
|
4. Install dependencies: `npm install`
|
||||||
5. Run cypress headless(`npm run test`) or in interactive mode(`npm run test:debug`)
|
5. Run cypress headless(`npm run test`) or in interactive mode(`npm run test:debug`)
|
||||||
|
|
||||||
|
#### End to End Production Runners
|
||||||
|
|
||||||
|
Jest and Puppeteer are used for end to end production runners and are contained in `test/`. To run these tests locally:
|
||||||
|
|
||||||
|
1. Copy .env.example to .env and fill in all variables
|
||||||
|
2. Run `npm run test:e2e`
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
Please read the [contribution guidelines](./CONTRIBUTING.md).
|
||||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
|
||||||
the rights to use your contribution. For details, visit https://cla.microsoft.com.
|
|
||||||
|
|
||||||
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
|
|
||||||
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
|
|
||||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
|
||||||
|
|
||||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
|
||||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
|
||||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"]
|
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"],
|
||||||
};
|
};
|
||||||
|
|||||||
3
configs/mpac.json
Normal file
3
configs/mpac.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"JUNO_ENDPOINT": "https://tools-staging.cosmos.azure.com"
|
||||||
|
}
|
||||||
3
configs/prod.json
Normal file
3
configs/prod.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com"
|
||||||
|
}
|
||||||
3
cypress/.gitignore
vendored
3
cypress/.gitignore
vendored
@@ -1 +1,4 @@
|
|||||||
cypress.env.json
|
cypress.env.json
|
||||||
|
cypress/report
|
||||||
|
cypress/screenshots
|
||||||
|
cypress/videos
|
||||||
51
cypress/cleanup.js
Normal file
51
cypress/cleanup.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// Cleans up old databases from previous test runs
|
||||||
|
const { CosmosClient } = require("@azure/cosmos");
|
||||||
|
|
||||||
|
// TODO: Add support for other API connection strings
|
||||||
|
const mongoRegex = RegExp("mongodb://.*:(.*)@(.*).mongo.cosmos.azure.com");
|
||||||
|
|
||||||
|
async function cleanup() {
|
||||||
|
const connectionString = process.env.CYPRESS_CONNECTION_STRING;
|
||||||
|
if (!connectionString) {
|
||||||
|
throw new Error("Connection string not provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
let client;
|
||||||
|
switch (true) {
|
||||||
|
case connectionString.includes("mongodb://"): {
|
||||||
|
const [, key, accountName] = connectionString.match(mongoRegex);
|
||||||
|
client = new CosmosClient({
|
||||||
|
key,
|
||||||
|
endpoint: `https://${accountName}.documents.azure.com:443/`
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// TODO: Add support for other API connection strings
|
||||||
|
default:
|
||||||
|
client = new CosmosClient(connectionString);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await client.databases.readAll().fetchAll();
|
||||||
|
return Promise.all(
|
||||||
|
response.resources.map(async db => {
|
||||||
|
const dbTimestamp = new Date(db._ts * 1000);
|
||||||
|
const twentyMinutesAgo = new Date(Date.now() - 1000 * 60 * 20);
|
||||||
|
if (dbTimestamp < twentyMinutesAgo) {
|
||||||
|
await client.database(db.id).delete();
|
||||||
|
console.log(`DELETED: ${db.id} | Timestamp: ${dbTimestamp}`);
|
||||||
|
} else {
|
||||||
|
console.log(`SKIPPED: ${db.id} | Timestamp: ${dbTimestamp}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
.then(() => {
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -23,7 +23,7 @@ context("Cassandra API Test - createDatabase", () => {
|
|||||||
const keyspaceId = `KeyspaceId${crypt.randomBytes(8).toString("hex")}`;
|
const keyspaceId = `KeyspaceId${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const tableId = `TableId112`;
|
const tableId = `TableId112`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -32,27 +32,15 @@ context("Cassandra API Test - createDatabase", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[id="keyspace-id"]').should("be.visible").type(keyspaceId);
|
||||||
.find('input[id="keyspace-id"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.type(keyspaceId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[class="textfontclr"]').type(tableId);
|
||||||
.find('input[class="textfontclr"]')
|
|
||||||
.type(tableId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('data-test="addCollection-createCollection"').click();
|
||||||
.find('data-test="addCollection-createCollection"')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ context("Graph API Test", () => {
|
|||||||
const graphId = `TestGraph${crypt.randomBytes(8).toString("hex")}`;
|
const graphId = `TestGraph${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const partitionKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const partitionKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -33,39 +33,21 @@ context("Graph API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').should("be.visible").type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(graphId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(graphId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(partitionKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(partitionKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ let crypt = require("crypto");
|
|||||||
|
|
||||||
context("Mongo API Test - createDatabase", () => {
|
context("Mongo API Test - createDatabase", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
connectionString.loginUsingConnectionString(connectionString.constants.mongo);
|
connectionString.loginUsingConnectionString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Create a new collection in Mongo API", () => {
|
it("Create a new collection in Mongo API", () => {
|
||||||
@@ -24,7 +24,7 @@ context("Mongo API Test - createDatabase", () => {
|
|||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -33,38 +33,21 @@ context("Mongo API Test - createDatabase", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find("#submitBtnAddCollection").click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ let crypt = require("crypto");
|
|||||||
|
|
||||||
context("Mongo API Test", () => {
|
context("Mongo API Test", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
connectionString.loginUsingConnectionString(connectionString.constants.mongo);
|
connectionString.loginUsingConnectionString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Create a new collection in Mongo API - Autopilot", () => {
|
it.skip("Create a new collection in Mongo API - Autopilot", () => {
|
||||||
const dbId = `TestDatabase${crypt.randomBytes(8).toString("hex")}`;
|
const dbId = `TestDatabase${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -33,34 +33,23 @@ context("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="throughputModeContainer"]')
|
.find('div[class="throughputModeContainer"]')
|
||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.and(input => {
|
.and((input) => {
|
||||||
expect(input.get(0).textContent, "first item").contains("Autopilot (preview)");
|
expect(input.get(0).textContent, "first item").contains("Autopilot (preview)");
|
||||||
expect(input.get(1).textContent, "second item").contains("Manual");
|
expect(input.get(1).textContent, "second item").contains("Manual");
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[id="newContainer-databaseThroughput-autoPilotRadio"]').check();
|
||||||
.find('input[id="newContainer-databaseThroughput-autoPilotRadio"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('select[name="autoPilotTiers"]')
|
.find('select[name="autoPilotTiers"]')
|
||||||
@@ -68,19 +57,13 @@ context("Mongo API Test", () => {
|
|||||||
// // .select('4,000 RU/s').should('have.value', '1');
|
// // .select('4,000 RU/s').should('have.value', '1');
|
||||||
|
|
||||||
.find('option[value="2"]')
|
.find('option[value="2"]')
|
||||||
.then($element => $element.get(1).setAttribute("selected", "selected"));
|
.then(($element) => $element.get(1).setAttribute("selected", "selected"));
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -4,20 +4,20 @@ let crypt = require("crypto");
|
|||||||
|
|
||||||
context("Mongo API Test", () => {
|
context("Mongo API Test", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
connectionString.loginUsingConnectionString(connectionString.constants.mongo);
|
connectionString.loginUsingConnectionString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Create a new collection in existing database in Mongo API", () => {
|
it.skip("Create a new collection in existing database in Mongo API", () => {
|
||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('span[class="nodeLabel"]')
|
.find('span[class="nodeLabel"]')
|
||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.then($span => {
|
.then(($span) => {
|
||||||
const dbId1 = $span.text();
|
const dbId1 = $span.text();
|
||||||
cy.log("DBBB", dbId1);
|
cy.log("DBBB", dbId1);
|
||||||
|
|
||||||
@@ -28,30 +28,17 @@ context("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-existingDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-existingDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-existingDatabase"]').type(dbId1);
|
||||||
.find('input[data-test="addCollection-existingDatabase"]')
|
|
||||||
.type(dbId1);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ const connectionString = require("../../../utilities/connectionString");
|
|||||||
|
|
||||||
let crypt = require("crypto");
|
let crypt = require("crypto");
|
||||||
|
|
||||||
context("Mongo API Test", () => {
|
context.skip("Mongo API Test", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
connectionString.loginUsingConnectionString(connectionString.constants.mongo);
|
connectionString.loginUsingConnectionString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Create a new collection in Mongo API - Provision database throughput", () => {
|
it("Create a new collection in Mongo API - Provision database throughput", () => {
|
||||||
@@ -12,7 +12,7 @@ context("Mongo API Test", () => {
|
|||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -21,50 +21,31 @@ context("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find(".createNewDatabaseOrUseExisting")
|
.find(".createNewDatabaseOrUseExisting")
|
||||||
.should("have.length", 2)
|
.should("have.length", 2)
|
||||||
.and(input => {
|
.and((input) => {
|
||||||
expect(input.get(0).textContent, "first item").contains("Create new");
|
expect(input.get(0).textContent, "first item").contains("Create new");
|
||||||
expect(input.get(1).textContent, "second item").contains("Use existing");
|
expect(input.get(1).textContent, "second item").contains("Use existing");
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
@@ -84,7 +65,7 @@ context("Mongo API Test", () => {
|
|||||||
const collectionIdTitle = `Add Collection`;
|
const collectionIdTitle = `Add Collection`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -93,42 +74,23 @@ context("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').uncheck();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.uncheck();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[id="tab2"]').check({ force: true });
|
||||||
.find('input[id="tab2"]')
|
|
||||||
.check({ force: true });
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
@@ -147,7 +109,7 @@ context("Mongo API Test", () => {
|
|||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -156,38 +118,21 @@ context("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').uncheck();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.uncheck();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[id="tab1"]').check({ force: true });
|
||||||
.find('input[id="tab1"]')
|
|
||||||
.check({ force: true });
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,16 @@ let crypt = require("crypto");
|
|||||||
|
|
||||||
context("SQL API Test", () => {
|
context("SQL API Test", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
connectionString.loginUsingConnectionString(connectionString.constants.sql);
|
connectionString.loginUsingConnectionString();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Create a new container in SQL API", () => {
|
it("Create a new container in SQL API", () => {
|
||||||
const dbId = `TestDatabase${crypt.randomBytes(8).toString("hex")}`;
|
const dbId = `TestDatabase${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
connectionString.loginUsingConnectionString();
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -33,38 +34,21 @@ context("SQL API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find("#submitBtnAddCollection").click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ context("Table API Test", () => {
|
|||||||
it("Create a new table in Table API", () => {
|
it("Create a new table in Table API", () => {
|
||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -31,22 +31,13 @@ context("Table API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ context("Emulator - createDatabase", () => {
|
|||||||
|
|
||||||
cy.get(".createNewDatabaseOrUseExisting")
|
cy.get(".createNewDatabaseOrUseExisting")
|
||||||
.should("have.length", 2)
|
.should("have.length", 2)
|
||||||
.and(input => {
|
.and((input) => {
|
||||||
expect(input.get(0).textContent, "first item").contains("Create new");
|
expect(input.get(0).textContent, "first item").contains("Create new");
|
||||||
expect(input.get(1).textContent, "second item").contains("Use existing");
|
expect(input.get(1).textContent, "second item").contains("Use existing");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,27 +38,15 @@ context("Emulator - Create database -> container -> item", () => {
|
|||||||
cy.get("[data-test=addCollection-partitionKeyValue]").type("/pk");
|
cy.get("[data-test=addCollection-partitionKeyValue]").type("/pk");
|
||||||
cy.get('input[name="createCollection"]').click();
|
cy.get('input[name="createCollection"]').click();
|
||||||
cy.get(".dataResourceTree").should("contain", databaseId);
|
cy.get(".dataResourceTree").should("contain", databaseId);
|
||||||
cy.get(".dataResourceTree")
|
cy.get(".dataResourceTree").contains(databaseId).click();
|
||||||
.contains(databaseId)
|
|
||||||
.click();
|
|
||||||
cy.get(".dataResourceTree").should("contain", collectionId);
|
cy.get(".dataResourceTree").should("contain", collectionId);
|
||||||
cy.get(".dataResourceTree")
|
cy.get(".dataResourceTree").contains(collectionId).click();
|
||||||
.contains(collectionId)
|
cy.get(".dataResourceTree").contains("Items").click();
|
||||||
.click();
|
cy.get(".dataResourceTree").contains("Items").click();
|
||||||
cy.get(".dataResourceTree")
|
|
||||||
.contains("Items")
|
|
||||||
.click();
|
|
||||||
cy.get(".dataResourceTree")
|
|
||||||
.contains("Items")
|
|
||||||
.click();
|
|
||||||
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
||||||
cy.get(".commandBarContainer")
|
cy.get(".commandBarContainer").contains("New Item").click();
|
||||||
.contains("New Item")
|
|
||||||
.click();
|
|
||||||
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
||||||
cy.get(".commandBarContainer")
|
cy.get(".commandBarContainer").contains("Save").click();
|
||||||
.contains("Save")
|
|
||||||
.click();
|
|
||||||
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
||||||
cy.get(".documentsGridHeaderContainer").should("contain", "replace_with_new_document_id");
|
cy.get(".documentsGridHeaderContainer").should("contain", "replace_with_new_document_id");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,25 +14,18 @@ context("Emulator - deleteCollection", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Delete a collection", () => {
|
it("Delete a collection", () => {
|
||||||
cy.get(".databaseId")
|
cy.get(".databaseId").last().click();
|
||||||
.last()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get(".collectionList")
|
cy.get(".collectionList")
|
||||||
.last()
|
.last()
|
||||||
.then($id => {
|
.then(($id) => {
|
||||||
const collectionId = $id.text();
|
const collectionId = $id.text();
|
||||||
|
|
||||||
cy.get('span[data-test="collectionEllipsisMenu"]').should("exist");
|
cy.get('span[data-test="collectionEllipsisMenu"]').should("exist");
|
||||||
|
|
||||||
cy.get('span[data-test="collectionEllipsisMenu"]')
|
cy.get('span[data-test="collectionEllipsisMenu"]').invoke("show").last().click();
|
||||||
.invoke("show")
|
|
||||||
.last()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('div[data-test="collectionContextMenu"]')
|
cy.get('div[data-test="collectionContextMenu"]').contains("Delete Container").click({ force: true });
|
||||||
.contains("Delete Container")
|
|
||||||
.click({ force: true });
|
|
||||||
|
|
||||||
cy.get('input[data-test="confirmCollectionId"]').type(collectionId.trim());
|
cy.get('input[data-test="confirmCollectionId"]').type(collectionId.trim());
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ context("Emulator - deleteDatabase", () => {
|
|||||||
url: "https://localhost:8081/_explorer/authorization/post/dbs/",
|
url: "https://localhost:8081/_explorer/authorization/post/dbs/",
|
||||||
headers: {
|
headers: {
|
||||||
"x-ms-date": date,
|
"x-ms-date": date,
|
||||||
authorization: "-"
|
authorization: "-",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
authToken = response.body.Token; // Getting auth token for collection creation
|
authToken = response.body.Token; // Getting auth token for collection creation
|
||||||
return new Cypress.Promise((resolve, reject) => {
|
return new Cypress.Promise((resolve, reject) => {
|
||||||
return resolve();
|
return resolve();
|
||||||
@@ -38,12 +38,12 @@ context("Emulator - deleteDatabase", () => {
|
|||||||
headers: {
|
headers: {
|
||||||
"x-ms-date": date,
|
"x-ms-date": date,
|
||||||
authorization: authToken,
|
authorization: authToken,
|
||||||
"x-ms-version": "2018-12-31"
|
"x-ms-version": "2018-12-31",
|
||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
id: dbId
|
id: dbId,
|
||||||
}
|
},
|
||||||
}).then(response => {
|
}).then((response) => {
|
||||||
cy.log("Response", response);
|
cy.log("Response", response);
|
||||||
db_rid = response.body._rid;
|
db_rid = response.body._rid;
|
||||||
return new Cypress.Promise((resolve, reject) => {
|
return new Cypress.Promise((resolve, reject) => {
|
||||||
@@ -59,19 +59,14 @@ context("Emulator - deleteDatabase", () => {
|
|||||||
|
|
||||||
cy.get(".databaseId")
|
cy.get(".databaseId")
|
||||||
.last()
|
.last()
|
||||||
.then($id => {
|
.then(($id) => {
|
||||||
const dbId = $id.text();
|
const dbId = $id.text();
|
||||||
|
|
||||||
cy.get('span[data-test="databaseEllipsisMenu"]').should("exist");
|
cy.get('span[data-test="databaseEllipsisMenu"]').should("exist");
|
||||||
|
|
||||||
cy.get('span[data-test="databaseEllipsisMenu"]')
|
cy.get('span[data-test="databaseEllipsisMenu"]').invoke("show").last().click();
|
||||||
.invoke("show")
|
|
||||||
.last()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('div[data-test="databaseContextMenu"]')
|
cy.get('div[data-test="databaseContextMenu"]').contains("Delete Database").click({ force: true });
|
||||||
.contains("Delete Database")
|
|
||||||
.click({ force: true });
|
|
||||||
|
|
||||||
cy.get('input[data-test="confirmDatabaseId"]').type(dbId.trim());
|
cy.get('input[data-test="confirmDatabaseId"]').type(dbId.trim());
|
||||||
|
|
||||||
|
|||||||
@@ -21,29 +21,25 @@ context("New Notebook smoke test", () => {
|
|||||||
cy.contains("New Notebook").click();
|
cy.contains("New Notebook").click();
|
||||||
|
|
||||||
// Check tab name
|
// Check tab name
|
||||||
cy.get("li.tabList .tabNavText").should($span => {
|
cy.get("li.tabList .tabNavText").should(($span) => {
|
||||||
const text = $span.text();
|
const text = $span.text();
|
||||||
expect(text).to.match(/^Untitled.*\.ipynb$/);
|
expect(text).to.match(/^Untitled.*\.ipynb$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for python3 | idle status
|
// Wait for python3 | idle status
|
||||||
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should($p => {
|
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should(($p) => {
|
||||||
const text = $p.text();
|
const text = $p.text();
|
||||||
expect(text).to.match(/^python3.*idle$/);
|
expect(text).to.match(/^python3.*idle$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Click on a cell
|
// Click on a cell
|
||||||
cy.get(".cell-container")
|
cy.get(".cell-container").as("cellContainer").click();
|
||||||
.as("cellContainer")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Type in some code
|
// Type in some code
|
||||||
cy.get("@cellContainer").type("2+4");
|
cy.get("@cellContainer").type("2+4");
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
cy.get('[data-test="Run"]')
|
cy.get('[data-test="Run"]').first().click();
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Verify results
|
// Verify results
|
||||||
cy.get("@cellContainer").within(() => {
|
cy.get("@cellContainer").within(() => {
|
||||||
@@ -51,39 +47,29 @@ context("New Notebook smoke test", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Restart kernel
|
// Restart kernel
|
||||||
cy.get('[data-test="Run"] button')
|
cy.get('[data-test="Run"] button').eq(-1).click();
|
||||||
.eq(-1)
|
cy.get("li").contains("Restart Kernel").click();
|
||||||
.click();
|
|
||||||
cy.get("li")
|
|
||||||
.contains("Restart Kernel")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Wait for python3 | restarting status
|
// Wait for python3 | restarting status
|
||||||
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should($p => {
|
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should(($p) => {
|
||||||
const text = $p.text();
|
const text = $p.text();
|
||||||
expect(text).to.match(/^python3.*restarting$/);
|
expect(text).to.match(/^python3.*restarting$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for python3 | idle status
|
// Wait for python3 | idle status
|
||||||
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should($p => {
|
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should(($p) => {
|
||||||
const text = $p.text();
|
const text = $p.text();
|
||||||
expect(text).to.match(/^python3.*idle$/);
|
expect(text).to.match(/^python3.*idle$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Click on a cell
|
// Click on a cell
|
||||||
cy.get(".cell-container")
|
cy.get(".cell-container").as("cellContainer").find(".input").as("codeInput").click();
|
||||||
.as("cellContainer")
|
|
||||||
.find(".input")
|
|
||||||
.as("codeInput")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Type in some code
|
// Type in some code
|
||||||
cy.get("@codeInput").type("{backspace}{backspace}{backspace}4+5");
|
cy.get("@codeInput").type("{backspace}{backspace}{backspace}4+5");
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
cy.get('[data-test="Run"]')
|
cy.get('[data-test="Run"]').first().click();
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Verify results
|
// Verify results
|
||||||
cy.get("@cellContainer").within(() => {
|
cy.get("@cellContainer").within(() => {
|
||||||
|
|||||||
@@ -11,15 +11,11 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const clickContextMenuAndSelectOption = (nodeLabel, option) => {
|
const clickContextMenuAndSelectOption = (nodeLabel, option) => {
|
||||||
cy.get(`.treeNodeHeader[data-test="${nodeLabel}"]`)
|
cy.get(`.treeNodeHeader[data-test="${nodeLabel}"]`).find("button.treeMenuEllipsis").click();
|
||||||
.find("button.treeMenuEllipsis")
|
cy.get('[data-test="treeComponentMenuItemContainer"]').contains(option).click();
|
||||||
.click();
|
|
||||||
cy.get('[data-test="treeComponentMenuItemContainer"]')
|
|
||||||
.contains(option)
|
|
||||||
.click();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createFolder = folder => {
|
const createFolder = (folder) => {
|
||||||
clickContextMenuAndSelectOption("My Notebooks/", "New Directory");
|
clickContextMenuAndSelectOption("My Notebooks/", "New Directory");
|
||||||
|
|
||||||
cy.get("#stringInputPane").within(() => {
|
cy.get("#stringInputPane").within(() => {
|
||||||
@@ -28,11 +24,9 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteItem = nodeName => {
|
const deleteItem = (nodeName) => {
|
||||||
clickContextMenuAndSelectOption(`${nodeName}`, "Delete");
|
clickContextMenuAndSelectOption(`${nodeName}`, "Delete");
|
||||||
cy.get(".ms-Dialog-main")
|
cy.get(".ms-Dialog-main").contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -56,9 +50,7 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
// Rename
|
// Rename
|
||||||
clickContextMenuAndSelectOption(`${folder}/`, "Rename");
|
clickContextMenuAndSelectOption(`${folder}/`, "Rename");
|
||||||
cy.get("#stringInputPane").within(() => {
|
cy.get("#stringInputPane").within(() => {
|
||||||
cy.get('input[name="collectionIdConfirmation"]')
|
cy.get('input[name="collectionIdConfirmation"]').clear().type(renamedFolder);
|
||||||
.clear()
|
|
||||||
.type(renamedFolder);
|
|
||||||
cy.get("form").submit();
|
cy.get("form").submit();
|
||||||
});
|
});
|
||||||
cy.get(`.treeNodeHeader[data-test="${renamedFolder}/"]`).should("exist");
|
cy.get(`.treeNodeHeader[data-test="${renamedFolder}/"]`).should("exist");
|
||||||
@@ -75,16 +67,12 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
|
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
|
||||||
|
|
||||||
// Verify tab is open
|
// Verify tab is open
|
||||||
cy.get(".tabList")
|
cy.get(".tabList").contains(newNotebookName).should("exist");
|
||||||
.contains(newNotebookName)
|
|
||||||
.should("exist");
|
|
||||||
|
|
||||||
// Close tab
|
// Close tab
|
||||||
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`)
|
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`).find(".cancelButton").click();
|
||||||
.find(".cancelButton")
|
|
||||||
.click();
|
|
||||||
// When running from command line, closing the tab is too fast
|
// When running from command line, closing the tab is too fast
|
||||||
cy.get("body").then($body => {
|
cy.get("body").then(($body) => {
|
||||||
if ($body.find(".ms-Dialog-main").length) {
|
if ($body.find(".ms-Dialog-main").length) {
|
||||||
// For some reason, this does not work
|
// For some reason, this does not work
|
||||||
// cy.get(".ms-Dialog-main").contains("Close").click();
|
// cy.get(".ms-Dialog-main").contains("Close").click();
|
||||||
@@ -100,14 +88,10 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
|
||||||
.find("button.treeMenuEllipsis")
|
.find("button.treeMenuEllipsis")
|
||||||
.click();
|
.click();
|
||||||
cy.get('[data-test="treeComponentMenuItemContainer"]')
|
cy.get('[data-test="treeComponentMenuItemContainer"]').contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Confirm
|
// Confirm
|
||||||
cy.get(".ms-Dialog-main")
|
cy.get(".ms-Dialog-main").contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
|
||||||
|
|
||||||
deleteItem(`${folder}/`);
|
deleteItem(`${folder}/`);
|
||||||
@@ -121,10 +105,8 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
|
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
|
||||||
|
|
||||||
// Close tab
|
// Close tab
|
||||||
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`)
|
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`).find(".cancelButton").click();
|
||||||
.find(".cancelButton")
|
cy.get("body").then(($body) => {
|
||||||
.click();
|
|
||||||
cy.get("body").then($body => {
|
|
||||||
if ($body.find(".ms-Dialog-main").length) {
|
if ($body.find(".ms-Dialog-main").length) {
|
||||||
// For some reason, this does not work
|
// For some reason, this does not work
|
||||||
// cy.get(".ms-Dialog-main").contains("Close").click();
|
// cy.get(".ms-Dialog-main").contains("Close").click();
|
||||||
@@ -140,14 +122,10 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
|
||||||
.find("button.treeMenuEllipsis")
|
.find("button.treeMenuEllipsis")
|
||||||
.click();
|
.click();
|
||||||
cy.get('[data-test="treeComponentMenuItemContainer"]')
|
cy.get('[data-test="treeComponentMenuItemContainer"]').contains("Rename").click();
|
||||||
.contains("Rename")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get("#stringInputPane").within(() => {
|
cy.get("#stringInputPane").within(() => {
|
||||||
cy.get('input[name="collectionIdConfirmation"]')
|
cy.get('input[name="collectionIdConfirmation"]').clear().type(renamedNotebookName);
|
||||||
.clear()
|
|
||||||
.type(renamedNotebookName);
|
|
||||||
cy.get("form").submit();
|
cy.get("form").submit();
|
||||||
});
|
});
|
||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
|
||||||
@@ -157,14 +135,10 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${renamedNotebookName}"]`)
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${renamedNotebookName}"]`)
|
||||||
.find("button.treeMenuEllipsis")
|
.find("button.treeMenuEllipsis")
|
||||||
.click();
|
.click();
|
||||||
cy.get('[data-test="treeComponentMenuItemContainer"]')
|
cy.get('[data-test="treeComponentMenuItemContainer"]').contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Confirm
|
// Confirm
|
||||||
cy.get(".ms-Dialog-main")
|
cy.get(".ms-Dialog-main").contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
// Give it time to settle
|
// Give it time to settle
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
deleteItem(`${folder}/`);
|
deleteItem(`${folder}/`);
|
||||||
|
|||||||
110
cypress/package-lock.json
generated
110
cypress/package-lock.json
generated
@@ -273,77 +273,12 @@
|
|||||||
"any-observable": "^0.3.0"
|
"any-observable": "^0.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/blob-util": {
|
"@types/sinonjs__fake-timers": {
|
||||||
"version": "1.3.3",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/blob-util/-/blob-util-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz",
|
||||||
"integrity": "sha512-4ahcL/QDnpjWA2Qs16ZMQif7HjGP2cw3AGjHabybjw7Vm1EKu+cfQN1D78BaZbS1WJNa1opSMF5HNMztx7lR0w==",
|
"integrity": "sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/bluebird": {
|
|
||||||
"version": "3.5.29",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.29.tgz",
|
|
||||||
"integrity": "sha512-kmVtnxTuUuhCET669irqQmPAez4KFnFVKvpleVRyfC3g+SHD1hIkFZcWLim9BVcwUBLO59o8VZE4yGCmTif8Yw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/chai": {
|
|
||||||
"version": "4.2.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.7.tgz",
|
|
||||||
"integrity": "sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/chai-jquery": {
|
|
||||||
"version": "1.1.40",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/chai-jquery/-/chai-jquery-1.1.40.tgz",
|
|
||||||
"integrity": "sha512-mCNEZ3GKP7T7kftKeIs7QmfZZQM7hslGSpYzKbOlR2a2HCFf9ph4nlMRA9UnuOETeOQYJVhJQK7MwGqNZVyUtQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/chai": "*",
|
|
||||||
"@types/jquery": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/jquery": {
|
|
||||||
"version": "3.3.31",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.31.tgz",
|
|
||||||
"integrity": "sha512-Lz4BAJihoFw5nRzKvg4nawXPzutkv7wmfQ5121avptaSIXlDNJCUuxZxX/G+9EVidZGuO0UBlk+YjKbwRKJigg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/sizzle": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/lodash": {
|
|
||||||
"version": "4.14.149",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
|
|
||||||
"integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/minimatch": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/mocha": {
|
|
||||||
"version": "5.2.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
|
|
||||||
"integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/sinon": {
|
|
||||||
"version": "7.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.1.tgz",
|
|
||||||
"integrity": "sha512-EZQUP3hSZQyTQRfiLqelC9NMWd1kqLcmQE0dMiklxBkgi84T+cHOhnKpgk4NnOWpGX863yE6+IaGnOXUNFqDnQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/sinon-chai": {
|
|
||||||
"version": "3.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.3.tgz",
|
|
||||||
"integrity": "sha512-TOUFS6vqS0PVL1I8NGVSNcFaNJtFoyZPXZ5zur+qlhDfOmQECZZM4H4kKgca6O8L+QceX/ymODZASfUfn+y4yQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/chai": "*",
|
|
||||||
"@types/sinon": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/sizzle": {
|
"@types/sizzle": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
|
||||||
@@ -827,24 +762,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cypress": {
|
"cypress": {
|
||||||
"version": "4.5.0",
|
"version": "4.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/cypress/-/cypress-4.8.0.tgz",
|
||||||
"integrity": "sha512-2A4g5FW5d2fHzq8HKUGAMVTnW6P8nlWYQALiCoGN4bqBLvgwhYM/oG9oKc2CS6LnvgHFiKivKzpm9sfk3uU3zQ==",
|
"integrity": "sha512-Lsff8lF8pq6k/ioNua783tCsxGSLp6gqGXecdIfqCkqjYiOA53XKuEf1CaQJLUVs1dHSf49eDUp/sb620oJjVQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@cypress/listr-verbose-renderer": "0.4.1",
|
"@cypress/listr-verbose-renderer": "0.4.1",
|
||||||
"@cypress/request": "2.88.5",
|
"@cypress/request": "2.88.5",
|
||||||
"@cypress/xvfb": "1.2.4",
|
"@cypress/xvfb": "1.2.4",
|
||||||
"@types/blob-util": "1.3.3",
|
"@types/sinonjs__fake-timers": "6.0.1",
|
||||||
"@types/bluebird": "3.5.29",
|
|
||||||
"@types/chai": "4.2.7",
|
|
||||||
"@types/chai-jquery": "1.1.40",
|
|
||||||
"@types/jquery": "3.3.31",
|
|
||||||
"@types/lodash": "4.14.149",
|
|
||||||
"@types/minimatch": "3.0.3",
|
|
||||||
"@types/mocha": "5.2.7",
|
|
||||||
"@types/sinon": "7.5.1",
|
|
||||||
"@types/sinon-chai": "3.2.3",
|
|
||||||
"@types/sizzle": "2.3.2",
|
"@types/sizzle": "2.3.2",
|
||||||
"arch": "2.1.1",
|
"arch": "2.1.1",
|
||||||
"bluebird": "3.7.2",
|
"bluebird": "3.7.2",
|
||||||
@@ -878,14 +804,6 @@
|
|||||||
"untildify": "4.0.0",
|
"untildify": "4.0.0",
|
||||||
"url": "0.11.0",
|
"url": "0.11.0",
|
||||||
"yauzl": "2.10.0"
|
"yauzl": "2.10.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"minimist": {
|
|
||||||
"version": "1.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
|
||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashdash": {
|
"dashdash": {
|
||||||
@@ -1081,12 +999,6 @@
|
|||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
|
||||||
"version": "1.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
|
||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.5",
|
"version": "0.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
@@ -1859,6 +1771,12 @@
|
|||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"minimist": {
|
||||||
|
"version": "1.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||||
|
|||||||
@@ -5,11 +5,13 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "cypress run",
|
"test": "cypress run",
|
||||||
|
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",
|
||||||
|
"test:sql": "cypress run --browser chrome --headless --spec \"./integration/dataexplorer/SQL/*\"",
|
||||||
"test:ci": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/ https-get://0.0.0.0:8081/_explorer/index.html && cypress run --browser chrome --headless",
|
"test:ci": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/ https-get://0.0.0.0:8081/_explorer/index.html && cypress run --browser chrome --headless",
|
||||||
"test:debug": "cypress open"
|
"test:debug": "cypress open"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cypress": "^4.5.0",
|
"cypress": "^4.8.0",
|
||||||
"mocha": "^7.0.1",
|
"mocha": "^7.0.1",
|
||||||
"mochawesome": "^4.1.0",
|
"mochawesome": "^4.1.0",
|
||||||
"mochawesome-merge": "^4.0.1",
|
"mochawesome-merge": "^4.0.1",
|
||||||
|
|||||||
@@ -1,56 +1,41 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
loginUsingConnectionString: function (api) {
|
loginUsingConnectionString: function() {
|
||||||
|
const prodUrl = Cypress.env("TEST_ENDPOINT") || "https://localhost:1234/hostedExplorer.html";
|
||||||
|
const timeout = 15000;
|
||||||
|
|
||||||
|
cy.visit(prodUrl);
|
||||||
|
cy.get('iframe[id="explorerMenu"]').should("be.visible");
|
||||||
|
|
||||||
const prodUrl = "https://cosmos.azure.com/";
|
cy.get("iframe").then($element => {
|
||||||
const timeout = 15000;
|
const $body = $element.contents().find("body");
|
||||||
|
|
||||||
cy.visit(prodUrl);
|
cy.wrap($body)
|
||||||
cy.get('iframe[id="explorerMenu"]').should("be.visible");
|
.find("#connectExplorer")
|
||||||
|
.should("exist")
|
||||||
|
.find("div[class='connectExplorer']")
|
||||||
|
.should("exist")
|
||||||
|
.find("p[class='welcomeText']")
|
||||||
|
.should("exist");
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.wrap($body.find("div > p.switchConnectTypeText"))
|
||||||
const $body = $element.contents().find("body");
|
.should("exist")
|
||||||
|
.last()
|
||||||
|
.click({ force: true });
|
||||||
|
|
||||||
cy.wrap($body)
|
const secret = Cypress.env("CONNECTION_STRING");
|
||||||
.find("#connectExplorer")
|
|
||||||
.should("exist")
|
|
||||||
.find("div[class='connectExplorer']")
|
|
||||||
.should("exist")
|
|
||||||
.find("p[class='welcomeText']")
|
|
||||||
.should("exist");
|
|
||||||
|
|
||||||
cy.wrap($body.find("div > p.switchConnectTypeText"))
|
cy.wrap($body)
|
||||||
.should("exist")
|
.find("input[class='inputToken']")
|
||||||
.last()
|
.should("exist")
|
||||||
.click({ force: true });
|
.type(secret, {
|
||||||
|
force: true
|
||||||
|
});
|
||||||
|
|
||||||
const secret = Cypress.env('connectionString')[api];
|
cy.wrap($body.find("input[value='Connect']"), { timeout })
|
||||||
|
.first()
|
||||||
|
.click({ force: true });
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wait(15000);
|
||||||
.find("input[class='inputToken']")
|
});
|
||||||
.should("exist")
|
}
|
||||||
.type(secret, {
|
};
|
||||||
force: true
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.wrap($body.find("input[value='Connect']"), { timeout })
|
|
||||||
.first()
|
|
||||||
.click({ force: true });
|
|
||||||
|
|
||||||
cy.wait(15000);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
|
||||||
.find(".connectExplorer > p:nth-child(3)")
|
|
||||||
.should("be.visible");
|
|
||||||
|
|
||||||
});
|
|
||||||
},
|
|
||||||
constants:{
|
|
||||||
sql: "sql",
|
|
||||||
mongo: "mongo",
|
|
||||||
table: "table",
|
|
||||||
graph: "graph",
|
|
||||||
cassandra: "cassandra"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
9
jest-puppeteer.config.js
Normal file
9
jest-puppeteer.config.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const isCI = require("is-ci");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
launch: {
|
||||||
|
headless: isCI,
|
||||||
|
slowMo: isCI ? null : 20,
|
||||||
|
defaultViewport: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
5
jest.config.e2e.js
Normal file
5
jest.config.e2e.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
preset: "jest-puppeteer",
|
||||||
|
testMatch: ["<rootDir>/test/**/*.spec.[jt]s?(x)"],
|
||||||
|
setupFiles: ["dotenv/config"],
|
||||||
|
};
|
||||||
@@ -42,8 +42,8 @@ module.exports = {
|
|||||||
branches: 18,
|
branches: 18,
|
||||||
functions: 22,
|
functions: 22,
|
||||||
lines: 28,
|
lines: 28,
|
||||||
statements: 27
|
statements: 27,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Make calling deprecated APIs throw helpful error messages
|
// Make calling deprecated APIs throw helpful error messages
|
||||||
@@ -76,7 +76,7 @@ module.exports = {
|
|||||||
"office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes
|
"office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes
|
||||||
"^dnd-core$": "dnd-core/dist/cjs",
|
"^dnd-core$": "dnd-core/dist/cjs",
|
||||||
"^react-dnd$": "react-dnd/dist/cjs",
|
"^react-dnd$": "react-dnd/dist/cjs",
|
||||||
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs"
|
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs",
|
||||||
},
|
},
|
||||||
|
|
||||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||||
@@ -150,7 +150,7 @@ module.exports = {
|
|||||||
// testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?|ts?)$",
|
// testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?|ts?)$",
|
||||||
|
|
||||||
// This option allows the use of a custom results processor
|
// This option allows the use of a custom results processor
|
||||||
testResultsProcessor: "./trxProcessor.js",
|
// testResultsProcessor: "./trxProcessor.js",
|
||||||
|
|
||||||
// This option allows use of a custom test runner
|
// This option allows use of a custom test runner
|
||||||
// testRunner: "jasmine2",
|
// testRunner: "jasmine2",
|
||||||
@@ -164,11 +164,11 @@ module.exports = {
|
|||||||
// A map from regular expressions to paths to transformers
|
// A map from regular expressions to paths to transformers
|
||||||
transform: {
|
transform: {
|
||||||
"^.+\\.html?$": "html-loader-jest",
|
"^.+\\.html?$": "html-loader-jest",
|
||||||
"^.+\\.[t|j]sx?$": "babel-jest"
|
"^.+\\.[t|j]sx?$": "babel-jest",
|
||||||
},
|
},
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||||
transformIgnorePatterns: ["/node_modules/", "/externals/"]
|
transformIgnorePatterns: ["/node_modules/", "/externals/"],
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||||
// unmockedModulePathPatterns: undefined,
|
// unmockedModulePathPatterns: undefined,
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
|
|
||||||
@SelectionColor: #3074B0;
|
@SelectionColor: #3074B0;
|
||||||
|
|
||||||
|
@FocusColor: #00bcf2;
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
METRICS
|
METRICS
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@@ -198,7 +200,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.focus() {
|
.focus() {
|
||||||
outline: 1px dashed @AccentMedium;
|
outline: 1px dashed @FocusColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************************************
|
/************************************************************************************************
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ body {
|
|||||||
font-family: @DataExplorerFont;
|
font-family: @DataExplorerFont;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
:focus {
|
||||||
|
.focus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.float-right {
|
.float-right {
|
||||||
@@ -570,6 +574,12 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fileImportButton {
|
||||||
|
height: 24px;
|
||||||
|
border: @ButtonBorderWidth solid transparent;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
.fileUploadSummaryContainer {
|
.fileUploadSummaryContainer {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
|
|
||||||
@@ -1016,6 +1026,18 @@ menuQuickStart {
|
|||||||
background: #262626;
|
background: #262626;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panelContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelContentWrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.contextual-pane {
|
.contextual-pane {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
right: 0 !important;
|
right: 0 !important;
|
||||||
@@ -1232,23 +1254,25 @@ menuQuickStart {
|
|||||||
padding: 2px 30px;
|
padding: 2px 30px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
border-color: #0072c6;
|
||||||
|
background-color: #0072c6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btncreatecoll1:hover {
|
.leftpanel-okbut .genericPaneSubmitBtn {
|
||||||
background: @AccentMediumHigh;
|
border: 1px solid @AccentMediumHigh;
|
||||||
|
background-color: @AccentMediumHigh;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-color: @AccentMediumHigh;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
height: 24px;
|
||||||
|
|
||||||
.btncreatecoll1:active {
|
&:active {
|
||||||
border: 1px solid #0072c6;
|
border-color: #0072c6;
|
||||||
background-color: #0072c6;
|
background-color: #0072c6;
|
||||||
color: white;
|
}
|
||||||
padding: 2px 30px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btncreatecoll1-off {
|
.btncreatecoll1-off {
|
||||||
@@ -1361,6 +1385,15 @@ p {
|
|||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.headerline .closePaneBtn {
|
||||||
|
float: right;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 16px;
|
||||||
|
height: 100%;
|
||||||
|
margin-right: 4px;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
.closeImg {
|
.closeImg {
|
||||||
float: right;
|
float: right;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -1710,6 +1743,13 @@ input::-webkit-calendar-picker-indicator {
|
|||||||
margin: (2 * @MediumSpace) 0px;
|
margin: (2 * @MediumSpace) 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contextual-pane .panelMainContent {
|
||||||
|
padding-left: 34px;
|
||||||
|
padding-right: 34px;
|
||||||
|
color: @BaseDark;
|
||||||
|
margin: (2 * @MediumSpace) 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.contextual-pane .paneFooter {
|
.contextual-pane .paneFooter {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
|||||||
9840
package-lock.json
generated
9840
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@@ -4,7 +4,7 @@
|
|||||||
"description": "Cosmos Explorer",
|
"description": "Cosmos Explorer",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/cosmos": "3.7.0",
|
"@azure/cosmos": "3.7.4",
|
||||||
"@azure/cosmos-language-service": "0.0.4",
|
"@azure/cosmos-language-service": "0.0.4",
|
||||||
"@jupyterlab/services": "4.2.0",
|
"@jupyterlab/services": "4.2.0",
|
||||||
"@jupyterlab/terminal": "1.2.1",
|
"@jupyterlab/terminal": "1.2.1",
|
||||||
@@ -37,22 +37,25 @@
|
|||||||
"@uifabric/react-cards": "0.109.53",
|
"@uifabric/react-cards": "0.109.53",
|
||||||
"@uifabric/styling": "7.11.2",
|
"@uifabric/styling": "7.11.2",
|
||||||
"abort-controller": "3.0.0",
|
"abort-controller": "3.0.0",
|
||||||
|
"applicationinsights": "1.8.0",
|
||||||
"babel-polyfill": "6.26.0",
|
"babel-polyfill": "6.26.0",
|
||||||
"bootstrap": "3.3.7",
|
"bootstrap": "3.3.7",
|
||||||
"canvas": "2.6.0",
|
"canvas": "2.6.0",
|
||||||
"clean-webpack-plugin": "0.1.19",
|
"clean-webpack-plugin": "0.1.19",
|
||||||
"copy-webpack-plugin": "4.5.4",
|
"copy-webpack-plugin": "6.0.2",
|
||||||
"crossroads": "0.12.2",
|
"crossroads": "0.12.2",
|
||||||
"css-element-queries": "1.1.1",
|
"css-element-queries": "1.1.1",
|
||||||
"datatables.net-colreorder-dt": "1.5.1",
|
"datatables.net-colreorder-dt": "1.5.1",
|
||||||
"datatables.net-dt": "1.10.19",
|
"datatables.net-dt": "1.10.19",
|
||||||
"date-fns": "1.29.0",
|
"date-fns": "1.29.0",
|
||||||
"dayjs": "1.8.19",
|
"dayjs": "1.8.19",
|
||||||
|
"dotenv": "8.2.0",
|
||||||
"es6-object-assign": "1.1.0",
|
"es6-object-assign": "1.1.0",
|
||||||
"es6-symbol": "3.1.3",
|
"es6-symbol": "3.1.3",
|
||||||
"eslint-plugin-jest": "23.8.2",
|
"eslint-plugin-jest": "23.13.2",
|
||||||
"hasher": "1.2.0",
|
"hasher": "1.2.0",
|
||||||
"immutable": "4.0.0-rc.12",
|
"immutable": "4.0.0-rc.12",
|
||||||
|
"is-ci": "2.0.0",
|
||||||
"jquery": "3.4.0",
|
"jquery": "3.4.0",
|
||||||
"jquery-typeahead": "2.10.6",
|
"jquery-typeahead": "2.10.6",
|
||||||
"jquery-ui-dist": "1.12.1",
|
"jquery-ui-dist": "1.12.1",
|
||||||
@@ -110,8 +113,8 @@
|
|||||||
"@types/text-encoding": "0.0.33",
|
"@types/text-encoding": "0.0.33",
|
||||||
"@types/underscore": "1.7.36",
|
"@types/underscore": "1.7.36",
|
||||||
"@types/webfontloader": "1.6.29",
|
"@types/webfontloader": "1.6.29",
|
||||||
"@typescript-eslint/eslint-plugin": "2.25.0",
|
"@typescript-eslint/eslint-plugin": "3.2.0",
|
||||||
"@typescript-eslint/parser": "2.25.0",
|
"@typescript-eslint/parser": "3.2.0",
|
||||||
"adal-angular": "1.0.15",
|
"adal-angular": "1.0.15",
|
||||||
"babel-jest": "24.9.0",
|
"babel-jest": "24.9.0",
|
||||||
"babel-loader": "8.1.0",
|
"babel-loader": "8.1.0",
|
||||||
@@ -123,9 +126,10 @@
|
|||||||
"enzyme": "3.10.0",
|
"enzyme": "3.10.0",
|
||||||
"enzyme-adapter-react-16": "1.15.1",
|
"enzyme-adapter-react-16": "1.15.1",
|
||||||
"enzyme-to-json": "3.4.3",
|
"enzyme-to-json": "3.4.3",
|
||||||
"eslint": "6.8.0",
|
"eslint": "7.3.1",
|
||||||
"eslint-cli": "1.1.1",
|
"eslint-cli": "1.1.1",
|
||||||
"eslint-plugin-react": "7.19.0",
|
"eslint-plugin-no-null": "1.0.2",
|
||||||
|
"eslint-plugin-react": "7.20.0",
|
||||||
"expose-loader": "0.7.5",
|
"expose-loader": "0.7.5",
|
||||||
"file-loader": "2.0.0",
|
"file-loader": "2.0.0",
|
||||||
"fs-extra": "7.0.0",
|
"fs-extra": "7.0.0",
|
||||||
@@ -133,29 +137,31 @@
|
|||||||
"html-loader-jest": "0.2.1",
|
"html-loader-jest": "0.2.1",
|
||||||
"html-webpack-plugin": "3.2.0",
|
"html-webpack-plugin": "3.2.0",
|
||||||
"inline-css": "2.2.5",
|
"inline-css": "2.2.5",
|
||||||
"jest": "24.9.0",
|
"jest": "25.5.4",
|
||||||
"jest-canvas-mock": "2.1.0",
|
"jest-canvas-mock": "2.1.0",
|
||||||
|
"jest-puppeteer": "4.4.0",
|
||||||
"jest-trx-results-processor": "0.0.7",
|
"jest-trx-results-processor": "0.0.7",
|
||||||
"less": "3.8.1",
|
"less": "3.8.1",
|
||||||
"less-loader": "4.1.0",
|
"less-loader": "4.1.0",
|
||||||
"less-vars-loader": "1.1.0",
|
"less-vars-loader": "1.1.0",
|
||||||
"mini-css-extract-plugin": "0.4.3",
|
"mini-css-extract-plugin": "0.4.3",
|
||||||
"monaco-editor-webpack-plugin": "1.7.0",
|
"monaco-editor-webpack-plugin": "1.7.0",
|
||||||
"prettier": "1.19.1",
|
"prettier": "2.0.5",
|
||||||
|
"puppeteer": "4.0.0",
|
||||||
"raw-loader": "0.5.1",
|
"raw-loader": "0.5.1",
|
||||||
"rimraf": "3.0.0",
|
"rimraf": "3.0.0",
|
||||||
"sinon": "3.2.1",
|
"sinon": "3.2.1",
|
||||||
"style-loader": "0.23.0",
|
"style-loader": "0.23.0",
|
||||||
"terser-webpack-plugin": "2.3.5",
|
"terser-webpack-plugin": "3.0.5",
|
||||||
"ts-loader": "6.2.2",
|
"ts-loader": "6.2.2",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"tslint-microsoft-contrib": "6.0.0",
|
"tslint-microsoft-contrib": "6.0.0",
|
||||||
"typescript": "3.8.3",
|
"typescript": "3.8.3",
|
||||||
"url-loader": "1.1.1",
|
"url-loader": "1.1.1",
|
||||||
"webpack": "4.41.2",
|
"webpack": "4.43.0",
|
||||||
"webpack-bundle-analyzer": "3.6.1",
|
"webpack-bundle-analyzer": "3.6.1",
|
||||||
"webpack-cli": "3.3.10",
|
"webpack-cli": "3.3.10",
|
||||||
"webpack-dev-server": "3.9.0",
|
"webpack-dev-server": "3.11.0",
|
||||||
"worker-loader": "2.0.0"
|
"worker-loader": "2.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -168,8 +174,8 @@
|
|||||||
"pack:fast": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode development --progress",
|
"pack:fast": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode development --progress",
|
||||||
"copyToConsumers": "node copyToConsumers",
|
"copyToConsumers": "node copyToConsumers",
|
||||||
"test": "rimraf coverage && jest",
|
"test": "rimraf coverage && jest",
|
||||||
|
"test:e2e": "jest -c ./jest.config.e2e.js --detectOpenHandles",
|
||||||
"watch": "npm run start",
|
"watch": "npm run start",
|
||||||
"integrationTest": "runIntegrationTests.cmd",
|
|
||||||
"build:ase": "gulp build:ase",
|
"build:ase": "gulp build:ase",
|
||||||
"compile": "tsc",
|
"compile": "tsc",
|
||||||
"compile:contracts": "tsc -p ./tsconfig.contracts.json",
|
"compile:contracts": "tsc -p ./tsconfig.contracts.json",
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
@echo off
|
|
||||||
|
|
||||||
@for /f "delims=" %%P in ('npm prefix -g') do set "NPM_PREFIX=%%P"
|
|
||||||
@echo npm prefix = %NPM_PREFIX%
|
|
||||||
@echo Compiling TypeScript Test Sources ...
|
|
||||||
call %NPM_PREFIX%\tsc -p ./test
|
|
||||||
if %errorlevel% neq 0 goto end
|
|
||||||
|
|
||||||
copy .\test\Integration\TestRunner.html .\test\out\test\Integration /y >nul 2>&1
|
|
||||||
|
|
||||||
@echo Copying files for test simulation against Emulator ...
|
|
||||||
rmdir "%ProgramFiles%\Azure Cosmos DB Emulator\Packages\DataExplorer\test" >nul 2>&1
|
|
||||||
mkdir "%ProgramFiles%\Azure Cosmos DB Emulator\Packages\DataExplorer\test" >nul 2>&1
|
|
||||||
|
|
||||||
xcopy .\node_modules\jasmine-core\lib .\test\out\lib /s /c /i /r /y >nul 2>&1
|
|
||||||
xcopy .\node_modules\jasmine-core\images .\test\out\lib\images /s /c /i /r /y >nul 2>&1
|
|
||||||
xcopy .\test\out "%ProgramFiles%\Azure Cosmos DB Emulator\Packages\DataExplorer\test" /s /c /i /r /y >nul 2>&1
|
|
||||||
|
|
||||||
@echo Initiating test runner ...
|
|
||||||
start https://localhost:8081/_explorer/test/test/Integration/TestRunner.html
|
|
||||||
@echo Done!
|
|
||||||
|
|
||||||
:end
|
|
||||||
@echo on
|
|
||||||
@@ -2,5 +2,5 @@ export enum AuthType {
|
|||||||
AAD = "aad",
|
AAD = "aad",
|
||||||
EncryptedToken = "encryptedtoken",
|
EncryptedToken = "encryptedtoken",
|
||||||
MasterKey = "masterkey",
|
MasterKey = "masterkey",
|
||||||
ResourceToken = "resourcetoken"
|
ResourceToken = "resourcetoken",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class BindingHandlersRegisterer {
|
|||||||
) {
|
) {
|
||||||
const value = ko.unwrap(wrappedValueAccessor());
|
const value = ko.unwrap(wrappedValueAccessor());
|
||||||
bindingContext.$data.isTemplateReady(value);
|
bindingContext.$data.isTemplateReady(value);
|
||||||
}
|
},
|
||||||
} as ko.BindingHandler;
|
} as ko.BindingHandler;
|
||||||
|
|
||||||
ReactBindingHandler.Registerer.register();
|
ReactBindingHandler.Registerer.register();
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export class Registerer {
|
|||||||
|
|
||||||
// Initial rendering at mount point
|
// Initial rendering at mount point
|
||||||
ReactDOM.render(adapter.renderComponent(), element);
|
ReactDOM.render(adapter.renderComponent(), element);
|
||||||
}
|
},
|
||||||
} as ko.BindingHandler;
|
} as ko.BindingHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class ArrayHashMap<T> {
|
|||||||
public forEach(key: string, iteratorFct: (value: T) => void) {
|
public forEach(key: string, iteratorFct: (value: T) => void) {
|
||||||
const values = this.store.get(key);
|
const values = this.store.get(key);
|
||||||
if (values) {
|
if (values) {
|
||||||
values.forEach(value => iteratorFct(value));
|
values.forEach((value) => iteratorFct(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,29 +104,25 @@ export class CapabilityNames {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Features {
|
export class Features {
|
||||||
public static readonly graphs = "graphs";
|
|
||||||
public static readonly cosmosdb = "cosmosdb";
|
public static readonly cosmosdb = "cosmosdb";
|
||||||
public static readonly enableChangeFeedPolicy = "enablechangefeedpolicy";
|
public static readonly enableChangeFeedPolicy = "enablechangefeedpolicy";
|
||||||
public static readonly enableRupm = "enablerupm";
|
public static readonly enableRupm = "enablerupm";
|
||||||
public static readonly cacheOptimizations = "dataexplorercacheoptimizations";
|
|
||||||
public static readonly executeSproc = "dataexplorerexecutesproc";
|
public static readonly executeSproc = "dataexplorerexecutesproc";
|
||||||
public static readonly hostedDataExplorer = "hosteddataexplorerenabled";
|
public static readonly hostedDataExplorer = "hosteddataexplorerenabled";
|
||||||
public static readonly enableTtl = "enablettl";
|
public static readonly enableTtl = "enablettl";
|
||||||
public static readonly enableNotebooks = "enablenotebooks";
|
public static readonly enableNotebooks = "enablenotebooks";
|
||||||
public static readonly enableGallery = "enablegallery";
|
public static readonly enableGallery = "enablegallery";
|
||||||
|
public static readonly enableGalleryPublish = "enablegallerypublish";
|
||||||
public static readonly enableSpark = "enablespark";
|
public static readonly enableSpark = "enablespark";
|
||||||
public static readonly livyEndpoint = "livyendpoint";
|
public static readonly livyEndpoint = "livyendpoint";
|
||||||
public static readonly settingsPane = "dataexplorersettingspane";
|
|
||||||
public static readonly throughputOverview = "throughputOverview";
|
|
||||||
public static readonly enableNteract = "enablenteract";
|
|
||||||
public static readonly notebookServerUrl = "notebookserverurl";
|
public static readonly notebookServerUrl = "notebookserverurl";
|
||||||
public static readonly notebookServerToken = "notebookservertoken";
|
public static readonly notebookServerToken = "notebookservertoken";
|
||||||
public static readonly notebookBasePath = "notebookbasepath";
|
public static readonly notebookBasePath = "notebookbasepath";
|
||||||
public static readonly enableLegacyResourceTree = "enablelegacyresourcetree";
|
|
||||||
public static readonly canExceedMaximumValue = "canexceedmaximumvalue";
|
public static readonly canExceedMaximumValue = "canexceedmaximumvalue";
|
||||||
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
|
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
|
||||||
public static readonly enableAutoPilotV2 = "enableautopilotv2";
|
public static readonly enableAutoPilotV2 = "enableautopilotv2";
|
||||||
public static readonly ttl90Days = "ttl90days";
|
public static readonly ttl90Days = "ttl90days";
|
||||||
|
public static readonly enableRightPanelV2 = "enablerightpanelv2";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AfecFeatures {
|
export class AfecFeatures {
|
||||||
@@ -144,7 +140,7 @@ export class Spark {
|
|||||||
"Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM",
|
"Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM",
|
||||||
"Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM",
|
"Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM",
|
||||||
"Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM",
|
"Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM",
|
||||||
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM"
|
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +155,7 @@ export class MongoDBAccounts {
|
|||||||
|
|
||||||
export enum MongoBackendEndpointType {
|
export enum MongoBackendEndpointType {
|
||||||
local,
|
local,
|
||||||
remote
|
remote,
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MongoBackend {
|
export class MongoBackend {
|
||||||
@@ -184,14 +180,14 @@ export class MongoBackend {
|
|||||||
koreasouth: MongoBackend.southEastAsiaEndpoint,
|
koreasouth: MongoBackend.southEastAsiaEndpoint,
|
||||||
southeastasia: MongoBackend.southEastAsiaEndpoint,
|
southeastasia: MongoBackend.southEastAsiaEndpoint,
|
||||||
southindia: MongoBackend.southEastAsiaEndpoint,
|
southindia: MongoBackend.southEastAsiaEndpoint,
|
||||||
westindia: MongoBackend.southEastAsiaEndpoint
|
westindia: MongoBackend.southEastAsiaEndpoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static endpointsByEnvironment: any = {
|
public static endpointsByEnvironment: any = {
|
||||||
default: MongoBackendEndpointType.local,
|
default: MongoBackendEndpointType.local,
|
||||||
localhost: MongoBackendEndpointType.local,
|
localhost: MongoBackendEndpointType.local,
|
||||||
prod1: MongoBackendEndpointType.remote,
|
prod1: MongoBackendEndpointType.remote,
|
||||||
prod2: MongoBackendEndpointType.remote
|
prod2: MongoBackendEndpointType.remote,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +238,7 @@ export class CassandraBackend {
|
|||||||
chinaeast: CassandraBackend.mc_default,
|
chinaeast: CassandraBackend.mc_default,
|
||||||
chinaeast2: CassandraBackend.mc_default,
|
chinaeast2: CassandraBackend.mc_default,
|
||||||
chinanorth: CassandraBackend.mc_default,
|
chinanorth: CassandraBackend.mc_default,
|
||||||
chinanorth2: CassandraBackend.mc_default
|
chinanorth2: CassandraBackend.mc_default,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
||||||
@@ -372,7 +368,7 @@ export class HttpStatusCodes {
|
|||||||
HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
|
HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
|
||||||
HttpStatusCodes.BadGateway,
|
HttpStatusCodes.BadGateway,
|
||||||
HttpStatusCodes.ServiceUnavailable,
|
HttpStatusCodes.ServiceUnavailable,
|
||||||
HttpStatusCodes.GatewayTimeout
|
HttpStatusCodes.GatewayTimeout,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,10 +422,7 @@ export class HashRoutePrefixes {
|
|||||||
public static docsWithIds(databaseId: string, collectionId: string, docId: string) {
|
public static docsWithIds(databaseId: string, collectionId: string, docId: string) {
|
||||||
const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId);
|
const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId);
|
||||||
|
|
||||||
return transformedDatabasePrefix
|
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("{doc_id}", docId).replace("/", ""); // strip the first slash since hasher adds it
|
||||||
.replace("{coll_id}", collectionId)
|
|
||||||
.replace("{doc_id}", docId)
|
|
||||||
.replace("/", ""); // strip the first slash since hasher adds it
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,7 +457,7 @@ export class OfferVersions {
|
|||||||
export enum ConflictOperationType {
|
export enum ConflictOperationType {
|
||||||
Replace = "replace",
|
Replace = "replace",
|
||||||
Create = "create",
|
Create = "create",
|
||||||
Delete = "delete"
|
Delete = "delete",
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AutoPilot {
|
export class AutoPilot {
|
||||||
@@ -477,28 +470,28 @@ export class AutoPilot {
|
|||||||
[AutopilotTier.Tier1]: "Tier 1",
|
[AutopilotTier.Tier1]: "Tier 1",
|
||||||
[AutopilotTier.Tier2]: "Tier 2",
|
[AutopilotTier.Tier2]: "Tier 2",
|
||||||
[AutopilotTier.Tier3]: "Tier 3",
|
[AutopilotTier.Tier3]: "Tier 3",
|
||||||
[AutopilotTier.Tier4]: "Tier 4"
|
[AutopilotTier.Tier4]: "Tier 4",
|
||||||
};
|
};
|
||||||
|
|
||||||
public static tierMaxRus = {
|
public static tierMaxRus = {
|
||||||
[AutopilotTier.Tier1]: 2000,
|
[AutopilotTier.Tier1]: 2000,
|
||||||
[AutopilotTier.Tier2]: 20000,
|
[AutopilotTier.Tier2]: 20000,
|
||||||
[AutopilotTier.Tier3]: 100000,
|
[AutopilotTier.Tier3]: 100000,
|
||||||
[AutopilotTier.Tier4]: 500000
|
[AutopilotTier.Tier4]: 500000,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static tierMinRus = {
|
public static tierMinRus = {
|
||||||
[AutopilotTier.Tier1]: 0,
|
[AutopilotTier.Tier1]: 0,
|
||||||
[AutopilotTier.Tier2]: 0,
|
[AutopilotTier.Tier2]: 0,
|
||||||
[AutopilotTier.Tier3]: 0,
|
[AutopilotTier.Tier3]: 0,
|
||||||
[AutopilotTier.Tier4]: 0
|
[AutopilotTier.Tier4]: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static tierStorageInGB = {
|
public static tierStorageInGB = {
|
||||||
[AutopilotTier.Tier1]: 50,
|
[AutopilotTier.Tier1]: 50,
|
||||||
[AutopilotTier.Tier2]: 200,
|
[AutopilotTier.Tier2]: 200,
|
||||||
[AutopilotTier.Tier3]: 1000,
|
[AutopilotTier.Tier3]: 1000,
|
||||||
[AutopilotTier.Tier4]: 5000
|
[AutopilotTier.Tier4]: 5000,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,7 +505,7 @@ export class DataExplorerFeatures {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DataExplorerFeaturesVersions: any = {
|
export const DataExplorerFeaturesVersions: any = {
|
||||||
OfferCache: DataExplorerVersions.v_1_0_1
|
OfferCache: DataExplorerVersions.v_1_0_1,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EmulatorMasterKey =
|
export const EmulatorMasterKey =
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ describe("tokenProvider", () => {
|
|||||||
resourceId: "",
|
resourceId: "",
|
||||||
resourceType: "dbs" as ResourceType,
|
resourceType: "dbs" as ResourceType,
|
||||||
headers: {},
|
headers: {},
|
||||||
getAuthorizationTokenUsingMasterKey: () => ""
|
getAuthorizationTokenUsingMasterKey: () => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -17,7 +17,7 @@ describe("tokenProvider", () => {
|
|||||||
window.fetch = jest.fn().mockImplementation(() => {
|
window.fetch = jest.fn().mockImplementation(() => {
|
||||||
return {
|
return {
|
||||||
json: () => "{}",
|
json: () => "{}",
|
||||||
headers: new Map()
|
headers: new Map(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -45,7 +45,7 @@ describe("getTokenFromAuthService", () => {
|
|||||||
window.fetch = jest.fn().mockImplementation(() => {
|
window.fetch = jest.fn().mockImplementation(() => {
|
||||||
return {
|
return {
|
||||||
json: () => "{}",
|
json: () => "{}",
|
||||||
headers: new Map()
|
headers: new Map(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -86,8 +86,8 @@ describe("endpoint", () => {
|
|||||||
documentEndpoint: "bar",
|
documentEndpoint: "bar",
|
||||||
gremlinEndpoint: "foo",
|
gremlinEndpoint: "foo",
|
||||||
tableEndpoint: "foo",
|
tableEndpoint: "foo",
|
||||||
cassandraEndpoint: "foo"
|
cassandraEndpoint: "foo",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
expect(endpoint()).toEqual("bar");
|
expect(endpoint()).toEqual("bar");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -63,13 +63,13 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"x-ms-encrypted-auth-token": _accessToken
|
"x-ms-encrypted-auth-token": _accessToken,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
verb,
|
verb,
|
||||||
resourceType,
|
resourceType,
|
||||||
resourceId
|
resourceId,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
//TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json()
|
//TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json()
|
||||||
const result = JSON.parse(await response.json());
|
const result = JSON.parse(await response.json());
|
||||||
@@ -93,9 +93,9 @@ export const CosmosClient = {
|
|||||||
key: _masterKey,
|
key: _masterKey,
|
||||||
tokenProvider,
|
tokenProvider,
|
||||||
connectionPolicy: {
|
connectionPolicy: {
|
||||||
enableEndpointDiscovery: false
|
enableEndpointDiscovery: false,
|
||||||
},
|
},
|
||||||
userAgentSuffix: "Azure Portal"
|
userAgentSuffix: "Azure Portal",
|
||||||
};
|
};
|
||||||
|
|
||||||
// In development we proxy requests to the backend via webpack. This is removed in production bundles.
|
// In development we proxy requests to the backend via webpack. This is removed in production bundles.
|
||||||
@@ -176,5 +176,5 @@ export const CosmosClient = {
|
|||||||
_client = null;
|
_client = null;
|
||||||
_resourceToken = value;
|
_resourceToken = value;
|
||||||
return value;
|
return value;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
PartitionKeyDefinition,
|
PartitionKeyDefinition,
|
||||||
QueryIterator,
|
QueryIterator,
|
||||||
Resource,
|
Resource,
|
||||||
TriggerDefinition
|
TriggerDefinition,
|
||||||
} from "@azure/cosmos";
|
} from "@azure/cosmos";
|
||||||
import { ContainerRequest } from "@azure/cosmos/dist-esm/client/Container/ContainerRequest";
|
import { ContainerRequest } from "@azure/cosmos/dist-esm/client/Container/ContainerRequest";
|
||||||
import { CosmosClient } from "./CosmosClient";
|
import { CosmosClient } from "./CosmosClient";
|
||||||
@@ -68,7 +68,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.storedProcedures.readAll(options)
|
.scripts.storedProcedures.readAll(options)
|
||||||
.fetchAll()
|
.fetchAll()
|
||||||
.then(response => response.resources as DataModels.StoredProcedure[])
|
.then((response) => response.resources as DataModels.StoredProcedure[])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.storedProcedure(requestedResource.id)
|
.scripts.storedProcedure(requestedResource.id)
|
||||||
.read(options)
|
.read(options)
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
.then((response) => response.resource as DataModels.StoredProcedure)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public readUserDefinedFunctions(
|
public readUserDefinedFunctions(
|
||||||
@@ -96,7 +96,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.userDefinedFunctions.readAll(options)
|
.scripts.userDefinedFunctions.readAll(options)
|
||||||
.fetchAll()
|
.fetchAll()
|
||||||
.then(response => response.resources as DataModels.UserDefinedFunction[])
|
.then((response) => response.resources as DataModels.UserDefinedFunction[])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public readUserDefinedFunction(
|
public readUserDefinedFunction(
|
||||||
@@ -110,7 +110,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.userDefinedFunction(requestedResource.id)
|
.scripts.userDefinedFunction(requestedResource.id)
|
||||||
.read(options)
|
.read(options)
|
||||||
.then(response => response.resource as DataModels.UserDefinedFunction)
|
.then((response) => response.resource as DataModels.UserDefinedFunction)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.triggers.readAll(options)
|
.scripts.triggers.readAll(options)
|
||||||
.fetchAll()
|
.fetchAll()
|
||||||
.then(response => response.resources as DataModels.Trigger[])
|
.then((response) => response.resources as DataModels.Trigger[])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.trigger(requestedResource.id)
|
.scripts.trigger(requestedResource.id)
|
||||||
.read(options)
|
.read(options)
|
||||||
.then(response => response.resource as DataModels.Trigger)
|
.then((response) => response.resource as DataModels.Trigger)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,13 +154,13 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.storedProcedure(storedProcedure.id())
|
.scripts.storedProcedure(storedProcedure.id())
|
||||||
.execute(partitionKeyValue, params, { enableScriptLogging: true })
|
.execute(partitionKeyValue, params, { enableScriptLogging: true })
|
||||||
.then(response =>
|
.then((response) =>
|
||||||
deferred.resolve({
|
deferred.resolve({
|
||||||
result: response.resource,
|
result: response.resource,
|
||||||
scriptLogs: response.headers[Constants.HttpHeaders.scriptLogResults]
|
scriptLogs: response.headers[Constants.HttpHeaders.scriptLogResults],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.catch(error => deferred.reject(error));
|
.catch((error) => deferred.reject(error));
|
||||||
|
|
||||||
return deferred.promise.timeout(
|
return deferred.promise.timeout(
|
||||||
Constants.ClientDefaults.requestTimeoutMs,
|
Constants.ClientDefaults.requestTimeoutMs,
|
||||||
@@ -177,7 +177,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.item(documentId.id(), partitionKey)
|
.item(documentId.id(), partitionKey)
|
||||||
.read()
|
.read()
|
||||||
.then(response => response.resource)
|
.then((response) => response.resource)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.item(documentId.id(), partitionKey)
|
.item(documentId.id(), partitionKey)
|
||||||
.replace(newDocument)
|
.replace(newDocument)
|
||||||
.then(response => response.resource)
|
.then((response) => response.resource)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
CosmosClient.client()
|
CosmosClient.client()
|
||||||
.offer(offer.id)
|
.offer(offer.id)
|
||||||
.replace(newOffer, options)
|
.replace(newOffer, options)
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
return Promise.all([this.refreshCachedOffers(), this.refreshCachedResources()]).then(() => response.resource);
|
return Promise.all([this.refreshCachedOffers(), this.refreshCachedResources()]).then(() => response.resource);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -260,7 +260,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.storedProcedure(storedProcedure.id)
|
.scripts.storedProcedure(storedProcedure.id)
|
||||||
.replace(storedProcedure, options)
|
.replace(storedProcedure, options)
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
.then((response) => response.resource as DataModels.StoredProcedure)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +275,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.userDefinedFunction(userDefinedFunction.id)
|
.scripts.userDefinedFunction(userDefinedFunction.id)
|
||||||
.replace(userDefinedFunction, options)
|
.replace(userDefinedFunction, options)
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
.then((response) => response.resource as DataModels.StoredProcedure)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +290,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.trigger(trigger.id)
|
.scripts.trigger(trigger.id)
|
||||||
.replace(trigger as TriggerDefinition, options)
|
.replace(trigger as TriggerDefinition, options)
|
||||||
.then(response => response.resource as DataModels.Trigger)
|
.then((response) => response.resource as DataModels.Trigger)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +300,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.database(collection.databaseId)
|
.database(collection.databaseId)
|
||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.items.create(newDocument)
|
.items.create(newDocument)
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
.then((response) => response.resource as DataModels.StoredProcedure)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +314,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.database(collection.databaseId)
|
.database(collection.databaseId)
|
||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.storedProcedures.create(newStoredProcedure, options)
|
.scripts.storedProcedures.create(newStoredProcedure, options)
|
||||||
.then(response => response.resource as DataModels.StoredProcedure)
|
.then((response) => response.resource as DataModels.StoredProcedure)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +328,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.database(collection.databaseId)
|
.database(collection.databaseId)
|
||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.userDefinedFunctions.create(newUserDefinedFunction, options)
|
.scripts.userDefinedFunctions.create(newUserDefinedFunction, options)
|
||||||
.then(response => response.resource as DataModels.UserDefinedFunction)
|
.then((response) => response.resource as DataModels.UserDefinedFunction)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +342,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.database(collection.databaseId)
|
.database(collection.databaseId)
|
||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.scripts.triggers.create(newTrigger as TriggerDefinition, options)
|
.scripts.triggers.create(newTrigger as TriggerDefinition, options)
|
||||||
.then(response => response.resource as DataModels.Trigger)
|
.then((response) => response.resource as DataModels.Trigger)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +437,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.database(database.id())
|
.database(database.id())
|
||||||
.containers.readAll()
|
.containers.readAll()
|
||||||
.fetchAll()
|
.fetchAll()
|
||||||
.then(response => response.resources as DataModels.Collection[])
|
.then((response) => response.resources as DataModels.Collection[])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +447,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
.database(databaseId)
|
.database(databaseId)
|
||||||
.container(collectionId)
|
.container(collectionId)
|
||||||
.read()
|
.read()
|
||||||
.then(response => response.resource)
|
.then((response) => response.resource)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,7 +491,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
CosmosClient.client()
|
CosmosClient.client()
|
||||||
.offers.readAll()
|
.offers.readAll()
|
||||||
.fetchAll()
|
.fetchAll()
|
||||||
.then(response => response.resources)
|
.then((response) => response.resources)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,7 +506,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
CosmosClient.client()
|
CosmosClient.client()
|
||||||
.offer(requestedResource.id)
|
.offer(requestedResource.id)
|
||||||
.read(options)
|
.read(options)
|
||||||
.then(response => ({ ...response.resource, headers: response.headers }))
|
.then((response) => ({ ...response.resource, headers: response.headers }))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,7 +515,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
CosmosClient.client()
|
CosmosClient.client()
|
||||||
.databases.readAll()
|
.databases.readAll()
|
||||||
.fetchAll()
|
.fetchAll()
|
||||||
.then(response => response.resources as DataModels.Database[])
|
.then((response) => response.resources as DataModels.Database[])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,11 +533,11 @@ export abstract class DataAccessUtilityBase {
|
|||||||
uniqueKeyPolicy,
|
uniqueKeyPolicy,
|
||||||
offerThroughput,
|
offerThroughput,
|
||||||
analyticalStorageTtl,
|
analyticalStorageTtl,
|
||||||
hasAutoPilotV2FeatureFlag
|
hasAutoPilotV2FeatureFlag,
|
||||||
} = request;
|
} = request;
|
||||||
|
|
||||||
const createBody: DatabaseRequest = {
|
const createBody: DatabaseRequest = {
|
||||||
id: databaseId
|
id: databaseId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: replace when SDK support autopilot
|
// TODO: replace when SDK support autopilot
|
||||||
@@ -545,11 +545,11 @@ export abstract class DataAccessUtilityBase {
|
|||||||
? !hasAutoPilotV2FeatureFlag
|
? !hasAutoPilotV2FeatureFlag
|
||||||
? {
|
? {
|
||||||
[Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({
|
[Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({
|
||||||
maxThroughput: request.autoPilot.maxThroughput
|
maxThroughput: request.autoPilot.maxThroughput,
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
[Constants.HttpHeaders.autoPilotTier]: request.autoPilot.autopilotTier
|
[Constants.HttpHeaders.autoPilotTier]: request.autoPilot.autopilotTier,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
if (databaseLevelThroughput) {
|
if (databaseLevelThroughput) {
|
||||||
@@ -562,7 +562,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
return Q(
|
return Q(
|
||||||
CosmosClient.client()
|
CosmosClient.client()
|
||||||
.databases.createIfNotExists(createBody, databaseOptions)
|
.databases.createIfNotExists(createBody, databaseOptions)
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
return response.database.containers.create(
|
return response.database.containers.create(
|
||||||
{
|
{
|
||||||
id: collectionId,
|
id: collectionId,
|
||||||
@@ -570,14 +570,14 @@ export abstract class DataAccessUtilityBase {
|
|||||||
indexingPolicy: indexingPolicy ? indexingPolicy : undefined,
|
indexingPolicy: indexingPolicy ? indexingPolicy : undefined,
|
||||||
uniqueKeyPolicy: uniqueKeyPolicy ? uniqueKeyPolicy : undefined,
|
uniqueKeyPolicy: uniqueKeyPolicy ? uniqueKeyPolicy : undefined,
|
||||||
analyticalStorageTtl: analyticalStorageTtl,
|
analyticalStorageTtl: analyticalStorageTtl,
|
||||||
throughput: databaseLevelThroughput || request.autoPilot ? undefined : offerThroughput
|
throughput: databaseLevelThroughput || request.autoPilot ? undefined : offerThroughput,
|
||||||
} as ContainerRequest, // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
|
} as ContainerRequest, // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
|
||||||
{
|
{
|
||||||
initialHeaders: databaseLevelThroughput ? undefined : initialHeaders
|
initialHeaders: databaseLevelThroughput ? undefined : initialHeaders,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.then(containerResponse => containerResponse.resource)
|
.then((containerResponse) => containerResponse.resource)
|
||||||
.finally(() => this.refreshCachedResources(options))
|
.finally(() => this.refreshCachedResources(options))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -591,7 +591,7 @@ export abstract class DataAccessUtilityBase {
|
|||||||
deferred.resolve(createdDatabase);
|
deferred.resolve(createdDatabase);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
_createDatabaseError => {
|
(_createDatabaseError) => {
|
||||||
deferred.reject(_createDatabaseError);
|
deferred.reject(_createDatabaseError);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -654,10 +654,10 @@ export abstract class DataAccessUtilityBase {
|
|||||||
const initialHeaders = autoPilot
|
const initialHeaders = autoPilot
|
||||||
? !hasAutoPilotV2FeatureFlag
|
? !hasAutoPilotV2FeatureFlag
|
||||||
? {
|
? {
|
||||||
[Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({ maxThroughput: autoPilot.maxThroughput })
|
[Constants.HttpHeaders.autoPilotThroughputSDK]: JSON.stringify({ maxThroughput: autoPilot.maxThroughput }),
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
[Constants.HttpHeaders.autoPilotTier]: autoPilot.autopilotTier
|
[Constants.HttpHeaders.autoPilotTier]: autoPilot.autopilotTier,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
if (!!databaseLevelThroughput) {
|
if (!!databaseLevelThroughput) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Q from "q";
|
|||||||
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { DataAccessUtilityBase } from "./DataAccessUtilityBase";
|
import { DataAccessUtilityBase } from "./DataAccessUtilityBase";
|
||||||
import { Logger } from "./Logger";
|
import * as Logger from "./Logger";
|
||||||
import { MessageHandler } from "./MessageHandler";
|
import { MessageHandler } from "./MessageHandler";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
import { MinimalQueryIterator, nextPage } from "./IteratorUtilities";
|
import { MinimalQueryIterator, nextPage } from "./IteratorUtilities";
|
||||||
@@ -376,7 +376,7 @@ export default class DocumentClientUtilityBase {
|
|||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
oldOffer: offer,
|
oldOffer: offer,
|
||||||
newOffer: newOffer,
|
newOffer: newOffer,
|
||||||
error: error
|
error: error,
|
||||||
}),
|
}),
|
||||||
"UpdateOffer",
|
"UpdateOffer",
|
||||||
error.code
|
error.code
|
||||||
@@ -583,7 +583,7 @@ export default class DocumentClientUtilityBase {
|
|||||||
);
|
);
|
||||||
deferred.resolve(createdStoredProcedure);
|
deferred.resolve(createdStoredProcedure);
|
||||||
},
|
},
|
||||||
error => {
|
(error) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
`Error while creating stored procedure for container ${collection.id()}:\n ${JSON.stringify(error)}`
|
`Error while creating stored procedure for container ${collection.id()}:\n ${JSON.stringify(error)}`
|
||||||
@@ -620,7 +620,7 @@ export default class DocumentClientUtilityBase {
|
|||||||
);
|
);
|
||||||
deferred.resolve(createdUserDefinedFunction);
|
deferred.resolve(createdUserDefinedFunction);
|
||||||
},
|
},
|
||||||
error => {
|
(error) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
`Error while creating user defined function for container ${collection.id()}:\n ${JSON.stringify(error)}`
|
`Error while creating user defined function for container ${collection.id()}:\n ${JSON.stringify(error)}`
|
||||||
@@ -1176,7 +1176,7 @@ export default class DocumentClientUtilityBase {
|
|||||||
}
|
}
|
||||||
MessageHandler.sendMessage({
|
MessageHandler.sendMessage({
|
||||||
type: MessageTypes.ForbiddenError,
|
type: MessageTypes.ForbiddenError,
|
||||||
reason: error && error.message ? error.message : error
|
reason: error && error.message ? error.message : error,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default class EditableUtility {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
observable.subscribe(edit => {
|
observable.subscribe((edit) => {
|
||||||
var edits = observable.edits && observable.edits();
|
var edits = observable.edits && observable.edits();
|
||||||
if (!edits) {
|
if (!edits) {
|
||||||
return;
|
return;
|
||||||
@@ -83,9 +83,9 @@ export default class EditableUtility {
|
|||||||
});
|
});
|
||||||
|
|
||||||
observable.editableIsValid = ko.observable<boolean>(true);
|
observable.editableIsValid = ko.observable<boolean>(true);
|
||||||
observable.subscribe(value => {
|
observable.subscribe((value) => {
|
||||||
const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || [];
|
const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || [];
|
||||||
const isValid = validations.every(validate => validate(value));
|
const isValid = validations.every((validate) => validate(value));
|
||||||
observable.editableIsValid(isValid);
|
observable.editableIsValid(isValid);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ describe("Error Parser Utility", () => {
|
|||||||
err.code = 400;
|
err.code = 400;
|
||||||
err.body = {
|
err.body = {
|
||||||
code: "BadRequest",
|
code: "BadRequest",
|
||||||
message
|
message,
|
||||||
};
|
};
|
||||||
err.headers = {};
|
err.headers = {};
|
||||||
err.activityId = "97b2e684-7505-4921-85f6-2513b9b28220";
|
err.activityId = "97b2e684-7505-4921-85f6-2513b9b28220";
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function _parse(err: any): DataModels.ErrorDataModel[] {
|
|||||||
normalizedErrors.push(err);
|
normalizedErrors.push(err);
|
||||||
} else {
|
} else {
|
||||||
const innerErrors: any[] = _getInnerErrors(err.message);
|
const innerErrors: any[] = _getInnerErrors(err.message);
|
||||||
normalizedErrors = innerErrors.map(innerError =>
|
normalizedErrors = innerErrors.map((innerError) =>
|
||||||
typeof innerError === "string" ? { message: innerError } : innerError
|
typeof innerError === "string" ? { message: innerError } : innerError
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ describe("nextPage", () => {
|
|||||||
queryMetrics: {},
|
queryMetrics: {},
|
||||||
requestCharge: 1,
|
requestCharge: 1,
|
||||||
headers: {},
|
headers: {},
|
||||||
activityId: "foo"
|
activityId: "foo",
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(await nextPage(fakeIterator, 10)).toMatchSnapshot();
|
expect(await nextPage(fakeIterator, 10)).toMatchSnapshot();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export interface MinimalQueryIterator {
|
|||||||
// Pick<QueryIterator<any>, "fetchNext">;
|
// Pick<QueryIterator<any>, "fetchNext">;
|
||||||
|
|
||||||
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
|
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
|
||||||
return documentsIterator.fetchNext().then(response => {
|
return documentsIterator.fetchNext().then((response) => {
|
||||||
const documents = response.resources;
|
const documents = response.resources;
|
||||||
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
|
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
|
||||||
const itemCount = (documents && documents.length) || 0;
|
const itemCount = (documents && documents.length) || 0;
|
||||||
@@ -26,7 +26,7 @@ export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex
|
|||||||
lastItemIndex: Number(firstItemIndex) + Number(itemCount),
|
lastItemIndex: Number(firstItemIndex) + Number(itemCount),
|
||||||
headers,
|
headers,
|
||||||
activityId: response.activityId,
|
activityId: response.activityId,
|
||||||
requestCharge: response.requestCharge
|
requestCharge: response.requestCharge,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { LogEntryLevel } from "../Contracts/Diagnostics";
|
import { LogEntryLevel } from "../Contracts/Diagnostics";
|
||||||
import { Logger } from "./Logger";
|
import * as Logger from "./Logger";
|
||||||
import { MessageHandler } from "./MessageHandler";
|
import { MessageHandler } from "./MessageHandler";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
|
|
||||||
|
|||||||
@@ -4,84 +4,68 @@ import { appInsights } from "../Shared/appInsights";
|
|||||||
import { SeverityLevel } from "@microsoft/applicationinsights-web";
|
import { SeverityLevel } from "@microsoft/applicationinsights-web";
|
||||||
|
|
||||||
// TODO: Move to a separate Diagnostics folder
|
// TODO: Move to a separate Diagnostics folder
|
||||||
export class Logger {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
public static logInfo(message: string | Record<string, any>, area: string, code?: number): void {
|
export function logInfo(message: string | Record<string, any>, area: string, code?: number): void {
|
||||||
let logMessage: string;
|
let logMessage: string;
|
||||||
if (typeof message === "string") {
|
if (typeof message === "string") {
|
||||||
logMessage = message;
|
logMessage = message;
|
||||||
} else {
|
} else {
|
||||||
logMessage = JSON.stringify(message, Object.getOwnPropertyNames(message));
|
logMessage = JSON.stringify(message, Object.getOwnPropertyNames(message));
|
||||||
}
|
|
||||||
const entry: Diagnostics.LogEntry = Logger._generateLogEntry(
|
|
||||||
Diagnostics.LogEntryLevel.Verbose,
|
|
||||||
logMessage,
|
|
||||||
area,
|
|
||||||
code
|
|
||||||
);
|
|
||||||
return Logger._logEntry(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static logWarning(message: string, area: string, code?: number): void {
|
|
||||||
const entry: Diagnostics.LogEntry = Logger._generateLogEntry(
|
|
||||||
Diagnostics.LogEntryLevel.Warning,
|
|
||||||
message,
|
|
||||||
area,
|
|
||||||
code
|
|
||||||
);
|
|
||||||
return Logger._logEntry(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static logError(message: string | Error, area: string, code?: number): void {
|
|
||||||
let logMessage: string;
|
|
||||||
if (typeof message === "string") {
|
|
||||||
logMessage = message;
|
|
||||||
} else {
|
|
||||||
logMessage = JSON.stringify(message, Object.getOwnPropertyNames(message));
|
|
||||||
}
|
|
||||||
const entry: Diagnostics.LogEntry = Logger._generateLogEntry(
|
|
||||||
Diagnostics.LogEntryLevel.Error,
|
|
||||||
logMessage,
|
|
||||||
area,
|
|
||||||
code
|
|
||||||
);
|
|
||||||
return Logger._logEntry(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static _logEntry(entry: Diagnostics.LogEntry): void {
|
|
||||||
MessageHandler.sendMessage({
|
|
||||||
type: MessageTypes.LogInfo,
|
|
||||||
data: JSON.stringify(entry)
|
|
||||||
});
|
|
||||||
|
|
||||||
const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => {
|
|
||||||
switch (level) {
|
|
||||||
case Diagnostics.LogEntryLevel.Custom:
|
|
||||||
case Diagnostics.LogEntryLevel.Debug:
|
|
||||||
case Diagnostics.LogEntryLevel.Verbose:
|
|
||||||
return SeverityLevel.Verbose;
|
|
||||||
case Diagnostics.LogEntryLevel.Warning:
|
|
||||||
return SeverityLevel.Warning;
|
|
||||||
case Diagnostics.LogEntryLevel.Error:
|
|
||||||
return SeverityLevel.Error;
|
|
||||||
default:
|
|
||||||
return SeverityLevel.Information;
|
|
||||||
}
|
|
||||||
})(entry.level);
|
|
||||||
appInsights.trackTrace({ message: entry.message, severityLevel }, { area: entry.area });
|
|
||||||
}
|
|
||||||
|
|
||||||
private static _generateLogEntry(
|
|
||||||
level: Diagnostics.LogEntryLevel,
|
|
||||||
message: string,
|
|
||||||
area: string,
|
|
||||||
code: number
|
|
||||||
): Diagnostics.LogEntry {
|
|
||||||
return {
|
|
||||||
timestamp: new Date().getUTCSeconds(),
|
|
||||||
level: level,
|
|
||||||
message: message,
|
|
||||||
area: area,
|
|
||||||
code: code
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
const entry: Diagnostics.LogEntry = _generateLogEntry(Diagnostics.LogEntryLevel.Verbose, logMessage, area, code);
|
||||||
|
return _logEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logWarning(message: string, area: string, code?: number): void {
|
||||||
|
const entry: Diagnostics.LogEntry = _generateLogEntry(Diagnostics.LogEntryLevel.Warning, message, area, code);
|
||||||
|
return _logEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logError(message: string | Error, area: string, code?: number): void {
|
||||||
|
let logMessage: string;
|
||||||
|
if (typeof message === "string") {
|
||||||
|
logMessage = message;
|
||||||
|
} else {
|
||||||
|
logMessage = JSON.stringify(message, Object.getOwnPropertyNames(message));
|
||||||
|
}
|
||||||
|
const entry: Diagnostics.LogEntry = _generateLogEntry(Diagnostics.LogEntryLevel.Error, logMessage, area, code);
|
||||||
|
return _logEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _logEntry(entry: Diagnostics.LogEntry): void {
|
||||||
|
MessageHandler.sendMessage({
|
||||||
|
type: MessageTypes.LogInfo,
|
||||||
|
data: JSON.stringify(entry),
|
||||||
|
});
|
||||||
|
|
||||||
|
const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => {
|
||||||
|
switch (level) {
|
||||||
|
case Diagnostics.LogEntryLevel.Custom:
|
||||||
|
case Diagnostics.LogEntryLevel.Debug:
|
||||||
|
case Diagnostics.LogEntryLevel.Verbose:
|
||||||
|
return SeverityLevel.Verbose;
|
||||||
|
case Diagnostics.LogEntryLevel.Warning:
|
||||||
|
return SeverityLevel.Warning;
|
||||||
|
case Diagnostics.LogEntryLevel.Error:
|
||||||
|
return SeverityLevel.Error;
|
||||||
|
default:
|
||||||
|
return SeverityLevel.Information;
|
||||||
|
}
|
||||||
|
})(entry.level);
|
||||||
|
appInsights.trackTrace({ message: entry.message, severityLevel }, { area: entry.area });
|
||||||
|
}
|
||||||
|
|
||||||
|
function _generateLogEntry(
|
||||||
|
level: Diagnostics.LogEntryLevel,
|
||||||
|
message: string,
|
||||||
|
area: string,
|
||||||
|
code: number
|
||||||
|
): Diagnostics.LogEntry {
|
||||||
|
return {
|
||||||
|
timestamp: new Date().getUTCSeconds(),
|
||||||
|
level: level,
|
||||||
|
message: message,
|
||||||
|
area: area,
|
||||||
|
code: code,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ describe("Message Handler", () => {
|
|||||||
let mockPromise: CachedDataPromise<any> = {
|
let mockPromise: CachedDataPromise<any> = {
|
||||||
id: "123",
|
id: "123",
|
||||||
startTime: new Date(),
|
startTime: new Date(),
|
||||||
deferred: Q.defer<any>()
|
deferred: Q.defer<any>(),
|
||||||
};
|
};
|
||||||
let mockMessage = { message: { id: "123", data: "{}" } };
|
let mockMessage = { message: { id: "123", data: "{}" } };
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ describe("Message Handler", () => {
|
|||||||
let mockPromise: CachedDataPromise<any> = {
|
let mockPromise: CachedDataPromise<any> = {
|
||||||
id: "123",
|
id: "123",
|
||||||
startTime: new Date(),
|
startTime: new Date(),
|
||||||
deferred: Q.defer<any>()
|
deferred: Q.defer<any>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export class MessageHandler {
|
|||||||
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
|
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
|
||||||
deferred: Q.defer<TResponseDataModel>(),
|
deferred: Q.defer<TResponseDataModel>(),
|
||||||
startTime: new Date(),
|
startTime: new Date(),
|
||||||
id: _.uniqueId()
|
id: _.uniqueId(),
|
||||||
};
|
};
|
||||||
MessageHandler.RequestMap[cachedDataPromise.id] = cachedDataPromise;
|
MessageHandler.RequestMap[cachedDataPromise.id] = cachedDataPromise;
|
||||||
MessageHandler.sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
|
MessageHandler.sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
|
||||||
@@ -63,7 +63,7 @@ export class MessageHandler {
|
|||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
signature: "pcIframe",
|
signature: "pcIframe",
|
||||||
data: data
|
data: data,
|
||||||
},
|
},
|
||||||
window.document.referrer
|
window.document.referrer
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
getEndpoint,
|
getEndpoint,
|
||||||
queryDocuments,
|
queryDocuments,
|
||||||
readDocument,
|
readDocument,
|
||||||
updateDocument
|
updateDocument,
|
||||||
} from "./MongoProxyClient";
|
} from "./MongoProxyClient";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { Collection, DatabaseAccount, DocumentId } from "../Contracts/ViewModels";
|
import { Collection, DatabaseAccount, DocumentId } from "../Contracts/ViewModels";
|
||||||
@@ -20,7 +20,7 @@ const fetchMock = () => {
|
|||||||
ok: true,
|
ok: true,
|
||||||
text: () => "{}",
|
text: () => "{}",
|
||||||
json: () => "{}",
|
json: () => "{}",
|
||||||
headers: new Map()
|
headers: new Map(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,8 +33,8 @@ const collection = {
|
|||||||
partitionKey: {
|
partitionKey: {
|
||||||
paths: ["/pk"],
|
paths: ["/pk"],
|
||||||
kind: "Hash",
|
kind: "Hash",
|
||||||
version: 1
|
version: 1,
|
||||||
}
|
},
|
||||||
} as Collection;
|
} as Collection;
|
||||||
|
|
||||||
const documentId = ({
|
const documentId = ({
|
||||||
@@ -44,8 +44,8 @@ const documentId = ({
|
|||||||
partitionKey: {
|
partitionKey: {
|
||||||
paths: ["/pk"],
|
paths: ["/pk"],
|
||||||
kind: "Hash",
|
kind: "Hash",
|
||||||
version: 1
|
version: 1,
|
||||||
}
|
},
|
||||||
} as unknown) as DocumentId;
|
} as unknown) as DocumentId;
|
||||||
|
|
||||||
const databaseAccount = {
|
const databaseAccount = {
|
||||||
@@ -58,8 +58,8 @@ const databaseAccount = {
|
|||||||
documentEndpoint: "bar",
|
documentEndpoint: "bar",
|
||||||
gremlinEndpoint: "foo",
|
gremlinEndpoint: "foo",
|
||||||
tableEndpoint: "foo",
|
tableEndpoint: "foo",
|
||||||
cassandraEndpoint: "foo"
|
cassandraEndpoint: "foo",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("MongoProxyClient", () => {
|
describe("MongoProxyClient", () => {
|
||||||
@@ -69,7 +69,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -100,7 +100,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -131,7 +131,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -162,7 +162,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -193,7 +193,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -225,7 +225,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ describe("MongoProxyClient", () => {
|
|||||||
sid: "a2",
|
sid: "a2",
|
||||||
rg: "c1",
|
rg: "c1",
|
||||||
dba: "main",
|
dba: "main",
|
||||||
is: false
|
is: false,
|
||||||
};
|
};
|
||||||
_createMongoCollectionWithARM("management.azure.com", properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
|
_createMongoCollectionWithARM("management.azure.com", properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
|
||||||
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
||||||
@@ -268,8 +268,8 @@ describe("MongoProxyClient", () => {
|
|||||||
{
|
{
|
||||||
properties: {
|
properties: {
|
||||||
options: { "x-ms-cosmos-offer-autopilot-tier": "1" },
|
options: { "x-ms-cosmos-offer-autopilot-tier": "1" },
|
||||||
resource: { id: "abc-collection" }
|
resource: { id: "abc-collection" },
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -285,7 +285,7 @@ describe("MongoProxyClient", () => {
|
|||||||
rg: "c1",
|
rg: "c1",
|
||||||
dba: "main",
|
dba: "main",
|
||||||
is: false,
|
is: false,
|
||||||
offerThroughput: 400
|
offerThroughput: 400,
|
||||||
};
|
};
|
||||||
_createMongoCollectionWithARM("management.azure.com", properties, undefined);
|
_createMongoCollectionWithARM("management.azure.com", properties, undefined);
|
||||||
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
||||||
@@ -294,8 +294,8 @@ describe("MongoProxyClient", () => {
|
|||||||
{
|
{
|
||||||
properties: {
|
properties: {
|
||||||
options: { throughput: "400" },
|
options: { throughput: "400" },
|
||||||
resource: { id: "abc-collection" }
|
resource: { id: "abc-collection" },
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClie
|
|||||||
const defaultHeaders = {
|
const defaultHeaders = {
|
||||||
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
|
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
|
||||||
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
|
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
|
||||||
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15"
|
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15",
|
||||||
};
|
};
|
||||||
|
|
||||||
function authHeaders(): any {
|
function authHeaders(): any {
|
||||||
@@ -31,13 +31,13 @@ function authHeaders(): any {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queryIterator(databaseId: string, collection: Collection, query: string) {
|
export function queryIterator(databaseId: string, collection: Collection, query: string): any {
|
||||||
let continuationToken: string = null;
|
let continuationToken: string;
|
||||||
return {
|
return {
|
||||||
fetchNext: () => {
|
fetchNext: () => {
|
||||||
return queryDocuments(databaseId, collection, false, query).then(response => {
|
return queryDocuments(databaseId, collection, false, query).then((response) => {
|
||||||
continuationToken = response.continuationToken;
|
continuationToken = response.continuationToken;
|
||||||
let headers = {} as any;
|
const headers = {} as any;
|
||||||
response.headers.forEach((value: any, key: any) => {
|
response.headers.forEach((value: any, key: any) => {
|
||||||
headers[key] = value;
|
headers[key] = value;
|
||||||
});
|
});
|
||||||
@@ -46,10 +46,10 @@ export function queryIterator(databaseId: string, collection: Collection, query:
|
|||||||
headers,
|
headers,
|
||||||
requestCharge: headers[CosmosSDKConstants.HttpHeaders.RequestCharge],
|
requestCharge: headers[CosmosSDKConstants.HttpHeaders.RequestCharge],
|
||||||
activityId: headers[CosmosSDKConstants.HttpHeaders.ActivityId],
|
activityId: headers[CosmosSDKConstants.HttpHeaders.ActivityId],
|
||||||
hasMoreResults: !!continuationToken
|
hasMoreResults: !!continuationToken,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,9 @@ export function queryDocuments(
|
|||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
collection && collection.partitionKey && !collection.partitionKey.systemKey ? collection.partitionKeyProperty : ""
|
collection && collection.partitionKey && !collection.partitionKey.systemKey
|
||||||
|
? collection.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount) || "";
|
const endpoint = getEndpoint(databaseAccount) || "";
|
||||||
@@ -91,7 +93,7 @@ export function queryDocuments(
|
|||||||
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
|
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
|
||||||
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
|
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
|
||||||
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
|
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
|
||||||
[HttpHeaders.contentType]: "application/query+json"
|
[HttpHeaders.contentType]: "application/query+json",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (continuationToken) {
|
if (continuationToken) {
|
||||||
@@ -104,24 +106,17 @@ export function queryDocuments(
|
|||||||
.fetch(`${endpoint}${path}?${queryString.stringify(params)}`, {
|
.fetch(`${endpoint}${path}?${queryString.stringify(params)}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ query }),
|
body: JSON.stringify({ query }),
|
||||||
headers
|
headers,
|
||||||
})
|
})
|
||||||
.then(async response => {
|
.then(async (response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return {
|
return {
|
||||||
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
|
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
|
||||||
documents: (await response.json()).Documents as DataModels.DocumentId[],
|
documents: (await response.json()).Documents as DataModels.DocumentId[],
|
||||||
headers: response.headers
|
headers: response.headers,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const errorMessage = await response.text();
|
return errorHandling(response, "querying documents", params);
|
||||||
if (response.status === HttpStatusCodes.Forbidden) {
|
|
||||||
MessageHandler.sendMessage({
|
|
||||||
type: MessageTypes.ForbiddenError,
|
|
||||||
reason: errorMessage
|
|
||||||
});
|
|
||||||
}
|
|
||||||
throw new Error(errorMessage);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +140,9 @@ export function readDocument(
|
|||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
|
? documentId.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
@@ -157,14 +154,14 @@ export function readDocument(
|
|||||||
...authHeaders(),
|
...authHeaders(),
|
||||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent(
|
[CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent(
|
||||||
JSON.stringify(documentId.partitionKeyHeader())
|
JSON.stringify(documentId.partitionKeyHeader())
|
||||||
)
|
),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(async response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
errorHandling(response);
|
return errorHandling(response, "reading document", params);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +182,7 @@ export function createDocument(
|
|||||||
sid: CosmosClient.subscriptionId(),
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : ""
|
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
@@ -196,14 +193,14 @@ export function createDocument(
|
|||||||
body: JSON.stringify(documentContent),
|
body: JSON.stringify(documentContent),
|
||||||
headers: {
|
headers: {
|
||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...authHeaders()
|
...authHeaders(),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(async response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
errorHandling(response);
|
return errorHandling(response, "creating document", params);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +225,9 @@ export function updateDocument(
|
|||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
|
? documentId.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
|
|
||||||
@@ -240,14 +239,14 @@ export function updateDocument(
|
|||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...authHeaders(),
|
...authHeaders(),
|
||||||
[HttpHeaders.contentType]: "application/json",
|
[HttpHeaders.contentType]: "application/json",
|
||||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
|
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(async response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
errorHandling(response);
|
return errorHandling(response, "updating document", params);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +270,9 @@ export function deleteDocument(
|
|||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
|
? documentId.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
|
|
||||||
@@ -282,14 +283,14 @@ export function deleteDocument(
|
|||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...authHeaders(),
|
...authHeaders(),
|
||||||
[HttpHeaders.contentType]: "application/json",
|
[HttpHeaders.contentType]: "application/json",
|
||||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
|
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(async response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
errorHandling(response);
|
return errorHandling(response, "deleting document", params);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +319,7 @@ export function createMongoCollectionWithProxy(
|
|||||||
sid: CosmosClient.subscriptionId(),
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
isAutoPilot: false
|
isAutoPilot: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (autopilotOptions) {
|
if (autopilotOptions) {
|
||||||
@@ -336,19 +337,15 @@ export function createMongoCollectionWithProxy(
|
|||||||
headers: {
|
headers: {
|
||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...authHeaders(),
|
...authHeaders(),
|
||||||
[HttpHeaders.contentType]: "application/json"
|
[HttpHeaders.contentType]: "application/json",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(async response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
return errorHandling(response, "creating collection", params);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error creating collection: ${await response.json()}, Payload: ${params}`
|
|
||||||
);
|
|
||||||
errorHandling(response);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,7 +376,7 @@ export function createMongoCollectionWithARM(
|
|||||||
sid: CosmosClient.subscriptionId(),
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
analyticalStorageTtl
|
analyticalStorageTtl,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (createDatabase) {
|
if (createDatabase) {
|
||||||
@@ -407,13 +404,16 @@ export function getEndpoint(databaseAccount: ViewModels.DatabaseAccount): string
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function errorHandling(response: any): Promise<any> {
|
async function errorHandling(response: any, action: string, params: any): Promise<any> {
|
||||||
const errorMessage = await response.text();
|
const errorMessage = await response.text();
|
||||||
|
// Log the error where the user can see it
|
||||||
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
|
ConsoleDataType.Error,
|
||||||
|
`Error ${action}: ${errorMessage}, Payload: ${JSON.stringify(params)}`
|
||||||
|
);
|
||||||
if (response.status === HttpStatusCodes.Forbidden) {
|
if (response.status === HttpStatusCodes.Forbidden) {
|
||||||
MessageHandler.sendMessage({
|
MessageHandler.sendMessage({ type: MessageTypes.ForbiddenError, reason: errorMessage });
|
||||||
type: MessageTypes.ForbiddenError,
|
return;
|
||||||
reason: errorMessage
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
throw new Error(errorMessage);
|
throw new Error(errorMessage);
|
||||||
}
|
}
|
||||||
@@ -432,10 +432,10 @@ export async function _createMongoCollectionWithARM(
|
|||||||
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
|
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
|
||||||
properties: {
|
properties: {
|
||||||
resource: {
|
resource: {
|
||||||
id: params.coll
|
id: params.coll,
|
||||||
},
|
},
|
||||||
options: {}
|
options: {},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (params.is) {
|
if (params.is) {
|
||||||
@@ -462,14 +462,6 @@ export async function _createMongoCollectionWithARM(
|
|||||||
rpPayloadToCreateCollection
|
rpPayloadToCreateCollection
|
||||||
);
|
);
|
||||||
} catch (response) {
|
} catch (response) {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
return errorHandling(response, "creating collection", undefined);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error creating collection: ${JSON.stringify(response)}`
|
|
||||||
);
|
|
||||||
if (response.status === HttpStatusCodes.Forbidden) {
|
|
||||||
MessageHandler.sendMessage({ type: MessageTypes.ForbiddenError });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Error(`Error creating collection`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export default class MongoUtility {
|
export default class MongoUtility {
|
||||||
public static tojson = function(x: any, indent: string, nolint: boolean) {
|
public static tojson = function (x: any, indent: string, nolint: boolean) {
|
||||||
if (x === null || x === undefined) {
|
if (x === null || x === undefined) {
|
||||||
return String(x);
|
return String(x);
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ export default class MongoUtility {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static tojsonObject = function(x: any, indent: string, nolint: boolean) {
|
private static tojsonObject = function (x: any, indent: string, nolint: boolean) {
|
||||||
var lineEnding = nolint ? " " : "\n";
|
var lineEnding = nolint ? " " : "\n";
|
||||||
var tabSpace = nolint ? "" : "\t";
|
var tabSpace = nolint ? "" : "\t";
|
||||||
indent = indent || "";
|
indent = indent || "";
|
||||||
@@ -114,7 +114,7 @@ export default class MongoUtility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add proper line endings, indents, and commas to each line
|
// Add proper line endings, indents, and commas to each line
|
||||||
s += $.map(pairs, function(pair) {
|
s += $.map(pairs, function (pair) {
|
||||||
return lineEnding + indent + pair;
|
return lineEnding + indent + pair;
|
||||||
}).join(",");
|
}).join(",");
|
||||||
s += lineEnding;
|
s += lineEnding;
|
||||||
@@ -124,7 +124,7 @@ export default class MongoUtility {
|
|||||||
return s + indent + "}";
|
return s + indent + "}";
|
||||||
};
|
};
|
||||||
|
|
||||||
private static tojsonArray = function(a: any, indent: string, nolint: boolean) {
|
private static tojsonArray = function (a: any, indent: string, nolint: boolean) {
|
||||||
if (a.length === 0) {
|
if (a.length === 0) {
|
||||||
return "[ ]";
|
return "[ ]";
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ export default class MongoUtility {
|
|||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
|
||||||
private static hasDefinedProperty = function(obj: any, prop: string): boolean {
|
private static hasDefinedProperty = function (obj: any, prop: string): boolean {
|
||||||
if (Object.getPrototypeOf === undefined || Object.getPrototypeOf(obj) === null) {
|
if (Object.getPrototypeOf === undefined || Object.getPrototypeOf(obj) === null) {
|
||||||
return false;
|
return false;
|
||||||
} else if (obj.hasOwnProperty(prop)) {
|
} else if (obj.hasOwnProperty(prop)) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class NotificationsClientBase implements ViewModels.NotificationsClient {
|
|||||||
url: url,
|
url: url,
|
||||||
type: "GET",
|
type: "GET",
|
||||||
headers: headers,
|
headers: headers,
|
||||||
cache: false
|
cache: false,
|
||||||
}).then(
|
}).then(
|
||||||
(notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => {
|
(notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => {
|
||||||
deferred.resolve(notifications);
|
deferred.resolve(notifications);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
|||||||
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { CosmosClient } from "./CosmosClient";
|
import { CosmosClient } from "./CosmosClient";
|
||||||
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
import { Logger } from "./Logger";
|
import * as Logger from "./Logger";
|
||||||
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
||||||
import { QueryUtils } from "../Utils/QueryUtils";
|
import { QueryUtils } from "../Utils/QueryUtils";
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
private static readonly PartitionKey: DataModels.PartitionKey = {
|
private static readonly PartitionKey: DataModels.PartitionKey = {
|
||||||
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
||||||
kind: BackendDefaults.partitionKeyKind,
|
kind: BackendDefaults.partitionKeyKind,
|
||||||
version: BackendDefaults.partitionKeyVersion
|
version: BackendDefaults.partitionKeyVersion,
|
||||||
};
|
};
|
||||||
private static readonly FetchQuery: string = "SELECT * FROM c";
|
private static readonly FetchQuery: string = "SELECT * FROM c";
|
||||||
private static readonly FetchMongoQuery: string = "{}";
|
private static readonly FetchMongoQuery: string = "{}";
|
||||||
@@ -38,7 +38,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
databaseId: SavedQueries.DatabaseName,
|
databaseId: SavedQueries.DatabaseName,
|
||||||
partitionKey: QueriesClient.PartitionKey,
|
partitionKey: QueriesClient.PartitionKey,
|
||||||
offerThroughput: SavedQueries.OfferThroughput,
|
offerThroughput: SavedQueries.OfferThroughput,
|
||||||
databaseLevelThroughput: undefined
|
databaseLevelThroughput: undefined,
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(collection: DataModels.Collection) => {
|
(collection: DataModels.Collection) => {
|
||||||
@@ -152,7 +152,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
resourceId: resourceId,
|
resourceId: resourceId,
|
||||||
queryName: queryName,
|
queryName: queryName,
|
||||||
query: query,
|
query: query,
|
||||||
id: id
|
id: id,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
this.validateQuery(parsedQuery);
|
this.validateQuery(parsedQuery);
|
||||||
@@ -219,7 +219,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
const documentId: ViewModels.DocumentId = new DocumentId(
|
const documentId: ViewModels.DocumentId = new DocumentId(
|
||||||
{
|
{
|
||||||
partitionKey: QueriesClient.PartitionKey,
|
partitionKey: QueriesClient.PartitionKey,
|
||||||
partitionKeyProperty: "id"
|
partitionKeyProperty: "id",
|
||||||
} as ViewModels.DocumentsTab,
|
} as ViewModels.DocumentsTab,
|
||||||
query,
|
query,
|
||||||
query.queryName
|
query.queryName
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { SplitterMetrics } from "./Constants";
|
|||||||
|
|
||||||
export enum SplitterDirection {
|
export enum SplitterDirection {
|
||||||
Horizontal = "horizontal",
|
Horizontal = "horizontal",
|
||||||
Vertical = "vertical"
|
Vertical = "vertical",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SplitterBounds {
|
export interface SplitterBounds {
|
||||||
@@ -50,7 +50,7 @@ export class Splitter {
|
|||||||
animate: true,
|
animate: true,
|
||||||
animateDuration: "fast",
|
animateDuration: "fast",
|
||||||
start: this.onResizeStart,
|
start: this.onResizeStart,
|
||||||
stop: this.onResizeStop
|
stop: this.onResizeStop,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isVerticalSplitter) {
|
if (isVerticalSplitter) {
|
||||||
@@ -90,9 +90,7 @@ export class Splitter {
|
|||||||
this.lastWidth = $(this.leftSide).width();
|
this.lastWidth = $(this.leftSide).width();
|
||||||
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
|
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
|
||||||
$(this.leftSide).css("width", "");
|
$(this.leftSide).css("width", "");
|
||||||
$(this.leftSide)
|
$(this.leftSide).resizable("option", "disabled", true).removeClass("ui-resizable-disabled"); // remove class so splitter is visible
|
||||||
.resizable("option", "disabled", true)
|
|
||||||
.removeClass("ui-resizable-disabled"); // remove class so splitter is visible
|
|
||||||
$(this.splitter).removeClass("ui-resizable-e");
|
$(this.splitter).removeClass("ui-resizable-e");
|
||||||
this.isCollapsed(true);
|
this.isCollapsed(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ export default class UrlUtility {
|
|||||||
type: type,
|
type: type,
|
||||||
objectBody: {
|
objectBody: {
|
||||||
id: id,
|
id: id,
|
||||||
self: resourcePath
|
self: resourcePath,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export enum Platform {
|
export enum Platform {
|
||||||
Portal = "Portal",
|
Portal = "Portal",
|
||||||
Hosted = "Hosted",
|
Hosted = "Hosted",
|
||||||
Emulator = "Emulator"
|
Emulator = "Emulator",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
@@ -45,7 +45,7 @@ let config: Config = {
|
|||||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
||||||
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
|
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
|
||||||
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
|
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
|
||||||
AZURESAMPLESCOSMOSDBPAT: "99e38770e29b4a61d7c49f188780504efd35cc86" //[SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification=" // this PAT is a "no scopes" PAT with zero access to any projects, this is just used to get around the dev.github.com rate limit when accessing public samples repo.")]
|
AZURESAMPLESCOSMOSDBPAT: "99e38770e29b4a61d7c49f188780504efd35cc86", //[SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification=" // this PAT is a "no scopes" PAT with zero access to any projects, this is just used to get around the dev.github.com rate limit when accessing public samples repo.")]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Injected for local develpment. These will be removed in the production bundle by webpack
|
// Injected for local develpment. These will be removed in the production bundle by webpack
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export enum TabKind {
|
|||||||
TableEntities,
|
TableEntities,
|
||||||
Graph,
|
Graph,
|
||||||
SQLQuery,
|
SQLQuery,
|
||||||
ScaleSettings
|
ScaleSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +20,7 @@ export enum PaneKind {
|
|||||||
DeleteDatabase,
|
DeleteDatabase,
|
||||||
GlobalSettings,
|
GlobalSettings,
|
||||||
AdHocAccess,
|
AdHocAccess,
|
||||||
SwitchDirectory
|
SwitchDirectory,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,5 +79,5 @@ export enum ActionType {
|
|||||||
OpenCollectionTab,
|
OpenCollectionTab,
|
||||||
OpenPane,
|
OpenPane,
|
||||||
TransmitCachedData,
|
TransmitCachedData,
|
||||||
OpenSampleNotebook
|
OpenSampleNotebook,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export enum ApiKind {
|
|||||||
Table,
|
Table,
|
||||||
Cassandra,
|
Cassandra,
|
||||||
Graph,
|
Graph,
|
||||||
MongoDBCompute
|
MongoDBCompute,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GenerateTokenResponse {
|
export interface GenerateTokenResponse {
|
||||||
@@ -297,7 +297,7 @@ export enum AutopilotTier {
|
|||||||
Tier1 = 1,
|
Tier1 = 1,
|
||||||
Tier2 = 2,
|
Tier2 = 2,
|
||||||
Tier3 = 3,
|
Tier3 = 3,
|
||||||
Tier4 = 4
|
Tier4 = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RpOptions {
|
export interface RpOptions {
|
||||||
@@ -363,7 +363,7 @@ export interface Notification {
|
|||||||
|
|
||||||
export enum ConflictResolutionMode {
|
export enum ConflictResolutionMode {
|
||||||
Custom = "Custom",
|
Custom = "Custom",
|
||||||
LastWriterWins = "LastWriterWins"
|
LastWriterWins = "LastWriterWins",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -522,7 +522,7 @@ export interface SparkClusterEndpoint {
|
|||||||
export enum SparkClusterEndpointKind {
|
export enum SparkClusterEndpointKind {
|
||||||
SparkUI = "SparkUI",
|
SparkUI = "SparkUI",
|
||||||
HistoryServerUI = "HistoryServerUI",
|
HistoryServerUI = "HistoryServerUI",
|
||||||
Livy = "Livy"
|
Livy = "Livy",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RpParameters {
|
export interface RpParameters {
|
||||||
@@ -704,49 +704,6 @@ export interface MemoryUsageInfo {
|
|||||||
totalKB: number;
|
totalKB: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotebookMetadata {
|
|
||||||
date: string;
|
|
||||||
description: string;
|
|
||||||
tags: string[];
|
|
||||||
author: string;
|
|
||||||
views: number;
|
|
||||||
likes: number;
|
|
||||||
downloads: number;
|
|
||||||
imageUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserMetadata {
|
|
||||||
likedNotebooks: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GitHubInfoJunoResponse {
|
|
||||||
encoding: string;
|
|
||||||
encodedContent: string;
|
|
||||||
content: string;
|
|
||||||
target: string;
|
|
||||||
submoduleGitUrl: string;
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
sha: string;
|
|
||||||
size: number;
|
|
||||||
type: {
|
|
||||||
stringValue: string;
|
|
||||||
value: number;
|
|
||||||
};
|
|
||||||
downloadUrl: string;
|
|
||||||
url: string;
|
|
||||||
gitUrl: string;
|
|
||||||
htmlUrl: string;
|
|
||||||
metadata?: NotebookMetadata;
|
|
||||||
officialSamplesIndex?: number;
|
|
||||||
isLikedNotebook?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LikedNotebooksJunoResponse {
|
|
||||||
likedNotebooksContent: GitHubInfoJunoResponse[];
|
|
||||||
userMetadata: UserMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface resourceTokenConnectionStringProperties {
|
export interface resourceTokenConnectionStringProperties {
|
||||||
accountEndpoint: string;
|
accountEndpoint: string;
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export enum LogEntryLevel {
|
|||||||
/**
|
/**
|
||||||
* Error level.
|
* Error level.
|
||||||
*/
|
*/
|
||||||
Error = 2
|
Error = 2,
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Schema of a log entry.
|
* Schema of a log entry.
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export enum MessageTypes {
|
|||||||
GetArcadiaToken,
|
GetArcadiaToken,
|
||||||
CreateWorkspace,
|
CreateWorkspace,
|
||||||
CreateSparkPool,
|
CreateSparkPool,
|
||||||
RefreshDatabaseAccount
|
RefreshDatabaseAccount,
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Versions, ActionContracts, Diagnostics };
|
export { Versions, ActionContracts, Diagnostics };
|
||||||
|
|||||||
@@ -9,14 +9,11 @@ import { AccessibleVerticalList } from "../Explorer/Tree/AccessibleVerticalList"
|
|||||||
import { ArcadiaWorkspaceItem } from "../Explorer/Controls/Arcadia/ArcadiaMenuPicker";
|
import { ArcadiaWorkspaceItem } from "../Explorer/Controls/Arcadia/ArcadiaMenuPicker";
|
||||||
import { CassandraTableKey, CassandraTableKeys, TableDataClient } from "../Explorer/Tables/TableDataClient";
|
import { CassandraTableKey, CassandraTableKeys, TableDataClient } from "../Explorer/Tables/TableDataClient";
|
||||||
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
import { CommandButtonOptions } from "../Explorer/Controls/CommandButton/CommandButton";
|
|
||||||
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { ExecuteSprocParam } from "../Explorer/Panes/ExecuteSprocParamsPane";
|
import { ExecuteSprocParam } from "../Explorer/Panes/ExecuteSprocParamsPane";
|
||||||
import { GitHubClient } from "../GitHub/GitHubClient";
|
import { GitHubClient } from "../GitHub/GitHubClient";
|
||||||
import { GitHubOAuthService } from "../GitHub/GitHubOAuthService";
|
|
||||||
import { IColumnSetting } from "../Explorer/Panes/Tables/TableColumnOptionsPane";
|
import { IColumnSetting } from "../Explorer/Panes/Tables/TableColumnOptionsPane";
|
||||||
import { IContentProvider } from "@nteract/core";
|
import { JunoClient, IGalleryItem } from "../Juno/JunoClient";
|
||||||
import { JunoClient } from "../Juno/JunoClient";
|
|
||||||
import { Library } from "./DataModels";
|
import { Library } from "./DataModels";
|
||||||
import { MostRecentActivity } from "../Explorer/MostRecentActivity/MostRecentActivity";
|
import { MostRecentActivity } from "../Explorer/MostRecentActivity/MostRecentActivity";
|
||||||
import { NotebookContentItem } from "../Explorer/Notebook/NotebookContentItem";
|
import { NotebookContentItem } from "../Explorer/Notebook/NotebookContentItem";
|
||||||
@@ -27,6 +24,8 @@ import { Splitter } from "../Common/Splitter";
|
|||||||
import { StringInputPane } from "../Explorer/Panes/StringInputPane";
|
import { StringInputPane } from "../Explorer/Panes/StringInputPane";
|
||||||
import { TextFieldProps } from "../Explorer/Controls/DialogReactComponent/DialogComponent";
|
import { TextFieldProps } from "../Explorer/Controls/DialogReactComponent/DialogComponent";
|
||||||
import { UploadDetails } from "../workers/upload/definitions";
|
import { UploadDetails } from "../workers/upload/definitions";
|
||||||
|
import { UploadItemsPaneAdapter } from "../Explorer/Panes/UploadItemsPaneAdapter";
|
||||||
|
import { ReactAdapter } from "../Bindings/ReactBindingHandler";
|
||||||
|
|
||||||
export interface ExplorerOptions {
|
export interface ExplorerOptions {
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
documentClientUtility: DocumentClientUtilityBase;
|
||||||
@@ -85,8 +84,10 @@ export interface Explorer {
|
|||||||
armEndpoint: ko.Observable<string>;
|
armEndpoint: ko.Observable<string>;
|
||||||
isFeatureEnabled: (feature: string) => boolean;
|
isFeatureEnabled: (feature: string) => boolean;
|
||||||
isGalleryEnabled: ko.Computed<boolean>;
|
isGalleryEnabled: ko.Computed<boolean>;
|
||||||
|
isGalleryPublishEnabled: ko.Computed<boolean>;
|
||||||
isGitHubPaneEnabled: ko.Observable<boolean>;
|
isGitHubPaneEnabled: ko.Observable<boolean>;
|
||||||
isGraphsEnabled: ko.Computed<boolean>;
|
isPublishNotebookPaneEnabled: ko.Observable<boolean>;
|
||||||
|
isRightPanelV2Enabled: ko.Computed<boolean>;
|
||||||
canExceedMaximumValue: ko.Computed<boolean>;
|
canExceedMaximumValue: ko.Computed<boolean>;
|
||||||
hasAutoPilotV2FeatureFlag: ko.Computed<boolean>;
|
hasAutoPilotV2FeatureFlag: ko.Computed<boolean>;
|
||||||
isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
||||||
@@ -141,6 +142,7 @@ export interface Explorer {
|
|||||||
executeSprocParamsPane: ExecuteSprocParamsPane;
|
executeSprocParamsPane: ExecuteSprocParamsPane;
|
||||||
renewAdHocAccessPane: RenewAdHocAccessPane;
|
renewAdHocAccessPane: RenewAdHocAccessPane;
|
||||||
uploadItemsPane: UploadItemsPane;
|
uploadItemsPane: UploadItemsPane;
|
||||||
|
uploadItemsPaneAdapter: UploadItemsPaneAdapter;
|
||||||
loadQueryPane: LoadQueryPane;
|
loadQueryPane: LoadQueryPane;
|
||||||
saveQueryPane: ContextualPane;
|
saveQueryPane: ContextualPane;
|
||||||
browseQueriesPane: BrowseQueriesPane;
|
browseQueriesPane: BrowseQueriesPane;
|
||||||
@@ -152,6 +154,7 @@ export interface Explorer {
|
|||||||
libraryManagePane: ContextualPane;
|
libraryManagePane: ContextualPane;
|
||||||
clusterLibraryPane: ContextualPane;
|
clusterLibraryPane: ContextualPane;
|
||||||
gitHubReposPane: ContextualPane;
|
gitHubReposPane: ContextualPane;
|
||||||
|
publishNotebookPaneAdapter: ReactAdapter;
|
||||||
|
|
||||||
// Facade
|
// Facade
|
||||||
logConsoleData(data: ConsoleData): void;
|
logConsoleData(data: ConsoleData): void;
|
||||||
@@ -223,22 +226,17 @@ export interface Explorer {
|
|||||||
arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
||||||
isNotebookTabActive: ko.Computed<boolean>;
|
isNotebookTabActive: ko.Computed<boolean>;
|
||||||
memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
||||||
|
notebookManager?: any; // This is dynamically loaded
|
||||||
openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean>; // True if it was opened, false otherwise
|
openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean>; // True if it was opened, false otherwise
|
||||||
resetNotebookWorkspace(): void;
|
resetNotebookWorkspace(): void;
|
||||||
importAndOpen: (path: string) => Promise<boolean>;
|
importAndOpen: (path: string) => Promise<boolean>;
|
||||||
importAndOpenFromGallery: (path: string, newName: string, content: any) => Promise<boolean>;
|
importAndOpenFromGallery: (name: string, content: string) => Promise<boolean>;
|
||||||
|
publishNotebook: (name: string, content: string) => void;
|
||||||
openNotebookTerminal: (kind: TerminalKind) => void;
|
openNotebookTerminal: (kind: TerminalKind) => void;
|
||||||
openGallery: () => void;
|
openGallery: (notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) => void;
|
||||||
openNotebookViewer: (
|
openNotebookViewer: (notebookUrl: string) => void;
|
||||||
notebookUrl: string,
|
|
||||||
notebookMetadata: DataModels.NotebookMetadata,
|
|
||||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
|
|
||||||
isLikedNotebook: boolean
|
|
||||||
) => void;
|
|
||||||
notebookWorkspaceManager: NotebookWorkspaceManager;
|
notebookWorkspaceManager: NotebookWorkspaceManager;
|
||||||
sparkClusterManager: SparkClusterManager;
|
sparkClusterManager: SparkClusterManager;
|
||||||
notebookContentProvider: IContentProvider;
|
|
||||||
gitHubOAuthService: GitHubOAuthService;
|
|
||||||
mostRecentActivity: MostRecentActivity;
|
mostRecentActivity: MostRecentActivity;
|
||||||
initNotebooks: (databaseAccount: DataModels.DatabaseAccount) => Promise<void>;
|
initNotebooks: (databaseAccount: DataModels.DatabaseAccount) => Promise<void>;
|
||||||
deleteCluster(): void;
|
deleteCluster(): void;
|
||||||
@@ -337,17 +335,6 @@ export interface Button {
|
|||||||
isSelected?: ko.Computed<boolean>;
|
isSelected?: ko.Computed<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommandButton {
|
|
||||||
disabled: ko.Subscribable<boolean>;
|
|
||||||
visible: ko.Subscribable<boolean>;
|
|
||||||
iconSrc: string;
|
|
||||||
commandButtonLabel: string | ko.Observable<string>;
|
|
||||||
tooltipText: string | ko.Observable<string>;
|
|
||||||
children: ko.ObservableArray<CommandButtonOptions>;
|
|
||||||
|
|
||||||
commandClickCallback: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NotificationConsole {
|
export interface NotificationConsole {
|
||||||
filteredConsoleData: ko.ObservableArray<ConsoleData>;
|
filteredConsoleData: ko.ObservableArray<ConsoleData>;
|
||||||
isConsoleExpanded: ko.Observable<boolean>;
|
isConsoleExpanded: ko.Observable<boolean>;
|
||||||
@@ -371,7 +358,6 @@ export interface TreeNode {
|
|||||||
id: ko.Observable<string>;
|
id: ko.Observable<string>;
|
||||||
database?: Database;
|
database?: Database;
|
||||||
collection?: Collection;
|
collection?: Collection;
|
||||||
contextMenu?: ContextMenu;
|
|
||||||
|
|
||||||
onNewQueryClick?(source: any, event: MouseEvent): void;
|
onNewQueryClick?(source: any, event: MouseEvent): void;
|
||||||
onNewStoredProcedureClick?(source: Collection, event: MouseEvent): void;
|
onNewStoredProcedureClick?(source: Collection, event: MouseEvent): void;
|
||||||
@@ -536,7 +522,7 @@ export interface StoredProcedure extends TreeNode {
|
|||||||
id: ko.Observable<string>;
|
id: ko.Observable<string>;
|
||||||
body: ko.Observable<string>;
|
body: ko.Observable<string>;
|
||||||
|
|
||||||
delete(source: TreeNode, event: MouseEvent | KeyboardEvent): void;
|
delete(): void;
|
||||||
open: () => void;
|
open: () => void;
|
||||||
select(): void;
|
select(): void;
|
||||||
execute(params: string[], partitionKeyValue?: string): void;
|
execute(params: string[], partitionKeyValue?: string): void;
|
||||||
@@ -550,7 +536,7 @@ export interface UserDefinedFunction extends TreeNode {
|
|||||||
id: ko.Observable<string>;
|
id: ko.Observable<string>;
|
||||||
body: ko.Observable<string>;
|
body: ko.Observable<string>;
|
||||||
|
|
||||||
delete(source: TreeNode, event: MouseEvent | KeyboardEvent): void;
|
delete(): void;
|
||||||
open: () => void;
|
open: () => void;
|
||||||
select(): void;
|
select(): void;
|
||||||
}
|
}
|
||||||
@@ -565,7 +551,7 @@ export interface Trigger extends TreeNode {
|
|||||||
triggerType: ko.Observable<string>;
|
triggerType: ko.Observable<string>;
|
||||||
triggerOperation: ko.Observable<string>;
|
triggerOperation: ko.Observable<string>;
|
||||||
|
|
||||||
delete(source: TreeNode, event: MouseEvent | KeyboardEvent): void;
|
delete(): void;
|
||||||
open: () => void;
|
open: () => void;
|
||||||
select(): void;
|
select(): void;
|
||||||
}
|
}
|
||||||
@@ -605,6 +591,16 @@ export interface GitHubReposPaneOptions extends PaneOptions {
|
|||||||
junoClient: JunoClient;
|
junoClient: JunoClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PublishNotebookPaneOptions extends PaneOptions {
|
||||||
|
junoClient: JunoClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PublishNotebookPaneOpenOptions {
|
||||||
|
name: string;
|
||||||
|
author: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AddCollectionPaneOptions extends PaneOptions {
|
export interface AddCollectionPaneOptions extends PaneOptions {
|
||||||
isPreferredApiTable: ko.Computed<boolean>;
|
isPreferredApiTable: ko.Computed<boolean>;
|
||||||
databaseId?: string;
|
databaseId?: string;
|
||||||
@@ -715,7 +711,7 @@ export interface UploadFilePane extends ContextualPane {
|
|||||||
export enum NeighborType {
|
export enum NeighborType {
|
||||||
SOURCES_ONLY,
|
SOURCES_ONLY,
|
||||||
TARGETS_ONLY,
|
TARGETS_ONLY,
|
||||||
BOTH
|
BOTH,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -884,16 +880,16 @@ export interface TerminalTabOptions extends TabOptions {
|
|||||||
export interface GalleryTabOptions extends TabOptions {
|
export interface GalleryTabOptions extends TabOptions {
|
||||||
account: DatabaseAccount;
|
account: DatabaseAccount;
|
||||||
container: Explorer;
|
container: Explorer;
|
||||||
|
junoClient: JunoClient;
|
||||||
|
notebookUrl?: string;
|
||||||
|
galleryItem?: IGalleryItem;
|
||||||
|
isFavorite?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotebookViewerTabOptions extends TabOptions {
|
export interface NotebookViewerTabOptions extends TabOptions {
|
||||||
account: DatabaseAccount;
|
account: DatabaseAccount;
|
||||||
container: Explorer;
|
container: Explorer;
|
||||||
notebookUrl: string;
|
notebookUrl: string;
|
||||||
notebookName: string;
|
|
||||||
notebookMetadata: DataModels.NotebookMetadata;
|
|
||||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>;
|
|
||||||
isLikedNotebook: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DocumentsTabOptions extends TabOptions {
|
export interface DocumentsTabOptions extends TabOptions {
|
||||||
@@ -1174,7 +1170,6 @@ export interface TriggerTab extends ScriptTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface GraphTab extends Tab {}
|
export interface GraphTab extends Tab {}
|
||||||
export interface NotebookTab extends Tab {}
|
|
||||||
export interface EditorPosition {
|
export interface EditorPosition {
|
||||||
line: number;
|
line: number;
|
||||||
column: number;
|
column: number;
|
||||||
@@ -1188,14 +1183,14 @@ export enum DocumentExplorerState {
|
|||||||
newDocumentInvalid,
|
newDocumentInvalid,
|
||||||
exisitingDocumentNoEdits,
|
exisitingDocumentNoEdits,
|
||||||
exisitingDocumentDirtyValid,
|
exisitingDocumentDirtyValid,
|
||||||
exisitingDocumentDirtyInvalid
|
exisitingDocumentDirtyInvalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum IndexingPolicyEditorState {
|
export enum IndexingPolicyEditorState {
|
||||||
noCollectionSelected,
|
noCollectionSelected,
|
||||||
noEdits,
|
noEdits,
|
||||||
dirtyValid,
|
dirtyValid,
|
||||||
dirtyInvalid
|
dirtyInvalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ScriptEditorState {
|
export enum ScriptEditorState {
|
||||||
@@ -1203,7 +1198,7 @@ export enum ScriptEditorState {
|
|||||||
newValid,
|
newValid,
|
||||||
exisitingNoEdits,
|
exisitingNoEdits,
|
||||||
exisitingDirtyValid,
|
exisitingDirtyValid,
|
||||||
exisitingDirtyInvalid
|
exisitingDirtyInvalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CollectionTabKind {
|
export enum CollectionTabKind {
|
||||||
@@ -1218,29 +1213,18 @@ export enum CollectionTabKind {
|
|||||||
MongoShell = 10,
|
MongoShell = 10,
|
||||||
DatabaseSettings = 11,
|
DatabaseSettings = 11,
|
||||||
Conflicts = 12,
|
Conflicts = 12,
|
||||||
Notebook = 13,
|
Notebook = 13 /* Deprecated */,
|
||||||
Terminal = 14,
|
Terminal = 14,
|
||||||
NotebookV2 = 15,
|
NotebookV2 = 15,
|
||||||
SparkMasterTab = 16,
|
SparkMasterTab = 16,
|
||||||
Gallery = 17,
|
Gallery = 17,
|
||||||
NotebookViewer = 18
|
NotebookViewer = 18,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TerminalKind {
|
export enum TerminalKind {
|
||||||
Default = 0,
|
Default = 0,
|
||||||
Mongo = 1,
|
Mongo = 1,
|
||||||
Cassandra = 2
|
Cassandra = 2,
|
||||||
}
|
|
||||||
|
|
||||||
export interface ContextMenu {
|
|
||||||
container: Explorer;
|
|
||||||
visible: ko.Observable<boolean>;
|
|
||||||
elementId: string;
|
|
||||||
options: ko.ObservableArray<CommandButtonOptions>;
|
|
||||||
tabIndex: ko.Observable<number>;
|
|
||||||
|
|
||||||
show(source: any, event: MouseEvent | KeyboardEvent): void;
|
|
||||||
hide(source: any, event: MouseEvent | KeyboardEvent): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataExplorerInputsFrame {
|
export interface DataExplorerInputsFrame {
|
||||||
@@ -1292,7 +1276,7 @@ export enum SubscriptionType {
|
|||||||
EA,
|
EA,
|
||||||
Free,
|
Free,
|
||||||
Internal,
|
Internal,
|
||||||
PAYG
|
PAYG,
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MonacoEditorSettings {
|
export class MonacoEditorSettings {
|
||||||
|
|||||||
@@ -6,19 +6,19 @@ describe("The Heatmap Control", () => {
|
|||||||
const dataPoints = {
|
const dataPoints = {
|
||||||
"1": {
|
"1": {
|
||||||
"2019-06-19T00:59:10Z": {
|
"2019-06-19T00:59:10Z": {
|
||||||
"Normalized Throughput": 0.35
|
"Normalized Throughput": 0.35,
|
||||||
},
|
},
|
||||||
"2019-06-19T00:48:10Z": {
|
"2019-06-19T00:48:10Z": {
|
||||||
"Normalized Throughput": 0.25
|
"Normalized Throughput": 0.25,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const chartCaptions = {
|
const chartCaptions = {
|
||||||
chartTitle: "chart title",
|
chartTitle: "chart title",
|
||||||
yAxisTitle: "YAxisTitle",
|
yAxisTitle: "YAxisTitle",
|
||||||
tooltipText: "Tooltip text",
|
tooltipText: "Tooltip text",
|
||||||
timeWindow: 123456789
|
timeWindow: 123456789,
|
||||||
};
|
};
|
||||||
|
|
||||||
let heatmap: Heatmap;
|
let heatmap: Heatmap;
|
||||||
@@ -75,12 +75,12 @@ describe("The Heatmap Control", () => {
|
|||||||
if (dayjs().utcOffset()) {
|
if (dayjs().utcOffset()) {
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
|
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
|
||||||
"2019-06-19T00:48:10Z",
|
"2019-06-19T00:48:10Z",
|
||||||
"2019-06-19T00:59:10Z"
|
"2019-06-19T00:59:10Z",
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
|
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
|
||||||
"2019-06-19T00:48:10Z",
|
"2019-06-19T00:48:10Z",
|
||||||
"2019-06-19T00:59:10Z"
|
"2019-06-19T00:59:10Z",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -106,9 +106,9 @@ describe("iframe rendering when there is no data", () => {
|
|||||||
data: {
|
data: {
|
||||||
chartData: {},
|
chartData: {},
|
||||||
chartSettings: {},
|
chartSettings: {},
|
||||||
theme: 4
|
theme: 4,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
||||||
@@ -126,9 +126,9 @@ describe("iframe rendering when there is no data", () => {
|
|||||||
data: {
|
data: {
|
||||||
chartData: {},
|
chartData: {},
|
||||||
chartSettings: {},
|
chartSettings: {},
|
||||||
theme: 2
|
theme: 2,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
HeatmapData,
|
HeatmapData,
|
||||||
LayoutSettings,
|
LayoutSettings,
|
||||||
PartitionTimeStampToData,
|
PartitionTimeStampToData,
|
||||||
PortalTheme
|
PortalTheme,
|
||||||
} from "./HeatmapDatatypes";
|
} from "./HeatmapDatatypes";
|
||||||
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
||||||
import { MessageHandler } from "../../Common/MessageHandler";
|
import { MessageHandler } from "../../Common/MessageHandler";
|
||||||
@@ -43,7 +43,7 @@ export class Heatmap {
|
|||||||
return {
|
return {
|
||||||
family: StyleConstants.DataExplorerFont,
|
family: StyleConstants.DataExplorerFont,
|
||||||
size,
|
size,
|
||||||
color
|
color,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ export class Heatmap {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
// go thru all rows and create 2d matrix for heatmap...
|
// go thru all rows and create 2d matrix for heatmap...
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
@@ -115,7 +115,7 @@ export class Heatmap {
|
|||||||
[0.7, "#E46612"],
|
[0.7, "#E46612"],
|
||||||
[0.8, "#E64914"],
|
[0.8, "#E64914"],
|
||||||
[0.9, "#B80016"],
|
[0.9, "#B80016"],
|
||||||
[1.0, "#B80016"]
|
[1.0, "#B80016"],
|
||||||
],
|
],
|
||||||
name: "",
|
name: "",
|
||||||
hovertemplate: this._heatmapCaptions.tooltipText,
|
hovertemplate: this._heatmapCaptions.tooltipText,
|
||||||
@@ -123,11 +123,11 @@ export class Heatmap {
|
|||||||
thickness: 15,
|
thickness: 15,
|
||||||
outlinewidth: 0,
|
outlinewidth: 0,
|
||||||
tickcolor: StyleConstants.BaseDark,
|
tickcolor: StyleConstants.BaseDark,
|
||||||
tickfont: this._getFontStyles(10, this._defaultFontColor)
|
tickfont: this._getFontStyles(10, this._defaultFontColor),
|
||||||
},
|
},
|
||||||
y: this._chartData.yAxisPoints,
|
y: this._chartData.yAxisPoints,
|
||||||
x: this._chartData.xAxisPoints
|
x: this._chartData.xAxisPoints,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ export class Heatmap {
|
|||||||
r: 10,
|
r: 10,
|
||||||
b: 35,
|
b: 35,
|
||||||
t: 30,
|
t: 30,
|
||||||
pad: 0
|
pad: 0,
|
||||||
},
|
},
|
||||||
paper_bgcolor: "transparent",
|
paper_bgcolor: "transparent",
|
||||||
plot_bgcolor: "transparent",
|
plot_bgcolor: "transparent",
|
||||||
@@ -154,7 +154,7 @@ export class Heatmap {
|
|||||||
autotick: true,
|
autotick: true,
|
||||||
fixedrange: true,
|
fixedrange: true,
|
||||||
ticks: "",
|
ticks: "",
|
||||||
showticklabels: false
|
showticklabels: false,
|
||||||
},
|
},
|
||||||
xaxis: {
|
xaxis: {
|
||||||
fixedrange: true,
|
fixedrange: true,
|
||||||
@@ -167,13 +167,13 @@ export class Heatmap {
|
|||||||
autotick: true,
|
autotick: true,
|
||||||
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
|
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
|
||||||
showticklabels: true,
|
showticklabels: true,
|
||||||
tickfont: this._getFontStyles(10)
|
tickfont: this._getFontStyles(10),
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: this._heatmapCaptions.chartTitle,
|
text: this._heatmapCaptions.chartTitle,
|
||||||
x: 0.01,
|
x: 0.01,
|
||||||
font: this._getFontStyles(13, this._defaultFontColor)
|
font: this._getFontStyles(13, this._defaultFontColor),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ export class Heatmap {
|
|||||||
return {
|
return {
|
||||||
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
|
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
|
||||||
responsive: true,*/
|
responsive: true,*/
|
||||||
displayModeBar: false
|
displayModeBar: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export enum PortalTheme {
|
|||||||
blue = 1,
|
blue = 1,
|
||||||
azure,
|
azure,
|
||||||
light,
|
light,
|
||||||
dark
|
dark,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HeatmapData {
|
export interface HeatmapData {
|
||||||
|
|||||||
@@ -4,10 +4,6 @@ import * as ko from "knockout";
|
|||||||
import "./ComponentRegisterer";
|
import "./ComponentRegisterer";
|
||||||
|
|
||||||
describe("Component Registerer", () => {
|
describe("Component Registerer", () => {
|
||||||
it("should register command-button component", () => {
|
|
||||||
expect(ko.components.isRegistered("command-button")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register input-typeahead component", () => {
|
it("should register input-typeahead component", () => {
|
||||||
expect(ko.components.isRegistered("input-typeahead")).toBe(true);
|
expect(ko.components.isRegistered("input-typeahead")).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -64,10 +60,6 @@ describe("Component Registerer", () => {
|
|||||||
expect(ko.components.isRegistered("graph-tab")).toBe(true);
|
expect(ko.components.isRegistered("graph-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register notebook-tab component", () => {
|
|
||||||
expect(ko.components.isRegistered("notebook-tab")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register notebookv2-tab component", () => {
|
it("should register notebookv2-tab component", () => {
|
||||||
expect(ko.components.isRegistered("notebookv2-tab")).toBe(true);
|
expect(ko.components.isRegistered("notebookv2-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -84,30 +76,6 @@ describe("Component Registerer", () => {
|
|||||||
expect(ko.components.isRegistered("mongo-shell-tab")).toBe(true);
|
expect(ko.components.isRegistered("mongo-shell-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register resource-tree component", () => {
|
|
||||||
expect(ko.components.isRegistered("resource-tree")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register database-node component", () => {
|
|
||||||
expect(ko.components.isRegistered("database-node")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register collection-node component", () => {
|
|
||||||
expect(ko.components.isRegistered("collection-node")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register stored-procedure-node component", () => {
|
|
||||||
expect(ko.components.isRegistered("stored-procedure-node")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register trigger-node component", () => {
|
|
||||||
expect(ko.components.isRegistered("trigger-node")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register user-defined-function-node component", () => {
|
|
||||||
expect(ko.components.isRegistered("user-defined-function-node")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should registeradd-collection-pane component", () => {
|
it("should registeradd-collection-pane component", () => {
|
||||||
expect(ko.components.isRegistered("add-collection-pane")).toBe(true);
|
expect(ko.components.isRegistered("add-collection-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -156,10 +124,6 @@ describe("Component Registerer", () => {
|
|||||||
expect(ko.components.isRegistered("manage-spark-cluster-pane")).toBe(true);
|
expect(ko.components.isRegistered("manage-spark-cluster-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register collection-node-context-menu component", () => {
|
|
||||||
expect(ko.components.isRegistered("collection-node-context-menu")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register dynamic-list component", () => {
|
it("should register dynamic-list component", () => {
|
||||||
expect(ko.components.isRegistered("dynamic-list")).toBe(true);
|
expect(ko.components.isRegistered("dynamic-list")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as PaneComponents from "./Panes/PaneComponents";
|
import * as PaneComponents from "./Panes/PaneComponents";
|
||||||
import * as TabComponents from "./Tabs/TabComponents";
|
import * as TabComponents from "./Tabs/TabComponents";
|
||||||
import * as TreeComponents from "./Tree/TreeComponents";
|
|
||||||
import { CollapsiblePanelComponent } from "./Controls/CollapsiblePanel/CollapsiblePanelComponent";
|
import { CollapsiblePanelComponent } from "./Controls/CollapsiblePanel/CollapsiblePanelComponent";
|
||||||
import { CommandButtonComponent } from "./Controls/CommandButton/CommandButton";
|
|
||||||
import { DiffEditorComponent } from "./Controls/DiffEditor/DiffEditorComponent";
|
import { DiffEditorComponent } from "./Controls/DiffEditor/DiffEditorComponent";
|
||||||
import { DynamicListComponent } from "./Controls/DynamicList/DynamicListComponent";
|
import { DynamicListComponent } from "./Controls/DynamicList/DynamicListComponent";
|
||||||
import { EditorComponent } from "./Controls/Editor/EditorComponent";
|
import { EditorComponent } from "./Controls/Editor/EditorComponent";
|
||||||
@@ -16,7 +14,6 @@ import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputI
|
|||||||
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
||||||
import { ToolbarComponent } from "./Controls/Toolbar/Toolbar";
|
import { ToolbarComponent } from "./Controls/Toolbar/Toolbar";
|
||||||
|
|
||||||
ko.components.register("command-button", CommandButtonComponent);
|
|
||||||
ko.components.register("toolbar", new ToolbarComponent());
|
ko.components.register("toolbar", new ToolbarComponent());
|
||||||
ko.components.register("input-typeahead", new InputTypeaheadComponent());
|
ko.components.register("input-typeahead", new InputTypeaheadComponent());
|
||||||
ko.components.register("new-vertex-form", NewVertexComponent);
|
ko.components.register("new-vertex-form", NewVertexComponent);
|
||||||
@@ -42,7 +39,6 @@ ko.components.register("tables-query-tab", new TabComponents.QueryTablesTab());
|
|||||||
ko.components.register("graph-tab", new TabComponents.GraphTab());
|
ko.components.register("graph-tab", new TabComponents.GraphTab());
|
||||||
ko.components.register("mongo-shell-tab", new TabComponents.MongoShellTab());
|
ko.components.register("mongo-shell-tab", new TabComponents.MongoShellTab());
|
||||||
ko.components.register("conflicts-tab", new TabComponents.ConflictsTab());
|
ko.components.register("conflicts-tab", new TabComponents.ConflictsTab());
|
||||||
ko.components.register("notebook-tab", new TabComponents.NotebookTab());
|
|
||||||
ko.components.register("notebookv2-tab", new TabComponents.NotebookV2Tab());
|
ko.components.register("notebookv2-tab", new TabComponents.NotebookV2Tab());
|
||||||
ko.components.register("terminal-tab", new TabComponents.TerminalTab());
|
ko.components.register("terminal-tab", new TabComponents.TerminalTab());
|
||||||
ko.components.register("spark-master-tab", new TabComponents.SparkMasterTab());
|
ko.components.register("spark-master-tab", new TabComponents.SparkMasterTab());
|
||||||
@@ -52,14 +48,6 @@ ko.components.register("notebook-viewer-tab", new TabComponents.NotebookViewerTa
|
|||||||
// Database Tabs
|
// Database Tabs
|
||||||
ko.components.register("database-settings-tab", new TabComponents.DatabaseSettingsTab());
|
ko.components.register("database-settings-tab", new TabComponents.DatabaseSettingsTab());
|
||||||
|
|
||||||
// Resource Tree nodes
|
|
||||||
ko.components.register("resource-tree", new TreeComponents.ResourceTree());
|
|
||||||
ko.components.register("database-node", new TreeComponents.DatabaseTreeNode());
|
|
||||||
ko.components.register("collection-node", new TreeComponents.CollectionTreeNode());
|
|
||||||
ko.components.register("stored-procedure-node", new TreeComponents.StoredProcedureTreeNode());
|
|
||||||
ko.components.register("trigger-node", new TreeComponents.TriggerTreeNode());
|
|
||||||
ko.components.register("user-defined-function-node", new TreeComponents.UserDefinedFunctionTreeNode());
|
|
||||||
|
|
||||||
// Panes
|
// Panes
|
||||||
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
||||||
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
||||||
@@ -93,6 +81,3 @@ ko.components.register("manage-spark-cluster-pane", new PaneComponents.ManageSpa
|
|||||||
ko.components.register("library-manage-pane", new PaneComponents.LibraryManagePaneComponent());
|
ko.components.register("library-manage-pane", new PaneComponents.LibraryManagePaneComponent());
|
||||||
ko.components.register("cluster-library-pane", new PaneComponents.ClusterLibraryPaneComponent());
|
ko.components.register("cluster-library-pane", new PaneComponents.ClusterLibraryPaneComponent());
|
||||||
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
|
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
|
||||||
|
|
||||||
// Menus
|
|
||||||
ko.components.register("collection-node-context-menu", new TreeComponents.CollectionTreeNodeContextMenu());
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import { CommandButtonOptions } from "./Controls/CommandButton/CommandButton";
|
|
||||||
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
import { TreeNodeMenuItem } from "./Controls/TreeComponent/TreeComponent";
|
||||||
import AddCollectionIcon from "../../images/AddCollection.svg";
|
import AddCollectionIcon from "../../images/AddCollection.svg";
|
||||||
import AddSqlQueryIcon from "../../images/AddSqlQuery_16x16.svg";
|
import AddSqlQueryIcon from "../../images/AddSqlQuery_16x16.svg";
|
||||||
@@ -33,13 +32,13 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const newCollectionMenuItem: TreeNodeMenuItem = {
|
const newCollectionMenuItem: TreeNodeMenuItem = {
|
||||||
iconSrc: AddCollectionIcon,
|
iconSrc: AddCollectionIcon,
|
||||||
onClick: () => container.onNewCollectionClicked(),
|
onClick: () => container.onNewCollectionClicked(),
|
||||||
label: container.addCollectionText()
|
label: container.addCollectionText(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteDatabaseMenuItem = {
|
const deleteDatabaseMenuItem = {
|
||||||
iconSrc: DeleteDatabaseIcon,
|
iconSrc: DeleteDatabaseIcon,
|
||||||
onClick: () => container.deleteDatabaseConfirmationPane.open(),
|
onClick: () => container.deleteDatabaseConfirmationPane.open(),
|
||||||
label: container.deleteDatabaseText()
|
label: container.deleteDatabaseText(),
|
||||||
};
|
};
|
||||||
return [newCollectionMenuItem, deleteDatabaseMenuItem];
|
return [newCollectionMenuItem, deleteDatabaseMenuItem];
|
||||||
}
|
}
|
||||||
@@ -53,7 +52,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
items.push({
|
items.push({
|
||||||
iconSrc: AddSqlQueryIcon,
|
iconSrc: AddSqlQueryIcon,
|
||||||
onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null),
|
onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null),
|
||||||
label: "New SQL Query"
|
label: "New SQL Query",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +60,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
items.push({
|
items.push({
|
||||||
iconSrc: AddSqlQueryIcon,
|
iconSrc: AddSqlQueryIcon,
|
||||||
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null),
|
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null),
|
||||||
label: "New Query"
|
label: "New Query",
|
||||||
});
|
});
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
@@ -70,7 +69,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
selectedCollection && selectedCollection.onNewMongoShellClick();
|
||||||
},
|
},
|
||||||
label: "New Shell"
|
label: "New Shell",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
|
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
|
||||||
},
|
},
|
||||||
label: "New Stored Procedure"
|
label: "New Stored Procedure",
|
||||||
});
|
});
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
@@ -90,7 +89,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null);
|
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null);
|
||||||
},
|
},
|
||||||
label: "New UDF"
|
label: "New UDF",
|
||||||
});
|
});
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
@@ -99,7 +98,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null);
|
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null);
|
||||||
},
|
},
|
||||||
label: "New Trigger"
|
label: "New Trigger",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,13 +108,16 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
|
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
|
||||||
},
|
},
|
||||||
label: container.deleteCollectionText()
|
label: container.deleteCollectionText(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createStoreProcedureContextMenuItems(container: ViewModels.Explorer): TreeNodeMenuItem[] {
|
public static createStoreProcedureContextMenuItems(
|
||||||
|
container: ViewModels.Explorer,
|
||||||
|
storedProcedure: ViewModels.StoredProcedure
|
||||||
|
): TreeNodeMenuItem[] {
|
||||||
if (container.isPreferredApiCassandra()) {
|
if (container.isPreferredApiCassandra()) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -123,16 +125,16 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
iconSrc: DeleteSprocIcon,
|
iconSrc: DeleteSprocIcon,
|
||||||
onClick: () => {
|
onClick: () => storedProcedure.delete(),
|
||||||
const selectedStoreProcedure: ViewModels.StoredProcedure = container.findSelectedStoredProcedure();
|
label: "Delete Store Procedure",
|
||||||
selectedStoreProcedure && selectedStoreProcedure.delete(selectedStoreProcedure, null);
|
},
|
||||||
},
|
|
||||||
label: "Delete Store Procedure"
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createTriggerContextMenuItems(container: ViewModels.Explorer): TreeNodeMenuItem[] {
|
public static createTriggerContextMenuItems(
|
||||||
|
container: ViewModels.Explorer,
|
||||||
|
trigger: ViewModels.Trigger
|
||||||
|
): TreeNodeMenuItem[] {
|
||||||
if (container.isPreferredApiCassandra()) {
|
if (container.isPreferredApiCassandra()) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -140,16 +142,16 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
iconSrc: DeleteTriggerIcon,
|
iconSrc: DeleteTriggerIcon,
|
||||||
onClick: () => {
|
onClick: () => trigger.delete(),
|
||||||
const selectedTrigger: ViewModels.Trigger = container.findSelectedTrigger();
|
label: "Delete Trigger",
|
||||||
selectedTrigger && selectedTrigger.delete(selectedTrigger, null);
|
},
|
||||||
},
|
|
||||||
label: "Delete Trigger"
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createUserDefinedFunctionContextMenuItems(container: ViewModels.Explorer): TreeNodeMenuItem[] {
|
public static createUserDefinedFunctionContextMenuItems(
|
||||||
|
container: ViewModels.Explorer,
|
||||||
|
userDefinedFunction: ViewModels.UserDefinedFunction
|
||||||
|
): TreeNodeMenuItem[] {
|
||||||
if (container.isPreferredApiCassandra()) {
|
if (container.isPreferredApiCassandra()) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -157,266 +159,9 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
iconSrc: DeleteUDFIcon,
|
iconSrc: DeleteUDFIcon,
|
||||||
onClick: () => {
|
onClick: () => userDefinedFunction.delete(),
|
||||||
const selectedUDF: ViewModels.UserDefinedFunction = container.findSelectedUDF();
|
label: "Delete User Defined Function",
|
||||||
selectedUDF && selectedUDF.delete(selectedUDF, null);
|
},
|
||||||
},
|
|
||||||
label: "Delete User Defined Function"
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Current resource tree (in KO)
|
|
||||||
* TODO: Remove when switching to new resource tree
|
|
||||||
*/
|
|
||||||
export class ContextMenuButtonFactory {
|
|
||||||
public static createDatabaseContextMenuButton(
|
|
||||||
container: ViewModels.Explorer,
|
|
||||||
btnParams: DatabaseContextMenuButtonParams
|
|
||||||
): CommandButtonOptions[] {
|
|
||||||
const addCollectionId = `${btnParams.databaseId}-${container.addCollectionText()}`;
|
|
||||||
const deleteDatabaseId = `${btnParams.databaseId}-${container.deleteDatabaseText()}`;
|
|
||||||
const newCollectionButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: AddCollectionIcon,
|
|
||||||
id: addCollectionId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
if (container.isPreferredApiCassandra()) {
|
|
||||||
container.cassandraAddCollectionPane.open();
|
|
||||||
} else {
|
|
||||||
container.addCollectionPane.open(container.selectedDatabaseId());
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedDatabase: ViewModels.Database = container.findSelectedDatabase();
|
|
||||||
selectedDatabase && selectedDatabase.contextMenu.hide(selectedDatabase, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: container.addCollectionText(),
|
|
||||||
hasPopup: true
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteDatabaseButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: DeleteDatabaseIcon,
|
|
||||||
id: deleteDatabaseId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const database: ViewModels.Database = container.findSelectedDatabase();
|
|
||||||
database.onDeleteDatabaseContextMenuClick(database, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: container.deleteDatabaseText(),
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: ko.computed<boolean>(() => container.isNoneSelected()),
|
|
||||||
visible: ko.computed<boolean>(() => !container.isNoneSelected())
|
|
||||||
};
|
|
||||||
|
|
||||||
return [newCollectionButtonOptions, deleteDatabaseButtonOptions];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static createCollectionContextMenuButton(
|
|
||||||
container: ViewModels.Explorer,
|
|
||||||
btnParams: CollectionContextMenuButtonParams
|
|
||||||
): CommandButtonOptions[] {
|
|
||||||
const newSqlQueryId = `${btnParams.databaseId}-${btnParams.collectionId}-newSqlQuery`;
|
|
||||||
const newSqlQueryForGraphId = `${btnParams.databaseId}-${btnParams.collectionId}-newSqlQueryForGraph`;
|
|
||||||
const newQueryForMongoId = `${btnParams.databaseId}-${btnParams.collectionId}-newQuery`;
|
|
||||||
const newShellForMongoId = `${btnParams.databaseId}-${btnParams.collectionId}-newShell`;
|
|
||||||
const newStoredProcedureId = `${btnParams.databaseId}-${btnParams.collectionId}-newStoredProcedure`;
|
|
||||||
const udfId = `${btnParams.databaseId}-${btnParams.collectionId}-udf`;
|
|
||||||
const newTriggerId = `${btnParams.databaseId}-${btnParams.collectionId}-newTrigger`;
|
|
||||||
const deleteCollectionId = `${btnParams.databaseId}-${btnParams.collectionId}-${container.deleteCollectionText()}`;
|
|
||||||
|
|
||||||
const newSQLQueryButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: AddSqlQueryIcon,
|
|
||||||
id: newSqlQueryId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: "New SQL Query",
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: ko.computed<boolean>(
|
|
||||||
() => container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiDocumentDB()
|
|
||||||
),
|
|
||||||
visible: ko.computed<boolean>(
|
|
||||||
() => !container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiDocumentDB()
|
|
||||||
)
|
|
||||||
//TODO: Merge with add query logic below, same goes for CommandBarButtonFactory
|
|
||||||
};
|
|
||||||
|
|
||||||
const newSQLQueryButtonOptionsForGraph: CommandButtonOptions = {
|
|
||||||
iconSrc: AddSqlQueryIcon,
|
|
||||||
id: newSqlQueryForGraphId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: "New SQL Query",
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiGraph()),
|
|
||||||
visible: ko.computed<boolean>(() => !container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiGraph())
|
|
||||||
};
|
|
||||||
|
|
||||||
const newMongoQueryButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: AddSqlQueryIcon,
|
|
||||||
id: newQueryForMongoId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: "New Query",
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: ko.computed<boolean>(
|
|
||||||
() => container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()
|
|
||||||
),
|
|
||||||
visible: ko.computed<boolean>(
|
|
||||||
() => !container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
const newMongoShellButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: HostedTerminalIcon,
|
|
||||||
id: newShellForMongoId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
|
||||||
},
|
|
||||||
commandButtonLabel: "New Shell",
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: ko.computed<boolean>(
|
|
||||||
() => container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()
|
|
||||||
),
|
|
||||||
visible: ko.computed<boolean>(
|
|
||||||
() => !container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
const newStoredProcedureButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: AddStoredProcedureIcon,
|
|
||||||
id: newStoredProcedureId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: "New Stored Procedure",
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
|
|
||||||
visible: ko.computed<boolean>(
|
|
||||||
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
const newUserDefinedFunctionButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: AddUdfIcon,
|
|
||||||
id: udfId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: "New UDF",
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
|
|
||||||
visible: ko.computed<boolean>(
|
|
||||||
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
const newTriggerButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: AddTriggerIcon,
|
|
||||||
id: newTriggerId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: "New Trigger",
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
|
|
||||||
visible: ko.computed<boolean>(
|
|
||||||
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteCollectionButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: DeleteCollectionIcon,
|
|
||||||
id: deleteCollectionId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
|
||||||
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: container.deleteCollectionText(),
|
|
||||||
hasPopup: true,
|
|
||||||
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
|
|
||||||
visible: ko.computed<boolean>(() => !container.isDatabaseNodeOrNoneSelected())
|
|
||||||
//TODO: Change to isCollectionNodeorNoneSelected and same in CommandBarButtonFactory
|
|
||||||
};
|
|
||||||
|
|
||||||
return [
|
|
||||||
newSQLQueryButtonOptions,
|
|
||||||
newSQLQueryButtonOptionsForGraph,
|
|
||||||
newMongoQueryButtonOptions,
|
|
||||||
newMongoShellButtonOptions,
|
|
||||||
newStoredProcedureButtonOptions,
|
|
||||||
newUserDefinedFunctionButtonOptions,
|
|
||||||
newTriggerButtonOptions,
|
|
||||||
deleteCollectionButtonOptions
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static createStoreProcedureContextMenuButton(container: ViewModels.Explorer): CommandButtonOptions[] {
|
|
||||||
const deleteStoredProcedureId = "Context Menu - Delete Stored Procedure";
|
|
||||||
const deleteStoreProcedureButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: DeleteSprocIcon,
|
|
||||||
id: deleteStoredProcedureId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedStoreProcedure: ViewModels.StoredProcedure = container.findSelectedStoredProcedure();
|
|
||||||
selectedStoreProcedure && selectedStoreProcedure.delete(selectedStoreProcedure, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: "Delete Stored Procedure",
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
|
|
||||||
visible: ko.computed<boolean>(
|
|
||||||
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
return [deleteStoreProcedureButtonOptions];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static createTriggerContextMenuButton(container: ViewModels.Explorer): CommandButtonOptions[] {
|
|
||||||
const deleteTriggerId = "Context Menu - Delete Trigger";
|
|
||||||
const deleteTriggerButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: DeleteTriggerIcon,
|
|
||||||
id: deleteTriggerId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedTrigger: ViewModels.Trigger = container.findSelectedTrigger();
|
|
||||||
selectedTrigger && selectedTrigger.delete(selectedTrigger, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: "Delete Trigger",
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
|
|
||||||
visible: ko.computed<boolean>(
|
|
||||||
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
return [deleteTriggerButtonOptions];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static createUserDefinedFunctionContextMenuButton(container: ViewModels.Explorer): CommandButtonOptions[] {
|
|
||||||
const deleteUserDefinedFunctionId = "Context Menu - Delete User Defined Function";
|
|
||||||
const deleteUserDefinedFunctionButtonOptions: CommandButtonOptions = {
|
|
||||||
iconSrc: DeleteUDFIcon,
|
|
||||||
id: deleteUserDefinedFunctionId,
|
|
||||||
onCommandClick: () => {
|
|
||||||
const selectedUDF: ViewModels.UserDefinedFunction = container.findSelectedUDF();
|
|
||||||
selectedUDF && selectedUDF.delete(selectedUDF, null);
|
|
||||||
},
|
|
||||||
commandButtonLabel: "Delete User Defined Function",
|
|
||||||
hasPopup: false,
|
|
||||||
disabled: ko.computed<boolean>(() => container.isDatabaseNodeOrNoneSelected()),
|
|
||||||
visible: ko.computed<boolean>(
|
|
||||||
() => !container.isDatabaseNodeOrNoneSelected() && !container.isPreferredApiCassandra()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
return [deleteUserDefinedFunctionButtonOptions];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export class AccessibleElement extends React.Component<AccessibleElementProps> {
|
|||||||
...elementProps,
|
...elementProps,
|
||||||
onKeyPress: this.onKeyPress,
|
onKeyPress: this.onKeyPress,
|
||||||
onClick: this.props.onActivated,
|
onClick: this.props.onActivated,
|
||||||
tabIndex
|
tabIndex,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
|
|||||||
super(props);
|
super(props);
|
||||||
this.isExpanded = props.isExpanded;
|
this.isExpanded = props.isExpanded;
|
||||||
this.state = {
|
this.state = {
|
||||||
isExpanded: true
|
isExpanded: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
|
|||||||
if (this.props.isExpanded !== this.isExpanded) {
|
if (this.props.isExpanded !== this.isExpanded) {
|
||||||
this.isExpanded = this.props.isExpanded;
|
this.isExpanded = this.props.isExpanded;
|
||||||
this.setState({
|
this.setState({
|
||||||
isExpanded: this.props.isExpanded
|
isExpanded: this.props.isExpanded,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const createBlankProps = (): AccountSwitchComponentProps => {
|
|||||||
subscriptions: [],
|
subscriptions: [],
|
||||||
selectedSubscriptionId: null,
|
selectedSubscriptionId: null,
|
||||||
isLoadingSubscriptions: false,
|
isLoadingSubscriptions: false,
|
||||||
onSubscriptionChange: jest.fn()
|
onSubscriptionChange: jest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ const createBlankAccount = (): DatabaseAccount => {
|
|||||||
properties: null,
|
properties: null,
|
||||||
location: "",
|
location: "",
|
||||||
tags: null,
|
tags: null,
|
||||||
type: ""
|
type: "",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ const createBlankSubscription = (): Subscription => {
|
|||||||
state: "",
|
state: "",
|
||||||
subscriptionPolicies: null,
|
subscriptionPolicies: null,
|
||||||
tenantId: "",
|
tenantId: "",
|
||||||
uniqueDisplayName: ""
|
uniqueDisplayName: "",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: "switchSubscription",
|
key: "switchSubscription",
|
||||||
onRender: this._renderSubscriptionDropdown.bind(this)
|
onRender: this._renderSubscriptionDropdown.bind(this),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "switchAccount",
|
key: "switchAccount",
|
||||||
onRender: this._renderAccountDropDown.bind(this)
|
onRender: this._renderAccountDropDown.bind(this),
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonStyles: IButtonStyles = {
|
const buttonStyles: IButtonStyles = {
|
||||||
@@ -51,27 +51,27 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
paddingLeft: 10,
|
paddingLeft: 10,
|
||||||
marginRight: 5,
|
marginRight: 5,
|
||||||
backgroundColor: StyleConstants.BaseDark,
|
backgroundColor: StyleConstants.BaseDark,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
rootHovered: {
|
rootHovered: {
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
rootFocused: {
|
rootFocused: {
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
rootPressed: {
|
rootPressed: {
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
rootExpanded: {
|
rootExpanded: {
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
textContainer: {
|
textContainer: {
|
||||||
flexGrow: "initial"
|
flexGrow: "initial",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonProps: IButtonProps = {
|
const buttonProps: IButtonProps = {
|
||||||
@@ -79,7 +79,7 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
menuProps: menuProps,
|
menuProps: menuProps,
|
||||||
styles: buttonStyles,
|
styles: buttonStyles,
|
||||||
className: "accountSwitchButton",
|
className: "accountSwitchButton",
|
||||||
id: "accountSwitchButton"
|
id: "accountSwitchButton",
|
||||||
};
|
};
|
||||||
|
|
||||||
return <DefaultButton {...buttonProps} />;
|
return <DefaultButton {...buttonProps} />;
|
||||||
@@ -87,11 +87,11 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
|
|
||||||
private _renderSubscriptionDropdown(): JSX.Element {
|
private _renderSubscriptionDropdown(): JSX.Element {
|
||||||
const { subscriptions, selectedSubscriptionId, isLoadingSubscriptions } = this.props;
|
const { subscriptions, selectedSubscriptionId, isLoadingSubscriptions } = this.props;
|
||||||
const options: IDropdownOption[] = subscriptions.map(sub => {
|
const options: IDropdownOption[] = subscriptions.map((sub) => {
|
||||||
return {
|
return {
|
||||||
key: sub.subscriptionId,
|
key: sub.subscriptionId,
|
||||||
text: sub.displayName,
|
text: sub.displayName,
|
||||||
data: sub
|
data: sub,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -109,8 +109,8 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
defaultSelectedKey: selectedSubscriptionId,
|
defaultSelectedKey: selectedSubscriptionId,
|
||||||
placeholder: placeHolderText,
|
placeholder: placeHolderText,
|
||||||
styles: {
|
styles: {
|
||||||
callout: "accountSwitchSubscriptionDropdownMenu"
|
callout: "accountSwitchSubscriptionDropdownMenu",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Dropdown {...dropdownProps} />;
|
return <Dropdown {...dropdownProps} />;
|
||||||
@@ -126,11 +126,11 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
|
|
||||||
private _renderAccountDropDown(): JSX.Element {
|
private _renderAccountDropDown(): JSX.Element {
|
||||||
const { accounts, selectedAccountName, isLoadingAccounts } = this.props;
|
const { accounts, selectedAccountName, isLoadingAccounts } = this.props;
|
||||||
const options: IDropdownOption[] = accounts.map(account => {
|
const options: IDropdownOption[] = accounts.map((account) => {
|
||||||
return {
|
return {
|
||||||
key: account.name,
|
key: account.name,
|
||||||
text: account.name,
|
text: account.name,
|
||||||
data: account
|
data: account,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// Fabric UI will also try to select the first non-disabled option from dropdown.
|
// Fabric UI will also try to select the first non-disabled option from dropdown.
|
||||||
@@ -138,7 +138,7 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
options.unshift({
|
options.unshift({
|
||||||
key: "select from list",
|
key: "select from list",
|
||||||
text: "Select Cosmos DB account from list",
|
text: "Select Cosmos DB account from list",
|
||||||
data: undefined
|
data: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const placeHolderText = isLoadingAccounts
|
const placeHolderText = isLoadingAccounts
|
||||||
@@ -155,8 +155,8 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
defaultSelectedKey: selectedAccountName,
|
defaultSelectedKey: selectedAccountName,
|
||||||
placeholder: placeHolderText,
|
placeholder: placeHolderText,
|
||||||
styles: {
|
styles: {
|
||||||
callout: "accountSwitchAccountDropdownMenu"
|
callout: "accountSwitchAccountDropdownMenu",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Dropdown {...dropdownProps} />;
|
return <Dropdown {...dropdownProps} />;
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { DefaultButton, IButtonStyles } from "office-ui-fabric-react/lib/Button"
|
|||||||
import {
|
import {
|
||||||
IContextualMenuItem,
|
IContextualMenuItem,
|
||||||
IContextualMenuProps,
|
IContextualMenuProps,
|
||||||
ContextualMenuItemType
|
ContextualMenuItemType,
|
||||||
} from "office-ui-fabric-react/lib/ContextualMenu";
|
} from "office-ui-fabric-react/lib/ContextualMenu";
|
||||||
import { Logger } from "../../../Common/Logger";
|
import * as Logger from "../../../Common/Logger";
|
||||||
|
|
||||||
export interface ArcadiaMenuPickerProps {
|
export interface ArcadiaMenuPickerProps {
|
||||||
selectText?: string;
|
selectText?: string;
|
||||||
@@ -33,7 +33,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
constructor(props: ArcadiaMenuPickerProps) {
|
constructor(props: ArcadiaMenuPickerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedSparkPool: props.selectedSparkPool
|
selectedSparkPool: props.selectedSparkPool,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
try {
|
try {
|
||||||
this.props.onSparkPoolSelect(e, item);
|
this.props.onSparkPoolSelect(e, item);
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedSparkPool: item.text
|
selectedSparkPool: item.text,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "ArcadiaMenuPicker/_onSparkPoolClicked");
|
Logger.logError(error, "ArcadiaMenuPicker/_onSparkPoolClicked");
|
||||||
@@ -68,28 +68,28 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { workspaces } = this.props;
|
const { workspaces } = this.props;
|
||||||
let workspaceMenuItems: IContextualMenuItem[] = workspaces.map(workspace => {
|
let workspaceMenuItems: IContextualMenuItem[] = workspaces.map((workspace) => {
|
||||||
let sparkPoolsMenuProps: IContextualMenuProps = {
|
let sparkPoolsMenuProps: IContextualMenuProps = {
|
||||||
items: workspace.sparkPools.map(
|
items: workspace.sparkPools.map(
|
||||||
(sparkpool): IContextualMenuItem => ({
|
(sparkpool): IContextualMenuItem => ({
|
||||||
key: sparkpool.id,
|
key: sparkpool.id,
|
||||||
text: sparkpool.name,
|
text: sparkpool.name,
|
||||||
onClick: this._onSparkPoolClicked
|
onClick: this._onSparkPoolClicked,
|
||||||
})
|
})
|
||||||
)
|
),
|
||||||
};
|
};
|
||||||
if (!sparkPoolsMenuProps.items.length) {
|
if (!sparkPoolsMenuProps.items.length) {
|
||||||
sparkPoolsMenuProps.items.push({
|
sparkPoolsMenuProps.items.push({
|
||||||
key: workspace.id,
|
key: workspace.id,
|
||||||
text: "Create new spark pool",
|
text: "Create new spark pool",
|
||||||
onClick: this._onCreateNewSparkPoolClicked
|
onClick: this._onCreateNewSparkPoolClicked,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: workspace.id,
|
key: workspace.id,
|
||||||
text: workspace.name,
|
text: workspace.name,
|
||||||
subMenuProps: this.props.disableSubmenu ? undefined : sparkPoolsMenuProps
|
subMenuProps: this.props.disableSubmenu ? undefined : sparkPoolsMenuProps,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
workspaceMenuItems.push({
|
workspaceMenuItems.push({
|
||||||
key: "create_workspace",
|
key: "create_workspace",
|
||||||
text: "Create new workspace",
|
text: "Create new workspace",
|
||||||
onClick: this._onCreateNewWorkspaceClicked
|
onClick: this._onCreateNewWorkspaceClicked,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,29 +106,29 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
backgroundColor: "transparent",
|
backgroundColor: "transparent",
|
||||||
margin: "auto 5px",
|
margin: "auto 5px",
|
||||||
padding: "0",
|
padding: "0",
|
||||||
border: "0"
|
border: "0",
|
||||||
},
|
},
|
||||||
rootHovered: {
|
rootHovered: {
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent",
|
||||||
},
|
},
|
||||||
rootChecked: {
|
rootChecked: {
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent",
|
||||||
},
|
},
|
||||||
rootFocused: {
|
rootFocused: {
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent",
|
||||||
},
|
},
|
||||||
rootExpanded: {
|
rootExpanded: {
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent",
|
||||||
},
|
},
|
||||||
flexContainer: {
|
flexContainer: {
|
||||||
height: "30px",
|
height: "30px",
|
||||||
border: "1px solid #a6a6a6",
|
border: "1px solid #a6a6a6",
|
||||||
padding: "0 8px"
|
padding: "0 8px",
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
fontWeight: "400",
|
fontWeight: "400",
|
||||||
fontSize: "12px"
|
fontSize: "12px",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -137,7 +137,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
persistMenu={true}
|
persistMenu={true}
|
||||||
className="arcadia-menu-picker"
|
className="arcadia-menu-picker"
|
||||||
menuProps={{
|
menuProps={{
|
||||||
items: workspaceMenuItems
|
items: workspaceMenuItems,
|
||||||
}}
|
}}
|
||||||
styles={dropdownStyle}
|
styles={dropdownStyle}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export class CollapsiblePanelComponent {
|
|||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
viewModel: CollapsiblePanelViewModel,
|
viewModel: CollapsiblePanelViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
@import "../../../../less/Common/Constants";
|
|
||||||
|
|
||||||
@ButtonIconSize: 18px;
|
|
||||||
|
|
||||||
.commandBar {
|
|
||||||
padding-left: @DefaultSpace;
|
|
||||||
border-bottom: @ButtonBorderWidth solid @BaseMedium;
|
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
|
||||||
height: @topcommandbarheight;
|
|
||||||
|
|
||||||
.staticCommands {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
display: flex;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overflowCommands {
|
|
||||||
display:flex;
|
|
||||||
flex: 1 0 auto;
|
|
||||||
|
|
||||||
.visibleCommands {
|
|
||||||
display: inline-flex;
|
|
||||||
list-style: none;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.partialSplitterContainer {
|
|
||||||
padding: @SmallSpace @DefaultSpace @SmallSpace @SmallSpace;
|
|
||||||
.flex-display();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandExpand {
|
|
||||||
border: none;
|
|
||||||
padding: 0px;
|
|
||||||
direction: rtl;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.hover();
|
|
||||||
cursor: pointer;
|
|
||||||
& > .commandDropdownContainer {
|
|
||||||
display: block !important; // TODO: Remove after reusing KO mouseover and mouseout event handlers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandDropdownLauncher {
|
|
||||||
direction: ltr;
|
|
||||||
padding-top: @SmallSpace;
|
|
||||||
|
|
||||||
.commandIcon {
|
|
||||||
vertical-align: text-top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandBarEllipses {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hiddenCommandsContainer > .commandDropdownLauncher {
|
|
||||||
padding: 0px @DefaultSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandDropdownContainer {
|
|
||||||
display: none;
|
|
||||||
z-index: 1000;
|
|
||||||
direction: ltr;
|
|
||||||
position: absolute;
|
|
||||||
width: fit-content;
|
|
||||||
padding: 0px;
|
|
||||||
background-color: @BaseLight;
|
|
||||||
box-shadow: 1px 2px 6px @BaseMediumHigh, -2px 2px 6px @BaseMediumHigh;
|
|
||||||
|
|
||||||
.commandDropdown {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.feedbackButton {
|
|
||||||
margin-right: @LargeSpace;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
command-button,
|
|
||||||
.commandButtonReact {
|
|
||||||
display: inline-flex;
|
|
||||||
.commandButtonComponent {
|
|
||||||
width: 100%;
|
|
||||||
color: @BaseHigh;
|
|
||||||
background-color: transparent;
|
|
||||||
text-decoration: none;
|
|
||||||
border: @ButtonBorderWidth solid transparent;
|
|
||||||
.flex-display();
|
|
||||||
|
|
||||||
&:hover:not(.commandDisabled) {
|
|
||||||
cursor: pointer;
|
|
||||||
.hover();
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active:not(.commandDisabled) {
|
|
||||||
border: @ButtonBorderWidth dashed @AccentMedium;
|
|
||||||
.active();
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus:not(.commandDisabled) {
|
|
||||||
border: @ButtonBorderWidth dashed @AccentMedium;
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandContent {
|
|
||||||
padding: @DefaultSpace @DefaultSpace @DefaultSpace;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
|
|
||||||
.commandIcon {
|
|
||||||
margin: 0 @SmallSpace 0 0;
|
|
||||||
vertical-align: text-top;
|
|
||||||
width: @ButtonIconSize;
|
|
||||||
height: @ButtonIconSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandLabel {
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandContent .hasHiddenItems {
|
|
||||||
padding-right: @SmallSpace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandButtonComponent.commandDisabled {
|
|
||||||
color: @BaseMediumHigh;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandExpand {
|
|
||||||
padding-top: @SmallSpace;
|
|
||||||
padding-bottom: @SmallSpace;
|
|
||||||
&:hover {
|
|
||||||
.hover();
|
|
||||||
& > .commandDropdownContainer {
|
|
||||||
display: block !important; // TODO: Remove after reusing KO mouseover and mouseout event handlers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandDropdownLauncher {
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-flex;
|
|
||||||
|
|
||||||
.commandButtonComponent {
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.expandDropdown {
|
|
||||||
padding: @SmallSpace;
|
|
||||||
|
|
||||||
img {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.partialSplitter {
|
|
||||||
margin: @SmallSpace 0px 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandButtonComponent[tabindex]:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedButton {
|
|
||||||
background-color: @AccentLow;
|
|
||||||
outline: none
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.partialSplitter {
|
|
||||||
border-left: @ButtonBorderWidth solid @BaseMediumHigh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.commandDropdown .commandButtonComponent {
|
|
||||||
padding-left: 0px;
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
import { CommandButtonComponent, CommandButtonOptions } from "./CommandButton";
|
|
||||||
|
|
||||||
const mockLabel = "Some Label";
|
|
||||||
const id = "Some id";
|
|
||||||
|
|
||||||
function buildComponent(buttonOptions: any) {
|
|
||||||
document.body.innerHTML = CommandButtonComponent.template as any;
|
|
||||||
const vm = new CommandButtonComponent.viewModel(buttonOptions);
|
|
||||||
ko.applyBindings(vm);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("Command Button Component", () => {
|
|
||||||
function buildButtonOptions(
|
|
||||||
onClick: () => void,
|
|
||||||
id?: string,
|
|
||||||
label?: string,
|
|
||||||
disabled?: ko.Observable<boolean>,
|
|
||||||
visible?: ko.Observable<boolean>,
|
|
||||||
tooltipText?: string
|
|
||||||
): { buttonProps: CommandButtonOptions } {
|
|
||||||
return {
|
|
||||||
buttonProps: {
|
|
||||||
iconSrc: "images/AddCollection.svg",
|
|
||||||
id: id,
|
|
||||||
commandButtonLabel: label || mockLabel,
|
|
||||||
disabled: disabled,
|
|
||||||
visible: visible,
|
|
||||||
tooltipText: tooltipText,
|
|
||||||
hasPopup: false,
|
|
||||||
onCommandClick: onClick
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildSplitterButtonOptions(
|
|
||||||
onClick: () => void,
|
|
||||||
id?: string,
|
|
||||||
label?: string,
|
|
||||||
disabled?: ko.Observable<boolean>,
|
|
||||||
visible?: ko.Observable<boolean>,
|
|
||||||
tooltipText?: string
|
|
||||||
): { buttonProps: CommandButtonOptions } {
|
|
||||||
const child: CommandButtonOptions = {
|
|
||||||
iconSrc: "images/settings_15x15.svg",
|
|
||||||
id: id,
|
|
||||||
commandButtonLabel: label || mockLabel,
|
|
||||||
disabled: disabled,
|
|
||||||
visible: visible,
|
|
||||||
tooltipText: tooltipText,
|
|
||||||
hasPopup: false,
|
|
||||||
onCommandClick: onClick
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
buttonProps: {
|
|
||||||
iconSrc: "images/AddCollection.svg",
|
|
||||||
id: id,
|
|
||||||
commandButtonLabel: label || mockLabel,
|
|
||||||
disabled: disabled,
|
|
||||||
visible: visible,
|
|
||||||
tooltipText: tooltipText,
|
|
||||||
hasPopup: false,
|
|
||||||
onCommandClick: onClick,
|
|
||||||
children: [child]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
ko.cleanNode(document);
|
|
||||||
document.body.innerHTML = "";
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Rendering", () => {
|
|
||||||
it("should display button label", () => {
|
|
||||||
const buttonOptions = buildButtonOptions(() => {
|
|
||||||
/** do nothing **/
|
|
||||||
}, mockLabel);
|
|
||||||
buildComponent(buttonOptions);
|
|
||||||
expect(document.getElementsByClassName("commandButtonComponent").item(0).textContent).toContain(mockLabel);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should display button icon", () => {
|
|
||||||
const buttonOptions = buildButtonOptions(() => {
|
|
||||||
/** do nothing **/
|
|
||||||
});
|
|
||||||
buildComponent(buttonOptions);
|
|
||||||
expect(
|
|
||||||
document
|
|
||||||
.getElementsByTagName("img")
|
|
||||||
.item(0)
|
|
||||||
.getAttribute("src")
|
|
||||||
).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Behavior", () => {
|
|
||||||
let clickSpy: jasmine.Spy;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
clickSpy = jasmine.createSpy("Command button click spy");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should trigger the click handler when the command button is clicked", () => {
|
|
||||||
const buttonOptions = buildButtonOptions(() => clickSpy());
|
|
||||||
buildComponent(buttonOptions);
|
|
||||||
document
|
|
||||||
.getElementsByClassName("commandButtonComponent")
|
|
||||||
.item(0)
|
|
||||||
.dispatchEvent(new Event("click"));
|
|
||||||
expect(clickSpy).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not trigger the click handler when command button is disabled", () => {
|
|
||||||
const buttonOptions = buildButtonOptions(() => clickSpy(), id, mockLabel, ko.observable(true));
|
|
||||||
buildComponent(buttonOptions);
|
|
||||||
document
|
|
||||||
.getElementsByClassName("commandButtonComponent")
|
|
||||||
.item(0)
|
|
||||||
.dispatchEvent(new Event("click"));
|
|
||||||
expect(clickSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not have a dropdown if it has no child", () => {
|
|
||||||
const buttonOptions = buildButtonOptions(() => clickSpy(), id, mockLabel, ko.observable(true));
|
|
||||||
buildComponent(buttonOptions);
|
|
||||||
const dropdownSize = document.getElementsByClassName("commandExpand").length;
|
|
||||||
expect(dropdownSize).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have a dropdown if it has a child", () => {
|
|
||||||
const buttonOptions = buildSplitterButtonOptions(() => clickSpy(), id, mockLabel, ko.observable(true));
|
|
||||||
buildComponent(buttonOptions);
|
|
||||||
const dropdownSize = document.getElementsByClassName("commandExpand").length;
|
|
||||||
expect(dropdownSize).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
/**
|
|
||||||
* How to use this component:
|
|
||||||
*
|
|
||||||
* In your html markup, use:
|
|
||||||
* <command-button params="{
|
|
||||||
* iconSrc: '/icon/example/src/',
|
|
||||||
* onCommandClick: () => { doSomething },
|
|
||||||
* commandButtonLabel: 'Some Label'
|
|
||||||
* disabled: true/false
|
|
||||||
* }">
|
|
||||||
* </command-button>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
|
||||||
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
|
||||||
import { KeyCodes } from "../../../Common/Constants";
|
|
||||||
|
|
||||||
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import template from "./command-button.html";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options for this component
|
|
||||||
*/
|
|
||||||
export interface CommandButtonOptions {
|
|
||||||
/**
|
|
||||||
* image source for the button icon
|
|
||||||
*/
|
|
||||||
iconSrc: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Id for the button icon
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Click handler for command button click
|
|
||||||
*/
|
|
||||||
onCommandClick: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Label for the button
|
|
||||||
*/
|
|
||||||
commandButtonLabel: string | ko.Observable<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if this button opens a tab or pane, false otherwise.
|
|
||||||
*/
|
|
||||||
hasPopup: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enabled/disabled state of command button
|
|
||||||
*/
|
|
||||||
disabled?: ko.Subscribable<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visibility/Invisibility of the button
|
|
||||||
*/
|
|
||||||
visible?: ko.Subscribable<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not the button should have the 'selectedButton' styling
|
|
||||||
*/
|
|
||||||
isSelected?: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text to displayed in the tooltip on hover
|
|
||||||
*/
|
|
||||||
tooltipText?: string | ko.Observable<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback triggered when the template is bound to the component
|
|
||||||
*/
|
|
||||||
onTemplateReady?: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* tabindex for the command button
|
|
||||||
*/
|
|
||||||
tabIndex?: ko.Observable<number>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Childrens command buttons to hide in the dropdown
|
|
||||||
*/
|
|
||||||
children?: CommandButtonOptions[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CommandButtonViewModel extends WaitsForTemplateViewModel implements ViewModels.CommandButton {
|
|
||||||
public commandClickCallback: () => void;
|
|
||||||
public commandButtonId: string;
|
|
||||||
public disabled: ko.Subscribable<boolean>;
|
|
||||||
public visible: ko.Subscribable<boolean>;
|
|
||||||
public isSelected: ko.Observable<boolean>;
|
|
||||||
public iconSrc: string;
|
|
||||||
public commandButtonLabel: ko.Observable<string>;
|
|
||||||
public tooltipText: ko.Observable<string>;
|
|
||||||
public tabIndex: ko.Observable<number>;
|
|
||||||
public isTemplateReady: ko.Observable<boolean>;
|
|
||||||
public hasPopup: boolean;
|
|
||||||
public children: ko.ObservableArray<CommandButtonOptions>;
|
|
||||||
|
|
||||||
public constructor(options: { buttonProps: CommandButtonOptions }) {
|
|
||||||
super();
|
|
||||||
const props = options.buttonProps;
|
|
||||||
const commandButtonLabel = props.commandButtonLabel;
|
|
||||||
const tooltipText = props.tooltipText;
|
|
||||||
this.commandButtonLabel =
|
|
||||||
typeof commandButtonLabel === "string" ? ko.observable<string>(commandButtonLabel) : commandButtonLabel;
|
|
||||||
this.commandButtonId = props.id;
|
|
||||||
this.disabled = props.disabled || ko.observable(false);
|
|
||||||
this.visible = props.visible || ko.observable(true);
|
|
||||||
this.isSelected = props.isSelected || ko.observable(false);
|
|
||||||
this.iconSrc = props.iconSrc;
|
|
||||||
this.tabIndex = props.tabIndex || ko.observable(0);
|
|
||||||
this.hasPopup = props.hasPopup;
|
|
||||||
this.children = ko.observableArray(props.children);
|
|
||||||
|
|
||||||
super.onTemplateReady((isTemplateReady: boolean) => {
|
|
||||||
if (isTemplateReady && props.onTemplateReady) {
|
|
||||||
props.onTemplateReady();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (tooltipText && typeof tooltipText === "string") {
|
|
||||||
this.tooltipText = ko.observable<string>(tooltipText);
|
|
||||||
} else if (tooltipText && typeof tooltipText === "function") {
|
|
||||||
this.tooltipText = tooltipText;
|
|
||||||
} else {
|
|
||||||
this.tooltipText = this.commandButtonLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.commandClickCallback = () => {
|
|
||||||
if (this.disabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const el = document.querySelector(".commandDropdownContainer") as HTMLElement;
|
|
||||||
if (el) {
|
|
||||||
el.style.display = "none";
|
|
||||||
}
|
|
||||||
props.onCommandClick();
|
|
||||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
|
||||||
commandButtonClicked: this.commandButtonLabel
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public onKeyPress(source: any, event: KeyboardEvent): boolean {
|
|
||||||
if (event.keyCode === KeyCodes.Space || event.keyCode === KeyCodes.Enter) {
|
|
||||||
this.commandClickCallback && this.commandClickCallback();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public onLauncherKeyDown(source: any, event: KeyboardEvent): boolean {
|
|
||||||
// TODO: Convert JQuery code into Knockout
|
|
||||||
if (event.keyCode === KeyCodes.DownArrow) {
|
|
||||||
$(event.target)
|
|
||||||
.parent()
|
|
||||||
.siblings()
|
|
||||||
.children(".commandExpand")
|
|
||||||
.children(".commandDropdownContainer")
|
|
||||||
.hide();
|
|
||||||
$(event.target)
|
|
||||||
.children(".commandDropdownContainer")
|
|
||||||
.show()
|
|
||||||
.focus();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (event.keyCode === KeyCodes.UpArrow) {
|
|
||||||
$(event.target)
|
|
||||||
.children(".commandDropdownContainer")
|
|
||||||
.hide();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class for ko component registration
|
|
||||||
*/
|
|
||||||
export const CommandButtonComponent = {
|
|
||||||
viewModel: CommandButtonViewModel,
|
|
||||||
template
|
|
||||||
};
|
|
||||||
@@ -15,15 +15,20 @@ import { ArcadiaMenuPickerProps } from "../Arcadia/ArcadiaMenuPicker";
|
|||||||
* Options for this component
|
* Options for this component
|
||||||
*/
|
*/
|
||||||
export interface CommandButtonComponentProps {
|
export interface CommandButtonComponentProps {
|
||||||
|
/**
|
||||||
|
* font icon name for the button
|
||||||
|
*/
|
||||||
|
iconName?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* image source for the button icon
|
* image source for the button icon
|
||||||
*/
|
*/
|
||||||
iconSrc: string;
|
iconSrc?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* image alt for accessibility
|
* image alt for accessibility
|
||||||
*/
|
*/
|
||||||
iconAlt: string;
|
iconAlt?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Click handler for command button click
|
* Click handler for command button click
|
||||||
@@ -144,9 +149,7 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
|
|||||||
private onLauncherKeyDown(event: React.KeyboardEvent<HTMLDivElement>): boolean {
|
private onLauncherKeyDown(event: React.KeyboardEvent<HTMLDivElement>): boolean {
|
||||||
if (event.keyCode === KeyCodes.DownArrow) {
|
if (event.keyCode === KeyCodes.DownArrow) {
|
||||||
$(this.dropdownElt).hide();
|
$(this.dropdownElt).hide();
|
||||||
$(this.dropdownElt)
|
$(this.dropdownElt).show().focus();
|
||||||
.show()
|
|
||||||
.focus();
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -182,7 +185,7 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
|
|||||||
}
|
}
|
||||||
this.props.onCommandClick(e);
|
this.props.onCommandClick(e);
|
||||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||||
commandButtonClicked: this.props.commandButtonLabel
|
commandButtonClicked: this.props.commandButtonLabel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
<span
|
|
||||||
class="commandButtonComponent"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="setTemplateReady: true,
|
|
||||||
css: {
|
|
||||||
commandDisabled: disabled,
|
|
||||||
selectedButton: isSelected
|
|
||||||
},
|
|
||||||
event: {
|
|
||||||
keypress: onKeyPress
|
|
||||||
},
|
|
||||||
attr: {
|
|
||||||
title: tooltipText,
|
|
||||||
id: commandButtonId,
|
|
||||||
tabindex: tabIndex ,
|
|
||||||
'aria-disabled': disabled,
|
|
||||||
'aria-haspopup': hasPopup
|
|
||||||
},
|
|
||||||
click: commandClickCallback,
|
|
||||||
visible: visible"
|
|
||||||
>
|
|
||||||
<div class="commandContent" data-bind="css: { hasHiddenItems: children().length > 0 }">
|
|
||||||
<img class="commandIcon" data-bind="attr: {src: iconSrc, alt: commandButtonLabel}" />
|
|
||||||
<span class="commandLabel" data-bind="text: commandButtonLabel"></span>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
<!-- ko if: children().length > 0 -->
|
|
||||||
<div class="commandExpand" tabindex="0" data-bind="visible: visible, event: { keydown: onLauncherKeyDown }">
|
|
||||||
<div class="commandDropdownLauncher">
|
|
||||||
<span class="partialSplitter"></span>
|
|
||||||
<span class="expandDropdown"> <img src="/QueryBuilder/CollapseChevronDown_16x.png" /> </span>
|
|
||||||
</div>
|
|
||||||
<div class="commandDropdownContainer">
|
|
||||||
<div class="commandDropdown" data-bind="foreach: children">
|
|
||||||
<command-button params="{buttonProps: $data}"></command-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
@@ -53,26 +53,26 @@ export class DialogComponent extends React.Component<DialogProps, {}> {
|
|||||||
subText: this.props.subText,
|
subText: this.props.subText,
|
||||||
styles: {
|
styles: {
|
||||||
title: { fontSize: DIALOG_TITLE_FONT_SIZE, fontWeight: DIALOG_TITLE_FONT_WEIGHT },
|
title: { fontSize: DIALOG_TITLE_FONT_SIZE, fontWeight: DIALOG_TITLE_FONT_WEIGHT },
|
||||||
subText: { fontSize: DIALOG_SUBTEXT_FONT_SIZE }
|
subText: { fontSize: DIALOG_SUBTEXT_FONT_SIZE },
|
||||||
},
|
},
|
||||||
showCloseButton: false
|
showCloseButton: false,
|
||||||
},
|
},
|
||||||
modalProps: { isBlocking: this.props.isModal },
|
modalProps: { isBlocking: this.props.isModal },
|
||||||
minWidth: DIALOG_MIN_WIDTH,
|
minWidth: DIALOG_MIN_WIDTH,
|
||||||
maxWidth: DIALOG_MAX_WIDTH
|
maxWidth: DIALOG_MAX_WIDTH,
|
||||||
};
|
};
|
||||||
const textFieldProps: ITextFieldProps = this.props.textFieldProps;
|
const textFieldProps: ITextFieldProps = this.props.textFieldProps;
|
||||||
const linkProps: LinkProps = this.props.linkProps;
|
const linkProps: LinkProps = this.props.linkProps;
|
||||||
const primaryButtonProps: IButtonProps = {
|
const primaryButtonProps: IButtonProps = {
|
||||||
text: this.props.primaryButtonText,
|
text: this.props.primaryButtonText,
|
||||||
disabled: this.props.primaryButtonDisabled || false,
|
disabled: this.props.primaryButtonDisabled || false,
|
||||||
onClick: this.props.onPrimaryButtonClick
|
onClick: this.props.onPrimaryButtonClick,
|
||||||
};
|
};
|
||||||
const secondaryButtonProps: IButtonProps =
|
const secondaryButtonProps: IButtonProps =
|
||||||
this.props.secondaryButtonText && this.props.onSecondaryButtonClick
|
this.props.secondaryButtonText && this.props.onSecondaryButtonClick
|
||||||
? {
|
? {
|
||||||
text: this.props.secondaryButtonText,
|
text: this.props.secondaryButtonText,
|
||||||
onClick: this.props.onSecondaryButtonClick
|
onClick: this.props.onSecondaryButtonClick,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class DiffEditorComponent {
|
|||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
viewModel: DiffEditorViewModel,
|
viewModel: DiffEditorViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ export class DiffEditorViewModel {
|
|||||||
lineNumbers: this.params.lineNumbers || "off",
|
lineNumbers: this.params.lineNumbers || "off",
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
ariaLabel: this.params.ariaLabel,
|
ariaLabel: this.params.ariaLabel,
|
||||||
theme: this.params.theme
|
theme: this.params.theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.params.renderSideBySide !== undefined) {
|
if (this.params.renderSideBySide !== undefined) {
|
||||||
@@ -120,7 +120,7 @@ export class DiffEditorViewModel {
|
|||||||
);
|
);
|
||||||
diffEditor.setModel({
|
diffEditor.setModel({
|
||||||
original: originalModel,
|
original: originalModel,
|
||||||
modified: modifiedModel
|
modified: modifiedModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
createCallback(diffEditor);
|
createCallback(diffEditor);
|
||||||
@@ -147,7 +147,7 @@ export class DiffEditorViewModel {
|
|||||||
this.observer.observe(document.body, {
|
this.observer.observe(document.body, {
|
||||||
attributes: true,
|
attributes: true,
|
||||||
subtree: true,
|
subtree: true,
|
||||||
childList: true
|
childList: true,
|
||||||
});
|
});
|
||||||
this.editor.focus();
|
this.editor.focus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const createBlankProps = (): DefaultDirectoryDropdownProps => {
|
|||||||
return {
|
return {
|
||||||
defaultDirectoryId: "",
|
defaultDirectoryId: "",
|
||||||
directories: [],
|
directories: [],
|
||||||
onDefaultDirectoryChange: jest.fn()
|
onDefaultDirectoryChange: jest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ const createBlankDirectory = (): Tenant => {
|
|||||||
displayName: "",
|
displayName: "",
|
||||||
domains: [],
|
domains: [],
|
||||||
id: "",
|
id: "",
|
||||||
tenantId: ""
|
tenantId: "",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,27 +90,15 @@ describe("test function", () => {
|
|||||||
|
|
||||||
const wrapper = mount(<DefaultDirectoryDropdownComponent {...props} />);
|
const wrapper = mount(<DefaultDirectoryDropdownComponent {...props} />);
|
||||||
|
|
||||||
wrapper
|
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
|
||||||
.find("div.defaultDirectoryDropdown")
|
|
||||||
.find("div.ms-Dropdown")
|
|
||||||
.simulate("click");
|
|
||||||
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
||||||
wrapper
|
wrapper.find("button.ms-Dropdown-item").at(1).simulate("click");
|
||||||
.find("button.ms-Dropdown-item")
|
|
||||||
.at(1)
|
|
||||||
.simulate("click");
|
|
||||||
expect(props.onDefaultDirectoryChange).toBeCalled();
|
expect(props.onDefaultDirectoryChange).toBeCalled();
|
||||||
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
||||||
|
|
||||||
wrapper
|
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
|
||||||
.find("div.defaultDirectoryDropdown")
|
|
||||||
.find("div.ms-Dropdown")
|
|
||||||
.simulate("click");
|
|
||||||
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
||||||
wrapper
|
wrapper.find("button.ms-Dropdown-item").at(0).simulate("click");
|
||||||
.find("button.ms-Dropdown-item")
|
|
||||||
.at(0)
|
|
||||||
.simulate("click");
|
|
||||||
expect(props.onDefaultDirectoryChange).toBeCalled();
|
expect(props.onDefaultDirectoryChange).toBeCalled();
|
||||||
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
|
|||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const lastVisitedOption: IDropdownOption = {
|
const lastVisitedOption: IDropdownOption = {
|
||||||
key: DefaultDirectoryDropdownComponent.lastVisitedKey,
|
key: DefaultDirectoryDropdownComponent.lastVisitedKey,
|
||||||
text: "Sign in to your last visited directory"
|
text: "Sign in to your last visited directory",
|
||||||
};
|
};
|
||||||
const directoryOptions: Array<IDropdownOption> = this.props.directories.map(
|
const directoryOptions: Array<IDropdownOption> = this.props.directories.map(
|
||||||
(dirc): IDropdownOption => {
|
(dirc): IDropdownOption => {
|
||||||
return {
|
return {
|
||||||
key: dirc.tenantId,
|
key: dirc.tenantId,
|
||||||
text: `${dirc.displayName}(${dirc.tenantId})`
|
text: `${dirc.displayName}(${dirc.tenantId})`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -35,7 +35,7 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
|
|||||||
options: dropDownOptions,
|
options: dropDownOptions,
|
||||||
defaultSelectedKey: this.props.defaultDirectoryId ? this.props.defaultDirectoryId : lastVisitedOption.key,
|
defaultSelectedKey: this.props.defaultDirectoryId ? this.props.defaultDirectoryId : lastVisitedOption.key,
|
||||||
onChange: this._onDropdownChange,
|
onChange: this._onDropdownChange,
|
||||||
className: "defaultDirectoryDropdown"
|
className: "defaultDirectoryDropdown",
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Dropdown {...dropDownProps} />;
|
return <Dropdown {...dropDownProps} />;
|
||||||
@@ -56,12 +56,12 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
|
|||||||
countryCode: undefined,
|
countryCode: undefined,
|
||||||
displayName: undefined,
|
displayName: undefined,
|
||||||
domains: [],
|
domains: [],
|
||||||
id: undefined
|
id: undefined,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedDirectory = _.find(this.props.directories, d => d.tenantId === option.key);
|
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === option.key);
|
||||||
if (!selectedDirectory) {
|
if (!selectedDirectory) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const createBlankProps = (): DirectoryListProps => {
|
|||||||
return {
|
return {
|
||||||
selectedDirectoryId: undefined,
|
selectedDirectoryId: undefined,
|
||||||
directories: [],
|
directories: [],
|
||||||
onNewDirectorySelected: jest.fn()
|
onNewDirectorySelected: jest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ const createBlankDirectory = (): Tenant => {
|
|||||||
displayName: undefined,
|
displayName: undefined,
|
||||||
domains: [],
|
domains: [],
|
||||||
id: undefined,
|
id: undefined,
|
||||||
tenantId: undefined
|
tenantId: undefined,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
filterText: ""
|
filterText: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,12 +38,12 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
const filteredItems =
|
const filteredItems =
|
||||||
originalItems && originalItems.length && filterText
|
originalItems && originalItems.length && filterText
|
||||||
? originalItems.filter(
|
? originalItems.filter(
|
||||||
directory =>
|
(directory) =>
|
||||||
directory.displayName &&
|
directory.displayName &&
|
||||||
directory.displayName.toLowerCase().indexOf(filterText && filterText.toLowerCase()) >= 0
|
directory.displayName.toLowerCase().indexOf(filterText && filterText.toLowerCase()) >= 0
|
||||||
)
|
)
|
||||||
: originalItems;
|
: originalItems;
|
||||||
const filteredItemsSelected = filteredItems.map(t => {
|
const filteredItemsSelected = filteredItems.map((t) => {
|
||||||
let tenant: ListTenant = t;
|
let tenant: ListTenant = t;
|
||||||
tenant.selected = t.tenantId === selectedDirectoryId;
|
tenant.selected = t.tenantId === selectedDirectoryId;
|
||||||
return tenant;
|
return tenant;
|
||||||
@@ -53,7 +53,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
className: "directoryListFilterTextBox",
|
className: "directoryListFilterTextBox",
|
||||||
placeholder: "Filter by directory name",
|
placeholder: "Filter by directory name",
|
||||||
onChange: this._onFilterChanged,
|
onChange: this._onFilterChanged,
|
||||||
ariaLabel: "Directory filter text box"
|
ariaLabel: "Directory filter text box",
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: add magnify glass to search bar with onRenderSuffix
|
// TODO: add magnify glass to search bar with onRenderSuffix
|
||||||
@@ -69,7 +69,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
|
|
||||||
private _onFilterChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text?: string): void => {
|
private _onFilterChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text?: string): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
filterText: text
|
filterText: text,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,19 +84,19 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
height: "auto",
|
height: "auto",
|
||||||
borderBottom: "1px solid #ccc",
|
borderBottom: "1px solid #ccc",
|
||||||
padding: "1px 0",
|
padding: "1px 0",
|
||||||
width: "100%"
|
width: "100%",
|
||||||
},
|
},
|
||||||
rootDisabled: {
|
rootDisabled: {
|
||||||
backgroundColor: "#f1f1f8"
|
backgroundColor: "#f1f1f8",
|
||||||
},
|
},
|
||||||
rootHovered: {
|
rootHovered: {
|
||||||
backgroundColor: "rgba(85,179,255,.1)"
|
backgroundColor: "rgba(85,179,255,.1)",
|
||||||
},
|
},
|
||||||
flexContainer: {
|
flexContainer: {
|
||||||
height: "auto",
|
height: "auto",
|
||||||
justifyContent: "flex-start"
|
justifyContent: "flex-start",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -115,7 +115,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
}
|
}
|
||||||
const buttonElement = e.currentTarget;
|
const buttonElement = e.currentTarget;
|
||||||
const selectedDirectoryId = buttonElement.getElementsByClassName("directoryListItemId")[0].textContent;
|
const selectedDirectoryId = buttonElement.getElementsByClassName("directoryListItemId")[0].textContent;
|
||||||
const selectedDirectory = _.find(this.props.directories, d => d.tenantId === selectedDirectoryId);
|
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === selectedDirectoryId);
|
||||||
|
|
||||||
this.props.onNewDirectorySelected(selectedDirectory);
|
this.props.onNewDirectorySelected(selectedDirectory);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ describe("Dynamic List Component", () => {
|
|||||||
placeholder: placeholder,
|
placeholder: placeholder,
|
||||||
listItems: items,
|
listItems: items,
|
||||||
buttonText: mockButton,
|
buttonText: mockButton,
|
||||||
ariaLabel: mockAriaLabel
|
ariaLabel: mockAriaLabel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,5 +112,5 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
|
|||||||
*/
|
*/
|
||||||
export const DynamicListComponent = {
|
export const DynamicListComponent = {
|
||||||
viewModel: DynamicListViewModel,
|
viewModel: DynamicListViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class EditorComponent {
|
|||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
viewModel: EditorViewModel,
|
viewModel: EditorViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export class EditorReact extends React.Component<EditorReactProps> {
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
ariaLabel: this.props.ariaLabel,
|
ariaLabel: this.props.ariaLabel,
|
||||||
theme: this.props.theme,
|
theme: this.props.theme,
|
||||||
automaticLayout: true
|
automaticLayout: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.rootNode.innerHTML = "";
|
this.rootNode.innerHTML = "";
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class ErrorDisplayComponent {
|
|||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
viewModel: ErrorDisplayViewModel,
|
viewModel: ErrorDisplayViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="warningErrorContainer" data-bind="visible: !!params.errorMsg()">
|
<div class="warningErrorContainer" data-bind="visible: !!params.errorMsg()">
|
||||||
<div class="warningErrorContent">
|
<div class="warningErrorContent">
|
||||||
<span><img src="/error_red.svg" alt="Error"/></span>
|
<span><img src="/error_red.svg" alt="Error" /></span>
|
||||||
<span class="settingErrorMsg warningErrorDetailsLinkContainer" data-bind="text: params.errorMsg()"></span>
|
<span class="settingErrorMsg warningErrorDetailsLinkContainer" data-bind="text: params.errorMsg()"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
.featurePanelComponentContainer {
|
||||||
|
width: 800px;
|
||||||
|
|
||||||
|
.urlContainer {
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
padding: 10px;
|
||||||
|
overflow: auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkboxRow {
|
||||||
|
width: 390px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { shallow } from "enzyme";
|
||||||
|
|
||||||
|
import { FeaturePanelComponent } from "./FeaturePanelComponent";
|
||||||
|
|
||||||
|
describe("Feature panel", () => {
|
||||||
|
it("renders all flags", () => {
|
||||||
|
const wrapper = shallow(<FeaturePanelComponent />);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user