mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-06 03:00:23 +00:00
Compare commits
31 Commits
MPAC-2020-
...
users/srna
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d45af21996 | ||
|
|
a64109ebaa | ||
|
|
70f9b28499 | ||
|
|
78e70cc7cc | ||
|
|
f132a8546c | ||
|
|
e6acf6686f | ||
|
|
2904a1a60d | ||
|
|
8c792fd147 | ||
|
|
2a53dfabb5 | ||
|
|
14ef40029d | ||
|
|
dab6e43d0d | ||
|
|
aea168c893 | ||
|
|
fea321cd68 | ||
|
|
2e49ed45c3 | ||
|
|
6d142f16f9 | ||
|
|
6dcdacc8c4 | ||
|
|
33969581ac | ||
|
|
dc67c5f40b | ||
|
|
acc65c9588 | ||
|
|
6860d8db1c | ||
|
|
3187756d03 | ||
|
|
0f4ff0e49f | ||
|
|
4f86015be7 | ||
|
|
46cca859e3 | ||
|
|
574fdcaf37 | ||
|
|
eab6506940 | ||
|
|
050da28d6e | ||
|
|
ffae9baca2 | ||
|
|
e491c1a042 | ||
|
|
444e25c086 | ||
|
|
99c6a7ebcc |
@@ -1,5 +1,6 @@
|
|||||||
**/node_modules/
|
**/node_modules/
|
||||||
dist/
|
dist/
|
||||||
|
Contracts/
|
||||||
src/Api/Apis.ts
|
src/Api/Apis.ts
|
||||||
src/AuthType.ts
|
src/AuthType.ts
|
||||||
src/Bindings/BindingHandlersRegisterer.ts
|
src/Bindings/BindingHandlersRegisterer.ts
|
||||||
@@ -137,7 +138,6 @@ src/Explorer/Panes/AddDatabasePane.test.ts
|
|||||||
src/Explorer/Panes/AddDatabasePane.ts
|
src/Explorer/Panes/AddDatabasePane.ts
|
||||||
src/Explorer/Panes/BrowseQueriesPane.ts
|
src/Explorer/Panes/BrowseQueriesPane.ts
|
||||||
src/Explorer/Panes/CassandraAddCollectionPane.ts
|
src/Explorer/Panes/CassandraAddCollectionPane.ts
|
||||||
src/Explorer/Panes/ClusterLibraryPane.ts
|
|
||||||
src/Explorer/Panes/ContextualPaneBase.ts
|
src/Explorer/Panes/ContextualPaneBase.ts
|
||||||
src/Explorer/Panes/DeleteCollectionConfirmationPane.test.ts
|
src/Explorer/Panes/DeleteCollectionConfirmationPane.test.ts
|
||||||
src/Explorer/Panes/DeleteCollectionConfirmationPane.ts
|
src/Explorer/Panes/DeleteCollectionConfirmationPane.ts
|
||||||
@@ -145,7 +145,6 @@ src/Explorer/Panes/DeleteDatabaseConfirmationPane.test.ts
|
|||||||
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
|
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
|
||||||
src/Explorer/Panes/ExecuteSprocParamsPane.ts
|
src/Explorer/Panes/ExecuteSprocParamsPane.ts
|
||||||
src/Explorer/Panes/GraphStylingPane.ts
|
src/Explorer/Panes/GraphStylingPane.ts
|
||||||
src/Explorer/Panes/LibraryManagePane.ts
|
|
||||||
src/Explorer/Panes/LoadQueryPane.ts
|
src/Explorer/Panes/LoadQueryPane.ts
|
||||||
src/Explorer/Panes/NewVertexPane.ts
|
src/Explorer/Panes/NewVertexPane.ts
|
||||||
src/Explorer/Panes/PaneComponents.ts
|
src/Explorer/Panes/PaneComponents.ts
|
||||||
@@ -299,11 +298,9 @@ src/Utils/DatabaseAccountUtils.ts
|
|||||||
src/Utils/JunoUtils.ts
|
src/Utils/JunoUtils.ts
|
||||||
src/Utils/MessageValidation.ts
|
src/Utils/MessageValidation.ts
|
||||||
src/Utils/NotebookConfigurationUtils.ts
|
src/Utils/NotebookConfigurationUtils.ts
|
||||||
src/Utils/NotificationConsoleUtils.ts
|
|
||||||
src/Utils/OfferUtils.test.ts
|
src/Utils/OfferUtils.test.ts
|
||||||
src/Utils/OfferUtils.ts
|
src/Utils/OfferUtils.ts
|
||||||
src/Utils/PricingUtils.test.ts
|
src/Utils/PricingUtils.test.ts
|
||||||
src/Utils/PricingUtils.ts
|
|
||||||
src/Utils/QueryUtils.test.ts
|
src/Utils/QueryUtils.test.ts
|
||||||
src/Utils/QueryUtils.ts
|
src/Utils/QueryUtils.ts
|
||||||
src/Utils/StringUtils.test.ts
|
src/Utils/StringUtils.test.ts
|
||||||
@@ -331,10 +328,6 @@ src/Explorer/Controls/Directory/DirectoryListComponent.test.tsx
|
|||||||
src/Explorer/Controls/Directory/DirectoryListComponent.tsx
|
src/Explorer/Controls/Directory/DirectoryListComponent.tsx
|
||||||
src/Explorer/Controls/Editor/EditorReact.tsx
|
src/Explorer/Controls/Editor/EditorReact.tsx
|
||||||
src/Explorer/Controls/InputTypeahead/InputTypeaheadComponent.tsx
|
src/Explorer/Controls/InputTypeahead/InputTypeaheadComponent.tsx
|
||||||
src/Explorer/Controls/LibraryManagement/ClusterLibraryGrid.tsx
|
|
||||||
src/Explorer/Controls/LibraryManagement/ClusterLibraryGridAdapter.tsx
|
|
||||||
src/Explorer/Controls/LibraryManagement/LibraryManage.tsx
|
|
||||||
src/Explorer/Controls/LibraryManagement/LibraryManageComponentAdapter.tsx
|
|
||||||
src/Explorer/Controls/Notebook/NotebookTerminalComponent.test.tsx
|
src/Explorer/Controls/Notebook/NotebookTerminalComponent.test.tsx
|
||||||
src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx
|
src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx
|
||||||
src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.tsx
|
src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.tsx
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module.exports = {
|
|||||||
browser: true,
|
browser: true,
|
||||||
es6: true
|
es6: true
|
||||||
},
|
},
|
||||||
plugins: ["@typescript-eslint", "no-null"],
|
plugins: ["@typescript-eslint", "no-null", "prefer-arrow"],
|
||||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||||
globals: {
|
globals: {
|
||||||
Atomics: "readonly",
|
Atomics: "readonly",
|
||||||
@@ -39,6 +39,8 @@ module.exports = {
|
|||||||
curly: "error",
|
curly: "error",
|
||||||
"@typescript-eslint/no-unused-vars": "error",
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
"@typescript-eslint/no-extraneous-class": "error",
|
"@typescript-eslint/no-extraneous-class": "error",
|
||||||
"no-null/no-null": "error"
|
"no-null/no-null": "error",
|
||||||
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
|
"prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -1 +1 @@
|
|||||||
* @Azure/cosmos-explorer-owners
|
* @Azure/cosmos-explorer-owners @Azure/azure-cosmos-explorer-developers
|
||||||
|
|||||||
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
@@ -1,9 +1,13 @@
|
|||||||
name: CI
|
name: CI
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master]
|
branches:
|
||||||
|
- master
|
||||||
|
- hotfix/*
|
||||||
|
- release/*
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [master]
|
branches:
|
||||||
|
- master
|
||||||
jobs:
|
jobs:
|
||||||
compile:
|
compile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -52,6 +56,7 @@ jobs:
|
|||||||
- run: npm run test
|
- run: npm run test
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: [lint, format, compile, unittest]
|
||||||
name: "Build"
|
name: "Build"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -75,6 +80,7 @@ jobs:
|
|||||||
path: dist/
|
path: dist/
|
||||||
endtoendemulator:
|
endtoendemulator:
|
||||||
name: "End To End Tests | Emulator | SQL"
|
name: "End To End Tests | Emulator | SQL"
|
||||||
|
needs: [lint, format, compile, unittest]
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -101,6 +107,7 @@ jobs:
|
|||||||
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
||||||
endtoendsql:
|
endtoendsql:
|
||||||
name: "End To End Tests | SQL"
|
name: "End To End Tests | SQL"
|
||||||
|
needs: [lint, format, compile, unittest]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -129,6 +136,7 @@ jobs:
|
|||||||
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
|
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
|
||||||
endtoendmongo:
|
endtoendmongo:
|
||||||
name: "End To End Tests | Mongo"
|
name: "End To End Tests | Mongo"
|
||||||
|
needs: [lint, format, compile, unittest]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -157,6 +165,7 @@ jobs:
|
|||||||
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
|
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
|
||||||
accessibility:
|
accessibility:
|
||||||
name: "Accessibility | Hosted"
|
name: "Accessibility | Hosted"
|
||||||
|
needs: [lint, format, compile, unittest]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -179,7 +188,7 @@ jobs:
|
|||||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||||
nuget:
|
nuget:
|
||||||
name: Publish Nuget
|
name: Publish Nuget
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
||||||
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
@@ -203,7 +212,7 @@ jobs:
|
|||||||
path: "*.nupkg"
|
path: "*.nupkg"
|
||||||
nugetmpac:
|
nugetmpac:
|
||||||
name: Publish Nuget MPAC
|
name: Publish Nuget MPAC
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
||||||
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# CosmosDB Explorer
|
# CosmosDB Explorer
|
||||||
|
|
||||||
|
UI for Azure Cosmos DB. Powers the [Azure Portal](https://portal.azure.com/), https://cosmos.azure.com/, and the [Cosmos DB Emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
- `npm install`
|
- `npm install`
|
||||||
@@ -87,6 +91,10 @@ Jest and Puppeteer are used for end to end production runners and are contained
|
|||||||
1. Copy .env.example to .env and fill in all variables
|
1. Copy .env.example to .env and fill in all variables
|
||||||
2. Run `npm run test:e2e`
|
2. Run `npm run test:e2e`
|
||||||
|
|
||||||
|
### Releasing
|
||||||
|
|
||||||
|
We generally adhear to the release strategy [documented by the Azure SDK Guidelines](https://azure.github.io/azure-sdk/policies_repobranching.html#release-branches). Most releases should happen from the master branch. If master contains commits that cannot be released, you may create a release from a `release/` or `hotfix/` branch. See linked documentation for more details.
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Please read the [contribution guidelines](./CONTRIBUTING.md).
|
Please read the [contribution guidelines](./CONTRIBUTING.md).
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ module.exports = {
|
|||||||
// An object that configures minimum threshold enforcement for coverage results
|
// An object that configures minimum threshold enforcement for coverage results
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
branches: 18,
|
branches: 20,
|
||||||
functions: 22,
|
functions: 24,
|
||||||
lines: 28,
|
lines: 30,
|
||||||
statements: 27
|
statements: 29.0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -2349,9 +2349,9 @@ a:link {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabsContainer {
|
.tabsManagerContainer {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
flex-grow: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
219
package-lock.json
generated
219
package-lock.json
generated
@@ -6024,6 +6024,16 @@
|
|||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
"pretty-format": "^24.9.0",
|
"pretty-format": "^24.9.0",
|
||||||
"semver": "^6.2.0"
|
"semver": "^6.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jest-validate": {
|
"jest-validate": {
|
||||||
@@ -7577,11 +7587,40 @@
|
|||||||
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
|
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/mkdirp": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "12.11.1",
|
"version": "12.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz",
|
||||||
"integrity": "sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A=="
|
"integrity": "sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A=="
|
||||||
},
|
},
|
||||||
|
"@types/node-fetch": {
|
||||||
|
"version": "2.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz",
|
||||||
|
"integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"form-data": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"form-data": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/normalize-package-data": {
|
"@types/normalize-package-data": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
||||||
@@ -7681,6 +7720,11 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/retry": {
|
||||||
|
"version": "0.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
||||||
|
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
|
||||||
|
},
|
||||||
"@types/shallowequal": {
|
"@types/shallowequal": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/shallowequal/-/shallowequal-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/shallowequal/-/shallowequal-1.1.1.tgz",
|
||||||
@@ -8403,7 +8447,6 @@
|
|||||||
"graceful-fs": "^4.1.11",
|
"graceful-fs": "^4.1.11",
|
||||||
"lru-cache": "^4.1.1",
|
"lru-cache": "^4.1.1",
|
||||||
"mississippi": "^2.0.0",
|
"mississippi": "^2.0.0",
|
||||||
"mkdirp": "^0.5.1",
|
|
||||||
"move-concurrently": "^1.0.1",
|
"move-concurrently": "^1.0.1",
|
||||||
"promise-inflight": "^1.0.1",
|
"promise-inflight": "^1.0.1",
|
||||||
"rimraf": "^2.6.2",
|
"rimraf": "^2.6.2",
|
||||||
@@ -9342,6 +9385,15 @@
|
|||||||
"pkg-dir": "^3.0.0"
|
"pkg-dir": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pify": {
|
"pify": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
||||||
@@ -9519,6 +9571,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
|
||||||
"integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA="
|
"integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA="
|
||||||
},
|
},
|
||||||
|
"base64-arraybuffer": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ=="
|
||||||
|
},
|
||||||
"base64-js": {
|
"base64-js": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||||
@@ -10656,6 +10713,14 @@
|
|||||||
"run-queue": "^1.0.0"
|
"run-queue": "^1.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"rimraf": {
|
"rimraf": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
@@ -10874,6 +10939,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/css-element-queries/-/css-element-queries-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/css-element-queries/-/css-element-queries-1.1.1.tgz",
|
||||||
"integrity": "sha512-/PX6Bkk77ShgbOx/mpawHdEvS3PGgy1mmMktcztDPndWdMJxcorcQiivrs+nEljqtBpvNEhAmQky9tQR6FSm8Q=="
|
"integrity": "sha512-/PX6Bkk77ShgbOx/mpawHdEvS3PGgy1mmMktcztDPndWdMJxcorcQiivrs+nEljqtBpvNEhAmQky9tQR6FSm8Q=="
|
||||||
},
|
},
|
||||||
|
"css-line-break": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==",
|
||||||
|
"requires": {
|
||||||
|
"base64-arraybuffer": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"css-loader": {
|
"css-loader": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.0.tgz",
|
||||||
@@ -12744,6 +12817,12 @@
|
|||||||
"integrity": "sha1-EjaoEjkTkKGHetQAfCbnRTQclR8=",
|
"integrity": "sha1-EjaoEjkTkKGHetQAfCbnRTQclR8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"eslint-plugin-prefer-arrow": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-C8YMhL+r8RMeMdYAw/rQtE6xNdMulj+zGWud/qIGnlmomiPRaLDGLMeskZ3alN6uMBojmooRimtdrXebLN4svQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"eslint-plugin-react": {
|
"eslint-plugin-react": {
|
||||||
"version": "7.20.0",
|
"version": "7.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.20.0.tgz",
|
||||||
@@ -15557,6 +15636,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"html2canvas": {
|
||||||
|
"version": "1.0.0-rc.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.0.0-rc.5.tgz",
|
||||||
|
"integrity": "sha512-DtNqPxJNXPoTajs+lVQzGS1SULRI4GQaROeU5R41xH8acffHukxRh/NBVcTBsfCkJSkLq91rih5TpbEwUP9yWA==",
|
||||||
|
"requires": {
|
||||||
|
"css-line-break": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"htmlparser2": {
|
"htmlparser2": {
|
||||||
"version": "3.10.1",
|
"version": "3.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
|
||||||
@@ -19754,6 +19841,16 @@
|
|||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"slash": "^2.0.0",
|
"slash": "^2.0.0",
|
||||||
"source-map": "^0.6.0"
|
"source-map": "^0.6.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jest-validate": {
|
"jest-validate": {
|
||||||
@@ -20337,6 +20434,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"promise": {
|
"promise": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||||
@@ -21287,12 +21394,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.5",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||||
"requires": {
|
|
||||||
"minimist": "^1.2.5"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"mkdirp-classic": {
|
"mkdirp-classic": {
|
||||||
"version": "0.5.3",
|
"version": "0.5.3",
|
||||||
@@ -21338,6 +21442,14 @@
|
|||||||
"run-queue": "^1.0.3"
|
"run-queue": "^1.0.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"rimraf": {
|
"rimraf": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
@@ -21677,6 +21789,14 @@
|
|||||||
"tar": "^4"
|
"tar": "^4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"rimraf": {
|
"rimraf": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
@@ -22171,11 +22291,11 @@
|
|||||||
"integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo="
|
"integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo="
|
||||||
},
|
},
|
||||||
"p-retry": {
|
"p-retry": {
|
||||||
"version": "3.0.1",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz",
|
||||||
"integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
|
"integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"@types/retry": "^0.12.0",
|
||||||
"retry": "^0.12.0"
|
"retry": "^0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -22576,6 +22696,15 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -23944,8 +24073,7 @@
|
|||||||
"retry": {
|
"retry": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||||
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
|
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"reusify": {
|
"reusify": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
@@ -25476,6 +25604,16 @@
|
|||||||
"mkdirp": "^0.5.0",
|
"mkdirp": "^0.5.0",
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"yallist": "^3.0.3"
|
"yallist": "^3.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tar-fs": {
|
"tar-fs": {
|
||||||
@@ -25833,6 +25971,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
|
||||||
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
|
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
|
||||||
},
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"yargs-parser": {
|
"yargs-parser": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
|
||||||
@@ -26901,6 +27047,15 @@
|
|||||||
"readable-stream": "^2.0.1"
|
"readable-stream": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "2.3.7",
|
"version": "2.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||||
@@ -27012,6 +27167,15 @@
|
|||||||
"integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==",
|
"integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
||||||
@@ -27224,6 +27388,15 @@
|
|||||||
"readable-stream": "^2.0.1"
|
"readable-stream": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "2.3.7",
|
"version": "2.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||||
@@ -27303,6 +27476,15 @@
|
|||||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"p-retry": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"retry": "^0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"schema-utils": {
|
"schema-utils": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
|
||||||
@@ -27531,6 +27713,17 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"mkdirp": "^0.5.1"
|
"mkdirp": "^0.5.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"write-file-atomic": {
|
"write-file-atomic": {
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -34,6 +34,8 @@
|
|||||||
"@nteract/transform-vega": "7.0.6",
|
"@nteract/transform-vega": "7.0.6",
|
||||||
"@octokit/rest": "17.9.2",
|
"@octokit/rest": "17.9.2",
|
||||||
"@phosphor/widgets": "1.9.3",
|
"@phosphor/widgets": "1.9.3",
|
||||||
|
"@types/mkdirp": "1.0.1",
|
||||||
|
"@types/node-fetch": "2.5.7",
|
||||||
"@uifabric/react-cards": "0.109.110",
|
"@uifabric/react-cards": "0.109.110",
|
||||||
"@uifabric/styling": "7.13.7",
|
"@uifabric/styling": "7.13.7",
|
||||||
"abort-controller": "3.0.0",
|
"abort-controller": "3.0.0",
|
||||||
@@ -54,15 +56,18 @@
|
|||||||
"es6-symbol": "3.1.3",
|
"es6-symbol": "3.1.3",
|
||||||
"eslint-plugin-jest": "23.13.2",
|
"eslint-plugin-jest": "23.13.2",
|
||||||
"hasher": "1.2.0",
|
"hasher": "1.2.0",
|
||||||
|
"html2canvas": "1.0.0-rc.5",
|
||||||
"immutable": "4.0.0-rc.12",
|
"immutable": "4.0.0-rc.12",
|
||||||
"is-ci": "2.0.0",
|
"is-ci": "2.0.0",
|
||||||
"jquery": "3.5.1",
|
"jquery": "3.5.1",
|
||||||
"jquery-typeahead": "2.10.6",
|
"jquery-typeahead": "2.10.6",
|
||||||
"jquery-ui-dist": "1.12.1",
|
"jquery-ui-dist": "1.12.1",
|
||||||
"knockout": "3.5.1",
|
"knockout": "3.5.1",
|
||||||
|
"mkdirp": "1.0.4",
|
||||||
"monaco-editor": "0.15.6",
|
"monaco-editor": "0.15.6",
|
||||||
"object.entries": "1.1.0",
|
"object.entries": "1.1.0",
|
||||||
"office-ui-fabric-react": "7.121.10",
|
"office-ui-fabric-react": "7.121.10",
|
||||||
|
"p-retry": "4.2.0",
|
||||||
"plotly.js-cartesian-dist-min": "1.52.3",
|
"plotly.js-cartesian-dist-min": "1.52.3",
|
||||||
"promise-polyfill": "8.1.0",
|
"promise-polyfill": "8.1.0",
|
||||||
"promise.prototype.finally": "3.1.0",
|
"promise.prototype.finally": "3.1.0",
|
||||||
@@ -130,6 +135,7 @@
|
|||||||
"eslint": "7.3.1",
|
"eslint": "7.3.1",
|
||||||
"eslint-cli": "1.1.1",
|
"eslint-cli": "1.1.1",
|
||||||
"eslint-plugin-no-null": "1.0.2",
|
"eslint-plugin-no-null": "1.0.2",
|
||||||
|
"eslint-plugin-prefer-arrow": "1.2.2",
|
||||||
"eslint-plugin-react": "7.20.0",
|
"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",
|
||||||
@@ -147,6 +153,7 @@
|
|||||||
"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",
|
||||||
|
"node-fetch": "2.6.0",
|
||||||
"prettier": "1.19.1",
|
"prettier": "1.19.1",
|
||||||
"puppeteer": "4.0.0",
|
"puppeteer": "4.0.0",
|
||||||
"raw-loader": "0.5.1",
|
"raw-loader": "0.5.1",
|
||||||
@@ -187,7 +194,8 @@
|
|||||||
"build:contracts": "npm run compile:contracts",
|
"build:contracts": "npm run compile:contracts",
|
||||||
"strictEligibleFiles": "node ./strict-migration-tools/index.js",
|
"strictEligibleFiles": "node ./strict-migration-tools/index.js",
|
||||||
"autoAddStrictEligibleFiles": "node ./strict-migration-tools/autoAdd.js",
|
"autoAddStrictEligibleFiles": "node ./strict-migration-tools/autoAdd.js",
|
||||||
"compile:fullStrict": "tsc -p ./tsconfig.json --strictNullChecks"
|
"compile:fullStrict": "tsc -p ./tsconfig.json --strictNullChecks",
|
||||||
|
"generateARMClients": "ts-node --compiler-options '{\"module\":\"commonjs\"}' utils/armClientGenerator/generator.ts"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import * as ViewModels from "../Contracts/ViewModels";
|
|
||||||
|
|
||||||
export class DefaultApi implements ViewModels.CosmosDbApi {
|
|
||||||
public isSystemDatabasePredicate = (database: ViewModels.Database): boolean => {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CassandraApi implements ViewModels.CosmosDbApi {
|
|
||||||
public isSystemDatabasePredicate = (database: ViewModels.Database): boolean => {
|
|
||||||
return database.id() === "system";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -101,6 +101,7 @@ export class CapabilityNames {
|
|||||||
public static readonly EnableNotebooks: string = "EnableNotebooks";
|
public static readonly EnableNotebooks: string = "EnableNotebooks";
|
||||||
public static readonly EnableStorageAnalytics: string = "EnableStorageAnalytics";
|
public static readonly EnableStorageAnalytics: string = "EnableStorageAnalytics";
|
||||||
public static readonly EnableMongo: string = "EnableMongo";
|
public static readonly EnableMongo: string = "EnableMongo";
|
||||||
|
public static readonly EnableServerless: string = "EnableServerless";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Features {
|
export class Features {
|
||||||
@@ -351,6 +352,7 @@ export class HttpStatusCodes {
|
|||||||
public static readonly Created: number = 201;
|
public static readonly Created: number = 201;
|
||||||
public static readonly Accepted: number = 202;
|
public static readonly Accepted: number = 202;
|
||||||
public static readonly NoContent: number = 204;
|
public static readonly NoContent: number = 204;
|
||||||
|
public static readonly NotModified: number = 304;
|
||||||
public static readonly Unauthorized: number = 401;
|
public static readonly Unauthorized: number = 401;
|
||||||
public static readonly Forbidden: number = 403;
|
public static readonly Forbidden: number = 403;
|
||||||
public static readonly NotFound: number = 404;
|
public static readonly NotFound: number = 404;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import * as Cosmos from "@azure/cosmos";
|
|||||||
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
|
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
|
||||||
import { DatabaseAccount } from "../Contracts/DataModels";
|
import { DatabaseAccount } from "../Contracts/DataModels";
|
||||||
import { HttpHeaders, EmulatorMasterKey } from "./Constants";
|
import { HttpHeaders, EmulatorMasterKey } from "./Constants";
|
||||||
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { config, Platform } from "../Config";
|
import { config, Platform } from "../Config";
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ import * as Constants from "../Common/Constants";
|
|||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { StringUtils } from "../Utils/StringUtils";
|
import { StringUtils } from "../Utils/StringUtils";
|
||||||
|
import Explorer from "../Explorer/Explorer";
|
||||||
|
|
||||||
export default class EnvironmentUtility {
|
export default class EnvironmentUtility {
|
||||||
public static getMongoBackendEndpoint(serverId: string, location: string, extensionEndpoint: string = ""): string {
|
public static getMongoBackendEndpoint(serverId: string, location: string, extensionEndpoint: string = ""): string {
|
||||||
@@ -26,7 +27,7 @@ export default class EnvironmentUtility {
|
|||||||
return window.authType === AuthType.AAD;
|
return window.authType === AuthType.AAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getCassandraBackendEndpoint(explorer: ViewModels.Explorer): string {
|
public static getCassandraBackendEndpoint(explorer: Explorer): string {
|
||||||
const defaultLocation: string = "default";
|
const defaultLocation: string = "default";
|
||||||
const location: string = EnvironmentUtility.normalizeRegionName(explorer.databaseAccount().location);
|
const location: string = EnvironmentUtility.normalizeRegionName(explorer.databaseAccount().location);
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,67 +1,69 @@
|
|||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
|
|
||||||
export function replaceKnownError(err: string): string {
|
export function replaceKnownError(err: string): string {
|
||||||
if (
|
if (
|
||||||
window.dataExplorer.subscriptionType() === ViewModels.SubscriptionType.Internal &&
|
window.dataExplorer.subscriptionType() === ViewModels.SubscriptionType.Internal &&
|
||||||
err.indexOf("SharedOffer is Disabled for your account") >= 0
|
err.indexOf("SharedOffer is Disabled for your account") >= 0
|
||||||
) {
|
) {
|
||||||
return "Database throughput is not supported for internal subscriptions.";
|
return "Database throughput is not supported for internal subscriptions.";
|
||||||
}
|
} else if (err.indexOf("Partition key paths must contain only valid") >= 0) {
|
||||||
|
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
|
||||||
return err;
|
}
|
||||||
}
|
|
||||||
|
return err;
|
||||||
export function parse(err: any): DataModels.ErrorDataModel[] {
|
}
|
||||||
try {
|
|
||||||
return _parse(err);
|
export function parse(err: any): DataModels.ErrorDataModel[] {
|
||||||
} catch (e) {
|
try {
|
||||||
return [<DataModels.ErrorDataModel>{ message: JSON.stringify(err) }];
|
return _parse(err);
|
||||||
}
|
} catch (e) {
|
||||||
}
|
return [<DataModels.ErrorDataModel>{ message: JSON.stringify(err) }];
|
||||||
|
}
|
||||||
function _parse(err: any): DataModels.ErrorDataModel[] {
|
}
|
||||||
var normalizedErrors: DataModels.ErrorDataModel[] = [];
|
|
||||||
if (err.message && !err.code) {
|
function _parse(err: any): DataModels.ErrorDataModel[] {
|
||||||
normalizedErrors.push(err);
|
var normalizedErrors: DataModels.ErrorDataModel[] = [];
|
||||||
} else {
|
if (err.message && !err.code) {
|
||||||
const innerErrors: any[] = _getInnerErrors(err.message);
|
normalizedErrors.push(err);
|
||||||
normalizedErrors = innerErrors.map(innerError =>
|
} else {
|
||||||
typeof innerError === "string" ? { message: innerError } : innerError
|
const innerErrors: any[] = _getInnerErrors(err.message);
|
||||||
);
|
normalizedErrors = innerErrors.map(innerError =>
|
||||||
}
|
typeof innerError === "string" ? { message: innerError } : innerError
|
||||||
|
);
|
||||||
return normalizedErrors;
|
}
|
||||||
}
|
|
||||||
|
return normalizedErrors;
|
||||||
function _getInnerErrors(message: string): any[] {
|
}
|
||||||
/*
|
|
||||||
The backend error message has an inner-message which is a stringified object.
|
function _getInnerErrors(message: string): any[] {
|
||||||
|
/*
|
||||||
For SQL errors, the "errors" property is an array of SqlErrorDataModel.
|
The backend error message has an inner-message which is a stringified object.
|
||||||
Example:
|
|
||||||
"Message: {"Errors":["Resource with specified id or name already exists"]}\r\nActivityId: 80005000008d40b6a, Request URI: /apps/19000c000c0a0005/services/mctestdocdbprod-MasterService-0-00066ab9937/partitions/900005f9000e676fb8/replicas/13000000000955p"
|
For SQL errors, the "errors" property is an array of SqlErrorDataModel.
|
||||||
For non-SQL errors the "Errors" propery is an array of string.
|
Example:
|
||||||
Example:
|
"Message: {"Errors":["Resource with specified id or name already exists"]}\r\nActivityId: 80005000008d40b6a, Request URI: /apps/19000c000c0a0005/services/mctestdocdbprod-MasterService-0-00066ab9937/partitions/900005f9000e676fb8/replicas/13000000000955p"
|
||||||
"Message: {"errors":[{"severity":"Error","location":{"start":7,"end":8},"code":"SC1001","message":"Syntax error, incorrect syntax near '.'."}]}\r\nActivityId: d3300016d4084e310a, Request URI: /apps/12401f9e1df77/services/dc100232b1f44545/partitions/f86f3bc0001a2f78/replicas/13085003638s"
|
For non-SQL errors the "Errors" propery is an array of string.
|
||||||
*/
|
Example:
|
||||||
|
"Message: {"errors":[{"severity":"Error","location":{"start":7,"end":8},"code":"SC1001","message":"Syntax error, incorrect syntax near '.'."}]}\r\nActivityId: d3300016d4084e310a, Request URI: /apps/12401f9e1df77/services/dc100232b1f44545/partitions/f86f3bc0001a2f78/replicas/13085003638s"
|
||||||
let innerMessage: any = null;
|
*/
|
||||||
|
|
||||||
const singleLineMessage = message.replace(/[\r\n]|\r|\n/g, "");
|
let innerMessage: any = null;
|
||||||
try {
|
|
||||||
// Multi-Partition error flavor
|
const singleLineMessage = message.replace(/[\r\n]|\r|\n/g, "");
|
||||||
const regExp = /^(.*)ActivityId: (.*)/g;
|
try {
|
||||||
const regString = regExp.exec(singleLineMessage);
|
// Multi-Partition error flavor
|
||||||
const innerMessageString = regString[1];
|
const regExp = /^(.*)ActivityId: (.*)/g;
|
||||||
innerMessage = JSON.parse(innerMessageString);
|
const regString = regExp.exec(singleLineMessage);
|
||||||
} catch (e) {
|
const innerMessageString = regString[1];
|
||||||
// Single-partition error flavor
|
innerMessage = JSON.parse(innerMessageString);
|
||||||
const regExp = /^Message: (.*)ActivityId: (.*), Request URI: (.*)/g;
|
} catch (e) {
|
||||||
const regString = regExp.exec(singleLineMessage);
|
// Single-partition error flavor
|
||||||
const innerMessageString = regString[1];
|
const regExp = /^Message: (.*)ActivityId: (.*), Request URI: (.*)/g;
|
||||||
innerMessage = JSON.parse(innerMessageString);
|
const regString = regExp.exec(singleLineMessage);
|
||||||
}
|
const innerMessageString = regString[1];
|
||||||
|
innerMessage = JSON.parse(innerMessageString);
|
||||||
return innerMessage.errors ? innerMessage.errors : innerMessage.Errors;
|
}
|
||||||
}
|
|
||||||
|
return innerMessage.errors ? innerMessage.errors : innerMessage.Errors;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,46 +1,26 @@
|
|||||||
|
jest.mock("./MessageHandler");
|
||||||
import { LogEntryLevel } from "../Contracts/Diagnostics";
|
import { LogEntryLevel } from "../Contracts/Diagnostics";
|
||||||
import * as Logger from "./Logger";
|
import * as Logger from "./Logger";
|
||||||
import { MessageHandler } from "./MessageHandler";
|
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
|
import { sendMessage } from "./MessageHandler";
|
||||||
|
|
||||||
describe("Logger", () => {
|
describe("Logger", () => {
|
||||||
let sendMessageSpy: jasmine.Spy;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sendMessageSpy = spyOn(MessageHandler, "sendMessage");
|
jest.resetAllMocks();
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
sendMessageSpy = null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should log info messages", () => {
|
it("should log info messages", () => {
|
||||||
Logger.logInfo("Test info", "DocDB");
|
Logger.logInfo("Test info", "DocDB");
|
||||||
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
expect(sendMessage).toBeCalled();
|
||||||
|
|
||||||
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
|
||||||
expect(spyArgs.data).toContain(LogEntryLevel.Verbose);
|
|
||||||
expect(spyArgs.data).toContain("DocDB");
|
|
||||||
expect(spyArgs.data).toContain("Test info");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should log error messages", () => {
|
it("should log error messages", () => {
|
||||||
Logger.logError("Test error", "DocDB");
|
Logger.logError("Test error", "DocDB");
|
||||||
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
expect(sendMessage).toBeCalled();
|
||||||
|
|
||||||
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
|
||||||
expect(spyArgs.data).toContain(LogEntryLevel.Error);
|
|
||||||
expect(spyArgs.data).toContain("DocDB");
|
|
||||||
expect(spyArgs.data).toContain("Test error");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should log warnings", () => {
|
it("should log warnings", () => {
|
||||||
Logger.logWarning("Test warning", "DocDB");
|
Logger.logWarning("Test warning", "DocDB");
|
||||||
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
expect(sendMessage).toBeCalled();
|
||||||
|
|
||||||
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
|
||||||
expect(spyArgs.data).toContain(LogEntryLevel.Warning);
|
|
||||||
expect(spyArgs.data).toContain("DocDB");
|
|
||||||
expect(spyArgs.data).toContain("Test warning");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MessageHandler } from "./MessageHandler";
|
import { sendMessage } from "./MessageHandler";
|
||||||
import { Diagnostics, MessageTypes } from "../Contracts/ExplorerContracts";
|
import { Diagnostics, MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
import { appInsights } from "../Shared/appInsights";
|
import { appInsights } from "../Shared/appInsights";
|
||||||
import { SeverityLevel } from "@microsoft/applicationinsights-web";
|
import { SeverityLevel } from "@microsoft/applicationinsights-web";
|
||||||
@@ -33,7 +33,7 @@ export function logError(message: string | Error, area: string, code?: number):
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _logEntry(entry: Diagnostics.LogEntry): void {
|
function _logEntry(entry: Diagnostics.LogEntry): void {
|
||||||
MessageHandler.sendMessage({
|
sendMessage({
|
||||||
type: MessageTypes.LogInfo,
|
type: MessageTypes.LogInfo,
|
||||||
data: JSON.stringify(entry)
|
data: JSON.stringify(entry)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,65 +1,29 @@
|
|||||||
import Q from "q";
|
import Q from "q";
|
||||||
import { CachedDataPromise, MessageHandler } from "./MessageHandler";
|
import * as MessageHandler from "./MessageHandler";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
|
|
||||||
class MockMessageHandler extends MessageHandler {
|
|
||||||
public static addToMap(key: string, value: CachedDataPromise<any>): void {
|
|
||||||
MessageHandler.RequestMap[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static mapContainsKey(key: string): boolean {
|
|
||||||
return MessageHandler.RequestMap[key] != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static clearAllEntries(): void {
|
|
||||||
MessageHandler.RequestMap = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static runGarbageCollector(): void {
|
|
||||||
MessageHandler.runGarbageCollector();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("Message Handler", () => {
|
describe("Message Handler", () => {
|
||||||
beforeEach(() => {
|
it("should handle cached message", async () => {
|
||||||
MockMessageHandler.clearAllEntries();
|
let mockPromise = {
|
||||||
});
|
|
||||||
|
|
||||||
xit("should send cached data message", (done: any) => {
|
|
||||||
const testValidationCallback = (e: MessageEvent) => {
|
|
||||||
expect(e.data.data).toEqual(
|
|
||||||
jasmine.objectContaining({ type: MessageTypes.AllDatabases, params: ["some param"] })
|
|
||||||
);
|
|
||||||
e.currentTarget.removeEventListener(e.type, testValidationCallback);
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
window.parent.addEventListener("message", testValidationCallback);
|
|
||||||
MockMessageHandler.sendCachedDataMessage(MessageTypes.AllDatabases, ["some param"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle cached message", () => {
|
|
||||||
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: "{}" } };
|
||||||
|
MessageHandler.RequestMap[mockPromise.id] = mockPromise;
|
||||||
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
MessageHandler.handleCachedDataMessage(mockMessage);
|
||||||
MockMessageHandler.handleCachedDataMessage(mockMessage);
|
|
||||||
expect(mockPromise.deferred.promise.isFulfilled()).toBe(true);
|
expect(mockPromise.deferred.promise.isFulfilled()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should delete fulfilled promises on running the garbage collector", () => {
|
it("should delete fulfilled promises on running the garbage collector", async () => {
|
||||||
let mockPromise: CachedDataPromise<any> = {
|
let message = {
|
||||||
id: "123",
|
id: "123",
|
||||||
startTime: new Date(),
|
startTime: new Date(),
|
||||||
deferred: Q.defer<any>()
|
deferred: Q.defer<any>()
|
||||||
};
|
};
|
||||||
|
|
||||||
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
MessageHandler.handleCachedDataMessage(message);
|
||||||
mockPromise.deferred.reject("some error");
|
MessageHandler.runGarbageCollector();
|
||||||
MockMessageHandler.runGarbageCollector();
|
expect(MessageHandler.RequestMap["123"]).toBeUndefined();
|
||||||
expect(MockMessageHandler.mapContainsKey(mockPromise.id)).toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,77 +9,65 @@ export interface CachedDataPromise<T> {
|
|||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export const RequestMap: Record<string, CachedDataPromise<any>> = {};
|
||||||
* For some reason, typescript emits a Map() in the compiled js output(despite the target being set to ES5) forcing us to define our own polyfill,
|
|
||||||
* so we define our own custom implementation of the ES6 Map to work around it.
|
|
||||||
*/
|
|
||||||
type Map = { [key: string]: CachedDataPromise<any> };
|
|
||||||
|
|
||||||
export class MessageHandler {
|
export function handleCachedDataMessage(message: any): void {
|
||||||
protected static RequestMap: Map = {};
|
const messageContent = message && message.message;
|
||||||
|
if (message == null || messageContent == null || messageContent.id == null || !RequestMap[messageContent.id]) {
|
||||||
public static handleCachedDataMessage(message: any): void {
|
return;
|
||||||
const messageContent = message && message.message;
|
|
||||||
if (
|
|
||||||
message == null ||
|
|
||||||
messageContent == null ||
|
|
||||||
messageContent.id == null ||
|
|
||||||
!MessageHandler.RequestMap[messageContent.id]
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cachedDataPromise = MessageHandler.RequestMap[messageContent.id];
|
|
||||||
if (messageContent.error != null) {
|
|
||||||
cachedDataPromise.deferred.reject(messageContent.error);
|
|
||||||
} else {
|
|
||||||
cachedDataPromise.deferred.resolve(JSON.parse(messageContent.data));
|
|
||||||
}
|
|
||||||
MessageHandler.runGarbageCollector();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static sendCachedDataMessage<TResponseDataModel>(
|
const cachedDataPromise = RequestMap[messageContent.id];
|
||||||
messageType: MessageTypes,
|
if (messageContent.error != null) {
|
||||||
params: Object[],
|
cachedDataPromise.deferred.reject(messageContent.error);
|
||||||
timeoutInMs?: number
|
} else {
|
||||||
): Q.Promise<TResponseDataModel> {
|
cachedDataPromise.deferred.resolve(JSON.parse(messageContent.data));
|
||||||
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
|
}
|
||||||
deferred: Q.defer<TResponseDataModel>(),
|
runGarbageCollector();
|
||||||
startTime: new Date(),
|
}
|
||||||
id: _.uniqueId()
|
|
||||||
};
|
|
||||||
MessageHandler.RequestMap[cachedDataPromise.id] = cachedDataPromise;
|
|
||||||
MessageHandler.sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
|
|
||||||
|
|
||||||
//TODO: Use telemetry to measure optimal time to resolve/reject promises
|
export function sendCachedDataMessage<TResponseDataModel>(
|
||||||
return cachedDataPromise.deferred.promise.timeout(
|
messageType: MessageTypes,
|
||||||
timeoutInMs || Constants.ClientDefaults.requestTimeoutMs,
|
params: Object[],
|
||||||
"Timed out while waiting for response from portal"
|
timeoutInMs?: number
|
||||||
|
): Q.Promise<TResponseDataModel> {
|
||||||
|
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
|
||||||
|
deferred: Q.defer<TResponseDataModel>(),
|
||||||
|
startTime: new Date(),
|
||||||
|
id: _.uniqueId()
|
||||||
|
};
|
||||||
|
RequestMap[cachedDataPromise.id] = cachedDataPromise;
|
||||||
|
sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
|
||||||
|
|
||||||
|
//TODO: Use telemetry to measure optimal time to resolve/reject promises
|
||||||
|
return cachedDataPromise.deferred.promise.timeout(
|
||||||
|
timeoutInMs || Constants.ClientDefaults.requestTimeoutMs,
|
||||||
|
"Timed out while waiting for response from portal"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendMessage(data: any): void {
|
||||||
|
if (canSendMessage()) {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
signature: "pcIframe",
|
||||||
|
data: data
|
||||||
|
},
|
||||||
|
window.document.referrer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public static sendMessage(data: any): void {
|
|
||||||
if (MessageHandler.canSendMessage()) {
|
export function canSendMessage(): boolean {
|
||||||
window.parent.postMessage(
|
return window.parent !== window;
|
||||||
{
|
}
|
||||||
signature: "pcIframe",
|
|
||||||
data: data
|
// TODO: This is exported just for testing. It should not be.
|
||||||
},
|
export function runGarbageCollector() {
|
||||||
window.document.referrer
|
Object.keys(RequestMap).forEach((key: string) => {
|
||||||
);
|
const promise: Q.Promise<any> = RequestMap[key].deferred.promise;
|
||||||
}
|
if (promise.isFulfilled() || promise.isRejected()) {
|
||||||
}
|
delete RequestMap[key];
|
||||||
|
}
|
||||||
public static canSendMessage(): boolean {
|
});
|
||||||
return window.parent !== window;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static runGarbageCollector() {
|
|
||||||
Object.keys(MessageHandler.RequestMap).forEach((key: string) => {
|
|
||||||
const promise: Q.Promise<any> = MessageHandler.RequestMap[key].deferred.promise;
|
|
||||||
if (promise.isFulfilled() || promise.isRejected()) {
|
|
||||||
delete MessageHandler.RequestMap[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ import {
|
|||||||
updateDocument
|
updateDocument
|
||||||
} from "./MongoProxyClient";
|
} from "./MongoProxyClient";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { Collection, DatabaseAccount, DocumentId } from "../Contracts/ViewModels";
|
import { Collection } from "../Contracts/ViewModels";
|
||||||
import { config } from "../Config";
|
import { config } from "../Config";
|
||||||
import { CosmosClient } from "./CosmosClient";
|
import { CosmosClient } from "./CosmosClient";
|
||||||
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
|
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
|
||||||
|
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||||
|
import { DatabaseAccount } from "../Contracts/DataModels";
|
||||||
jest.mock("../ResourceProvider/ResourceProviderClient.ts");
|
jest.mock("../ResourceProvider/ResourceProviderClient.ts");
|
||||||
|
|
||||||
const databaseId = "testDB";
|
const databaseId = "testDB";
|
||||||
@@ -171,7 +173,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("builds the correct URL", () => {
|
it("builds the correct URL", () => {
|
||||||
updateDocument(databaseId, collection, documentId, {});
|
updateDocument(databaseId, collection, documentId, "{}");
|
||||||
expect(window.fetch).toHaveBeenCalledWith(
|
expect(window.fetch).toHaveBeenCalledWith(
|
||||||
"https://main.documentdb.ext.azure.com/api/mongo/explorer?db=testDB&coll=testCollection&resourceUrl=bardb%2FtestDB%2Fdb%2FtestCollection%2Fdocs%2FtestId&rid=testId&rtype=docs&sid=&rg=&dba=foo&pk=pk",
|
"https://main.documentdb.ext.azure.com/api/mongo/explorer?db=testDB&coll=testCollection&resourceUrl=bardb%2FtestDB%2Fdb%2FtestCollection%2Fdocs%2FtestId&rid=testId&rtype=docs&sid=&rg=&dba=foo&pk=pk",
|
||||||
expect.any(Object)
|
expect.any(Object)
|
||||||
@@ -180,7 +182,7 @@ describe("MongoProxyClient", () => {
|
|||||||
|
|
||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
config.MONGO_BACKEND_ENDPOINT = "https://localhost:1234";
|
config.MONGO_BACKEND_ENDPOINT = "https://localhost:1234";
|
||||||
updateDocument(databaseId, collection, documentId, {});
|
updateDocument(databaseId, collection, documentId, "{}");
|
||||||
expect(window.fetch).toHaveBeenCalledWith(
|
expect(window.fetch).toHaveBeenCalledWith(
|
||||||
"https://localhost:1234/api/mongo/explorer?db=testDB&coll=testCollection&resourceUrl=bardb%2FtestDB%2Fdb%2FtestCollection%2Fdocs%2FtestId&rid=testId&rtype=docs&sid=&rg=&dba=foo&pk=pk",
|
"https://localhost:1234/api/mongo/explorer?db=testDB&coll=testCollection&resourceUrl=bardb%2FtestDB%2Fdb%2FtestCollection%2Fdocs%2FtestId&rid=testId&rtype=docs&sid=&rg=&dba=foo&pk=pk",
|
||||||
expect.any(Object)
|
expect.any(Object)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as Constants from "../Common/Constants";
|
import * as Constants from "../Common/Constants";
|
||||||
import * as DataExplorerConstants from "../Common/Constants";
|
import * as DataExplorerConstants from "../Common/Constants";
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
|
||||||
import EnvironmentUtility from "./EnvironmentUtility";
|
import EnvironmentUtility from "./EnvironmentUtility";
|
||||||
import queryString from "querystring";
|
import queryString from "querystring";
|
||||||
import { AddDbUtilities } from "../Shared/AddDatabaseUtility";
|
import { AddDbUtilities } from "../Shared/AddDatabaseUtility";
|
||||||
@@ -12,10 +11,12 @@ import { config } from "../Config";
|
|||||||
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
|
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
|
||||||
import { CosmosClient } from "./CosmosClient";
|
import { CosmosClient } from "./CosmosClient";
|
||||||
import { MessageHandler } from "./MessageHandler";
|
import { sendMessage } from "./MessageHandler";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||||
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
|
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
|
||||||
|
import { MinimalQueryIterator } from "./IteratorUtilities";
|
||||||
|
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||||
|
|
||||||
const defaultHeaders = {
|
const defaultHeaders = {
|
||||||
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
|
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
|
||||||
@@ -23,7 +24,7 @@ const defaultHeaders = {
|
|||||||
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15"
|
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15"
|
||||||
};
|
};
|
||||||
|
|
||||||
function authHeaders(): any {
|
function authHeaders() {
|
||||||
if (window.authType === AuthType.EncryptedToken) {
|
if (window.authType === AuthType.EncryptedToken) {
|
||||||
return { [HttpHeaders.guestAccessToken]: CosmosClient.accessToken() };
|
return { [HttpHeaders.guestAccessToken]: CosmosClient.accessToken() };
|
||||||
} else {
|
} else {
|
||||||
@@ -31,21 +32,21 @@ function authHeaders(): any {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queryIterator(databaseId: string, collection: Collection, query: string): any {
|
export function queryIterator(databaseId: string, collection: Collection, query: string): MinimalQueryIterator {
|
||||||
let continuationToken: string;
|
let continuationToken: string;
|
||||||
return {
|
return {
|
||||||
fetchNext: () => {
|
fetchNext: () => {
|
||||||
return queryDocuments(databaseId, collection, false, query).then(response => {
|
return queryDocuments(databaseId, collection, false, query).then(response => {
|
||||||
continuationToken = response.continuationToken;
|
continuationToken = response.continuationToken;
|
||||||
const headers = {} as any;
|
const headers: { [key: string]: string | number } = {};
|
||||||
response.headers.forEach((value: any, key: any) => {
|
response.headers.forEach((value, key) => {
|
||||||
headers[key] = value;
|
headers[key] = value;
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
resources: response.documents,
|
resources: response.documents,
|
||||||
headers,
|
headers,
|
||||||
requestCharge: headers[CosmosSDKConstants.HttpHeaders.RequestCharge],
|
requestCharge: Number(headers[CosmosSDKConstants.HttpHeaders.RequestCharge]),
|
||||||
activityId: headers[CosmosSDKConstants.HttpHeaders.ActivityId],
|
activityId: String(headers[CosmosSDKConstants.HttpHeaders.ActivityId]),
|
||||||
hasMoreResults: !!continuationToken
|
hasMoreResults: !!continuationToken
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -114,14 +115,15 @@ export function queryDocuments(
|
|||||||
headers: response.headers
|
headers: response.headers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return errorHandling(response, "querying documents", params);
|
errorHandling(response, "querying documents", params);
|
||||||
|
return undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readDocument(
|
export function readDocument(
|
||||||
databaseId: string,
|
databaseId: string,
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
documentId: ViewModels.DocumentId
|
documentId: DocumentId
|
||||||
): Promise<DataModels.DocumentId> {
|
): Promise<DataModels.DocumentId> {
|
||||||
const databaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||||
@@ -165,7 +167,7 @@ export function createDocument(
|
|||||||
databaseId: string,
|
databaseId: string,
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
partitionKeyProperty: string,
|
partitionKeyProperty: string,
|
||||||
documentContent: any
|
documentContent: unknown
|
||||||
): Promise<DataModels.DocumentId> {
|
): Promise<DataModels.DocumentId> {
|
||||||
const databaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||||
@@ -203,8 +205,8 @@ export function createDocument(
|
|||||||
export function updateDocument(
|
export function updateDocument(
|
||||||
databaseId: string,
|
databaseId: string,
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
documentId: ViewModels.DocumentId,
|
documentId: DocumentId,
|
||||||
documentContent: any
|
documentContent: string
|
||||||
): Promise<DataModels.DocumentId> {
|
): Promise<DataModels.DocumentId> {
|
||||||
const databaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||||
@@ -244,11 +246,7 @@ export function updateDocument(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteDocument(
|
export function deleteDocument(databaseId: string, collection: Collection, documentId: DocumentId): Promise<void> {
|
||||||
databaseId: string,
|
|
||||||
collection: Collection,
|
|
||||||
documentId: ViewModels.DocumentId
|
|
||||||
): Promise<any> {
|
|
||||||
const databaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||||
const idComponents = documentId.self.split("/");
|
const idComponents = documentId.self.split("/");
|
||||||
@@ -295,7 +293,7 @@ export function createMongoCollectionWithProxy(
|
|||||||
sharedThroughput: boolean,
|
sharedThroughput: boolean,
|
||||||
isSharded: boolean,
|
isSharded: boolean,
|
||||||
autopilotOptions?: DataModels.RpOptions
|
autopilotOptions?: DataModels.RpOptions
|
||||||
): Promise<any> {
|
): Promise<DataModels.Collection> {
|
||||||
const databaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const params: DataModels.MongoParameters = {
|
const params: DataModels.MongoParameters = {
|
||||||
resourceUrl: databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint,
|
resourceUrl: databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint,
|
||||||
@@ -335,7 +333,7 @@ export function createMongoCollectionWithProxy(
|
|||||||
)
|
)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return undefined;
|
return response.json();
|
||||||
}
|
}
|
||||||
return errorHandling(response, "creating collection", params);
|
return errorHandling(response, "creating collection", params);
|
||||||
});
|
});
|
||||||
@@ -352,7 +350,7 @@ export function createMongoCollectionWithARM(
|
|||||||
sharedThroughput: boolean,
|
sharedThroughput: boolean,
|
||||||
isSharded: boolean,
|
isSharded: boolean,
|
||||||
additionalOptions?: DataModels.RpOptions
|
additionalOptions?: DataModels.RpOptions
|
||||||
): Promise<any> {
|
): Promise<DataModels.CreateCollectionWithRpResponse> {
|
||||||
const databaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const params: DataModels.MongoParameters = {
|
const params: DataModels.MongoParameters = {
|
||||||
resourceUrl: databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint,
|
resourceUrl: databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint,
|
||||||
@@ -383,7 +381,7 @@ export function createMongoCollectionWithARM(
|
|||||||
return _createMongoCollectionWithARM(armEndpoint, params, additionalOptions);
|
return _createMongoCollectionWithARM(armEndpoint, params, additionalOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEndpoint(databaseAccount: ViewModels.DatabaseAccount): string {
|
export function getEndpoint(databaseAccount: DataModels.DatabaseAccount): string {
|
||||||
const serverId = window.dataExplorer.serverId();
|
const serverId = window.dataExplorer.serverId();
|
||||||
const extensionEndpoint = window.dataExplorer.extensionEndpoint();
|
const extensionEndpoint = window.dataExplorer.extensionEndpoint();
|
||||||
let url = config.MONGO_BACKEND_ENDPOINT
|
let url = config.MONGO_BACKEND_ENDPOINT
|
||||||
@@ -396,7 +394,9 @@ export function getEndpoint(databaseAccount: ViewModels.DatabaseAccount): string
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function errorHandling(response: any, action: string, params: any): Promise<any> {
|
// TODO: This function throws most of the time except on Forbidden which is a bit strange
|
||||||
|
// It causes problems for TypeScript understanding the types
|
||||||
|
async function errorHandling(response: Response, action: string, params: unknown): Promise<void> {
|
||||||
const errorMessage = await response.text();
|
const errorMessage = await response.text();
|
||||||
// Log the error where the user can see it
|
// Log the error where the user can see it
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
@@ -404,7 +404,7 @@ async function errorHandling(response: any, action: string, params: any): Promis
|
|||||||
`Error ${action}: ${errorMessage}, Payload: ${JSON.stringify(params)}`
|
`Error ${action}: ${errorMessage}, Payload: ${JSON.stringify(params)}`
|
||||||
);
|
);
|
||||||
if (response.status === HttpStatusCodes.Forbidden) {
|
if (response.status === HttpStatusCodes.Forbidden) {
|
||||||
MessageHandler.sendMessage({ type: MessageTypes.ForbiddenError, reason: errorMessage });
|
sendMessage({ type: MessageTypes.ForbiddenError, reason: errorMessage });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Error(errorMessage);
|
throw new Error(errorMessage);
|
||||||
@@ -420,7 +420,7 @@ export async function _createMongoCollectionWithARM(
|
|||||||
armEndpoint: string,
|
armEndpoint: string,
|
||||||
params: DataModels.MongoParameters,
|
params: DataModels.MongoParameters,
|
||||||
rpOptions: DataModels.RpOptions
|
rpOptions: DataModels.RpOptions
|
||||||
): Promise<any> {
|
): Promise<DataModels.CreateCollectionWithRpResponse> {
|
||||||
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
|
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
|
||||||
properties: {
|
properties: {
|
||||||
resource: {
|
resource: {
|
||||||
@@ -448,12 +448,13 @@ export async function _createMongoCollectionWithARM(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await new ResourceProviderClient(armEndpoint).putAsync(
|
return new ResourceProviderClient<DataModels.CreateCollectionWithRpResponse>(armEndpoint).putAsync(
|
||||||
getARMCreateCollectionEndpoint(params),
|
getARMCreateCollectionEndpoint(params),
|
||||||
DataExplorerConstants.ArmApiVersions.publicVersion,
|
DataExplorerConstants.ArmApiVersions.publicVersion,
|
||||||
rpPayloadToCreateCollection
|
rpPayloadToCreateCollection
|
||||||
);
|
);
|
||||||
} catch (response) {
|
} catch (response) {
|
||||||
return errorHandling(response, "creating collection", undefined);
|
errorHandling(response, "creating collection", undefined);
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import * as ViewModels from "../Contracts/ViewModels";
|
|||||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||||
import { CosmosClient } from "./CosmosClient";
|
import { CosmosClient } from "./CosmosClient";
|
||||||
|
|
||||||
export class NotificationsClientBase implements ViewModels.NotificationsClient {
|
export class NotificationsClientBase {
|
||||||
private _extensionEndpoint: string;
|
private _extensionEndpoint: string;
|
||||||
private _notificationsApiSuffix: string;
|
private _notificationsApiSuffix: string;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ export class NotificationsClientBase implements ViewModels.NotificationsClient {
|
|||||||
|
|
||||||
public fetchNotifications(): Q.Promise<DataModels.Notification[]> {
|
public fetchNotifications(): Q.Promise<DataModels.Notification[]> {
|
||||||
const deferred: Q.Deferred<DataModels.Notification[]> = Q.defer<DataModels.Notification[]>();
|
const deferred: Q.Deferred<DataModels.Notification[]> = Q.defer<DataModels.Notification[]>();
|
||||||
const databaseAccount: ViewModels.DatabaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const subscriptionId: string = CosmosClient.subscriptionId();
|
const subscriptionId: string = CosmosClient.subscriptionId();
|
||||||
const resourceGroup: string = CosmosClient.resourceGroup();
|
const resourceGroup: string = CosmosClient.resourceGroup();
|
||||||
const url: string = `${this._extensionEndpoint}${this._notificationsApiSuffix}?accountName=${databaseAccount.name}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
|
const url: string = `${this._extensionEndpoint}${this._notificationsApiSuffix}?accountName=${databaseAccount.name}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
|
||||||
|
|||||||
@@ -8,10 +8,19 @@ import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/Notificat
|
|||||||
import { CosmosClient } from "./CosmosClient";
|
import { CosmosClient } from "./CosmosClient";
|
||||||
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
import * as Logger from "./Logger";
|
import * as Logger from "./Logger";
|
||||||
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||||
import { QueryUtils } from "../Utils/QueryUtils";
|
import { QueryUtils } from "../Utils/QueryUtils";
|
||||||
|
import Explorer from "../Explorer/Explorer";
|
||||||
|
import {
|
||||||
|
getOrCreateDatabaseAndCollection,
|
||||||
|
createDocument,
|
||||||
|
queryDocuments,
|
||||||
|
queryDocumentsPage,
|
||||||
|
deleteDocument
|
||||||
|
} from "./DocumentClientUtilityBase";
|
||||||
|
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
|
||||||
|
|
||||||
export class QueriesClient implements ViewModels.QueriesClient {
|
export class QueriesClient {
|
||||||
private static readonly PartitionKey: DataModels.PartitionKey = {
|
private static readonly PartitionKey: DataModels.PartitionKey = {
|
||||||
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
||||||
kind: BackendDefaults.partitionKeyKind,
|
kind: BackendDefaults.partitionKeyKind,
|
||||||
@@ -20,7 +29,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
private static readonly FetchQuery: string = "SELECT * FROM c";
|
private static readonly FetchQuery: string = "SELECT * FROM c";
|
||||||
private static readonly FetchMongoQuery: string = "{}";
|
private static readonly FetchMongoQuery: string = "{}";
|
||||||
|
|
||||||
public constructor(private container: ViewModels.Explorer) {}
|
public constructor(private container: Explorer) {}
|
||||||
|
|
||||||
public async setupQueriesCollection(): Promise<DataModels.Collection> {
|
public async setupQueriesCollection(): Promise<DataModels.Collection> {
|
||||||
const queriesCollection: ViewModels.Collection = this.findQueriesCollection();
|
const queriesCollection: ViewModels.Collection = this.findQueriesCollection();
|
||||||
@@ -32,14 +41,13 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
ConsoleDataType.InProgress,
|
ConsoleDataType.InProgress,
|
||||||
"Setting up account for saving queries"
|
"Setting up account for saving queries"
|
||||||
);
|
);
|
||||||
return this.container.documentClientUtility
|
return getOrCreateDatabaseAndCollection({
|
||||||
.getOrCreateDatabaseAndCollection({
|
collectionId: SavedQueries.CollectionName,
|
||||||
collectionId: SavedQueries.CollectionName,
|
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) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
@@ -88,8 +96,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
`Saving query ${query.queryName}`
|
`Saving query ${query.queryName}`
|
||||||
);
|
);
|
||||||
query.id = query.queryName;
|
query.id = query.queryName;
|
||||||
return this.container.documentClientUtility
|
return createDocument(queriesCollection, query)
|
||||||
.createDocument(queriesCollection, query)
|
|
||||||
.then(
|
.then(
|
||||||
(savedQuery: DataModels.Query) => {
|
(savedQuery: DataModels.Query) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
@@ -130,17 +137,11 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
|
|
||||||
const options: any = { enableCrossPartitionQuery: true };
|
const options: any = { enableCrossPartitionQuery: true };
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
|
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
|
||||||
return this.container.documentClientUtility
|
return queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
|
||||||
.queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
|
|
||||||
.then(
|
.then(
|
||||||
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
|
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
|
||||||
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
|
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
|
||||||
this.container.documentClientUtility.queryDocumentsPage(
|
queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex, options);
|
||||||
queriesCollection.id(),
|
|
||||||
queryIterator,
|
|
||||||
firstItemIndex,
|
|
||||||
options
|
|
||||||
);
|
|
||||||
return QueryUtils.queryAllPages(fetchQueries).then(
|
return QueryUtils.queryAllPages(fetchQueries).then(
|
||||||
(results: ViewModels.QueryResults) => {
|
(results: ViewModels.QueryResults) => {
|
||||||
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
|
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
|
||||||
@@ -216,17 +217,16 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
`Deleting query ${query.queryName}`
|
`Deleting query ${query.queryName}`
|
||||||
);
|
);
|
||||||
query.id = query.queryName;
|
query.id = query.queryName;
|
||||||
const documentId: ViewModels.DocumentId = new DocumentId(
|
const documentId = new DocumentId(
|
||||||
{
|
{
|
||||||
partitionKey: QueriesClient.PartitionKey,
|
partitionKey: QueriesClient.PartitionKey,
|
||||||
partitionKeyProperty: "id"
|
partitionKeyProperty: "id"
|
||||||
} as ViewModels.DocumentsTab,
|
} as DocumentsTab,
|
||||||
query,
|
query,
|
||||||
query.queryName
|
query.queryName
|
||||||
); // TODO: Remove DocumentId's dependency on DocumentsTab
|
); // TODO: Remove DocumentId's dependency on DocumentsTab
|
||||||
const options: any = { partitionKey: query.resourceId };
|
const options: any = { partitionKey: query.resourceId };
|
||||||
return this.container.documentClientUtility
|
return deleteDocument(queriesCollection, documentId)
|
||||||
.deleteDocument(queriesCollection, documentId)
|
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
@@ -249,7 +249,7 @@ export class QueriesClient implements ViewModels.QueriesClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getResourceId(): string {
|
public getResourceId(): string {
|
||||||
const databaseAccount: ViewModels.DatabaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const databaseAccountName: string = (databaseAccount && databaseAccount.name) || "";
|
const databaseAccountName: string = (databaseAccount && databaseAccount.name) || "";
|
||||||
const subscriptionId: string = CosmosClient.subscriptionId() || "";
|
const subscriptionId: string = CosmosClient.subscriptionId() || "";
|
||||||
const resourceGroup: string = CosmosClient.resourceGroup() || "";
|
const resourceGroup: string = CosmosClient.resourceGroup() || "";
|
||||||
|
|||||||
16
src/Common/dataAccess/deleteCollection.test.ts
Normal file
16
src/Common/dataAccess/deleteCollection.test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
jest.mock("../../Utils/arm/request");
|
||||||
|
jest.mock("../MessageHandler");
|
||||||
|
import { deleteCollection } from "./deleteCollection";
|
||||||
|
import { armRequest } from "../../Utils/arm/request";
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
import { sendCachedDataMessage } from "../MessageHandler";
|
||||||
|
|
||||||
|
describe("deleteCollection", () => {
|
||||||
|
it("should call ARM if logged in with AAD", async () => {
|
||||||
|
window.authType = AuthType.AAD;
|
||||||
|
(sendCachedDataMessage as jest.Mock).mockResolvedValue(undefined);
|
||||||
|
await deleteCollection("database", "collection");
|
||||||
|
expect(armRequest).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
// TODO: Test non-AAD case
|
||||||
|
});
|
||||||
31
src/Common/dataAccess/deleteCollection.ts
Normal file
31
src/Common/dataAccess/deleteCollection.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { CosmosClient } from "../CosmosClient";
|
||||||
|
import { refreshCachedResources } from "../DataAccessUtilityBase";
|
||||||
|
import { logConsoleProgress, logConsoleInfo, logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
import { deleteSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||||
|
|
||||||
|
export async function deleteCollection(databaseId: string, collectionId: string): Promise<void> {
|
||||||
|
const clearMessage = logConsoleProgress(`Deleting container ${collectionId}`);
|
||||||
|
try {
|
||||||
|
if (window.authType === AuthType.AAD) {
|
||||||
|
await deleteSqlContainer(
|
||||||
|
CosmosClient.subscriptionId(),
|
||||||
|
CosmosClient.resourceGroup(),
|
||||||
|
CosmosClient.databaseAccount().name,
|
||||||
|
databaseId,
|
||||||
|
collectionId
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await CosmosClient.client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.delete();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while deleting container ${collectionId}:\n ${JSON.stringify(error)}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
logConsoleInfo(`Successfully deleted container ${collectionId}`);
|
||||||
|
clearMessage();
|
||||||
|
await refreshCachedResources();
|
||||||
|
}
|
||||||
16
src/Common/dataAccess/deleteDatabase.test.ts
Normal file
16
src/Common/dataAccess/deleteDatabase.test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
jest.mock("../../Utils/arm/request");
|
||||||
|
jest.mock("../MessageHandler");
|
||||||
|
import { deleteDatabase } from "./deleteDatabase";
|
||||||
|
import { armRequest } from "../../Utils/arm/request";
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
import { sendCachedDataMessage } from "../MessageHandler";
|
||||||
|
|
||||||
|
describe("deleteDatabase", () => {
|
||||||
|
it("should call ARM if logged in with AAD", async () => {
|
||||||
|
window.authType = AuthType.AAD;
|
||||||
|
(sendCachedDataMessage as jest.Mock).mockResolvedValue(undefined);
|
||||||
|
await deleteDatabase("database");
|
||||||
|
expect(armRequest).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
// TODO: Test non-AAD case
|
||||||
|
});
|
||||||
34
src/Common/dataAccess/deleteDatabase.ts
Normal file
34
src/Common/dataAccess/deleteDatabase.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { CosmosClient } from "../CosmosClient";
|
||||||
|
import { refreshCachedResources } from "../DataAccessUtilityBase";
|
||||||
|
import { logConsoleProgress, logConsoleError, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { deleteSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
import { logError } from "../Logger";
|
||||||
|
import { sendNotificationForError } from "./sendNotificationForError";
|
||||||
|
|
||||||
|
export async function deleteDatabase(databaseId: string): Promise<void> {
|
||||||
|
const clearMessage = logConsoleProgress(`Deleting database ${databaseId}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (window.authType === AuthType.AAD) {
|
||||||
|
await deleteSqlDatabase(
|
||||||
|
CosmosClient.subscriptionId(),
|
||||||
|
CosmosClient.resourceGroup(),
|
||||||
|
CosmosClient.databaseAccount().name,
|
||||||
|
databaseId
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await CosmosClient.client()
|
||||||
|
.database(databaseId)
|
||||||
|
.delete();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logConsoleError(`Error while deleting database ${databaseId}:\n ${JSON.stringify(error)}`);
|
||||||
|
logError(JSON.stringify(error), "DeleteDatabase", error.code);
|
||||||
|
sendNotificationForError(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
logConsoleInfo(`Successfully deleted database ${databaseId}`);
|
||||||
|
clearMessage();
|
||||||
|
await refreshCachedResources();
|
||||||
|
}
|
||||||
20
src/Common/dataAccess/sendNotificationForError.ts
Normal file
20
src/Common/dataAccess/sendNotificationForError.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import * as Constants from "../Constants";
|
||||||
|
import { sendMessage } from "../MessageHandler";
|
||||||
|
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
||||||
|
|
||||||
|
interface CosmosError {
|
||||||
|
code: number;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendNotificationForError(error: CosmosError): void {
|
||||||
|
if (error && error.code === Constants.HttpStatusCodes.Forbidden) {
|
||||||
|
if (error.message && error.message.toLowerCase().indexOf("sharedoffer is disabled for your account") > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendMessage({
|
||||||
|
type: MessageTypes.ForbiddenError,
|
||||||
|
reason: error && error.message ? error.message : error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -437,10 +437,8 @@ export interface Tenant {
|
|||||||
export interface AccountKeys {
|
export interface AccountKeys {
|
||||||
primaryMasterKey: string;
|
primaryMasterKey: string;
|
||||||
secondaryMasterKey: string;
|
secondaryMasterKey: string;
|
||||||
properties: {
|
primaryReadonlyMasterKey: string;
|
||||||
primaryReadonlyMasterKey: string;
|
secondaryReadonlyMasterKey: string;
|
||||||
secondaryReadonlyMasterKey: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AfecFeature {
|
export interface AfecFeature {
|
||||||
|
|||||||
@@ -1,306 +1,16 @@
|
|||||||
import * as DataModels from "./DataModels";
|
import * as DataModels from "./DataModels";
|
||||||
import * as Entities from "../Explorer/Tables/Entities";
|
|
||||||
import * as monaco from "monaco-editor";
|
|
||||||
import DocumentClientUtilityBase from "../Common/DocumentClientUtilityBase";
|
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import QueryViewModel from "../Explorer/Tables/QueryBuilder/QueryViewModel";
|
import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient";
|
||||||
import TableEntityListViewModel from "../Explorer/Tables/DataTable/TableEntityListViewModel";
|
|
||||||
import { AccessibleVerticalList } from "../Explorer/Tree/AccessibleVerticalList";
|
|
||||||
import { ArcadiaWorkspaceItem } from "../Explorer/Controls/Arcadia/ArcadiaMenuPicker";
|
|
||||||
import { CassandraTableKey, CassandraTableKeys, TableDataClient } from "../Explorer/Tables/TableDataClient";
|
|
||||||
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { ExecuteSprocParam } from "../Explorer/Panes/ExecuteSprocParamsPane";
|
|
||||||
import { GitHubClient } from "../GitHub/GitHubClient";
|
|
||||||
import { IColumnSetting } from "../Explorer/Panes/Tables/TableColumnOptionsPane";
|
|
||||||
import { JunoClient, IGalleryItem } from "../Juno/JunoClient";
|
|
||||||
import { Library } from "./DataModels";
|
|
||||||
import { MostRecentActivity } from "../Explorer/MostRecentActivity/MostRecentActivity";
|
|
||||||
import { NotebookContentItem } from "../Explorer/Notebook/NotebookContentItem";
|
|
||||||
import { PlatformType } from "../PlatformType";
|
|
||||||
import { QueryMetrics } from "@azure/cosmos";
|
import { QueryMetrics } from "@azure/cosmos";
|
||||||
import { SetupNotebooksPane } from "../Explorer/Panes/SetupNotebooksPane";
|
|
||||||
import { Splitter } from "../Common/Splitter";
|
|
||||||
import { StringInputPane } from "../Explorer/Panes/StringInputPane";
|
|
||||||
import { TabsManager } from "../Explorer/Tabs/TabsManager";
|
|
||||||
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 Explorer from "../Explorer/Explorer";
|
||||||
import { ReactAdapter } from "../Bindings/ReactBindingHandler";
|
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
|
||||||
|
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
||||||
export interface ExplorerOptions {
|
import Trigger from "../Explorer/Tree/Trigger";
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||||
notificationsClient: NotificationsClient;
|
import ConflictId from "../Explorer/Tree/ConflictId";
|
||||||
isEmulator: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Capability extends DataModels.Capability {}
|
|
||||||
|
|
||||||
export interface ConfigurationOverrides extends DataModels.ConfigurationOverrides {}
|
|
||||||
|
|
||||||
export interface NavbarButtonConfig extends CommandButtonComponentProps {}
|
|
||||||
|
|
||||||
export interface DatabaseAccount extends DataModels.DatabaseAccount {}
|
|
||||||
|
|
||||||
export interface Explorer {
|
|
||||||
flight: ko.Observable<string>;
|
|
||||||
handleMessage(event: MessageEvent): void;
|
|
||||||
isRefreshingExplorer: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
databaseAccount: ko.Observable<DatabaseAccount>;
|
|
||||||
subscriptionType: ko.Observable<SubscriptionType>;
|
|
||||||
quotaId: ko.Observable<string>;
|
|
||||||
hasWriteAccess: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
defaultExperience: ko.Observable<string>;
|
|
||||||
isPreferredApiDocumentDB: ko.Computed<boolean>;
|
|
||||||
isPreferredApiCassandra: ko.Computed<boolean>;
|
|
||||||
isPreferredApiTable: ko.Computed<boolean>;
|
|
||||||
isPreferredApiGraph: ko.Computed<boolean>;
|
|
||||||
isPreferredApiMongoDB: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
isDatabaseNodeOrNoneSelected(): boolean;
|
|
||||||
isDatabaseNodeSelected(): boolean;
|
|
||||||
isNodeKindSelected(nodeKind: string): boolean;
|
|
||||||
isNoneSelected(): boolean;
|
|
||||||
isSelectedDatabaseShared(): boolean;
|
|
||||||
deleteDatabaseText: ko.Observable<string>;
|
|
||||||
deleteCollectionText: ko.Subscribable<string>; // Our code assigns to a ko.Observable, but unit test assigns to ko.Computed
|
|
||||||
|
|
||||||
addCollectionText: ko.Observable<string>;
|
|
||||||
addDatabaseText: ko.Observable<string>;
|
|
||||||
collectionTitle: ko.Observable<string>;
|
|
||||||
collectionTreeNodeAltText: ko.Observable<string>;
|
|
||||||
refreshTreeTitle: ko.Observable<string>;
|
|
||||||
|
|
||||||
isAccountReady: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
collectionCreationDefaults: CollectionCreationDefaults;
|
|
||||||
isEmulator: boolean;
|
|
||||||
features: ko.Observable<any>;
|
|
||||||
serverId: ko.Observable<string>;
|
|
||||||
extensionEndpoint: ko.Observable<string>;
|
|
||||||
armEndpoint: ko.Observable<string>;
|
|
||||||
isFeatureEnabled: (feature: string) => boolean;
|
|
||||||
isGalleryPublishEnabled: ko.Computed<boolean>;
|
|
||||||
isGitHubPaneEnabled: ko.Observable<boolean>;
|
|
||||||
isPublishNotebookPaneEnabled: ko.Observable<boolean>;
|
|
||||||
isRightPanelV2Enabled: ko.Computed<boolean>;
|
|
||||||
canExceedMaximumValue: ko.Computed<boolean>;
|
|
||||||
hasAutoPilotV2FeatureFlag: ko.Computed<boolean>;
|
|
||||||
isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
|
||||||
isNotificationConsoleExpanded: ko.Observable<boolean>;
|
|
||||||
isTryCosmosDBSubscription: ko.Observable<boolean>;
|
|
||||||
canSaveQueries: ko.Computed<boolean>;
|
|
||||||
parentFrameDataExplorerVersion: ko.Observable<string>;
|
|
||||||
|
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
|
||||||
notificationsClient: NotificationsClient;
|
|
||||||
queriesClient: QueriesClient;
|
|
||||||
tableDataClient: TableDataClient;
|
|
||||||
splitter: Splitter;
|
|
||||||
notificationConsoleData: ko.ObservableArray<ConsoleData>;
|
|
||||||
|
|
||||||
// Selection
|
|
||||||
selectedNode: ko.Observable<TreeNode>;
|
|
||||||
|
|
||||||
// Tree
|
|
||||||
databases: ko.ObservableArray<Database>;
|
|
||||||
nonSystemDatabases: ko.Computed<Database[]>;
|
|
||||||
selectedDatabaseId: ko.Computed<string>;
|
|
||||||
selectedCollectionId: ko.Computed<string>;
|
|
||||||
isLeftPaneExpanded: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
// Resource Token
|
|
||||||
resourceTokenDatabaseId: ko.Observable<string>;
|
|
||||||
resourceTokenCollectionId: ko.Observable<string>;
|
|
||||||
resourceTokenCollection: ko.Observable<CollectionBase>;
|
|
||||||
resourceTokenPartitionKey: ko.Observable<string>;
|
|
||||||
isAuthWithResourceToken: ko.Observable<boolean>;
|
|
||||||
isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
// Tabs
|
|
||||||
isTabsContentExpanded: ko.Observable<boolean>;
|
|
||||||
tabsManager: TabsManager;
|
|
||||||
|
|
||||||
// Contextual Panes
|
|
||||||
addDatabasePane: AddDatabasePane;
|
|
||||||
addCollectionPane: AddCollectionPane;
|
|
||||||
deleteCollectionConfirmationPane: DeleteCollectionConfirmationPane;
|
|
||||||
deleteDatabaseConfirmationPane: DeleteDatabaseConfirmationPane;
|
|
||||||
graphStylingPane: GraphStylingPane;
|
|
||||||
addTableEntityPane: AddTableEntityPane;
|
|
||||||
editTableEntityPane: EditTableEntityPane;
|
|
||||||
tableColumnOptionsPane: TableColumnOptionsPane;
|
|
||||||
querySelectPane: QuerySelectPane;
|
|
||||||
newVertexPane: NewVertexPane;
|
|
||||||
cassandraAddCollectionPane: CassandraAddCollectionPane;
|
|
||||||
settingsPane: SettingsPane;
|
|
||||||
executeSprocParamsPane: ExecuteSprocParamsPane;
|
|
||||||
renewAdHocAccessPane: RenewAdHocAccessPane;
|
|
||||||
uploadItemsPane: UploadItemsPane;
|
|
||||||
uploadItemsPaneAdapter: UploadItemsPaneAdapter;
|
|
||||||
loadQueryPane: LoadQueryPane;
|
|
||||||
saveQueryPane: ContextualPane;
|
|
||||||
browseQueriesPane: BrowseQueriesPane;
|
|
||||||
uploadFilePane: UploadFilePane;
|
|
||||||
stringInputPane: StringInputPane;
|
|
||||||
setupNotebooksPane: SetupNotebooksPane;
|
|
||||||
libraryManagePane: ContextualPane;
|
|
||||||
clusterLibraryPane: ContextualPane;
|
|
||||||
gitHubReposPane: ContextualPane;
|
|
||||||
publishNotebookPaneAdapter: ReactAdapter;
|
|
||||||
|
|
||||||
// Facade
|
|
||||||
logConsoleData(data: ConsoleData): void;
|
|
||||||
isNodeKindSelected(nodeKind: string): boolean;
|
|
||||||
initDataExplorerWithFrameInputs(inputs: DataExplorerInputsFrame): Q.Promise<void>;
|
|
||||||
toggleLeftPaneExpanded(): void;
|
|
||||||
refreshDatabaseForResourceToken(): Q.Promise<void>;
|
|
||||||
refreshAllDatabases(isInitialLoad?: boolean): Q.Promise<any>;
|
|
||||||
closeAllPanes(): void;
|
|
||||||
findSelectedDatabase(): Database;
|
|
||||||
findDatabaseWithId(databaseRid: string): Database;
|
|
||||||
isLastDatabase(): boolean;
|
|
||||||
isLastNonEmptyDatabase(): boolean;
|
|
||||||
findSelectedCollection(): Collection;
|
|
||||||
isLastCollection(): boolean;
|
|
||||||
findSelectedStoredProcedure(): StoredProcedure;
|
|
||||||
findSelectedUDF(): UserDefinedFunction;
|
|
||||||
findSelectedTrigger(): Trigger;
|
|
||||||
findCollection(rid: string): Collection;
|
|
||||||
provideFeedbackEmail(): void;
|
|
||||||
expandConsole: () => void;
|
|
||||||
collapseConsole: () => void;
|
|
||||||
generateSharedAccessData(): void;
|
|
||||||
getPlatformType(): PlatformType;
|
|
||||||
isConnectExplorerVisible(): boolean;
|
|
||||||
isRunningOnNationalCloud(): boolean;
|
|
||||||
displayConnectExplorerForm(): void;
|
|
||||||
hideConnectExplorerForm(): void;
|
|
||||||
displayContextSwitchPromptForConnectionString(connectionString: string): void;
|
|
||||||
displayGuestAccessTokenRenewalPrompt(): void;
|
|
||||||
rebindDocumentClientUtility(documentClientUtility: DocumentClientUtilityBase): void;
|
|
||||||
renewExplorerShareAccess: (explorer: Explorer, token: string) => Q.Promise<void>;
|
|
||||||
renewShareAccess(accessInput: string): Q.Promise<void>;
|
|
||||||
onUpdateTabsButtons: (buttons: NavbarButtonConfig[]) => void;
|
|
||||||
onNewCollectionClicked: () => void;
|
|
||||||
showOkModalDialog: (title: string, msg: string) => void;
|
|
||||||
showOkCancelModalDialog: (
|
|
||||||
title: string,
|
|
||||||
msg: string,
|
|
||||||
okLabel: string,
|
|
||||||
onOk: () => void,
|
|
||||||
cancelLabel: string,
|
|
||||||
onCancel: () => void
|
|
||||||
) => void;
|
|
||||||
showOkCancelTextFieldModalDialog: (
|
|
||||||
title: string,
|
|
||||||
msg: string,
|
|
||||||
okLabel: string,
|
|
||||||
onOk: () => void,
|
|
||||||
cancelLabel: string,
|
|
||||||
onCancel: () => void,
|
|
||||||
textFiledProps: TextFieldProps,
|
|
||||||
isPrimaryButtonDisabled?: boolean
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
// Analytics
|
|
||||||
isNotebookEnabled: ko.Observable<boolean>;
|
|
||||||
isSparkEnabled: ko.Observable<boolean>;
|
|
||||||
isNotebooksEnabledForAccount: ko.Observable<boolean>;
|
|
||||||
isSparkEnabledForAccount: ko.Observable<boolean>;
|
|
||||||
hasStorageAnalyticsAfecFeature: ko.Observable<boolean>;
|
|
||||||
openEnableSynapseLinkDialog(): void;
|
|
||||||
isSynapseLinkUpdating: ko.Observable<boolean>;
|
|
||||||
notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>;
|
|
||||||
sparkClusterConnectionInfo: ko.Observable<DataModels.SparkClusterConnectionInfo>;
|
|
||||||
arcadiaToken: ko.Observable<string>;
|
|
||||||
arcadiaWorkspaces: ko.ObservableArray<ArcadiaWorkspaceItem>;
|
|
||||||
memoryUsageInfo: ko.Observable<DataModels.MemoryUsageInfo>;
|
|
||||||
notebookManager?: any; // This is dynamically loaded
|
|
||||||
openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean>; // True if it was opened, false otherwise
|
|
||||||
resetNotebookWorkspace(): void;
|
|
||||||
importAndOpen: (path: string) => Promise<boolean>;
|
|
||||||
importAndOpenContent: (name: string, content: string) => Promise<boolean>;
|
|
||||||
publishNotebook: (name: string, content: string) => void;
|
|
||||||
openNotebookTerminal: (kind: TerminalKind) => void;
|
|
||||||
openGallery: (notebookUrl?: string, galleryItem?: IGalleryItem, isFavorite?: boolean) => void;
|
|
||||||
openNotebookViewer: (notebookUrl: string) => void;
|
|
||||||
notebookWorkspaceManager: NotebookWorkspaceManager;
|
|
||||||
sparkClusterManager: SparkClusterManager;
|
|
||||||
mostRecentActivity: MostRecentActivity;
|
|
||||||
initNotebooks: (databaseAccount: DataModels.DatabaseAccount) => Promise<void>;
|
|
||||||
deleteCluster(): void;
|
|
||||||
openSparkMasterTab(): Promise<void>;
|
|
||||||
handleOpenFileAction(path: string): Promise<void>;
|
|
||||||
|
|
||||||
// Notebook operations
|
|
||||||
openNotebook(notebookContentItem: NotebookContentItem): Promise<boolean>; // True if it was opened, false otherwise
|
|
||||||
deleteNotebookFile: (item: NotebookContentItem) => Promise<void>;
|
|
||||||
onCreateDirectory(parent: NotebookContentItem): Q.Promise<NotebookContentItem>;
|
|
||||||
onNewNotebookClicked: (parent?: NotebookContentItem) => void;
|
|
||||||
onUploadToNotebookServerClicked: (parent?: NotebookContentItem) => void;
|
|
||||||
renameNotebook: (notebookFile: NotebookContentItem) => Q.Promise<NotebookContentItem>;
|
|
||||||
readFile: (notebookFile: NotebookContentItem) => Promise<string>;
|
|
||||||
downloadFile: (notebookFile: NotebookContentItem) => Promise<void>;
|
|
||||||
createNotebookContentItemFile: (name: string, filepath: string) => NotebookContentItem;
|
|
||||||
refreshContentItem(item: NotebookContentItem): Promise<void>;
|
|
||||||
getNotebookBasePath(): string;
|
|
||||||
|
|
||||||
createWorkspace(): Promise<string>;
|
|
||||||
createSparkPool(workspaceId: string): Promise<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NotebookWorkspaceManager {
|
|
||||||
getNotebookWorkspacesAsync(cosmosAccountResourceId: string): Promise<DataModels.NotebookWorkspace[]>;
|
|
||||||
getNotebookWorkspaceAsync(
|
|
||||||
cosmosAccountResourceId: string,
|
|
||||||
notebookWorkspaceId: string
|
|
||||||
): Promise<DataModels.NotebookWorkspace>;
|
|
||||||
createNotebookWorkspaceAsync(cosmosdbResourceId: string, notebookWorkspaceId: string): Promise<void>;
|
|
||||||
deleteNotebookWorkspaceAsync(cosmosdbResourceId: string, notebookWorkspaceId: string): Promise<void>;
|
|
||||||
getNotebookConnectionInfoAsync(
|
|
||||||
cosmosAccountResourceId: string,
|
|
||||||
notebookWorkspaceId: string
|
|
||||||
): Promise<DataModels.NotebookWorkspaceConnectionInfo>;
|
|
||||||
startNotebookWorkspaceAsync(cosmosdbResourceId: string, notebookWorkspaceId: string): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface KernelConnectionMetadata {
|
|
||||||
name: string;
|
|
||||||
configurationEndpoints: DataModels.NotebookConfigurationEndpoints;
|
|
||||||
notebookConnectionInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SparkClusterManager {
|
|
||||||
getClustersAsync(cosmosAccountResourceId: string): Promise<DataModels.SparkCluster[]>;
|
|
||||||
getClusterAsync(cosmosAccountResourceId: string, clusterId: string): Promise<DataModels.SparkCluster>;
|
|
||||||
createClusterAsync(cosmosAccountResourceId: string, cluster: Partial<DataModels.SparkCluster>): Promise<void>;
|
|
||||||
updateClusterAsync(
|
|
||||||
cosmosAccountResourceId: string,
|
|
||||||
clusterId: string,
|
|
||||||
sparkCluster: DataModels.SparkCluster
|
|
||||||
): Promise<DataModels.SparkCluster>;
|
|
||||||
deleteClusterAsync(cosmosAccountResourceId: string, clusterId: string): Promise<void>;
|
|
||||||
getClusterConnectionInfoAsync(
|
|
||||||
cosmosAccountResourceId: string,
|
|
||||||
clusterId: string
|
|
||||||
): Promise<DataModels.SparkClusterConnectionInfo>;
|
|
||||||
getLibrariesAsync(cosmosdbResourceId: string): Promise<Library[]>;
|
|
||||||
getLibraryAsync(cosmosdbResourceId: string, libraryName: string): Promise<Library>;
|
|
||||||
addLibraryAsync(cosmosdbResourceId: string, libraryName: string, library: Library): Promise<void>;
|
|
||||||
deleteLibraryAsync(cosmosdbResourceId: string, libraryName: string): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ArcadiaResourceManager {
|
|
||||||
getWorkspacesAsync(arcadiaResourceId: string): Promise<DataModels.ArcadiaWorkspace[]>;
|
|
||||||
getWorkspaceAsync(arcadiaResourceId: string, workspaceId: string): Promise<DataModels.ArcadiaWorkspace>;
|
|
||||||
listWorkspacesAsync(subscriptionIds: string[]): Promise<DataModels.ArcadiaWorkspace[]>;
|
|
||||||
listSparkPoolsAsync(resourceId: string): Promise<DataModels.SparkPool[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TokenProvider {
|
export interface TokenProvider {
|
||||||
getAuthHeader(): Promise<Headers>;
|
getAuthHeader(): Promise<Headers>;
|
||||||
@@ -340,11 +50,6 @@ export interface WaitsForTemplate {
|
|||||||
isTemplateReady: ko.Observable<boolean>;
|
isTemplateReady: ko.Observable<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdHocAccessData {
|
|
||||||
readWriteUrl: string;
|
|
||||||
readUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TreeNode {
|
export interface TreeNode {
|
||||||
nodeKind: string;
|
nodeKind: string;
|
||||||
rid: string;
|
rid: string;
|
||||||
@@ -466,236 +171,15 @@ export interface Collection extends CollectionBase {
|
|||||||
getLabel(): string;
|
getLabel(): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DocumentId {
|
|
||||||
container: DocumentsTab;
|
|
||||||
rid: string;
|
|
||||||
self: string;
|
|
||||||
ts: string;
|
|
||||||
partitionKeyValue: any;
|
|
||||||
partitionKeyProperty: string;
|
|
||||||
partitionKey: DataModels.PartitionKey;
|
|
||||||
stringPartitionKeyValue: string;
|
|
||||||
id: ko.Observable<string>;
|
|
||||||
|
|
||||||
isDirty: ko.Observable<boolean>;
|
|
||||||
click(): void;
|
|
||||||
getPartitionKeyValueAsString(): string;
|
|
||||||
loadDocument(): Q.Promise<any>;
|
|
||||||
partitionKeyHeader(): Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConflictId {
|
|
||||||
container: ConflictsTab;
|
|
||||||
rid: string;
|
|
||||||
self: string;
|
|
||||||
ts: string;
|
|
||||||
partitionKeyValue: any;
|
|
||||||
partitionKeyProperty: string;
|
|
||||||
partitionKey: DataModels.PartitionKey;
|
|
||||||
stringPartitionKeyValue: string;
|
|
||||||
id: ko.Observable<string>;
|
|
||||||
operationType: string;
|
|
||||||
resourceId: string;
|
|
||||||
resourceType: string;
|
|
||||||
|
|
||||||
isDirty: ko.Observable<boolean>;
|
|
||||||
click(): void;
|
|
||||||
buildDocumentIdFromConflict(partitionKeyValue: any): DocumentId;
|
|
||||||
getPartitionKeyValueAsString(): string;
|
|
||||||
loadConflict(): Q.Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StoredProcedure extends TreeNode {
|
|
||||||
container: Explorer;
|
|
||||||
collection: Collection;
|
|
||||||
rid: string;
|
|
||||||
self: string;
|
|
||||||
id: ko.Observable<string>;
|
|
||||||
body: ko.Observable<string>;
|
|
||||||
|
|
||||||
delete(): void;
|
|
||||||
open: () => void;
|
|
||||||
select(): void;
|
|
||||||
execute(params: string[], partitionKeyValue?: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserDefinedFunction extends TreeNode {
|
|
||||||
container: Explorer;
|
|
||||||
collection: Collection;
|
|
||||||
rid: string;
|
|
||||||
self: string;
|
|
||||||
id: ko.Observable<string>;
|
|
||||||
body: ko.Observable<string>;
|
|
||||||
|
|
||||||
delete(): void;
|
|
||||||
open: () => void;
|
|
||||||
select(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Trigger extends TreeNode {
|
|
||||||
container: Explorer;
|
|
||||||
collection: Collection;
|
|
||||||
rid: string;
|
|
||||||
self: string;
|
|
||||||
id: ko.Observable<string>;
|
|
||||||
body: ko.Observable<string>;
|
|
||||||
triggerType: ko.Observable<string>;
|
|
||||||
triggerOperation: ko.Observable<string>;
|
|
||||||
|
|
||||||
delete(): void;
|
|
||||||
open: () => void;
|
|
||||||
select(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options used to initialize pane
|
* Options used to initialize pane
|
||||||
*/
|
*/
|
||||||
export interface PaneOptions {
|
export interface PaneOptions {
|
||||||
id: string;
|
id: string;
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
|
||||||
visible: ko.Observable<boolean>;
|
visible: ko.Observable<boolean>;
|
||||||
container?: Explorer;
|
container?: Explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContextualPane {
|
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
|
||||||
formErrors: ko.Observable<string>;
|
|
||||||
formErrorsDetails: ko.Observable<string>;
|
|
||||||
id: string;
|
|
||||||
title: ko.Observable<string>;
|
|
||||||
visible: ko.Observable<boolean>;
|
|
||||||
firstFieldHasFocus: ko.Observable<boolean>;
|
|
||||||
isExecuting: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
submit: () => void;
|
|
||||||
cancel: () => void;
|
|
||||||
open: () => void;
|
|
||||||
close: () => void;
|
|
||||||
resetData: () => void;
|
|
||||||
showErrorDetails: () => void;
|
|
||||||
onCloseKeyPress(source: any, event: KeyboardEvent): void;
|
|
||||||
onPaneKeyDown(source: any, event: KeyboardEvent): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GitHubReposPaneOptions extends PaneOptions {
|
|
||||||
gitHubClient: GitHubClient;
|
|
||||||
junoClient: JunoClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PublishNotebookPaneOptions extends PaneOptions {
|
|
||||||
junoClient: JunoClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PublishNotebookPaneOpenOptions {
|
|
||||||
name: string;
|
|
||||||
author: string;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AddCollectionPaneOptions extends PaneOptions {
|
|
||||||
isPreferredApiTable: ko.Computed<boolean>;
|
|
||||||
databaseId?: string;
|
|
||||||
databaseSelfLink?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AddCollectionPane extends ContextualPane {
|
|
||||||
collectionIdTitle: ko.Observable<string>;
|
|
||||||
collectionWithThroughputInSharedTitle: ko.Observable<string>;
|
|
||||||
databaseId: ko.Observable<string>;
|
|
||||||
partitionKey: ko.Observable<string>;
|
|
||||||
storage: ko.Observable<string>;
|
|
||||||
throughputSinglePartition: ko.Observable<number>;
|
|
||||||
throughputMultiPartition: ko.Observable<number>;
|
|
||||||
|
|
||||||
open: (databaseId?: string) => void;
|
|
||||||
onStorageOptionsKeyDown(source: any, event: KeyboardEvent): boolean;
|
|
||||||
onRupmOptionsKeyDown(source: any, event: KeyboardEvent): void;
|
|
||||||
onEnableSynapseLinkButtonClicked: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AddDatabasePane extends ContextualPane {}
|
|
||||||
|
|
||||||
export interface DeleteDatabaseConfirmationPane extends ContextualPane {
|
|
||||||
databaseIdConfirmation: ko.Observable<string>;
|
|
||||||
databaseIdConfirmationText: ko.Observable<string>;
|
|
||||||
databaseDeleteFeedback: ko.Observable<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeleteCollectionConfirmationPane extends ContextualPane {
|
|
||||||
collectionIdConfirmation: ko.Observable<string>;
|
|
||||||
collectionIdConfirmationText: ko.Observable<string>;
|
|
||||||
containerDeleteFeedback: ko.Observable<string>;
|
|
||||||
recordDeleteFeedback: ko.Observable<boolean>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SettingsPane extends ContextualPane {
|
|
||||||
pageOption: ko.Observable<string>;
|
|
||||||
customItemPerPage: ko.Observable<number>;
|
|
||||||
crossPartitionQueryEnabled: ko.Observable<boolean>;
|
|
||||||
maxDegreeOfParallelism: ko.Observable<number>;
|
|
||||||
shouldShowQueryPageOptions: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
onCustomPageOptionsKeyDown(source: any, event: KeyboardEvent): boolean;
|
|
||||||
onUnlimitedPageOptionKeyDown(source: any, event: KeyboardEvent): boolean;
|
|
||||||
onJsonDisplayResultsKeyDown(source: any, event: KeyboardEvent): boolean;
|
|
||||||
onGraphDisplayResultsKeyDown(source: any, event: KeyboardEvent): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExecuteSprocParamsPane extends ContextualPane {
|
|
||||||
params: ko.ObservableArray<ExecuteSprocParam>;
|
|
||||||
partitionKeyValue: ko.Observable<string>;
|
|
||||||
|
|
||||||
addNewParam(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RenewAdHocAccessPane extends ContextualPane {
|
|
||||||
accessKey: ko.Observable<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UploadItemsPane extends ContextualPane {
|
|
||||||
selectedFilesTitle: ko.Observable<string>;
|
|
||||||
files: ko.Observable<FileList>;
|
|
||||||
|
|
||||||
updateSelectedFiles(element: any, event: any): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LoadQueryPane extends ContextualPane {
|
|
||||||
selectedFilesTitle: ko.Observable<string>;
|
|
||||||
files: ko.Observable<FileList>;
|
|
||||||
|
|
||||||
loadQueryFromFile(file: File): Q.Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BrowseQueriesPane extends ContextualPane {
|
|
||||||
loadSavedQuery: (savedQuery: DataModels.Query) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UploadFilePaneOpenOptions {
|
|
||||||
paneTitle: string;
|
|
||||||
selectFileInputLabel: string;
|
|
||||||
errorMessage: string; // Could not upload notebook
|
|
||||||
inProgressMessage: string; // Uploading notebook
|
|
||||||
successMessage: string; // Successfully uploaded notebook
|
|
||||||
onSubmit: (file: File) => Promise<any>;
|
|
||||||
extensions?: string; // input accept field. E.g: .ipynb
|
|
||||||
submitButtonLabel?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StringInputPaneOpenOptions {
|
|
||||||
paneTitle: string;
|
|
||||||
inputLabel: string;
|
|
||||||
errorMessage: string;
|
|
||||||
inProgressMessage: string;
|
|
||||||
successMessage: string;
|
|
||||||
onSubmit: (input: string) => Promise<any>;
|
|
||||||
submitButtonLabel: string;
|
|
||||||
defaultInput?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UploadFilePane extends ContextualPane {
|
|
||||||
openWithOptions: (options: UploadFilePaneOpenOptions) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Graph configuration
|
* Graph configuration
|
||||||
*/
|
*/
|
||||||
@@ -740,41 +224,6 @@ export interface InputProperty {
|
|||||||
values: InputPropertyValue[];
|
values: InputPropertyValue[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GraphStylingPane extends ContextualPane {
|
|
||||||
setData(graphConfigUIData: GraphConfigUiData): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NewVertexPane extends ContextualPane {
|
|
||||||
setPartitionKeyProperty: (pKeyProp: string) => void;
|
|
||||||
subscribeOnSubmitCreate: (callback: (newVertexData: NewVertexData) => void) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AddTableEntityPane extends ContextualPane {
|
|
||||||
tableViewModel: TableEntityListViewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EditTableEntityPane extends ContextualPane {
|
|
||||||
originEntity: Entities.ITableEntity;
|
|
||||||
tableViewModel: TableEntityListViewModel;
|
|
||||||
originalNumberOfProperties: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TableColumnOptionsPane extends ContextualPane {
|
|
||||||
tableViewModel: TableEntityListViewModel;
|
|
||||||
parameters: IColumnSetting;
|
|
||||||
setDisplayedColumns(columnNames: string[], order: number[], visible: boolean[]): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QuerySelectPane extends ContextualPane {
|
|
||||||
queryViewModel: QueryViewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CassandraAddCollectionPane extends ContextualPane {
|
|
||||||
createTableQuery: ko.Observable<string>;
|
|
||||||
keyspaceId: ko.Observable<string>;
|
|
||||||
userTableQuery: ko.Observable<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Editable<T> extends ko.Observable<T> {
|
export interface Editable<T> extends ko.Observable<T> {
|
||||||
setBaseline(baseline: T): void;
|
setBaseline(baseline: T): void;
|
||||||
|
|
||||||
@@ -800,19 +249,6 @@ export interface DocumentRequestContainer {
|
|||||||
resourceName?: string;
|
resourceName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotificationsClient {
|
|
||||||
fetchNotifications(): Q.Promise<DataModels.Notification[]>;
|
|
||||||
setExtensionEndpoint(extensionEndpoint: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QueriesClient {
|
|
||||||
setupQueriesCollection(): Promise<DataModels.Collection>;
|
|
||||||
saveQuery(query: DataModels.Query): Promise<void>;
|
|
||||||
getQueries(): Promise<DataModels.Query[]>;
|
|
||||||
deleteQuery(query: DataModels.Query): Promise<void>;
|
|
||||||
getResourceId(): string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DocumentClientOption {
|
export interface DocumentClientOption {
|
||||||
endpoint?: string;
|
endpoint?: string;
|
||||||
masterKey?: string;
|
masterKey?: string;
|
||||||
@@ -824,11 +260,10 @@ export interface TabOptions {
|
|||||||
tabKind: CollectionTabKind;
|
tabKind: CollectionTabKind;
|
||||||
title: string;
|
title: string;
|
||||||
tabPath: string;
|
tabPath: string;
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
|
||||||
selfLink: string;
|
selfLink: string;
|
||||||
isActive: ko.Observable<boolean>;
|
isActive: ko.Observable<boolean>;
|
||||||
hashLocation: string;
|
hashLocation: string;
|
||||||
onUpdateTabsButtons: (buttons: NavbarButtonConfig[]) => void;
|
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]) => void;
|
||||||
isTabsContentExpanded?: ko.Observable<boolean>;
|
isTabsContentExpanded?: ko.Observable<boolean>;
|
||||||
onLoadStartKey?: number;
|
onLoadStartKey?: number;
|
||||||
|
|
||||||
@@ -841,47 +276,6 @@ export interface TabOptions {
|
|||||||
theme?: string;
|
theme?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SparkMasterTabOptions extends TabOptions {
|
|
||||||
clusterConnectionInfo: DataModels.SparkClusterConnectionInfo;
|
|
||||||
container: Explorer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GraphTabOptions extends TabOptions {
|
|
||||||
account: DatabaseAccount;
|
|
||||||
masterKey: string;
|
|
||||||
collectionId: string;
|
|
||||||
databaseId: string;
|
|
||||||
collectionPartitionKeyProperty: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NotebookTabOptions extends TabOptions {
|
|
||||||
account: DatabaseAccount;
|
|
||||||
masterKey: string;
|
|
||||||
container: Explorer;
|
|
||||||
notebookContentItem: NotebookContentItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TerminalTabOptions extends TabOptions {
|
|
||||||
account: DatabaseAccount;
|
|
||||||
container: Explorer;
|
|
||||||
kind: TerminalKind;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GalleryTabOptions extends TabOptions {
|
|
||||||
account: DatabaseAccount;
|
|
||||||
container: Explorer;
|
|
||||||
junoClient: JunoClient;
|
|
||||||
notebookUrl?: string;
|
|
||||||
galleryItem?: IGalleryItem;
|
|
||||||
isFavorite?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NotebookViewerTabOptions extends TabOptions {
|
|
||||||
account: DatabaseAccount;
|
|
||||||
container: Explorer;
|
|
||||||
notebookUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DocumentsTabOptions extends TabOptions {
|
export interface DocumentsTabOptions extends TabOptions {
|
||||||
partitionKey: DataModels.PartitionKey;
|
partitionKey: DataModels.PartitionKey;
|
||||||
documentIds: ko.ObservableArray<DocumentId>;
|
documentIds: ko.ObservableArray<DocumentId>;
|
||||||
@@ -909,262 +303,15 @@ export interface ScriptTabOption extends TabOptions {
|
|||||||
partitionKey?: DataModels.PartitionKey;
|
partitionKey?: DataModels.PartitionKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tabs
|
|
||||||
export interface Tab {
|
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
|
||||||
node: TreeNode; // Can be null
|
|
||||||
collection: CollectionBase;
|
|
||||||
rid: string;
|
|
||||||
|
|
||||||
tabKind: CollectionTabKind;
|
|
||||||
tabId: string;
|
|
||||||
isActive: ko.Observable<boolean>;
|
|
||||||
isMouseOver: ko.Observable<boolean>;
|
|
||||||
tabPath: ko.Observable<string>;
|
|
||||||
tabTitle: ko.Observable<string>;
|
|
||||||
hashLocation: ko.Observable<string>;
|
|
||||||
closeTabButton: Button;
|
|
||||||
onCloseTabButtonClick(): void;
|
|
||||||
onTabClick(): Q.Promise<any>;
|
|
||||||
onKeyPressActivate(source: any, event: KeyboardEvent): void;
|
|
||||||
onKeyPressClose(source: any, event: KeyboardEvent): void;
|
|
||||||
onActivate(): Q.Promise<any>;
|
|
||||||
refresh(): void;
|
|
||||||
closeButtonTabIndex: ko.Computed<number>;
|
|
||||||
isExecutionError: ko.Observable<boolean>;
|
|
||||||
isExecuting: ko.Observable<boolean>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DocumentsTab extends Tab {
|
|
||||||
/* Documents Grid */
|
|
||||||
selectDocument(documentId: DocumentId): Q.Promise<any>;
|
|
||||||
selectedDocumentId: ko.Observable<DocumentId>;
|
|
||||||
selectedDocumentContent: Editable<any>;
|
|
||||||
onDocumentIdClick(documentId: DocumentId): Q.Promise<any>;
|
|
||||||
dataContentsGridScrollHeight: ko.Observable<string>;
|
|
||||||
accessibleDocumentList: AccessibleVerticalList;
|
|
||||||
documentContentsGridId: string;
|
|
||||||
|
|
||||||
partitionKey: DataModels.PartitionKey;
|
|
||||||
idHeader: string;
|
|
||||||
partitionKeyPropertyHeader: string;
|
|
||||||
partitionKeyProperty: string;
|
|
||||||
documentIds: ko.ObservableArray<DocumentId>;
|
|
||||||
|
|
||||||
/* Documents Filter */
|
|
||||||
filterContent: ko.Observable<string>;
|
|
||||||
appliedFilter: ko.Observable<string>;
|
|
||||||
lastFilterContents: ko.ObservableArray<string>;
|
|
||||||
isFilterExpanded: ko.Observable<boolean>;
|
|
||||||
applyFilterButton: Button;
|
|
||||||
onShowFilterClick(): Q.Promise<any>;
|
|
||||||
onHideFilterClick(): Q.Promise<any>;
|
|
||||||
onApplyFilterClick(): Q.Promise<any>;
|
|
||||||
|
|
||||||
/* Document Editor */
|
|
||||||
isEditorDirty: ko.Computed<boolean>;
|
|
||||||
editorState: ko.Observable<DocumentExplorerState>;
|
|
||||||
onValidDocumentEdit(content: any): Q.Promise<any>;
|
|
||||||
onInvalidDocumentEdit(content: any): Q.Promise<any>;
|
|
||||||
|
|
||||||
onNewDocumentClick(): Q.Promise<any>;
|
|
||||||
onSaveNewDocumentClick(): Q.Promise<any>;
|
|
||||||
onRevertNewDocumentClick(): Q.Promise<any>;
|
|
||||||
onSaveExisitingDocumentClick(): Q.Promise<any>;
|
|
||||||
onRevertExisitingDocumentClick(): Q.Promise<any>;
|
|
||||||
onDeleteExisitingDocumentClick(): Q.Promise<any>;
|
|
||||||
|
|
||||||
/* Errors */
|
|
||||||
displayedError: ko.Observable<string>;
|
|
||||||
|
|
||||||
initDocumentEditor(documentId: DocumentId, content: any): Q.Promise<any>;
|
|
||||||
loadNextPage(): Q.Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConflictsTab extends Tab {
|
|
||||||
/* Conflicts Grid */
|
|
||||||
selectedConflictId: ko.Observable<ConflictId>;
|
|
||||||
selectedConflictContent: Editable<any>;
|
|
||||||
selectedConflictCurrent: Editable<any>;
|
|
||||||
onConflictIdClick(conflictId: ConflictId): Q.Promise<any>;
|
|
||||||
dataContentsGridScrollHeight: ko.Observable<string>;
|
|
||||||
accessibleDocumentList: AccessibleVerticalList;
|
|
||||||
documentContentsGridId: string;
|
|
||||||
|
|
||||||
partitionKey: DataModels.PartitionKey;
|
|
||||||
partitionKeyPropertyHeader: string;
|
|
||||||
partitionKeyProperty: string;
|
|
||||||
conflictIds: ko.ObservableArray<ConflictId>;
|
|
||||||
|
|
||||||
/* Document Editor */
|
|
||||||
isEditorDirty: ko.Computed<boolean>;
|
|
||||||
editorState: ko.Observable<DocumentExplorerState>;
|
|
||||||
onValidDocumentEdit(content: any): Q.Promise<any>;
|
|
||||||
onInvalidDocumentEdit(content: any): Q.Promise<any>;
|
|
||||||
loadingConflictData: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
onAcceptChangesClick(): Q.Promise<any>;
|
|
||||||
onDiscardClick(): Q.Promise<any>;
|
|
||||||
|
|
||||||
initDocumentEditorForCreate(documentId: ConflictId, documentToInsert: any): Q.Promise<any>;
|
|
||||||
initDocumentEditorForReplace(documentId: ConflictId, conflictContent: any, currentContent: any): Q.Promise<any>;
|
|
||||||
initDocumentEditorForDelete(documentId: ConflictId, documentToDelete: any): Q.Promise<any>;
|
|
||||||
initDocumentEditorForNoOp(conflictId: ConflictId): Q.Promise<any>;
|
|
||||||
loadNextPage(): Q.Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SettingsTab extends Tab {
|
|
||||||
/*state*/
|
|
||||||
throughput: ko.Observable<number>;
|
|
||||||
timeToLive: ko.Observable<string>;
|
|
||||||
timeToLiveSeconds: ko.Observable<number>;
|
|
||||||
geospatialVisible: ko.Computed<boolean>;
|
|
||||||
geospatialConfigType: ko.Observable<string>;
|
|
||||||
indexingPolicyContent: ko.Observable<DataModels.IndexingPolicy>;
|
|
||||||
rupm: ko.Observable<string>;
|
|
||||||
requestUnitsUsageCost: ko.Computed<string>;
|
|
||||||
canThroughputExceedMaximumValue: ko.Computed<boolean>;
|
|
||||||
shouldDisplayPortalUsePrompt: ko.Computed<boolean>;
|
|
||||||
warningMessage: ko.Computed<string>;
|
|
||||||
ttlOffFocused: ko.Observable<boolean>;
|
|
||||||
ttlOnDefaultFocused: ko.Observable<boolean>;
|
|
||||||
ttlOnFocused: ko.Observable<boolean>;
|
|
||||||
indexingPolicyElementFocused: ko.Observable<boolean>;
|
|
||||||
notificationStatusInfo: ko.Observable<string>;
|
|
||||||
shouldShowNotificationStatusPrompt: ko.Computed<boolean>;
|
|
||||||
shouldShowStatusBar: ko.Computed<boolean>;
|
|
||||||
pendingNotification: ko.Observable<DataModels.Notification>;
|
|
||||||
|
|
||||||
conflictResolutionPolicyMode: ko.Observable<string>;
|
|
||||||
conflictResolutionPolicyPath: ko.Observable<string>;
|
|
||||||
conflictResolutionPolicyProcedure: ko.Observable<string>;
|
|
||||||
|
|
||||||
rupmVisible: ko.Computed<boolean>;
|
|
||||||
costsVisible: ko.Computed<boolean>;
|
|
||||||
minRUAnotationVisible: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
/* Command Bar */
|
|
||||||
saveSettingsButton: Button;
|
|
||||||
discardSettingsChangesButton: Button;
|
|
||||||
onSaveClick(): Q.Promise<any>;
|
|
||||||
onRevertClick(): Q.Promise<any>;
|
|
||||||
|
|
||||||
/* Indexing Policy Editor */
|
|
||||||
isIndexingPolicyEditorInitializing: ko.Observable<boolean>;
|
|
||||||
indexingPolicyEditor: ko.Observable<monaco.editor.IStandaloneCodeEditor>;
|
|
||||||
onValidIndexingPolicyEdit(content: any): Q.Promise<any>;
|
|
||||||
onInvalidIndexingPolicyEdit(content: any): Q.Promise<any>;
|
|
||||||
|
|
||||||
onSaveClick(): Q.Promise<any>;
|
|
||||||
onRevertClick(): Q.Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DatabaseSettingsTab extends Tab {
|
|
||||||
/*state*/
|
|
||||||
throughput: ko.Observable<number>;
|
|
||||||
requestUnitsUsageCost: ko.PureComputed<string>;
|
|
||||||
canThroughputExceedMaximumValue: ko.Computed<boolean>;
|
|
||||||
warningMessage: ko.Computed<string>;
|
|
||||||
notificationStatusInfo: ko.Observable<string>;
|
|
||||||
shouldShowNotificationStatusPrompt: ko.Computed<boolean>;
|
|
||||||
shouldShowStatusBar: ko.Computed<boolean>;
|
|
||||||
pendingNotification: ko.Observable<DataModels.Notification>;
|
|
||||||
|
|
||||||
costsVisible: ko.Computed<boolean>;
|
|
||||||
minRUAnotationVisible: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
/* Command Bar */
|
|
||||||
saveSettingsButton: Button;
|
|
||||||
discardSettingsChangesButton: Button;
|
|
||||||
onSaveClick(): Q.Promise<any>;
|
|
||||||
onRevertClick(): Q.Promise<any>;
|
|
||||||
|
|
||||||
/* Errors */
|
|
||||||
displayedError: ko.Observable<string>;
|
|
||||||
|
|
||||||
onSaveClick(): Q.Promise<any>;
|
|
||||||
onRevertClick(): Q.Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WaitsForTemplate {
|
export interface WaitsForTemplate {
|
||||||
isTemplateReady: ko.Observable<boolean>;
|
isTemplateReady: ko.Observable<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryTab extends Tab {
|
|
||||||
queryEditorId: string;
|
|
||||||
isQueryMetricsEnabled: ko.Computed<boolean>;
|
|
||||||
activityId: ko.Observable<string>;
|
|
||||||
|
|
||||||
/* Command Bar */
|
|
||||||
executeQueryButton: Button;
|
|
||||||
fetchNextPageButton: Button;
|
|
||||||
saveQueryButton: Button;
|
|
||||||
onExecuteQueryClick(): Q.Promise<any>;
|
|
||||||
onFetchNextPageClick(): Q.Promise<any>;
|
|
||||||
|
|
||||||
/*Query Editor*/
|
|
||||||
initialEditorContent: ko.Observable<string>;
|
|
||||||
sqlQueryEditorContent: ko.Observable<string>;
|
|
||||||
sqlStatementToExecute: ko.Observable<string>;
|
|
||||||
|
|
||||||
/* Results */
|
|
||||||
allResultsMetadata: ko.ObservableArray<QueryResultsMetadata>;
|
|
||||||
|
|
||||||
/* Errors */
|
|
||||||
errors: ko.ObservableArray<QueryError>;
|
|
||||||
|
|
||||||
/* Status */
|
|
||||||
statusMessge: ko.Observable<string>;
|
|
||||||
statusIcon: ko.Observable<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ScriptTab extends Tab {
|
|
||||||
id: Editable<string>;
|
|
||||||
editorId: string;
|
|
||||||
|
|
||||||
saveButton: Button;
|
|
||||||
updateButton: Button;
|
|
||||||
discardButton: Button;
|
|
||||||
deleteButton: Button;
|
|
||||||
|
|
||||||
editorState: ko.Observable<ScriptEditorState>;
|
|
||||||
editorContent: ko.Observable<string>;
|
|
||||||
editor: ko.Observable<monaco.editor.IStandaloneCodeEditor>;
|
|
||||||
|
|
||||||
errors: ko.ObservableArray<QueryError>;
|
|
||||||
statusMessge: ko.Observable<string>;
|
|
||||||
statusIcon: ko.Observable<string>;
|
|
||||||
|
|
||||||
formFields: ko.ObservableArray<Editable<any>>;
|
|
||||||
formIsValid: ko.Computed<boolean>;
|
|
||||||
formIsDirty: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
isNew: ko.Observable<boolean>;
|
|
||||||
resource: ko.Observable<DataModels.Resource>;
|
|
||||||
|
|
||||||
setBaselines(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StoredProcedureTab extends ScriptTab {
|
|
||||||
onExecuteSprocsResult(result: any, logsData: any): void;
|
|
||||||
onExecuteSprocsError(error: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserDefinedFunctionTab extends ScriptTab {}
|
|
||||||
|
|
||||||
export interface TriggerTab extends ScriptTab {
|
|
||||||
triggerType: Editable<string>;
|
|
||||||
triggerOperation: Editable<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GraphTab extends Tab {}
|
|
||||||
export interface EditorPosition {
|
export interface EditorPosition {
|
||||||
line: number;
|
line: number;
|
||||||
column: number;
|
column: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MongoShellTab extends Tab {}
|
|
||||||
|
|
||||||
export enum DocumentExplorerState {
|
export enum DocumentExplorerState {
|
||||||
noDocumentSelected,
|
noDocumentSelected,
|
||||||
newDocumentValid,
|
newDocumentValid,
|
||||||
@@ -1282,40 +429,8 @@ export interface AuthorizationTokenHeaderMetadata {
|
|||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TelemetryActions {
|
|
||||||
sendEvent(name: string, telemetryProperties?: { [propertyName: string]: string }): Q.Promise<any>;
|
|
||||||
sendError(errorInfo: DataModels.ITelemetryError): Q.Promise<any>;
|
|
||||||
sendMetric(
|
|
||||||
name: string,
|
|
||||||
metricNumber: number,
|
|
||||||
telemetryProperties?: { [propertyName: string]: string }
|
|
||||||
): Q.Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConfigurationOverrides {
|
|
||||||
EnableBsonSchema: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CosmosDbApi {
|
|
||||||
isSystemDatabasePredicate: (database: Database) => boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DropdownOption<T> {
|
export interface DropdownOption<T> {
|
||||||
text: string;
|
text: string;
|
||||||
value: T;
|
value: T;
|
||||||
disable?: boolean;
|
disable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INotebookContainerClient {
|
|
||||||
resetWorkspace: () => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface INotebookContentClient {
|
|
||||||
updateItemChildren: (item: NotebookContentItem) => Promise<void>;
|
|
||||||
createNewNotebookFile: (parent: NotebookContentItem) => Promise<NotebookContentItem>;
|
|
||||||
deleteContentItem: (item: NotebookContentItem) => Promise<void>;
|
|
||||||
uploadFileAsync: (name: string, content: string, parent: NotebookContentItem) => Promise<NotebookContentItem>;
|
|
||||||
renameNotebook: (item: NotebookContentItem, targetName: string) => Promise<NotebookContentItem>;
|
|
||||||
createDirectory: (parent: NotebookContentItem, newDirectoryName: string) => Promise<NotebookContentItem>;
|
|
||||||
readFileContent: (filePath: string) => Promise<string>;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
PortalTheme
|
PortalTheme
|
||||||
} from "./HeatmapDatatypes";
|
} from "./HeatmapDatatypes";
|
||||||
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
||||||
import { MessageHandler } from "../../Common/MessageHandler";
|
import { sendCachedDataMessage, sendMessage } from "../../Common/MessageHandler";
|
||||||
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
||||||
import { StyleConstants } from "../../Common/Constants";
|
import { StyleConstants } from "../../Common/Constants";
|
||||||
import "./Heatmap.less";
|
import "./Heatmap.less";
|
||||||
@@ -209,7 +209,7 @@ export class Heatmap {
|
|||||||
for (let i = 0; i < this._chartData.dataPoints.length; i++) {
|
for (let i = 0; i < this._chartData.dataPoints.length; i++) {
|
||||||
output.push(this._chartData.dataPoints[i][xAxisIndex]);
|
output.push(this._chartData.dataPoints[i][xAxisIndex]);
|
||||||
}
|
}
|
||||||
MessageHandler.sendCachedDataMessage(MessageTypes.LogInfo, output);
|
sendCachedDataMessage(MessageTypes.LogInfo, output);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,4 +266,4 @@ export function handleMessage(event: MessageEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("message", handleMessage, false);
|
window.addEventListener("message", handleMessage, false);
|
||||||
MessageHandler.sendMessage("ready");
|
sendMessage("ready");
|
||||||
|
|||||||
@@ -123,12 +123,4 @@ describe("Component Registerer", () => {
|
|||||||
it("should register throughput-input component", () => {
|
it("should register throughput-input component", () => {
|
||||||
expect(ko.components.isRegistered("throughput-input")).toBe(true);
|
expect(ko.components.isRegistered("throughput-input")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register library-manage-pane component", () => {
|
|
||||||
expect(ko.components.isRegistered("library-manage-pane")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register cluster-library-pane component", () => {
|
|
||||||
expect(ko.components.isRegistered("cluster-library-pane")).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponen
|
|||||||
import { TabsManagerKOComponent } from "./Tabs/TabsManager";
|
import { TabsManagerKOComponent } from "./Tabs/TabsManager";
|
||||||
import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputInputComponent";
|
import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputInputComponent";
|
||||||
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
||||||
import { ToolbarComponent } from "./Controls/Toolbar/Toolbar";
|
|
||||||
|
|
||||||
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);
|
||||||
ko.components.register("error-display", new ErrorDisplayComponent());
|
ko.components.register("error-display", new ErrorDisplayComponent());
|
||||||
@@ -78,6 +76,4 @@ ko.components.register("browse-queries-pane", new PaneComponents.BrowseQueriesPa
|
|||||||
ko.components.register("upload-file-pane", new PaneComponents.UploadFilePaneComponent());
|
ko.components.register("upload-file-pane", new PaneComponents.UploadFilePaneComponent());
|
||||||
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
||||||
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
|
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
|
||||||
ko.components.register("library-manage-pane", new PaneComponents.LibraryManagePaneComponent());
|
|
||||||
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());
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import AddTriggerIcon from "../../images/AddTrigger.svg";
|
|||||||
import DeleteTriggerIcon from "../../images/DeleteTrigger.svg";
|
import DeleteTriggerIcon from "../../images/DeleteTrigger.svg";
|
||||||
import DeleteUDFIcon from "../../images/DeleteUDF.svg";
|
import DeleteUDFIcon from "../../images/DeleteUDF.svg";
|
||||||
import DeleteSprocIcon from "../../images/DeleteSproc.svg";
|
import DeleteSprocIcon from "../../images/DeleteSproc.svg";
|
||||||
|
import Explorer from "./Explorer";
|
||||||
|
import UserDefinedFunction from "./Tree/UserDefinedFunction";
|
||||||
|
import StoredProcedure from "./Tree/StoredProcedure";
|
||||||
|
import Trigger from "./Tree/Trigger";
|
||||||
|
|
||||||
export interface CollectionContextMenuButtonParams {
|
export interface CollectionContextMenuButtonParams {
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
@@ -26,7 +30,7 @@ export interface DatabaseContextMenuButtonParams {
|
|||||||
*/
|
*/
|
||||||
export class ResourceTreeContextMenuButtonFactory {
|
export class ResourceTreeContextMenuButtonFactory {
|
||||||
public static createDatabaseContextMenu(
|
public static createDatabaseContextMenu(
|
||||||
container: ViewModels.Explorer,
|
container: Explorer,
|
||||||
selectedDatabase: ViewModels.Database
|
selectedDatabase: ViewModels.Database
|
||||||
): TreeNodeMenuItem[] {
|
): TreeNodeMenuItem[] {
|
||||||
const newCollectionMenuItem: TreeNodeMenuItem = {
|
const newCollectionMenuItem: TreeNodeMenuItem = {
|
||||||
@@ -44,7 +48,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static createCollectionContextMenuButton(
|
public static createCollectionContextMenuButton(
|
||||||
container: ViewModels.Explorer,
|
container: Explorer,
|
||||||
selectedCollection: ViewModels.Collection
|
selectedCollection: ViewModels.Collection
|
||||||
): TreeNodeMenuItem[] {
|
): TreeNodeMenuItem[] {
|
||||||
const items: TreeNodeMenuItem[] = [];
|
const items: TreeNodeMenuItem[] = [];
|
||||||
@@ -115,8 +119,8 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static createStoreProcedureContextMenuItems(
|
public static createStoreProcedureContextMenuItems(
|
||||||
container: ViewModels.Explorer,
|
container: Explorer,
|
||||||
storedProcedure: ViewModels.StoredProcedure
|
storedProcedure: StoredProcedure
|
||||||
): TreeNodeMenuItem[] {
|
): TreeNodeMenuItem[] {
|
||||||
if (container.isPreferredApiCassandra()) {
|
if (container.isPreferredApiCassandra()) {
|
||||||
return [];
|
return [];
|
||||||
@@ -131,10 +135,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createTriggerContextMenuItems(
|
public static createTriggerContextMenuItems(container: Explorer, trigger: Trigger): TreeNodeMenuItem[] {
|
||||||
container: ViewModels.Explorer,
|
|
||||||
trigger: ViewModels.Trigger
|
|
||||||
): TreeNodeMenuItem[] {
|
|
||||||
if (container.isPreferredApiCassandra()) {
|
if (container.isPreferredApiCassandra()) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -149,8 +150,8 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static createUserDefinedFunctionContextMenuItems(
|
public static createUserDefinedFunctionContextMenuItems(
|
||||||
container: ViewModels.Explorer,
|
container: Explorer,
|
||||||
userDefinedFunction: ViewModels.UserDefinedFunction
|
userDefinedFunction: UserDefinedFunction
|
||||||
): TreeNodeMenuItem[] {
|
): TreeNodeMenuItem[] {
|
||||||
if (container.isPreferredApiCassandra()) {
|
if (container.isPreferredApiCassandra()) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -23,8 +23,5 @@ interface ErrorDisplayParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ErrorDisplayViewModel {
|
class ErrorDisplayViewModel {
|
||||||
private params: ErrorDisplayParams;
|
public constructor(public params: ErrorDisplayParams) {}
|
||||||
public constructor(params: ErrorDisplayParams) {
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { DefaultButton, IButtonProps, ITextFieldProps, TextField } from "office-ui-fabric-react";
|
import { DefaultButton, IButtonProps, ITextFieldProps, TextField } from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
import { RepoListItem } from "./GitHubReposComponent";
|
import { RepoListItem } from "./GitHubReposComponent";
|
||||||
@@ -9,9 +8,10 @@ import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
|||||||
import { IGitHubRepo } from "../../../GitHub/GitHubClient";
|
import { IGitHubRepo } from "../../../GitHub/GitHubClient";
|
||||||
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import UrlUtility from "../../../Common/UrlUtility";
|
import UrlUtility from "../../../Common/UrlUtility";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
|
||||||
export interface AddRepoComponentProps {
|
export interface AddRepoComponentProps {
|
||||||
container: ViewModels.Explorer;
|
container: Explorer;
|
||||||
getRepo: (owner: string, repo: string) => Promise<IGitHubRepo>;
|
getRepo: (owner: string, repo: string) => Promise<IGitHubRepo>;
|
||||||
pinRepo: (item: RepoListItem) => void;
|
pinRepo: (item: RepoListItem) => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { DetailsList, IColumn, SelectionMode } from "office-ui-fabric-react/lib/DetailsList";
|
|
||||||
import { Library } from "../../../Contracts/DataModels";
|
|
||||||
|
|
||||||
export interface ClusterLibraryItem extends Library {
|
|
||||||
installed: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ClusterLibraryGridProps {
|
|
||||||
libraryItems: ClusterLibraryItem[];
|
|
||||||
onInstalledChanged: (libraryName: string, installed: boolean) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ClusterLibraryGrid(props: ClusterLibraryGridProps): JSX.Element {
|
|
||||||
const onInstalledChanged = (e: React.FormEvent<HTMLInputElement>) => {
|
|
||||||
const target = e.target;
|
|
||||||
const libraryName = (target as any).dataset.name;
|
|
||||||
const checked = (target as any).checked;
|
|
||||||
return props.onInstalledChanged(libraryName, checked);
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns: IColumn[] = [
|
|
||||||
{
|
|
||||||
key: "name",
|
|
||||||
name: "Name",
|
|
||||||
fieldName: "name",
|
|
||||||
minWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "installed",
|
|
||||||
name: "Installed",
|
|
||||||
minWidth: 100,
|
|
||||||
onRender: (item: ClusterLibraryItem) => {
|
|
||||||
return <input type="checkbox" checked={item.installed} onChange={onInstalledChanged} data-name={item.name} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return <DetailsList columns={columns} items={props.libraryItems} selectionMode={SelectionMode.none} />;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|
||||||
import { ClusterLibraryGrid, ClusterLibraryGridProps } from "./ClusterLibraryGrid";
|
|
||||||
|
|
||||||
export class ClusterLibraryGridAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<ClusterLibraryGridProps>;
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return <ClusterLibraryGrid {...this.parameters()} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import DeleteIcon from "../../../../images/delete.svg";
|
|
||||||
import { Button } from "office-ui-fabric-react/lib/Button";
|
|
||||||
import { DetailsList, IColumn, SelectionMode } from "office-ui-fabric-react/lib/DetailsList";
|
|
||||||
import { Library } from "../../../Contracts/DataModels";
|
|
||||||
import { Label } from "office-ui-fabric-react/lib/Label";
|
|
||||||
import { SparkLibrary } from "../../../Common/Constants";
|
|
||||||
import { TextField } from "office-ui-fabric-react/lib/TextField";
|
|
||||||
|
|
||||||
export interface LibraryManageComponentProps {
|
|
||||||
addProps: {
|
|
||||||
nameProps: LibraryAddNameTextFieldProps;
|
|
||||||
urlProps: LibraryAddUrlTextFieldProps;
|
|
||||||
buttonProps: LibraryAddButtonProps;
|
|
||||||
};
|
|
||||||
gridProps: LibraryManageGridProps;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LibraryManageComponent(props: LibraryManageComponentProps): JSX.Element {
|
|
||||||
const {
|
|
||||||
addProps: { nameProps, urlProps, buttonProps },
|
|
||||||
gridProps
|
|
||||||
} = props;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="library-add-container">
|
|
||||||
<LibraryAddNameTextField {...nameProps} />
|
|
||||||
<LibraryAddUrlTextField {...urlProps} />
|
|
||||||
<LibraryAddButton {...buttonProps} />
|
|
||||||
</div>
|
|
||||||
<div className="library-grid-container">
|
|
||||||
<Label>All Libraries</Label>
|
|
||||||
<LibraryManageGrid {...gridProps} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LibraryManageGridProps {
|
|
||||||
items: Library[];
|
|
||||||
onLibraryDeleteClick: (libraryName: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LibraryManageGrid(props: LibraryManageGridProps): JSX.Element {
|
|
||||||
const columns: IColumn[] = [
|
|
||||||
{
|
|
||||||
key: "name",
|
|
||||||
name: "Name",
|
|
||||||
fieldName: "name",
|
|
||||||
minWidth: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "delete",
|
|
||||||
name: "Delete",
|
|
||||||
minWidth: 60,
|
|
||||||
onRender: (item: Library) => {
|
|
||||||
const onDelete = () => {
|
|
||||||
props.onLibraryDeleteClick(item.name);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<span className="library-delete">
|
|
||||||
<img src={DeleteIcon} alt="Delete" onClick={onDelete} />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
return <DetailsList columns={columns} items={props.items} selectionMode={SelectionMode.none} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LibraryAddButtonProps {
|
|
||||||
disabled: boolean;
|
|
||||||
onLibraryAddClick: (event: React.FormEvent<any>) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LibraryAddButton(props: LibraryAddButtonProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Button text="Add" className="library-add-button" onClick={props.onLibraryAddClick} disabled={props.disabled} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LibraryAddUrlTextFieldProps {
|
|
||||||
libraryAddress: string;
|
|
||||||
onLibraryAddressChange: (libraryAddress: string) => void;
|
|
||||||
onLibraryAddressValidated: (errorMessage: string, value: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LibraryAddUrlTextField(props: LibraryAddUrlTextFieldProps): JSX.Element {
|
|
||||||
const handleTextChange = (e: React.FormEvent<any>, libraryAddress: string) => {
|
|
||||||
props.onLibraryAddressChange(libraryAddress);
|
|
||||||
};
|
|
||||||
const validateText = (text: string): string => {
|
|
||||||
if (!text) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
const libraryUrlRegex = /^(https:\/\/.+\/)(.+)\.(jar)$/gi;
|
|
||||||
const isValidUrl = libraryUrlRegex.test(text);
|
|
||||||
if (isValidUrl) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return "Need to be a valid https uri";
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<TextField
|
|
||||||
value={props.libraryAddress}
|
|
||||||
label="Url"
|
|
||||||
type="url"
|
|
||||||
className="library-add-textfield"
|
|
||||||
onChange={handleTextChange}
|
|
||||||
onGetErrorMessage={validateText}
|
|
||||||
onNotifyValidationResult={props.onLibraryAddressValidated}
|
|
||||||
placeholder="https://myrepo/myjar.jar"
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LibraryAddNameTextFieldProps {
|
|
||||||
libraryName: string;
|
|
||||||
onLibraryNameChange: (libraryName: string) => void;
|
|
||||||
onLibraryNameValidated: (errorMessage: string, value: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LibraryAddNameTextField(props: LibraryAddNameTextFieldProps): JSX.Element {
|
|
||||||
const handleTextChange = (e: React.FormEvent<any>, libraryName: string) => {
|
|
||||||
props.onLibraryNameChange(libraryName);
|
|
||||||
};
|
|
||||||
const validateText = (text: string): string => {
|
|
||||||
if (!text) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
const length = text.length;
|
|
||||||
if (length < SparkLibrary.nameMinLength || length > SparkLibrary.nameMaxLength) {
|
|
||||||
return "Library name length need to be between 3 and 63.";
|
|
||||||
}
|
|
||||||
const nameRegex = /^[a-z0-9][-a-z0-9]*[a-z0-9]$/gi;
|
|
||||||
const isValidUrl = nameRegex.test(text);
|
|
||||||
if (isValidUrl) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return "Need to be a valid name. Letters, numbers and - are allowed";
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<TextField
|
|
||||||
value={props.libraryName}
|
|
||||||
label="Name"
|
|
||||||
type="text"
|
|
||||||
className="library-add-textfield"
|
|
||||||
onChange={handleTextChange}
|
|
||||||
onGetErrorMessage={validateText}
|
|
||||||
onNotifyValidationResult={props.onLibraryNameValidated}
|
|
||||||
placeholder="myjar"
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|
||||||
import { LibraryManageComponent, LibraryManageComponentProps } from "./LibraryManage";
|
|
||||||
|
|
||||||
export class LibraryManageComponentAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<LibraryManageComponentProps>;
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return <LibraryManageComponent {...this.parameters()} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as Logger from "../../../Common/Logger";
|
import * as Logger from "../../../Common/Logger";
|
||||||
import { NotificationConsoleUtils } from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { StringUtils } from "../../../Utils/StringUtils";
|
import { StringUtils } from "../../../Utils/StringUtils";
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ export interface GalleryCardComponentProps {
|
|||||||
export class GalleryCardComponent extends React.Component<GalleryCardComponentProps> {
|
export class GalleryCardComponent extends React.Component<GalleryCardComponentProps> {
|
||||||
public static readonly CARD_WIDTH = 256;
|
public static readonly CARD_WIDTH = 256;
|
||||||
private static readonly cardImageHeight = 144;
|
private static readonly cardImageHeight = 144;
|
||||||
|
public static readonly cardHeightToWidthRatio =
|
||||||
|
GalleryCardComponent.cardImageHeight / GalleryCardComponent.CARD_WIDTH;
|
||||||
private static readonly cardDescriptionMaxChars = 88;
|
private static readonly cardDescriptionMaxChars = 88;
|
||||||
private static readonly cardItemGapBig = 10;
|
private static readonly cardItemGapBig = 10;
|
||||||
private static readonly cardItemGapSmall = 8;
|
private static readonly cardItemGapSmall = 8;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { JunoClient, IGalleryItem } from "../../../Juno/JunoClient";
|
import { JunoClient, IGalleryItem } from "../../../Juno/JunoClient";
|
||||||
import { GalleryTab, SortBy, GalleryViewerComponentProps, GalleryViewerComponent } from "./GalleryViewerComponent";
|
import { GalleryTab, SortBy, GalleryViewerComponentProps, GalleryViewerComponent } from "./GalleryViewerComponent";
|
||||||
import { NotebookViewerComponentProps, NotebookViewerComponent } from "../NotebookViewer/NotebookViewerComponent";
|
import { NotebookViewerComponentProps, NotebookViewerComponent } from "../NotebookViewer/NotebookViewerComponent";
|
||||||
import * as GalleryUtils from "../../../Utils/GalleryUtils";
|
import * as GalleryUtils from "../../../Utils/GalleryUtils";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
|
||||||
export interface GalleryAndNotebookViewerComponentProps {
|
export interface GalleryAndNotebookViewerComponentProps {
|
||||||
container?: ViewModels.Explorer;
|
container?: Explorer;
|
||||||
junoClient: JunoClient;
|
junoClient: JunoClient;
|
||||||
notebookUrl?: string;
|
notebookUrl?: string;
|
||||||
galleryItem?: IGalleryItem;
|
galleryItem?: IGalleryItem;
|
||||||
|
|||||||
@@ -15,18 +15,18 @@ import {
|
|||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as Logger from "../../../Common/Logger";
|
import * as Logger from "../../../Common/Logger";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { IGalleryItem, JunoClient } from "../../../Juno/JunoClient";
|
import { IGalleryItem, JunoClient } from "../../../Juno/JunoClient";
|
||||||
import * as GalleryUtils from "../../../Utils/GalleryUtils";
|
import * as GalleryUtils from "../../../Utils/GalleryUtils";
|
||||||
import { NotificationConsoleUtils } from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { DialogComponent, DialogProps } from "../DialogReactComponent/DialogComponent";
|
import { DialogComponent, DialogProps } from "../DialogReactComponent/DialogComponent";
|
||||||
import { GalleryCardComponent, GalleryCardComponentProps } from "./Cards/GalleryCardComponent";
|
import { GalleryCardComponent, GalleryCardComponentProps } from "./Cards/GalleryCardComponent";
|
||||||
import "./GalleryViewerComponent.less";
|
import "./GalleryViewerComponent.less";
|
||||||
import { HttpStatusCodes } from "../../../Common/Constants";
|
import { HttpStatusCodes } from "../../../Common/Constants";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
|
||||||
export interface GalleryViewerComponentProps {
|
export interface GalleryViewerComponentProps {
|
||||||
container?: ViewModels.Explorer;
|
container?: Explorer;
|
||||||
junoClient: JunoClient;
|
junoClient: JunoClient;
|
||||||
selectedTab: GalleryTab;
|
selectedTab: GalleryTab;
|
||||||
sortBy: SortBy;
|
sortBy: SortBy;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import * as Logger from "../../../Common/Logger";
|
|||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { IGalleryItem, JunoClient } from "../../../Juno/JunoClient";
|
import { IGalleryItem, JunoClient } from "../../../Juno/JunoClient";
|
||||||
import * as GalleryUtils from "../../../Utils/GalleryUtils";
|
import * as GalleryUtils from "../../../Utils/GalleryUtils";
|
||||||
import { NotificationConsoleUtils } from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { NotebookClientV2 } from "../../Notebook/NotebookClientV2";
|
import { NotebookClientV2 } from "../../Notebook/NotebookClientV2";
|
||||||
import { NotebookComponentBootstrapper } from "../../Notebook/NotebookComponent/NotebookComponentBootstrapper";
|
import { NotebookComponentBootstrapper } from "../../Notebook/NotebookComponent/NotebookComponentBootstrapper";
|
||||||
@@ -18,9 +18,11 @@ import NotebookReadOnlyRenderer from "../../Notebook/NotebookRenderer/NotebookRe
|
|||||||
import { DialogComponent, DialogProps } from "../DialogReactComponent/DialogComponent";
|
import { DialogComponent, DialogProps } from "../DialogReactComponent/DialogComponent";
|
||||||
import { NotebookMetadataComponent } from "./NotebookMetadataComponent";
|
import { NotebookMetadataComponent } from "./NotebookMetadataComponent";
|
||||||
import "./NotebookViewerComponent.less";
|
import "./NotebookViewerComponent.less";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
import { SessionStorageUtility } from "../../../Shared/StorageUtility";
|
||||||
|
|
||||||
export interface NotebookViewerComponentProps {
|
export interface NotebookViewerComponentProps {
|
||||||
container?: ViewModels.Explorer;
|
container?: Explorer;
|
||||||
junoClient?: JunoClient;
|
junoClient?: JunoClient;
|
||||||
notebookUrl: string;
|
notebookUrl: string;
|
||||||
galleryItem?: IGalleryItem;
|
galleryItem?: IGalleryItem;
|
||||||
@@ -87,13 +89,13 @@ export class NotebookViewerComponent extends React.Component<
|
|||||||
this.notebookComponentBootstrapper.setContent("json", notebook);
|
this.notebookComponentBootstrapper.setContent("json", notebook);
|
||||||
this.setState({ content: notebook, showProgressBar: false });
|
this.setState({ content: notebook, showProgressBar: false });
|
||||||
|
|
||||||
if (this.props.galleryItem) {
|
if (this.props.galleryItem && !SessionStorageUtility.getEntry(this.props.galleryItem.id)) {
|
||||||
const response = await this.props.junoClient.increaseNotebookViews(this.props.galleryItem.id);
|
const response = await this.props.junoClient.increaseNotebookViews(this.props.galleryItem.id);
|
||||||
if (!response.data) {
|
if (!response.data) {
|
||||||
throw new Error(`Received HTTP ${response.status} while increasing notebook views`);
|
throw new Error(`Received HTTP ${response.status} while increasing notebook views`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ galleryItem: response.data });
|
this.setState({ galleryItem: response.data });
|
||||||
|
SessionStorageUtility.setEntry(this.props.galleryItem?.id, "true");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.setState({ showProgressBar: false });
|
this.setState({ showProgressBar: false });
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ import { TextField, ITextFieldProps, ITextField } from "office-ui-fabric-react/l
|
|||||||
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
|
||||||
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
|
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
|
||||||
|
import { QueriesClient } from "../../../Common/QueriesClient";
|
||||||
|
|
||||||
export interface QueriesGridComponentProps {
|
export interface QueriesGridComponentProps {
|
||||||
queriesClient: ViewModels.QueriesClient;
|
queriesClient: QueriesClient;
|
||||||
onQuerySelect: (query: DataModels.Query) => void;
|
onQuerySelect: (query: DataModels.Query) => void;
|
||||||
containerVisible: boolean;
|
containerVisible: boolean;
|
||||||
saveQueryEnabled: boolean;
|
saveQueryEnabled: boolean;
|
||||||
@@ -217,7 +218,7 @@ export class QueriesGridComponent extends React.Component<QueriesGridComponentPr
|
|||||||
menuItem: any
|
menuItem: any
|
||||||
) => {
|
) => {
|
||||||
if (window.confirm("Are you sure you want to delete this query?")) {
|
if (window.confirm("Are you sure you want to delete this query?")) {
|
||||||
const container: ViewModels.Explorer = window.dataExplorer;
|
const container = window.dataExplorer;
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.DeleteSavedQuery, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.DeleteSavedQuery, {
|
||||||
databaseAccountName: container && container.databaseAccount().name,
|
databaseAccountName: container && container.databaseAccount().name,
|
||||||
defaultExperience: container && container.defaultExperience(),
|
defaultExperience: container && container.defaultExperience(),
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import * as React from "react";
|
|||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { QueriesGridComponent, QueriesGridComponentProps } from "./QueriesGridComponent";
|
import { QueriesGridComponent, QueriesGridComponentProps } from "./QueriesGridComponent";
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
|
||||||
export class QueriesGridComponentAdapter implements ReactAdapter {
|
export class QueriesGridComponentAdapter implements ReactAdapter {
|
||||||
public parameters: ko.Observable<number>;
|
public parameters: ko.Observable<number>;
|
||||||
|
|
||||||
constructor(private container: ViewModels.Explorer) {
|
constructor(private container: Explorer) {
|
||||||
this.parameters = ko.observable<number>(Date.now());
|
this.parameters = ko.observable<number>(Date.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* Utilities for validation */
|
/* Utilities for validation */
|
||||||
|
|
||||||
export const onValidateValueChange = (newValue: string, minValue?: number, maxValue?: number): number => {
|
export const onValidateValueChange = (newValue: string, minValue?: number, maxValue?: number): number | undefined => {
|
||||||
let numericValue = parseInt(newValue);
|
let numericValue = parseInt(newValue);
|
||||||
if (!isNaN(numericValue) && isFinite(numericValue)) {
|
if (!isNaN(numericValue) && isFinite(numericValue)) {
|
||||||
if (minValue !== undefined && numericValue < minValue) {
|
if (minValue !== undefined && numericValue < minValue) {
|
||||||
@@ -16,7 +16,7 @@ export const onValidateValueChange = (newValue: string, minValue?: number, maxVa
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onIncrementValue = (newValue: string, step: number, max?: number): number => {
|
export const onIncrementValue = (newValue: string, step: number, max?: number): number | undefined => {
|
||||||
const numericValue = parseInt(newValue);
|
const numericValue = parseInt(newValue);
|
||||||
if (!isNaN(numericValue) && isFinite(numericValue)) {
|
if (!isNaN(numericValue) && isFinite(numericValue)) {
|
||||||
const newValue = numericValue + step;
|
const newValue = numericValue + step;
|
||||||
@@ -25,7 +25,7 @@ export const onIncrementValue = (newValue: string, step: number, max?: number):
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onDecrementValue = (newValue: string, step: number, min?: number): number => {
|
export const onDecrementValue = (newValue: string, step: number, min?: number): number | undefined => {
|
||||||
const numericValue = parseInt(newValue);
|
const numericValue = parseInt(newValue);
|
||||||
if (!isNaN(numericValue) && isFinite(numericValue)) {
|
if (!isNaN(numericValue) && isFinite(numericValue)) {
|
||||||
const newValue = numericValue - step;
|
const newValue = numericValue - step;
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
@import "../../../../less/Common/Constants";
|
|
||||||
|
|
||||||
.labelWithRedAsterisk {
|
|
||||||
line-height: 18px;
|
|
||||||
font-size: @DefaultFontSize;
|
|
||||||
font-family: @DataExplorerFont;
|
|
||||||
color: @DefaultFontColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.labelWithRedAsterisk::before {
|
|
||||||
content: "* ";
|
|
||||||
color: @SelectionHigh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clusterSettingsDropdown {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { Dropdown, IDropdownOption, IDropdownProps } from "office-ui-fabric-react/lib/Dropdown";
|
|
||||||
import { Slider, ISliderProps } from "office-ui-fabric-react/lib/Slider";
|
|
||||||
import { Stack, IStackItemStyles, IStackStyles } from "office-ui-fabric-react/lib/Stack";
|
|
||||||
import { TextField, ITextFieldProps } from "office-ui-fabric-react/lib/TextField";
|
|
||||||
import { Spark } from "../../../Common/Constants";
|
|
||||||
import { SparkCluster } from "../../../Contracts/DataModels";
|
|
||||||
|
|
||||||
export interface ClusterSettingsComponentProps {
|
|
||||||
cluster: SparkCluster;
|
|
||||||
onClusterSettingsChanged: (cluster: SparkCluster) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ClusterSettingsComponent extends React.Component<ClusterSettingsComponentProps, {}> {
|
|
||||||
constructor(props: ClusterSettingsComponentProps) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{this.getMasterSizeDropdown()}
|
|
||||||
{this.getWorkerSizeDropdown()}
|
|
||||||
{this.getWorkerCountSliderInput()}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getMasterSizeDropdown(): JSX.Element {
|
|
||||||
const driverSize: string =
|
|
||||||
this.props.cluster && this.props.cluster.properties && this.props.cluster.properties.driverSize;
|
|
||||||
const masterSizeOptions: IDropdownOption[] = Spark.SKUs.keys().map(sku => ({
|
|
||||||
key: sku,
|
|
||||||
text: Spark.SKUs.get(sku)
|
|
||||||
}));
|
|
||||||
const masterSizeDropdownProps: IDropdownProps = {
|
|
||||||
label: "Master Size",
|
|
||||||
options: masterSizeOptions,
|
|
||||||
defaultSelectedKey: driverSize,
|
|
||||||
onChange: this._onDriverSizeChange,
|
|
||||||
styles: {
|
|
||||||
root: "clusterSettingsDropdown"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return <Dropdown {...masterSizeDropdownProps} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getWorkerSizeDropdown(): JSX.Element {
|
|
||||||
const workerSize: string =
|
|
||||||
this.props.cluster && this.props.cluster.properties && this.props.cluster.properties.workerSize;
|
|
||||||
const workerSizeOptions: IDropdownOption[] = Spark.SKUs.keys().map(sku => ({
|
|
||||||
key: sku,
|
|
||||||
text: Spark.SKUs.get(sku)
|
|
||||||
}));
|
|
||||||
const workerSizeDropdownProps: IDropdownProps = {
|
|
||||||
label: "Worker Size",
|
|
||||||
options: workerSizeOptions,
|
|
||||||
defaultSelectedKey: workerSize,
|
|
||||||
onChange: this._onWorkerSizeChange,
|
|
||||||
styles: {
|
|
||||||
label: "labelWithRedAsterisk",
|
|
||||||
root: "clusterSettingsDropdown"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return <Dropdown {...workerSizeDropdownProps} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getWorkerCountSliderInput(): JSX.Element {
|
|
||||||
const workerCount: number =
|
|
||||||
(this.props.cluster &&
|
|
||||||
this.props.cluster.properties &&
|
|
||||||
this.props.cluster.properties.workerInstanceCount !== undefined &&
|
|
||||||
this.props.cluster.properties.workerInstanceCount) ||
|
|
||||||
0;
|
|
||||||
const stackStyle: IStackStyles = {
|
|
||||||
root: {
|
|
||||||
paddingTop: 5
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const sliderItemStyle: IStackItemStyles = {
|
|
||||||
root: {
|
|
||||||
width: "100%",
|
|
||||||
paddingRight: 20
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const workerCountSliderProps: ISliderProps = {
|
|
||||||
min: 0,
|
|
||||||
max: Spark.MaxWorkerCount,
|
|
||||||
step: 1,
|
|
||||||
value: workerCount,
|
|
||||||
showValue: false,
|
|
||||||
onChange: this._onWorkerCountChange,
|
|
||||||
styles: {
|
|
||||||
root: {
|
|
||||||
width: "100%",
|
|
||||||
paddingRight: 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const workerCountTextFieldProps: ITextFieldProps = {
|
|
||||||
value: workerCount.toString(),
|
|
||||||
styles: {
|
|
||||||
fieldGroup: {
|
|
||||||
width: 45,
|
|
||||||
height: 25
|
|
||||||
},
|
|
||||||
field: {
|
|
||||||
textAlign: "center"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onChange: this._onWorkerCountTextFieldChange
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack styles={stackStyle}>
|
|
||||||
<span className="labelWithRedAsterisk">Worker Nodes</span>
|
|
||||||
<Stack horizontal verticalAlign="center">
|
|
||||||
<Slider {...workerCountSliderProps} />
|
|
||||||
<TextField {...workerCountTextFieldProps} />
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onDriverSizeChange = (_event: React.FormEvent, selectedOption: IDropdownOption) => {
|
|
||||||
const newValue: string = selectedOption.key as string;
|
|
||||||
const cluster = this.props.cluster;
|
|
||||||
if (cluster) {
|
|
||||||
cluster.properties.driverSize = newValue;
|
|
||||||
this.props.onClusterSettingsChanged(cluster);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onWorkerSizeChange = (_event: React.FormEvent, selectedOption: IDropdownOption) => {
|
|
||||||
const newValue: string = selectedOption.key as string;
|
|
||||||
const cluster = this.props.cluster;
|
|
||||||
if (cluster) {
|
|
||||||
cluster.properties.workerSize = newValue;
|
|
||||||
this.props.onClusterSettingsChanged(cluster);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onWorkerCountChange = (count: number) => {
|
|
||||||
count = Math.min(count, Spark.MaxWorkerCount);
|
|
||||||
const cluster = this.props.cluster;
|
|
||||||
if (cluster) {
|
|
||||||
cluster.properties.workerInstanceCount = count;
|
|
||||||
this.props.onClusterSettingsChanged(cluster);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _onWorkerCountTextFieldChange = (_event: React.FormEvent, newValue: string) => {
|
|
||||||
const count = parseInt(newValue);
|
|
||||||
if (!isNaN(count)) {
|
|
||||||
this._onWorkerCountChange(count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { ClusterSettingsComponent, ClusterSettingsComponentProps } from "./ClusterSettingsComponent";
|
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|
||||||
|
|
||||||
export class ClusterSettingsComponentAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<ClusterSettingsComponentProps>;
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return <ClusterSettingsComponent {...this.parameters()} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
import IToolbarDisplayable from "./IToolbarDisplayable";
|
|
||||||
|
|
||||||
interface IToolbarAction extends IToolbarDisplayable {
|
|
||||||
type: "action";
|
|
||||||
action: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IToolbarAction;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
interface IToolbarDisplayable {
|
|
||||||
id: string;
|
|
||||||
title: ko.Subscribable<string>;
|
|
||||||
displayName: ko.Subscribable<string>;
|
|
||||||
enabled: ko.Subscribable<boolean>;
|
|
||||||
visible: ko.Observable<boolean>;
|
|
||||||
focused: ko.Observable<boolean>;
|
|
||||||
icon: string;
|
|
||||||
mouseDown: (data: any, event: MouseEvent) => any;
|
|
||||||
keyUp: (data: any, event: KeyboardEvent) => any;
|
|
||||||
keyDown: (data: any, event: KeyboardEvent) => any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IToolbarDisplayable;
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
import IToolbarDisplayable from "./IToolbarDisplayable";
|
|
||||||
|
|
||||||
interface IToolbarDropDown extends IToolbarDisplayable {
|
|
||||||
type: "dropdown";
|
|
||||||
subgroup: IActionConfigItem[];
|
|
||||||
expanded: ko.Observable<boolean>;
|
|
||||||
open: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IDropdown {
|
|
||||||
type: "dropdown";
|
|
||||||
title: string;
|
|
||||||
displayName: string;
|
|
||||||
id: string;
|
|
||||||
enabled: ko.Observable<boolean>;
|
|
||||||
visible?: ko.Observable<boolean>;
|
|
||||||
icon?: string;
|
|
||||||
subgroup?: IActionConfigItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ISeperator {
|
|
||||||
type: "separator";
|
|
||||||
visible?: ko.Observable<boolean>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IToggle {
|
|
||||||
type: "toggle";
|
|
||||||
title: string;
|
|
||||||
displayName: string;
|
|
||||||
checkedTitle: string;
|
|
||||||
checkedDisplayName: string;
|
|
||||||
id: string;
|
|
||||||
checked: ko.Observable<boolean>;
|
|
||||||
enabled: ko.Observable<boolean>;
|
|
||||||
visible?: ko.Observable<boolean>;
|
|
||||||
icon?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IAction {
|
|
||||||
type: "action";
|
|
||||||
title: string;
|
|
||||||
displayName: string;
|
|
||||||
id: string;
|
|
||||||
action: () => any;
|
|
||||||
enabled: ko.Subscribable<boolean>;
|
|
||||||
visible?: ko.Observable<boolean>;
|
|
||||||
icon?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IActionConfigItem = ISeperator | IAction | IToggle | IDropdown;
|
|
||||||
|
|
||||||
export default IToolbarDropDown;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
import IToolbarAction from "./IToolbarAction";
|
|
||||||
import IToolbarToggle from "./IToolbarToggle";
|
|
||||||
import IToolbarSeperator from "./IToolbarSeperator";
|
|
||||||
import IToolbarDropDown from "./IToolbarDropDown";
|
|
||||||
|
|
||||||
type IToolbarItem = IToolbarAction | IToolbarToggle | IToolbarSeperator | IToolbarDropDown;
|
|
||||||
|
|
||||||
export default IToolbarItem;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
interface IToolbarSeperator {
|
|
||||||
type: "separator";
|
|
||||||
visible: ko.Observable<boolean>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default IToolbarSeperator;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
import IToolbarDisplayable from "./IToolbarDisplayable";
|
|
||||||
|
|
||||||
interface IToolbarToggle extends IToolbarDisplayable {
|
|
||||||
type: "toggle";
|
|
||||||
checked: ko.Observable<boolean>;
|
|
||||||
toggle: () => void;
|
|
||||||
}
|
|
||||||
export default IToolbarToggle;
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
var keyCodes = {
|
|
||||||
RightClick: 3,
|
|
||||||
Enter: 13,
|
|
||||||
Esc: 27,
|
|
||||||
Tab: 9,
|
|
||||||
LeftArrow: 37,
|
|
||||||
UpArrow: 38,
|
|
||||||
RightArrow: 39,
|
|
||||||
DownArrow: 40,
|
|
||||||
Delete: 46,
|
|
||||||
A: 65,
|
|
||||||
B: 66,
|
|
||||||
C: 67,
|
|
||||||
D: 68,
|
|
||||||
E: 69,
|
|
||||||
F: 70,
|
|
||||||
G: 71,
|
|
||||||
H: 72,
|
|
||||||
I: 73,
|
|
||||||
J: 74,
|
|
||||||
K: 75,
|
|
||||||
L: 76,
|
|
||||||
M: 77,
|
|
||||||
N: 78,
|
|
||||||
O: 79,
|
|
||||||
P: 80,
|
|
||||||
Q: 81,
|
|
||||||
R: 82,
|
|
||||||
S: 83,
|
|
||||||
T: 84,
|
|
||||||
U: 85,
|
|
||||||
V: 86,
|
|
||||||
W: 87,
|
|
||||||
X: 88,
|
|
||||||
Y: 89,
|
|
||||||
Z: 90,
|
|
||||||
Period: 190,
|
|
||||||
DecimalPoint: 110,
|
|
||||||
F1: 112,
|
|
||||||
F2: 113,
|
|
||||||
F3: 114,
|
|
||||||
F4: 115,
|
|
||||||
F5: 116,
|
|
||||||
F6: 117,
|
|
||||||
F7: 118,
|
|
||||||
F8: 119,
|
|
||||||
F9: 120,
|
|
||||||
F10: 121,
|
|
||||||
F11: 122,
|
|
||||||
F12: 123,
|
|
||||||
Dash: 189
|
|
||||||
};
|
|
||||||
|
|
||||||
export default keyCodes;
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
import { IDropdown } from "./IToolbarDropDown";
|
|
||||||
import { IActionConfigItem } from "./IToolbarDropDown";
|
|
||||||
import IToolbarItem from "./IToolbarItem";
|
|
||||||
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import ToolbarDropDown from "./ToolbarDropDown";
|
|
||||||
import ToolbarAction from "./ToolbarAction";
|
|
||||||
import ToolbarToggle from "./ToolbarToggle";
|
|
||||||
import template from "./toolbar.html";
|
|
||||||
|
|
||||||
export default class Toolbar {
|
|
||||||
private _toolbarWidth = ko.observable<number>();
|
|
||||||
private _actionConfigs: IActionConfigItem[];
|
|
||||||
private _afterExecute: (id: string) => void;
|
|
||||||
|
|
||||||
private _hasFocus: boolean = false;
|
|
||||||
private _focusedSubscription: ko.Subscription;
|
|
||||||
|
|
||||||
constructor(actionItems: IActionConfigItem[], afterExecute?: (id: string) => void) {
|
|
||||||
this._actionConfigs = actionItems;
|
|
||||||
this._afterExecute = afterExecute;
|
|
||||||
this.toolbarItems.subscribe(this._focusFirstEnabledItem);
|
|
||||||
|
|
||||||
$(window).resize(() => {
|
|
||||||
this._toolbarWidth($(".toolbar").width());
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
this._toolbarWidth($(".toolbar").width());
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
public toolbarItems: ko.PureComputed<IToolbarItem[]> = ko.pureComputed(() => {
|
|
||||||
var remainingToolbarSpace = this._toolbarWidth();
|
|
||||||
var toolbarItems: IToolbarItem[] = [];
|
|
||||||
|
|
||||||
var moreItem: IDropdown = {
|
|
||||||
type: "dropdown",
|
|
||||||
title: "More",
|
|
||||||
displayName: "More",
|
|
||||||
id: "more-actions-toggle",
|
|
||||||
enabled: ko.observable(true),
|
|
||||||
visible: ko.observable(true),
|
|
||||||
icon: "images/ASX_More.svg",
|
|
||||||
subgroup: []
|
|
||||||
};
|
|
||||||
|
|
||||||
var showHasMoreItem = false;
|
|
||||||
var addSeparator = false;
|
|
||||||
this._actionConfigs.forEach(actionConfig => {
|
|
||||||
if (actionConfig.type === "separator") {
|
|
||||||
addSeparator = true;
|
|
||||||
} else if (remainingToolbarSpace / 60 > 2) {
|
|
||||||
if (addSeparator) {
|
|
||||||
addSeparator = false;
|
|
||||||
toolbarItems.push(Toolbar._createToolbarItemFromConfig({ type: "separator" }));
|
|
||||||
remainingToolbarSpace -= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
toolbarItems.push(Toolbar._createToolbarItemFromConfig(actionConfig));
|
|
||||||
remainingToolbarSpace -= 60;
|
|
||||||
} else {
|
|
||||||
showHasMoreItem = true;
|
|
||||||
if (addSeparator) {
|
|
||||||
addSeparator = false;
|
|
||||||
moreItem.subgroup.push({
|
|
||||||
type: "separator"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!!actionConfig) {
|
|
||||||
moreItem.subgroup.push(actionConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (showHasMoreItem) {
|
|
||||||
toolbarItems.push(
|
|
||||||
Toolbar._createToolbarItemFromConfig({ type: "separator" }),
|
|
||||||
Toolbar._createToolbarItemFromConfig(moreItem)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return toolbarItems;
|
|
||||||
});
|
|
||||||
|
|
||||||
public focus() {
|
|
||||||
this._hasFocus = true;
|
|
||||||
this._focusFirstEnabledItem(this.toolbarItems());
|
|
||||||
}
|
|
||||||
|
|
||||||
private _focusFirstEnabledItem = (items: IToolbarItem[]) => {
|
|
||||||
if (!!this._focusedSubscription) {
|
|
||||||
// no memory leaks! :D
|
|
||||||
this._focusedSubscription.dispose();
|
|
||||||
}
|
|
||||||
if (this._hasFocus) {
|
|
||||||
for (var i = 0; i < items.length; i++) {
|
|
||||||
if (items[i].type !== "separator" && (<any>items[i]).enabled()) {
|
|
||||||
(<any>items[i]).focused(true);
|
|
||||||
this._focusedSubscription = (<any>items[i]).focused.subscribe((newValue: any) => {
|
|
||||||
if (!newValue) {
|
|
||||||
this._hasFocus = false;
|
|
||||||
this._focusedSubscription.dispose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static _createToolbarItemFromConfig(
|
|
||||||
configItem: IActionConfigItem,
|
|
||||||
afterExecute?: (id: string) => void
|
|
||||||
): IToolbarItem {
|
|
||||||
switch (configItem.type) {
|
|
||||||
case "dropdown":
|
|
||||||
return new ToolbarDropDown(configItem, afterExecute);
|
|
||||||
case "action":
|
|
||||||
return new ToolbarAction(configItem, afterExecute);
|
|
||||||
case "toggle":
|
|
||||||
return new ToolbarToggle(configItem, afterExecute);
|
|
||||||
case "separator":
|
|
||||||
return {
|
|
||||||
type: "separator",
|
|
||||||
visible: ko.observable(true)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class for ko component registration
|
|
||||||
*/
|
|
||||||
export class ToolbarComponent {
|
|
||||||
constructor() {
|
|
||||||
return {
|
|
||||||
viewModel: Toolbar,
|
|
||||||
template
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import { IAction } from "./IToolbarDropDown";
|
|
||||||
import IToolbarAction from "./IToolbarAction";
|
|
||||||
import KeyCodes from "./KeyCodes";
|
|
||||||
import Utilities from "./Utilities";
|
|
||||||
|
|
||||||
export default class ToolbarAction implements IToolbarAction {
|
|
||||||
public type: "action" = "action";
|
|
||||||
public id: string;
|
|
||||||
public icon: string;
|
|
||||||
public title: ko.Observable<string>;
|
|
||||||
public displayName: ko.Observable<string>;
|
|
||||||
public enabled: ko.Subscribable<boolean>;
|
|
||||||
public visible: ko.Observable<boolean>;
|
|
||||||
public focused: ko.Observable<boolean>;
|
|
||||||
public action: () => void;
|
|
||||||
private _afterExecute: (id: string) => void;
|
|
||||||
|
|
||||||
constructor(actionItem: IAction, afterExecute?: (id: string) => void) {
|
|
||||||
this.action = actionItem.action;
|
|
||||||
this.title = ko.observable(actionItem.title);
|
|
||||||
this.displayName = ko.observable(actionItem.displayName);
|
|
||||||
this.id = actionItem.id;
|
|
||||||
this.enabled = actionItem.enabled;
|
|
||||||
this.visible = actionItem.visible ? actionItem.visible : ko.observable(true);
|
|
||||||
this.focused = ko.observable(false);
|
|
||||||
this.icon = actionItem.icon;
|
|
||||||
this._afterExecute = afterExecute;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _executeAction = () => {
|
|
||||||
this.action();
|
|
||||||
if (!!this._afterExecute) {
|
|
||||||
this._afterExecute(this.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public mouseDown = (data: any, event: MouseEvent): boolean => {
|
|
||||||
this._executeAction();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
public keyUp = (data: any, event: KeyboardEvent): boolean => {
|
|
||||||
var handled: boolean = false;
|
|
||||||
|
|
||||||
handled = Utilities.onEnter(event, ($sourceElement: JQuery) => {
|
|
||||||
this._executeAction();
|
|
||||||
if ($sourceElement.hasClass("active")) {
|
|
||||||
$sourceElement.removeClass("active");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return !handled;
|
|
||||||
};
|
|
||||||
|
|
||||||
public keyDown = (data: any, event: KeyboardEvent): boolean => {
|
|
||||||
var handled: boolean = false;
|
|
||||||
|
|
||||||
handled = Utilities.onEnter(event, ($sourceElement: JQuery) => {
|
|
||||||
if ($sourceElement.hasClass("active")) {
|
|
||||||
$sourceElement.removeClass("active");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!handled) {
|
|
||||||
// Reset color if [shift-] tabbing, 'up/down arrowing', or 'esc'-aping away from button while holding down 'enter'
|
|
||||||
Utilities.onKeys(
|
|
||||||
event,
|
|
||||||
[KeyCodes.Tab, KeyCodes.UpArrow, KeyCodes.DownArrow, KeyCodes.Esc],
|
|
||||||
($sourceElement: JQuery) => {
|
|
||||||
if ($sourceElement.hasClass("active")) {
|
|
||||||
$sourceElement.removeClass("active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !handled;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import { IDropdown } from "./IToolbarDropDown";
|
|
||||||
import { IActionConfigItem } from "./IToolbarDropDown";
|
|
||||||
import IToolbarDropDown from "./IToolbarDropDown";
|
|
||||||
import KeyCodes from "./KeyCodes";
|
|
||||||
import Utilities from "./Utilities";
|
|
||||||
|
|
||||||
interface IMenuItem {
|
|
||||||
id?: string;
|
|
||||||
type: "normal" | "separator" | "submenu";
|
|
||||||
label?: string;
|
|
||||||
enabled?: boolean;
|
|
||||||
visible?: boolean;
|
|
||||||
submenu?: IMenuItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ToolbarDropDown implements IToolbarDropDown {
|
|
||||||
public type: "dropdown" = "dropdown";
|
|
||||||
public title: ko.Observable<string>;
|
|
||||||
public displayName: ko.Observable<string>;
|
|
||||||
public id: string;
|
|
||||||
public enabled: ko.Observable<boolean>;
|
|
||||||
public visible: ko.Observable<boolean>;
|
|
||||||
public focused: ko.Observable<boolean>;
|
|
||||||
public icon: string;
|
|
||||||
public subgroup: IActionConfigItem[] = [];
|
|
||||||
public expanded: ko.Observable<boolean> = ko.observable(false);
|
|
||||||
private _afterExecute: (id: string) => void;
|
|
||||||
|
|
||||||
constructor(dropdown: IDropdown, afterExecute?: (id: string) => void) {
|
|
||||||
this.subgroup = dropdown.subgroup;
|
|
||||||
this.title = ko.observable(dropdown.title);
|
|
||||||
this.displayName = ko.observable(dropdown.displayName);
|
|
||||||
this.id = dropdown.id;
|
|
||||||
this.enabled = dropdown.enabled;
|
|
||||||
this.visible = dropdown.visible ? dropdown.visible : ko.observable(true);
|
|
||||||
this.focused = ko.observable(false);
|
|
||||||
this.icon = dropdown.icon;
|
|
||||||
this._afterExecute = afterExecute;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static _convertToMenuItem = (
|
|
||||||
actionConfigs: IActionConfigItem[],
|
|
||||||
actionMap: { [id: string]: () => void } = {}
|
|
||||||
): { menuItems: IMenuItem[]; actionMap: { [id: string]: () => void } } => {
|
|
||||||
var returnValue = {
|
|
||||||
menuItems: actionConfigs.map<IMenuItem>((actionConfig: IActionConfigItem, index, array) => {
|
|
||||||
var menuItem: IMenuItem;
|
|
||||||
switch (actionConfig.type) {
|
|
||||||
case "action":
|
|
||||||
menuItem = <IMenuItem>{
|
|
||||||
id: actionConfig.id,
|
|
||||||
type: "normal",
|
|
||||||
label: actionConfig.displayName,
|
|
||||||
enabled: actionConfig.enabled(),
|
|
||||||
visible: actionConfig.visible ? actionConfig.visible() : true
|
|
||||||
};
|
|
||||||
actionMap[actionConfig.id] = actionConfig.action;
|
|
||||||
break;
|
|
||||||
case "dropdown":
|
|
||||||
menuItem = <IMenuItem>{
|
|
||||||
id: actionConfig.id,
|
|
||||||
type: "submenu",
|
|
||||||
label: actionConfig.displayName,
|
|
||||||
enabled: actionConfig.enabled(),
|
|
||||||
visible: actionConfig.visible ? actionConfig.visible() : true,
|
|
||||||
submenu: ToolbarDropDown._convertToMenuItem(actionConfig.subgroup, actionMap).menuItems
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "toggle":
|
|
||||||
menuItem = <IMenuItem>{
|
|
||||||
id: actionConfig.id,
|
|
||||||
type: "normal",
|
|
||||||
label: actionConfig.checked() ? actionConfig.checkedDisplayName : actionConfig.displayName,
|
|
||||||
enabled: actionConfig.enabled(),
|
|
||||||
visible: actionConfig.visible ? actionConfig.visible() : true
|
|
||||||
};
|
|
||||||
actionMap[actionConfig.id] = () => {
|
|
||||||
actionConfig.checked(!actionConfig.checked());
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case "separator":
|
|
||||||
menuItem = <IMenuItem>{
|
|
||||||
type: "separator",
|
|
||||||
visible: true
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return menuItem;
|
|
||||||
}),
|
|
||||||
actionMap: actionMap
|
|
||||||
};
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
public open = () => {
|
|
||||||
if (!!(<any>window).host) {
|
|
||||||
var convertedMenuItem = ToolbarDropDown._convertToMenuItem(this.subgroup);
|
|
||||||
|
|
||||||
(<any>window).host
|
|
||||||
.executeProviderOperation("MenuManager.showMenu", {
|
|
||||||
iFrameStack: [`#${window.frameElement.id}`],
|
|
||||||
anchor: `#${this.id}`,
|
|
||||||
menuItems: convertedMenuItem.menuItems
|
|
||||||
})
|
|
||||||
.then((id?: string) => {
|
|
||||||
if (!!id && !!convertedMenuItem.actionMap[id]) {
|
|
||||||
convertedMenuItem.actionMap[id]();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!!this._afterExecute) {
|
|
||||||
this._afterExecute(this.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public mouseDown = (data: any, event: MouseEvent): boolean => {
|
|
||||||
this.open();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
public keyUp = (data: any, event: KeyboardEvent): boolean => {
|
|
||||||
var handled: boolean = false;
|
|
||||||
|
|
||||||
handled = Utilities.onEnter(event, ($sourceElement: JQuery) => {
|
|
||||||
this.open();
|
|
||||||
if ($sourceElement.hasClass("active")) {
|
|
||||||
$sourceElement.removeClass("active");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return !handled;
|
|
||||||
};
|
|
||||||
|
|
||||||
public keyDown = (data: any, event: KeyboardEvent): boolean => {
|
|
||||||
var handled: boolean = false;
|
|
||||||
|
|
||||||
handled = Utilities.onEnter(event, ($sourceElement: JQuery) => {
|
|
||||||
if ($sourceElement.hasClass("active")) {
|
|
||||||
$sourceElement.removeClass("active");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!handled) {
|
|
||||||
// Reset color if [shift-] tabbing, 'up/down arrowing', or 'esc'-aping away from button while holding down 'enter'
|
|
||||||
Utilities.onKeys(
|
|
||||||
event,
|
|
||||||
[KeyCodes.Tab, KeyCodes.UpArrow, KeyCodes.DownArrow, KeyCodes.Esc],
|
|
||||||
($sourceElement: JQuery) => {
|
|
||||||
if ($sourceElement.hasClass("active")) {
|
|
||||||
$sourceElement.removeClass("active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !handled;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import { IToggle } from "./IToolbarDropDown";
|
|
||||||
import IToolbarToggle from "./IToolbarToggle";
|
|
||||||
import KeyCodes from "./KeyCodes";
|
|
||||||
import Utilities from "./Utilities";
|
|
||||||
|
|
||||||
export default class ToolbarToggle implements IToolbarToggle {
|
|
||||||
public type: "toggle" = "toggle";
|
|
||||||
public checked: ko.Observable<boolean>;
|
|
||||||
public id: string;
|
|
||||||
public enabled: ko.Observable<boolean>;
|
|
||||||
public visible: ko.Observable<boolean>;
|
|
||||||
public focused: ko.Observable<boolean>;
|
|
||||||
public icon: string;
|
|
||||||
|
|
||||||
private _title: string;
|
|
||||||
private _displayName: string;
|
|
||||||
private _checkedTitle: string;
|
|
||||||
private _checkedDisplayName: string;
|
|
||||||
|
|
||||||
private _afterExecute: (id: string) => void;
|
|
||||||
|
|
||||||
constructor(toggleItem: IToggle, afterExecute?: (id: string) => void) {
|
|
||||||
this._title = toggleItem.title;
|
|
||||||
this._displayName = toggleItem.displayName;
|
|
||||||
this.id = toggleItem.id;
|
|
||||||
this.enabled = toggleItem.enabled;
|
|
||||||
this.visible = toggleItem.visible ? toggleItem.visible : ko.observable(true);
|
|
||||||
this.focused = ko.observable(false);
|
|
||||||
this.icon = toggleItem.icon;
|
|
||||||
this.checked = toggleItem.checked;
|
|
||||||
this._checkedTitle = toggleItem.checkedTitle;
|
|
||||||
this._checkedDisplayName = toggleItem.checkedDisplayName;
|
|
||||||
this._afterExecute = afterExecute;
|
|
||||||
}
|
|
||||||
|
|
||||||
public title = ko.pureComputed(() => {
|
|
||||||
if (this.checked()) {
|
|
||||||
return this._checkedTitle;
|
|
||||||
} else {
|
|
||||||
return this._title;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
public displayName = ko.pureComputed(() => {
|
|
||||||
if (this.checked()) {
|
|
||||||
return this._checkedDisplayName;
|
|
||||||
} else {
|
|
||||||
return this._displayName;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
public toggle = () => {
|
|
||||||
this.checked(!this.checked());
|
|
||||||
|
|
||||||
if (this.checked() && !!this._afterExecute) {
|
|
||||||
this._afterExecute(this.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public mouseDown = (data: any, event: MouseEvent): boolean => {
|
|
||||||
this.toggle();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
public keyUp = (data: any, event: KeyboardEvent): boolean => {
|
|
||||||
var handled: boolean = false;
|
|
||||||
|
|
||||||
handled = Utilities.onEnter(event, ($sourceElement: JQuery) => {
|
|
||||||
this.toggle();
|
|
||||||
if ($sourceElement.hasClass("active")) {
|
|
||||||
$sourceElement.removeClass("active");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
return !handled;
|
|
||||||
};
|
|
||||||
|
|
||||||
public keyDown = (data: any, event: KeyboardEvent): boolean => {
|
|
||||||
var handled: boolean = false;
|
|
||||||
|
|
||||||
handled = Utilities.onEnter(event, ($sourceElement: JQuery) => {
|
|
||||||
if ($sourceElement.hasClass("active")) {
|
|
||||||
$sourceElement.removeClass("active");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!handled) {
|
|
||||||
// Reset color if [shift-] tabbing, 'up/down arrowing', or 'esc'-aping away from button while holding down 'enter'
|
|
||||||
Utilities.onKeys(
|
|
||||||
event,
|
|
||||||
[KeyCodes.Tab, KeyCodes.UpArrow, KeyCodes.DownArrow, KeyCodes.Esc],
|
|
||||||
($sourceElement: JQuery) => {
|
|
||||||
if ($sourceElement.hasClass("active")) {
|
|
||||||
$sourceElement.removeClass("active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !handled;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
/*!---------------------------------------------------------
|
|
||||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
||||||
*----------------------------------------------------------*/
|
|
||||||
|
|
||||||
import KeyCodes from "./KeyCodes";
|
|
||||||
|
|
||||||
export default class Utilities {
|
|
||||||
/**
|
|
||||||
* Executes an action on a keyboard event.
|
|
||||||
* Modifiers: ctrlKey - control/command key, shiftKey - shift key, altKey - alt/option key;
|
|
||||||
* pass on 'null' to ignore the modifier (default).
|
|
||||||
*/
|
|
||||||
public static onKey(
|
|
||||||
event: any,
|
|
||||||
eventKeyCode: number,
|
|
||||||
action: ($sourceElement: JQuery) => void,
|
|
||||||
metaKey: boolean = null,
|
|
||||||
shiftKey: boolean = null,
|
|
||||||
altKey: boolean = null
|
|
||||||
): boolean {
|
|
||||||
var source: any = event.target || event.srcElement,
|
|
||||||
keyCode: number = event.keyCode,
|
|
||||||
$sourceElement = $(source),
|
|
||||||
handled: boolean = false;
|
|
||||||
|
|
||||||
if (
|
|
||||||
$sourceElement.length &&
|
|
||||||
keyCode === eventKeyCode &&
|
|
||||||
$.isFunction(action) &&
|
|
||||||
(metaKey === null || metaKey === event.metaKey) &&
|
|
||||||
(shiftKey === null || shiftKey === event.shiftKey) &&
|
|
||||||
(altKey === null || altKey === event.altKey)
|
|
||||||
) {
|
|
||||||
action($sourceElement);
|
|
||||||
handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an action on the first matched keyboard event.
|
|
||||||
*/
|
|
||||||
public static onKeys(
|
|
||||||
event: any,
|
|
||||||
eventKeyCodes: number[],
|
|
||||||
action: ($sourceElement: JQuery) => void,
|
|
||||||
metaKey: boolean = null,
|
|
||||||
shiftKey: boolean = null,
|
|
||||||
altKey: boolean = null
|
|
||||||
): boolean {
|
|
||||||
var handled: boolean = false,
|
|
||||||
keyCount: number,
|
|
||||||
i: number;
|
|
||||||
|
|
||||||
if ($.isArray(eventKeyCodes)) {
|
|
||||||
keyCount = eventKeyCodes.length;
|
|
||||||
|
|
||||||
for (i = 0; i < keyCount; ++i) {
|
|
||||||
handled = Utilities.onKey(event, eventKeyCodes[i], action, metaKey, shiftKey, altKey);
|
|
||||||
|
|
||||||
if (handled) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an action on an 'enter' keyboard event.
|
|
||||||
*/
|
|
||||||
public static onEnter(
|
|
||||||
event: any,
|
|
||||||
action: ($sourceElement: JQuery) => void,
|
|
||||||
metaKey: boolean = null,
|
|
||||||
shiftKey: boolean = null,
|
|
||||||
altKey: boolean = null
|
|
||||||
): boolean {
|
|
||||||
return Utilities.onKey(event, KeyCodes.Enter, action, metaKey, shiftKey, altKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an action on a 'tab' keyboard event.
|
|
||||||
*/
|
|
||||||
public static onTab(
|
|
||||||
event: any,
|
|
||||||
action: ($sourceElement: JQuery) => void,
|
|
||||||
metaKey: boolean = null,
|
|
||||||
shiftKey: boolean = null,
|
|
||||||
altKey: boolean = null
|
|
||||||
): boolean {
|
|
||||||
return Utilities.onKey(event, KeyCodes.Tab, action, metaKey, shiftKey, altKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an action on an 'Esc' keyboard event.
|
|
||||||
*/
|
|
||||||
public static onEsc(
|
|
||||||
event: any,
|
|
||||||
action: ($sourceElement: JQuery) => void,
|
|
||||||
metaKey: boolean = null,
|
|
||||||
shiftKey: boolean = null,
|
|
||||||
altKey: boolean = null
|
|
||||||
): boolean {
|
|
||||||
return Utilities.onKey(event, KeyCodes.Esc, action, metaKey, shiftKey, altKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an action on an 'UpArrow' keyboard event.
|
|
||||||
*/
|
|
||||||
public static onUpArrow(
|
|
||||||
event: any,
|
|
||||||
action: ($sourceElement: JQuery) => void,
|
|
||||||
metaKey: boolean = null,
|
|
||||||
shiftKey: boolean = null,
|
|
||||||
altKey: boolean = null
|
|
||||||
): boolean {
|
|
||||||
return Utilities.onKey(event, KeyCodes.UpArrow, action, metaKey, shiftKey, altKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an action on a 'DownArrow' keyboard event.
|
|
||||||
*/
|
|
||||||
public static onDownArrow(
|
|
||||||
event: any,
|
|
||||||
action: ($sourceElement: JQuery) => void,
|
|
||||||
metaKey: boolean = null,
|
|
||||||
shiftKey: boolean = null,
|
|
||||||
altKey: boolean = null
|
|
||||||
): boolean {
|
|
||||||
return Utilities.onKey(event, KeyCodes.DownArrow, action, metaKey, shiftKey, altKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an action on a mouse event.
|
|
||||||
*/
|
|
||||||
public static onButton(event: any, eventButtonCode: number, action: ($sourceElement: JQuery) => void): boolean {
|
|
||||||
var source: any = event.currentTarget;
|
|
||||||
var buttonCode: number = event.button;
|
|
||||||
var $sourceElement = $(source);
|
|
||||||
var handled: boolean = false;
|
|
||||||
|
|
||||||
if ($sourceElement.length && buttonCode === eventButtonCode && $.isFunction(action)) {
|
|
||||||
action($sourceElement);
|
|
||||||
handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes an action on a 'left' mouse event.
|
|
||||||
*/
|
|
||||||
public static onLeftButton(event: any, action: ($sourceElement: JQuery) => void): boolean {
|
|
||||||
return Utilities.onButton(event, buttonCodes.Left, action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var buttonCodes = {
|
|
||||||
None: -1,
|
|
||||||
Left: 0,
|
|
||||||
Middle: 1,
|
|
||||||
Right: 2
|
|
||||||
};
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<div class="toolbar">
|
|
||||||
<!-- ko template: { name: 'toolbarItemTemplate', foreach: toolbarItems } -->
|
|
||||||
<!-- /ko -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/html" id="toolbarItemTemplate">
|
|
||||||
<!-- ko if: type === "action" -->
|
|
||||||
<div class="toolbar-group" data-bind="visible: visible">
|
|
||||||
<button class="toolbar-group-button" data-bind="hasFocus: focused, attr: {id: id, title: title, 'aria-label': displayName}, event: { mousedown: mouseDown, keydown: keyDown, keyup: keyUp }, enable: enabled">
|
|
||||||
<div class="toolbar-group-button-icon">
|
|
||||||
<div class="toolbar_icon" data-bind="icon: icon"></div>
|
|
||||||
</div>
|
|
||||||
<span data-bind="text: displayName"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
|
|
||||||
<!-- ko if: type === "toggle" -->
|
|
||||||
<div class="toolbar-group" data-bind="visible: visible">
|
|
||||||
<button class="toolbar-group-button toggle-button" data-bind="hasFocus: focused, attr: {id: id, title: title}, event: { mousedown: mouseDown, keydown: keyDown, keyup: keyUp }, enable: enabled">
|
|
||||||
<div class="toolbar-group-button-icon" data-bind="css: { 'toggle-checked': checked }">
|
|
||||||
<div class="toolbar_icon" data-bind="icon: icon"></div>
|
|
||||||
</div>
|
|
||||||
<span data-bind="text: displayName"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- ko if: type === "dropdown" -->
|
|
||||||
<div class="toolbar-group" data-bind="visible: visible">
|
|
||||||
<div class="dropdown" data-bind="attr: {id: (id + '-dropdown')}">
|
|
||||||
<button role="menu" class="toolbar-group-button" data-bind="hasFocus: focused, attr: {id: id, title: title, 'aria-label': displayName}, event: { mousedown: mouseDown, keydown: keyDown, keyup: keyUp }, enable: enabled">
|
|
||||||
<div class="toolbar-group-button-icon">
|
|
||||||
<div class="toolbar_icon" data-bind="icon: icon"></div>
|
|
||||||
</div>
|
|
||||||
<span data-bind="text: displayName"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
|
|
||||||
<!-- ko if: type === "separator" -->
|
|
||||||
<div class="toolbar-group vertical-separator" data-bind="visible: visible"></div>
|
|
||||||
<!-- /ko -->
|
|
||||||
</script>
|
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
|
jest.mock("../../Common/DocumentClientUtilityBase");
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import DocumentClientUtilityBase from "../../Common/DocumentClientUtilityBase";
|
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import { CollectionStub, DatabaseStub, ExplorerStub } from "../OpenActionsStubs";
|
|
||||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||||
import { CosmosClient } from "../../Common/CosmosClient";
|
import { CosmosClient } from "../../Common/CosmosClient";
|
||||||
|
import * as DocumentClientUtility from "../../Common/DocumentClientUtilityBase";
|
||||||
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
|
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
|
||||||
describe("ContainerSampleGenerator", () => {
|
describe("ContainerSampleGenerator", () => {
|
||||||
const createExplorerStub = (database: ViewModels.Database): ExplorerStub => {
|
const createExplorerStub = (database: ViewModels.Database): Explorer => {
|
||||||
const explorerStub = new ExplorerStub();
|
const explorerStub = {} as Explorer;
|
||||||
explorerStub.nonSystemDatabases = ko.computed(() => [database]);
|
explorerStub.nonSystemDatabases = ko.computed(() => [database]);
|
||||||
explorerStub.isPreferredApiGraph = ko.computed<boolean>(() => false);
|
explorerStub.isPreferredApiGraph = ko.computed<boolean>(() => false);
|
||||||
explorerStub.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
explorerStub.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||||
@@ -53,28 +54,21 @@ describe("ContainerSampleGenerator", () => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
const collection = new CollectionStub({ id: ko.observable(sampleCollectionId) });
|
const collection = { id: ko.observable(sampleCollectionId) } as ViewModels.Collection;
|
||||||
const database = new DatabaseStub({
|
const database = {
|
||||||
id: ko.observable(sampleDatabaseId),
|
id: ko.observable(sampleDatabaseId),
|
||||||
collections: ko.observableArray([collection])
|
collections: ko.observableArray<ViewModels.Collection>([collection])
|
||||||
});
|
} as ViewModels.Database;
|
||||||
database.findCollectionWithId = () => collection;
|
database.findCollectionWithId = () => collection;
|
||||||
|
|
||||||
const explorerStub = createExplorerStub(database);
|
const explorerStub = createExplorerStub(database);
|
||||||
explorerStub.isPreferredApiDocumentDB = ko.computed<boolean>(() => true);
|
explorerStub.isPreferredApiDocumentDB = ko.computed<boolean>(() => true);
|
||||||
|
|
||||||
const fakeDocumentClientUtility = sinon.createStubInstance(DocumentClientUtilityBase);
|
|
||||||
fakeDocumentClientUtility.getOrCreateDatabaseAndCollection.returns(Q.resolve(collection));
|
|
||||||
fakeDocumentClientUtility.createDocument.returns(Q.resolve());
|
|
||||||
|
|
||||||
explorerStub.documentClientUtility = fakeDocumentClientUtility;
|
|
||||||
|
|
||||||
const generator = await ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub);
|
const generator = await ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub);
|
||||||
generator.setData(sampleData);
|
generator.setData(sampleData);
|
||||||
|
|
||||||
await generator.createSampleContainerAsync();
|
await generator.createSampleContainerAsync();
|
||||||
|
|
||||||
expect(fakeDocumentClientUtility.createDocument.called).toBe(true);
|
expect(DocumentClientUtility.createDocument).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should send gremlin queries for Graph API account", async () => {
|
it("should send gremlin queries for Graph API account", async () => {
|
||||||
@@ -98,29 +92,23 @@ describe("ContainerSampleGenerator", () => {
|
|||||||
"g.addV('person').property(id, '1').property('_partitionKey','pk').property('name', 'Eva').property('age', 44)"
|
"g.addV('person').property(id, '1').property('_partitionKey','pk').property('name', 'Eva').property('age', 44)"
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
const collection = new CollectionStub({ id: ko.observable(sampleCollectionId) });
|
const collection = { id: ko.observable(sampleCollectionId) } as ViewModels.Collection;
|
||||||
const database = new DatabaseStub({
|
const database = {
|
||||||
id: ko.observable(sampleDatabaseId),
|
id: ko.observable(sampleDatabaseId),
|
||||||
collections: ko.observableArray([collection])
|
collections: ko.observableArray<ViewModels.Collection>([collection])
|
||||||
});
|
} as ViewModels.Database;
|
||||||
database.findCollectionWithId = () => collection;
|
database.findCollectionWithId = () => collection;
|
||||||
collection.databaseId = database.id();
|
collection.databaseId = database.id();
|
||||||
|
|
||||||
const explorerStub = createExplorerStub(database);
|
const explorerStub = createExplorerStub(database);
|
||||||
explorerStub.isPreferredApiGraph = ko.computed<boolean>(() => true);
|
explorerStub.isPreferredApiGraph = ko.computed<boolean>(() => true);
|
||||||
|
|
||||||
const fakeDocumentClientUtility = sinon.createStubInstance(DocumentClientUtilityBase);
|
|
||||||
fakeDocumentClientUtility.getOrCreateDatabaseAndCollection.returns(Q.resolve(collection));
|
|
||||||
fakeDocumentClientUtility.createDocument.returns(Q.resolve());
|
|
||||||
|
|
||||||
explorerStub.documentClientUtility = fakeDocumentClientUtility;
|
|
||||||
|
|
||||||
const generator = await ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub);
|
const generator = await ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub);
|
||||||
generator.setData(sampleData);
|
generator.setData(sampleData);
|
||||||
|
|
||||||
await generator.createSampleContainerAsync();
|
await generator.createSampleContainerAsync();
|
||||||
|
|
||||||
expect(fakeDocumentClientUtility.createDocument.called).toBe(false);
|
expect(DocumentClientUtility.createDocument).toHaveBeenCalled();
|
||||||
expect(executeStub.called).toBe(true);
|
expect(executeStub.called).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import GraphTab from ".././Tabs/GraphTab";
|
|||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { CosmosClient } from "../../Common/CosmosClient";
|
import { CosmosClient } from "../../Common/CosmosClient";
|
||||||
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
|
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
|
||||||
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
import { createDocument, getOrCreateDatabaseAndCollection } from "../../Common/DocumentClientUtilityBase";
|
||||||
|
|
||||||
interface SampleDataFile extends DataModels.CreateDatabaseAndCollectionRequest {
|
interface SampleDataFile extends DataModels.CreateDatabaseAndCollectionRequest {
|
||||||
data: any[];
|
data: any[];
|
||||||
@@ -14,12 +16,12 @@ interface SampleDataFile extends DataModels.CreateDatabaseAndCollectionRequest {
|
|||||||
export class ContainerSampleGenerator {
|
export class ContainerSampleGenerator {
|
||||||
private sampleDataFile: SampleDataFile;
|
private sampleDataFile: SampleDataFile;
|
||||||
|
|
||||||
private constructor(private container: ViewModels.Explorer) {}
|
private constructor(private container: Explorer) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory function to load the json data file
|
* Factory function to load the json data file
|
||||||
*/
|
*/
|
||||||
public static async createSampleGeneratorAsync(container: ViewModels.Explorer): Promise<ContainerSampleGenerator> {
|
public static async createSampleGeneratorAsync(container: Explorer): Promise<ContainerSampleGenerator> {
|
||||||
const generator = new ContainerSampleGenerator(container);
|
const generator = new ContainerSampleGenerator(container);
|
||||||
let dataFileContent: any;
|
let dataFileContent: any;
|
||||||
if (container.isPreferredApiGraph()) {
|
if (container.isPreferredApiGraph()) {
|
||||||
@@ -63,7 +65,7 @@ export class ContainerSampleGenerator {
|
|||||||
options.initialHeaders[Constants.HttpHeaders.usePolygonsSmallerThanAHemisphere] = true;
|
options.initialHeaders[Constants.HttpHeaders.usePolygonsSmallerThanAHemisphere] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.container.documentClientUtility.getOrCreateDatabaseAndCollection(createRequest, options);
|
await getOrCreateDatabaseAndCollection(createRequest, options);
|
||||||
await this.container.refreshAllDatabases();
|
await this.container.refreshAllDatabases();
|
||||||
const database = this.container.findDatabaseWithId(this.sampleDataFile.databaseId);
|
const database = this.container.findDatabaseWithId(this.sampleDataFile.databaseId);
|
||||||
if (!database) {
|
if (!database) {
|
||||||
@@ -102,7 +104,7 @@ export class ContainerSampleGenerator {
|
|||||||
} else {
|
} else {
|
||||||
// For SQL all queries are executed at the same time
|
// For SQL all queries are executed at the same time
|
||||||
this.sampleDataFile.data.forEach(doc => {
|
this.sampleDataFile.data.forEach(doc => {
|
||||||
const subPromise = this.container.documentClientUtility.createDocument(collection, doc);
|
const subPromise = createDocument(collection, doc);
|
||||||
subPromise.catch(reason => NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, reason));
|
subPromise.catch(reason => NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, reason));
|
||||||
promises.push(subPromise);
|
promises.push(subPromise);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import { ExplorerStub, DatabaseStub, CollectionStub } from "../OpenActionsStubs";
|
|
||||||
import { DataSamplesUtil } from "./DataSamplesUtil";
|
import { DataSamplesUtil } from "./DataSamplesUtil";
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
import { Database, Collection } from "../../Contracts/ViewModels";
|
||||||
|
|
||||||
describe("DataSampleUtils", () => {
|
describe("DataSampleUtils", () => {
|
||||||
const sampleCollectionId = "sampleCollectionId";
|
const sampleCollectionId = "sampleCollectionId";
|
||||||
const sampleDatabaseId = "sampleDatabaseId";
|
const sampleDatabaseId = "sampleDatabaseId";
|
||||||
|
|
||||||
it("should not create sample collection if collection already exists", async () => {
|
it("should not create sample collection if collection already exists", async () => {
|
||||||
const collection = new CollectionStub({ id: ko.observable(sampleCollectionId) });
|
const collection = { id: ko.observable(sampleCollectionId) } as Collection;
|
||||||
const database = new DatabaseStub({
|
const database = {
|
||||||
id: ko.observable(sampleDatabaseId),
|
id: ko.observable(sampleDatabaseId),
|
||||||
collections: ko.observableArray([collection])
|
collections: ko.observableArray<Collection>([collection])
|
||||||
});
|
} as Database;
|
||||||
const explorer = new ExplorerStub();
|
const explorer = {} as Explorer;
|
||||||
explorer.nonSystemDatabases = ko.computed(() => [database]);
|
explorer.nonSystemDatabases = ko.computed(() => [database]);
|
||||||
explorer.showOkModalDialog = () => {};
|
explorer.showOkModalDialog = () => {};
|
||||||
const dataSamplesUtil = new DataSamplesUtil(explorer);
|
const dataSamplesUtil = new DataSamplesUtil(explorer);
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
|
||||||
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
|
||||||
export class DataSamplesUtil {
|
export class DataSamplesUtil {
|
||||||
private static readonly DialogTitle = "Create Sample Container";
|
private static readonly DialogTitle = "Create Sample Container";
|
||||||
constructor(private container: ViewModels.Explorer) {}
|
constructor(private container: Explorer) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if Database/Container is already there: if so, show modal to delete
|
* Check if Database/Container is already there: if so, show modal to delete
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ import { GraphData, D3Node, D3Link } from "./GraphData";
|
|||||||
import { HashMap } from "../../../Common/HashMap";
|
import { HashMap } from "../../../Common/HashMap";
|
||||||
import { BaseType } from "d3";
|
import { BaseType } from "d3";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { NotificationConsoleUtils } from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import { GraphConfig } from "../../Tabs/GraphTab";
|
import { GraphConfig } from "../../Tabs/GraphTab";
|
||||||
import { GraphExplorer } from "./GraphExplorer";
|
import { GraphExplorer } from "./GraphExplorer";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
jest.mock("../../../Common/DocumentClientUtilityBase");
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
import { mount, ReactWrapper } from "enzyme";
|
import { mount, ReactWrapper } from "enzyme";
|
||||||
@@ -7,11 +8,11 @@ import { GraphExplorer, GraphExplorerProps, GraphAccessor, GraphHighlightedNodeD
|
|||||||
import * as D3ForceGraph from "./D3ForceGraph";
|
import * as D3ForceGraph from "./D3ForceGraph";
|
||||||
import { GraphData } from "./GraphData";
|
import { GraphData } from "./GraphData";
|
||||||
import { TabComponent } from "../../Controls/Tabs/TabComponent";
|
import { TabComponent } from "../../Controls/Tabs/TabComponent";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as StorageUtility from "../../../Shared/StorageUtility";
|
import * as StorageUtility from "../../../Shared/StorageUtility";
|
||||||
import GraphTab from "../../Tabs/GraphTab";
|
import GraphTab from "../../Tabs/GraphTab";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
|
import { queryDocuments, queryDocumentsPage } from "../../../Common/DocumentClientUtilityBase";
|
||||||
|
|
||||||
describe("Check whether query result is vertex array", () => {
|
describe("Check whether query result is vertex array", () => {
|
||||||
it("should reject null as vertex array", () => {
|
it("should reject null as vertex array", () => {
|
||||||
@@ -134,7 +135,7 @@ describe("GraphExplorer", () => {
|
|||||||
const COLLECTION_SELF_LINK = "collectionSelfLink";
|
const COLLECTION_SELF_LINK = "collectionSelfLink";
|
||||||
const gremlinRU = 789.12;
|
const gremlinRU = 789.12;
|
||||||
|
|
||||||
const createMockProps = (documentClientUtility?: any): GraphExplorerProps => {
|
const createMockProps = (): GraphExplorerProps => {
|
||||||
const graphConfig = GraphTab.createGraphConfig();
|
const graphConfig = GraphTab.createGraphConfig();
|
||||||
const graphConfigUi = GraphTab.createGraphConfigUiData(graphConfig);
|
const graphConfigUi = GraphTab.createGraphConfigUiData(graphConfig);
|
||||||
|
|
||||||
@@ -149,7 +150,6 @@ describe("GraphExplorer", () => {
|
|||||||
onIsValidQueryChange: (isValidQuery: boolean): void => {},
|
onIsValidQueryChange: (isValidQuery: boolean): void => {},
|
||||||
|
|
||||||
collectionPartitionKeyProperty: "collectionPartitionKeyProperty",
|
collectionPartitionKeyProperty: "collectionPartitionKeyProperty",
|
||||||
documentClientUtility: documentClientUtility,
|
|
||||||
collectionRid: COLLECTION_RID,
|
collectionRid: COLLECTION_RID,
|
||||||
collectionSelfLink: COLLECTION_SELF_LINK,
|
collectionSelfLink: COLLECTION_SELF_LINK,
|
||||||
graphBackendEndpoint: "graphBackendEndpoint",
|
graphBackendEndpoint: "graphBackendEndpoint",
|
||||||
@@ -188,7 +188,6 @@ describe("GraphExplorer", () => {
|
|||||||
let wrapper: ReactWrapper;
|
let wrapper: ReactWrapper;
|
||||||
|
|
||||||
let connectStub: sinon.SinonSpy;
|
let connectStub: sinon.SinonSpy;
|
||||||
let queryDocStub: sinon.SinonSpy;
|
|
||||||
let submitToBackendSpy: sinon.SinonSpy;
|
let submitToBackendSpy: sinon.SinonSpy;
|
||||||
let renderResultAsJsonStub: sinon.SinonSpy;
|
let renderResultAsJsonStub: sinon.SinonSpy;
|
||||||
let onMiddlePaneInitializedStub: sinon.SinonSpy;
|
let onMiddlePaneInitializedStub: sinon.SinonSpy;
|
||||||
@@ -215,46 +214,6 @@ describe("GraphExplorer", () => {
|
|||||||
[query: string]: AjaxResponse;
|
[query: string]: AjaxResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createDocumentClientUtilityMock = (docDBResponse: AjaxResponse) => {
|
|
||||||
const mock = {
|
|
||||||
queryDocuments: () => {},
|
|
||||||
queryDocumentsPage: (
|
|
||||||
rid: string,
|
|
||||||
iterator: any,
|
|
||||||
firstItemIndex: number,
|
|
||||||
options: any
|
|
||||||
): Q.Promise<ViewModels.QueryResults> => {
|
|
||||||
const qresult = {
|
|
||||||
hasMoreResults: false,
|
|
||||||
firstItemIndex: firstItemIndex,
|
|
||||||
lastItemIndex: 0,
|
|
||||||
itemCount: 0,
|
|
||||||
documents: docDBResponse.response,
|
|
||||||
activityId: "",
|
|
||||||
headers: [] as any[],
|
|
||||||
requestCharge: gVRU
|
|
||||||
};
|
|
||||||
|
|
||||||
return Q.resolve(qresult);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fakeIterator: any = {
|
|
||||||
nextItem: (callback: (error: any, document: DataModels.DocumentId) => void): void => {},
|
|
||||||
hasMoreResults: () => false,
|
|
||||||
executeNext: (callback: (error: any, documents: DataModels.DocumentId[], headers: any) => void): void => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
queryDocStub = sinon.stub(mock, "queryDocuments").callsFake(
|
|
||||||
(container: ViewModels.DocumentRequestContainer, query: string, options: any): Q.Promise<any> => {
|
|
||||||
(fakeIterator as any)._query = query;
|
|
||||||
return Q.resolve(fakeIterator);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return mock;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setupMocks = (
|
const setupMocks = (
|
||||||
graphExplorer: GraphExplorer,
|
graphExplorer: GraphExplorer,
|
||||||
backendResponses: BackendResponses,
|
backendResponses: BackendResponses,
|
||||||
@@ -333,7 +292,29 @@ describe("GraphExplorer", () => {
|
|||||||
done: any,
|
done: any,
|
||||||
ignoreD3Update: boolean
|
ignoreD3Update: boolean
|
||||||
): GraphExplorer => {
|
): GraphExplorer => {
|
||||||
const props: GraphExplorerProps = createMockProps(createDocumentClientUtilityMock(docDBResponse));
|
(queryDocuments as jest.Mock).mockImplementation((container: any, query: string, options: any) => {
|
||||||
|
return Q.resolve({
|
||||||
|
_query: query,
|
||||||
|
nextItem: (callback: (error: any, document: DataModels.DocumentId) => void): void => {},
|
||||||
|
hasMoreResults: () => false,
|
||||||
|
executeNext: (callback: (error: any, documents: DataModels.DocumentId[], headers: any) => void): void => {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
(queryDocumentsPage as jest.Mock).mockImplementation(
|
||||||
|
(rid: string, iterator: any, firstItemIndex: number, options: any) => {
|
||||||
|
return Q.resolve({
|
||||||
|
hasMoreResults: false,
|
||||||
|
firstItemIndex: firstItemIndex,
|
||||||
|
lastItemIndex: 0,
|
||||||
|
itemCount: 0,
|
||||||
|
documents: docDBResponse.response,
|
||||||
|
activityId: "",
|
||||||
|
headers: [] as any[],
|
||||||
|
requestCharge: gVRU
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const props: GraphExplorerProps = createMockProps();
|
||||||
wrapper = mount(<GraphExplorer {...props} />);
|
wrapper = mount(<GraphExplorer {...props} />);
|
||||||
graphExplorerInstance = wrapper.instance() as GraphExplorer;
|
graphExplorerInstance = wrapper.instance() as GraphExplorer;
|
||||||
setupMocks(graphExplorerInstance, backendResponses, done, ignoreD3Update);
|
setupMocks(graphExplorerInstance, backendResponses, done, ignoreD3Update);
|
||||||
@@ -341,7 +322,7 @@ describe("GraphExplorer", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cleanUpStubsWrapper = () => {
|
const cleanUpStubsWrapper = () => {
|
||||||
queryDocStub.restore();
|
jest.resetAllMocks();
|
||||||
connectStub.restore();
|
connectStub.restore();
|
||||||
submitToBackendSpy.restore();
|
submitToBackendSpy.restore();
|
||||||
renderResultAsJsonStub.restore();
|
renderResultAsJsonStub.restore();
|
||||||
@@ -378,22 +359,11 @@ describe("GraphExplorer", () => {
|
|||||||
expect((graphExplorerInstance.submitToBackend as sinon.SinonSpy).calledWith("g.V()")).toBe(false);
|
expect((graphExplorerInstance.submitToBackend as sinon.SinonSpy).calledWith("g.V()")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should submit g.V() as docdb query with proper query", () => {
|
|
||||||
expect(
|
|
||||||
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[2]
|
|
||||||
).toBe(DOCDB_G_DOT_V_QUERY);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should submit g.V() as docdb query with proper parameters", () => {
|
it("should submit g.V() as docdb query with proper parameters", () => {
|
||||||
expect(
|
expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
|
||||||
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[0]
|
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
|
||||||
).toEqual("databaseId");
|
enableCrossPartitionQuery: true
|
||||||
expect(
|
});
|
||||||
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[1]
|
|
||||||
).toEqual("collectionId");
|
|
||||||
expect(
|
|
||||||
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[3]
|
|
||||||
).toEqual({ maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE, enableCrossPartitionQuery: true });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call backend thrice (user query, fetch outE, then fetch inE)", () => {
|
it("should call backend thrice (user query, fetch outE, then fetch inE)", () => {
|
||||||
@@ -426,22 +396,11 @@ describe("GraphExplorer", () => {
|
|||||||
expect((graphExplorerInstance.submitToBackend as sinon.SinonSpy).calledWith("g.V()")).toBe(false);
|
expect((graphExplorerInstance.submitToBackend as sinon.SinonSpy).calledWith("g.V()")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should submit g.V() as docdb query with proper query", () => {
|
|
||||||
expect(
|
|
||||||
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[2]
|
|
||||||
).toBe(DOCDB_G_DOT_V_QUERY);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should submit g.V() as docdb query with proper parameters", () => {
|
it("should submit g.V() as docdb query with proper parameters", () => {
|
||||||
expect(
|
expect(queryDocuments).toBeCalledWith("databaseId", "collectionId", DOCDB_G_DOT_V_QUERY, {
|
||||||
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[0]
|
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
|
||||||
).toEqual("databaseId");
|
enableCrossPartitionQuery: true
|
||||||
expect(
|
});
|
||||||
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[1]
|
|
||||||
).toEqual("collectionId");
|
|
||||||
expect(
|
|
||||||
(graphExplorerInstance.props.documentClientUtility.queryDocuments as sinon.SinonSpy).getCall(0).args[3]
|
|
||||||
).toEqual({ maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE, enableCrossPartitionQuery: true });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call backend thrice (user query, fetch outE, then fetch inE)", () => {
|
it("should call backend thrice (user query, fetch outE, then fetch inE)", () => {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import * as D3ForceGraph from "./D3ForceGraph";
|
|||||||
import { GraphVizComponentProps } from "./GraphVizComponent";
|
import { GraphVizComponentProps } from "./GraphVizComponent";
|
||||||
import * as GraphData from "./GraphData";
|
import * as GraphData from "./GraphData";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { NotificationConsoleUtils } from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import { GraphUtil } from "./GraphUtil";
|
import { GraphUtil } from "./GraphUtil";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
@@ -28,7 +28,7 @@ import * as Constants from "../../../Common/Constants";
|
|||||||
import { InputProperty } from "../../../Contracts/ViewModels";
|
import { InputProperty } from "../../../Contracts/ViewModels";
|
||||||
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";
|
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";
|
||||||
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
|
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
|
||||||
import DocumentClientUtilityBase from "../../../Common/DocumentClientUtilityBase";
|
import { queryDocuments, queryDocumentsPage } from "../../../Common/DocumentClientUtilityBase";
|
||||||
|
|
||||||
export interface GraphAccessor {
|
export interface GraphAccessor {
|
||||||
applyFilter: () => void;
|
applyFilter: () => void;
|
||||||
@@ -47,7 +47,6 @@ export interface GraphExplorerProps {
|
|||||||
onIsValidQueryChange: (isValidQuery: boolean) => void;
|
onIsValidQueryChange: (isValidQuery: boolean) => void;
|
||||||
|
|
||||||
collectionPartitionKeyProperty: string;
|
collectionPartitionKeyProperty: string;
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
|
||||||
collectionRid: string;
|
collectionRid: string;
|
||||||
collectionSelfLink: string;
|
collectionSelfLink: string;
|
||||||
graphBackendEndpoint: string;
|
graphBackendEndpoint: string;
|
||||||
@@ -697,7 +696,6 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
* @param cmd
|
* @param cmd
|
||||||
*/
|
*/
|
||||||
public submitToBackend(cmd: string): Q.Promise<GremlinClient.GremlinRequestResult> {
|
public submitToBackend(cmd: string): Q.Promise<GremlinClient.GremlinRequestResult> {
|
||||||
console.log("submit:", cmd);
|
|
||||||
const id = GraphExplorer.reportToConsole(ConsoleDataType.InProgress, `Executing: ${cmd}`);
|
const id = GraphExplorer.reportToConsole(ConsoleDataType.InProgress, `Executing: ${cmd}`);
|
||||||
this.setExecuteCounter(this.executeCounter + 1);
|
this.setExecuteCounter(this.executeCounter + 1);
|
||||||
|
|
||||||
@@ -730,26 +728,24 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
*/
|
*/
|
||||||
public executeNonPagedDocDbQuery(query: string): Q.Promise<DataModels.DocumentId[]> {
|
public executeNonPagedDocDbQuery(query: string): Q.Promise<DataModels.DocumentId[]> {
|
||||||
// TODO maxItemCount: this reduces throttling, but won't cap the # of results
|
// TODO maxItemCount: this reduces throttling, but won't cap the # of results
|
||||||
return this.props.documentClientUtility
|
return queryDocuments(this.props.databaseId, this.props.collectionId, query, {
|
||||||
.queryDocuments(this.props.databaseId, this.props.collectionId, query, {
|
maxItemCount: GraphExplorer.PAGE_ALL,
|
||||||
maxItemCount: GraphExplorer.PAGE_ALL,
|
enableCrossPartitionQuery:
|
||||||
enableCrossPartitionQuery:
|
StorageUtility.LocalStorageUtility.getEntryString(StorageUtility.StorageKey.IsCrossPartitionQueryEnabled) ===
|
||||||
StorageUtility.LocalStorageUtility.getEntryString(StorageUtility.StorageKey.IsCrossPartitionQueryEnabled) ===
|
"true"
|
||||||
"true"
|
}).then(
|
||||||
})
|
(iterator: QueryIterator<ItemDefinition & Resource>) => {
|
||||||
.then(
|
return iterator.fetchNext().then(response => response.resources);
|
||||||
(iterator: QueryIterator<ItemDefinition & Resource>) => {
|
},
|
||||||
return iterator.fetchNext().then(response => response.resources);
|
(reason: any) => {
|
||||||
},
|
GraphExplorer.reportToConsole(
|
||||||
(reason: any) => {
|
ConsoleDataType.Error,
|
||||||
GraphExplorer.reportToConsole(
|
`Failed to execute non-paged query ${query}. Reason:${reason}`,
|
||||||
ConsoleDataType.Error,
|
reason
|
||||||
`Failed to execute non-paged query ${query}. Reason:${reason}`,
|
);
|
||||||
reason
|
return null;
|
||||||
);
|
}
|
||||||
return null;
|
);
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1732,12 +1728,10 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
query = `select root.id, root.${this.props.collectionPartitionKeyProperty} from root where IS_DEFINED(root._isEdge) = false order by root._ts asc`;
|
query = `select root.id, root.${this.props.collectionPartitionKeyProperty} from root where IS_DEFINED(root._isEdge) = false order by root._ts asc`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.props.documentClientUtility
|
return queryDocuments(this.props.databaseId, this.props.collectionId, query, {
|
||||||
.queryDocuments(this.props.databaseId, this.props.collectionId, query, {
|
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
|
||||||
maxItemCount: GraphExplorer.ROOT_LIST_PAGE_SIZE,
|
enableCrossPartitionQuery: LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
|
||||||
enableCrossPartitionQuery:
|
})
|
||||||
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
|
|
||||||
})
|
|
||||||
.then(
|
.then(
|
||||||
(iterator: QueryIterator<ItemDefinition & Resource>) => {
|
(iterator: QueryIterator<ItemDefinition & Resource>) => {
|
||||||
this.currentDocDBQueryInfo = {
|
this.currentDocDBQueryInfo = {
|
||||||
@@ -1766,16 +1760,15 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
.currentDocDBQueryInfo.index + GraphExplorer.ROOT_LIST_PAGE_SIZE})`;
|
.currentDocDBQueryInfo.index + GraphExplorer.ROOT_LIST_PAGE_SIZE})`;
|
||||||
const id = GraphExplorer.reportToConsole(ConsoleDataType.InProgress, `Executing: ${queryInfoStr}`);
|
const id = GraphExplorer.reportToConsole(ConsoleDataType.InProgress, `Executing: ${queryInfoStr}`);
|
||||||
|
|
||||||
return this.props.documentClientUtility
|
return queryDocumentsPage(
|
||||||
.queryDocumentsPage(
|
this.props.collectionRid,
|
||||||
this.props.collectionRid,
|
this.currentDocDBQueryInfo.iterator,
|
||||||
this.currentDocDBQueryInfo.iterator,
|
this.currentDocDBQueryInfo.index,
|
||||||
this.currentDocDBQueryInfo.index,
|
{
|
||||||
{
|
enableCrossPartitionQuery:
|
||||||
enableCrossPartitionQuery:
|
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
|
||||||
LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true"
|
}
|
||||||
}
|
)
|
||||||
)
|
|
||||||
.then((results: ViewModels.QueryResults) => {
|
.then((results: ViewModels.QueryResults) => {
|
||||||
GraphExplorer.clearConsoleProgress(id);
|
GraphExplorer.clearConsoleProgress(id);
|
||||||
this.currentDocDBQueryInfo.index = results.lastItemIndex + 1;
|
this.currentDocDBQueryInfo.index = results.lastItemIndex + 1;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|||||||
import { GraphConfig } from "../../Tabs/GraphTab";
|
import { GraphConfig } from "../../Tabs/GraphTab";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { GraphExplorer, GraphAccessor } from "./GraphExplorer";
|
import { GraphExplorer, GraphAccessor } from "./GraphExplorer";
|
||||||
import DocumentClientUtilityBase from "../../../Common/DocumentClientUtilityBase";
|
|
||||||
|
|
||||||
interface Parameter {
|
interface Parameter {
|
||||||
onIsNewVertexDisabledChange: (isEnabled: boolean) => void;
|
onIsNewVertexDisabledChange: (isEnabled: boolean) => void;
|
||||||
@@ -18,7 +17,6 @@ interface Parameter {
|
|||||||
graphConfig?: GraphConfig;
|
graphConfig?: GraphConfig;
|
||||||
|
|
||||||
collectionPartitionKeyProperty: string;
|
collectionPartitionKeyProperty: string;
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
|
||||||
collectionRid: string;
|
collectionRid: string;
|
||||||
collectionSelfLink: string;
|
collectionSelfLink: string;
|
||||||
graphBackendEndpoint: string;
|
graphBackendEndpoint: string;
|
||||||
@@ -51,7 +49,6 @@ export class GraphExplorerAdapter implements ReactAdapter {
|
|||||||
onIsGraphDisplayed={this.params.onIsGraphDisplayed}
|
onIsGraphDisplayed={this.params.onIsGraphDisplayed}
|
||||||
onResetDefaultGraphConfigValues={this.params.onResetDefaultGraphConfigValues}
|
onResetDefaultGraphConfigValues={this.params.onResetDefaultGraphConfigValues}
|
||||||
collectionPartitionKeyProperty={this.params.collectionPartitionKeyProperty}
|
collectionPartitionKeyProperty={this.params.collectionPartitionKeyProperty}
|
||||||
documentClientUtility={this.params.documentClientUtility}
|
|
||||||
collectionRid={this.params.collectionRid}
|
collectionRid={this.params.collectionRid}
|
||||||
collectionSelfLink={this.params.collectionSelfLink}
|
collectionSelfLink={this.params.collectionSelfLink}
|
||||||
graphBackendEndpoint={this.params.graphBackendEndpoint}
|
graphBackendEndpoint={this.params.graphBackendEndpoint}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as sinon from "sinon";
|
import * as sinon from "sinon";
|
||||||
import { GremlinClient, GremlinClientParameters } from "./GremlinClient";
|
import { GremlinClient, GremlinClientParameters } from "./GremlinClient";
|
||||||
import { NotificationConsoleUtils } from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import * as Logger from "../../../Common/Logger";
|
import * as Logger from "../../../Common/Logger";
|
||||||
|
|
||||||
describe("Gremlin Client", () => {
|
describe("Gremlin Client", () => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import * as Q from "q";
|
import * as Q from "q";
|
||||||
import { GremlinSimpleClient, Result } from "./GremlinSimpleClient";
|
import { GremlinSimpleClient, Result } from "./GremlinSimpleClient";
|
||||||
import { NotificationConsoleUtils } from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { HashMap } from "../../../Common/HashMap";
|
import { HashMap } from "../../../Common/HashMap";
|
||||||
import * as Logger from "../../../Common/Logger";
|
import * as Logger from "../../../Common/Logger";
|
||||||
|
|||||||
@@ -11,14 +11,16 @@ import { CommandBarComponentButtonFactory } from "./CommandBarComponentButtonFac
|
|||||||
import { CommandBar, ICommandBarItemProps } from "office-ui-fabric-react/lib/CommandBar";
|
import { CommandBar, ICommandBarItemProps } from "office-ui-fabric-react/lib/CommandBar";
|
||||||
import { StyleConstants } from "../../../Common/Constants";
|
import { StyleConstants } from "../../../Common/Constants";
|
||||||
import { CommandBarUtil } from "./CommandBarUtil";
|
import { CommandBarUtil } from "./CommandBarUtil";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
|
||||||
export class CommandBarComponentAdapter implements ReactAdapter {
|
export class CommandBarComponentAdapter implements ReactAdapter {
|
||||||
public parameters: ko.Observable<number>;
|
public parameters: ko.Observable<number>;
|
||||||
public container: ViewModels.Explorer;
|
public container: Explorer;
|
||||||
private tabsButtons: ViewModels.NavbarButtonConfig[];
|
private tabsButtons: CommandButtonComponentProps[];
|
||||||
private isNotebookTabActive: ko.Computed<boolean>;
|
private isNotebookTabActive: ko.Computed<boolean>;
|
||||||
|
|
||||||
constructor(container: ViewModels.Explorer) {
|
constructor(container: Explorer) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.tabsButtons = [];
|
this.tabsButtons = [];
|
||||||
this.isNotebookTabActive = ko.computed(() =>
|
this.isNotebookTabActive = ko.computed(() =>
|
||||||
@@ -43,14 +45,15 @@ export class CommandBarComponentAdapter implements ReactAdapter {
|
|||||||
container.isHostedDataExplorerEnabled,
|
container.isHostedDataExplorerEnabled,
|
||||||
container.isSynapseLinkUpdating,
|
container.isSynapseLinkUpdating,
|
||||||
container.databaseAccount,
|
container.databaseAccount,
|
||||||
this.isNotebookTabActive
|
this.isNotebookTabActive,
|
||||||
|
container.isServerlessEnabled
|
||||||
];
|
];
|
||||||
|
|
||||||
ko.computed(() => ko.toJSON(toWatch)).subscribe(() => this.triggerRender());
|
ko.computed(() => ko.toJSON(toWatch)).subscribe(() => this.triggerRender());
|
||||||
this.parameters = ko.observable(Date.now());
|
this.parameters = ko.observable(Date.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
public onUpdateTabsButtons(buttons: ViewModels.NavbarButtonConfig[]): void {
|
public onUpdateTabsButtons(buttons: CommandButtonComponentProps[]): void {
|
||||||
this.tabsButtons = buttons;
|
this.tabsButtons = buttons;
|
||||||
this.triggerRender();
|
this.triggerRender();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,70 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { CommandBarComponentButtonFactory } from "./CommandBarComponentButtonFactory";
|
import { CommandBarComponentButtonFactory } from "./CommandBarComponentButtonFactory";
|
||||||
import { ExplorerStub } from "../../OpenActionsStubs";
|
|
||||||
import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
|
import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
|
||||||
import NotebookManager from "../../Notebook/NotebookManager";
|
import NotebookManager from "../../Notebook/NotebookManager";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
|
||||||
describe("CommandBarComponentButtonFactory tests", () => {
|
describe("CommandBarComponentButtonFactory tests", () => {
|
||||||
let mockExplorer: ViewModels.Explorer;
|
let mockExplorer: Explorer;
|
||||||
|
|
||||||
describe("Enable notebook button", () => {
|
describe("Enable Azure Synapse Link Button", () => {
|
||||||
const enableNotebookBtnLabel = "Enable Notebooks (Preview)";
|
const enableAzureSynapseLinkBtnLabel = "Enable Azure Synapse Link (Preview)";
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockExplorer = new ExplorerStub();
|
mockExplorer = {} as Explorer;
|
||||||
mockExplorer.addCollectionText = ko.observable("mockText");
|
mockExplorer.addCollectionText = ko.observable("mockText");
|
||||||
mockExplorer.isAuthWithResourceToken = ko.observable(false);
|
mockExplorer.isAuthWithResourceToken = ko.observable(false);
|
||||||
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
||||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
|
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||||
|
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||||
|
mockExplorer.isRunningOnNationalCloud = () => false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Account is not serverless - button should be visible", () => {
|
||||||
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
|
|
||||||
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||||
|
const enableAzureSynapseLinkBtn = buttons.find(
|
||||||
|
button => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||||
|
);
|
||||||
|
expect(enableAzureSynapseLinkBtn).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Account is serverless - button should be hidden", () => {
|
||||||
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => true);
|
||||||
|
|
||||||
|
const buttons = CommandBarComponentButtonFactory.createStaticCommandBarButtons(mockExplorer);
|
||||||
|
const enableAzureSynapseLinkBtn = buttons.find(
|
||||||
|
button => button.commandButtonLabel === enableAzureSynapseLinkBtnLabel
|
||||||
|
);
|
||||||
|
expect(enableAzureSynapseLinkBtn).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Enable notebook button", () => {
|
||||||
|
const enableNotebookBtnLabel = "Enable Notebooks (Preview)";
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
mockExplorer = {} as Explorer;
|
||||||
|
mockExplorer.addCollectionText = ko.observable("mockText");
|
||||||
|
mockExplorer.isAuthWithResourceToken = ko.observable(false);
|
||||||
|
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
||||||
|
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||||
|
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
||||||
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
|
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||||
|
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
||||||
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Notebooks is already enabled - button should be hidden", () => {
|
it("Notebooks is already enabled - button should be hidden", () => {
|
||||||
@@ -75,15 +118,17 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
const openMongoShellBtnLabel = "Open Mongo Shell";
|
const openMongoShellBtnLabel = "Open Mongo Shell";
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockExplorer = new ExplorerStub();
|
mockExplorer = {} as Explorer;
|
||||||
mockExplorer.addCollectionText = ko.observable("mockText");
|
mockExplorer.addCollectionText = ko.observable("mockText");
|
||||||
mockExplorer.isAuthWithResourceToken = ko.observable(false);
|
mockExplorer.isAuthWithResourceToken = ko.observable(false);
|
||||||
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
||||||
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -155,15 +200,17 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
const openCassandraShellBtnLabel = "Open Cassandra Shell";
|
const openCassandraShellBtnLabel = "Open Cassandra Shell";
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockExplorer = new ExplorerStub();
|
mockExplorer = {} as Explorer;
|
||||||
mockExplorer.addCollectionText = ko.observable("mockText");
|
mockExplorer.addCollectionText = ko.observable("mockText");
|
||||||
mockExplorer.isAuthWithResourceToken = ko.observable(false);
|
mockExplorer.isAuthWithResourceToken = ko.observable(false);
|
||||||
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
||||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||||
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -236,13 +283,14 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
const manageGitHubSettingsBtnLabel = "Manage GitHub settings";
|
const manageGitHubSettingsBtnLabel = "Manage GitHub settings";
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockExplorer = new ExplorerStub();
|
mockExplorer = {} as Explorer;
|
||||||
mockExplorer.addCollectionText = ko.observable("mockText");
|
mockExplorer.addCollectionText = ko.observable("mockText");
|
||||||
mockExplorer.isAuthWithResourceToken = ko.observable(false);
|
mockExplorer.isAuthWithResourceToken = ko.observable(false);
|
||||||
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
||||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
||||||
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||||
@@ -250,6 +298,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.notebookManager = new NotebookManager();
|
mockExplorer.notebookManager = new NotebookManager();
|
||||||
mockExplorer.notebookManager.gitHubOAuthService = new GitHubOAuthService(undefined);
|
mockExplorer.notebookManager.gitHubOAuthService = new GitHubOAuthService(undefined);
|
||||||
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -294,13 +343,14 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
|
|
||||||
describe("Resource token", () => {
|
describe("Resource token", () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockExplorer = new ExplorerStub();
|
mockExplorer = {} as Explorer;
|
||||||
mockExplorer.addCollectionText = ko.observable("mockText");
|
mockExplorer.addCollectionText = ko.observable("mockText");
|
||||||
mockExplorer.isAuthWithResourceToken = ko.observable(true);
|
mockExplorer.isAuthWithResourceToken = ko.observable(true);
|
||||||
mockExplorer.isPreferredApiDocumentDB = ko.computed(() => true);
|
mockExplorer.isPreferredApiDocumentDB = ko.computed(() => true);
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
||||||
mockExplorer.isResourceTokenCollectionNodeSelected = ko.computed(() => true);
|
mockExplorer.isResourceTokenCollectionNodeSelected = ko.computed(() => true);
|
||||||
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should only show New SQL Query and Open Query buttons", () => {
|
it("should only show New SQL Query and Open Query buttons", () => {
|
||||||
|
|||||||
@@ -25,17 +25,19 @@ import ResetWorkspaceIcon from "../../../../images/notebook/Notebook-reset-works
|
|||||||
import GitHubIcon from "../../../../images/github.svg";
|
import GitHubIcon from "../../../../images/github.svg";
|
||||||
import SynapseIcon from "../../../../images/synapse-link.svg";
|
import SynapseIcon from "../../../../images/synapse-link.svg";
|
||||||
import { config, Platform } from "../../../Config";
|
import { config, Platform } from "../../../Config";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
|
||||||
export class CommandBarComponentButtonFactory {
|
export class CommandBarComponentButtonFactory {
|
||||||
private static counter: number = 0;
|
private static counter: number = 0;
|
||||||
|
|
||||||
public static createStaticCommandBarButtons(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig[] {
|
public static createStaticCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
if (container.isAuthWithResourceToken()) {
|
if (container.isAuthWithResourceToken()) {
|
||||||
return CommandBarComponentButtonFactory.createStaticCommandBarButtonsForResourceToken(container);
|
return CommandBarComponentButtonFactory.createStaticCommandBarButtonsForResourceToken(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newCollectionBtn = CommandBarComponentButtonFactory.createNewCollectionGroup(container);
|
const newCollectionBtn = CommandBarComponentButtonFactory.createNewCollectionGroup(container);
|
||||||
const buttons: ViewModels.NavbarButtonConfig[] = [newCollectionBtn];
|
const buttons: CommandButtonComponentProps[] = [newCollectionBtn];
|
||||||
|
|
||||||
const addSynapseLink = CommandBarComponentButtonFactory.createOpenSynapseLinkDialogButton(container);
|
const addSynapseLink = CommandBarComponentButtonFactory.createOpenSynapseLinkDialogButton(container);
|
||||||
if (addSynapseLink) {
|
if (addSynapseLink) {
|
||||||
@@ -111,7 +113,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
|
|
||||||
if (CommandBarComponentButtonFactory.areScriptsSupported(container)) {
|
if (CommandBarComponentButtonFactory.areScriptsSupported(container)) {
|
||||||
const label = "New Stored Procedure";
|
const label = "New Stored Procedure";
|
||||||
const newStoredProcedureBtn: ViewModels.NavbarButtonConfig = {
|
const newStoredProcedureBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: AddStoredProcedureIcon,
|
iconSrc: AddStoredProcedureIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
@@ -132,12 +134,12 @@ export class CommandBarComponentButtonFactory {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createContextCommandBarButtons(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig[] {
|
public static createContextCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
const buttons: ViewModels.NavbarButtonConfig[] = [];
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
|
|
||||||
if (!container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()) {
|
if (!container.isDatabaseNodeOrNoneSelected() && container.isPreferredApiMongoDB()) {
|
||||||
const label = "New Shell";
|
const label = "New Shell";
|
||||||
const newMongoShellBtn: ViewModels.NavbarButtonConfig = {
|
const newMongoShellBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: HostedTerminalIcon,
|
iconSrc: HostedTerminalIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
@@ -155,15 +157,15 @@ export class CommandBarComponentButtonFactory {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createControlCommandBarButtons(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig[] {
|
public static createControlCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
const buttons: ViewModels.NavbarButtonConfig[] = [];
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
if (window.dataExplorerPlatform === PlatformType.Hosted) {
|
if (window.dataExplorerPlatform === PlatformType.Hosted) {
|
||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!container.isPreferredApiCassandra()) {
|
if (!container.isPreferredApiCassandra()) {
|
||||||
const label = "Settings";
|
const label = "Settings";
|
||||||
const settingsPaneButton: ViewModels.NavbarButtonConfig = {
|
const settingsPaneButton: CommandButtonComponentProps = {
|
||||||
iconSrc: SettingsIcon,
|
iconSrc: SettingsIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => container.settingsPane.open(),
|
onCommandClick: () => container.settingsPane.open(),
|
||||||
@@ -178,7 +180,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
|
|
||||||
if (container.isHostedDataExplorerEnabled()) {
|
if (container.isHostedDataExplorerEnabled()) {
|
||||||
const label = "Open Full Screen";
|
const label = "Open Full Screen";
|
||||||
const fullScreenButton: ViewModels.NavbarButtonConfig = {
|
const fullScreenButton: CommandButtonComponentProps = {
|
||||||
iconSrc: OpenInTabIcon,
|
iconSrc: OpenInTabIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => container.generateSharedAccessData(),
|
onCommandClick: () => container.generateSharedAccessData(),
|
||||||
@@ -194,7 +196,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
|
|
||||||
if (!container.hasOwnProperty("isEmulator") || !container.isEmulator) {
|
if (!container.hasOwnProperty("isEmulator") || !container.isEmulator) {
|
||||||
const label = "Feedback";
|
const label = "Feedback";
|
||||||
const feedbackButtonOptions: ViewModels.NavbarButtonConfig = {
|
const feedbackButtonOptions: CommandButtonComponentProps = {
|
||||||
iconSrc: FeedbackIcon,
|
iconSrc: FeedbackIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => container.provideFeedbackEmail(),
|
onCommandClick: () => container.provideFeedbackEmail(),
|
||||||
@@ -210,7 +212,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createDivider(): ViewModels.NavbarButtonConfig {
|
public static createDivider(): CommandButtonComponentProps {
|
||||||
const label = `divider${CommandBarComponentButtonFactory.counter++}`;
|
const label = `divider${CommandBarComponentButtonFactory.counter++}`;
|
||||||
return {
|
return {
|
||||||
isDivider: true,
|
isDivider: true,
|
||||||
@@ -223,11 +225,11 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static areScriptsSupported(container: ViewModels.Explorer): boolean {
|
private static areScriptsSupported(container: Explorer): boolean {
|
||||||
return container.isPreferredApiDocumentDB() || container.isPreferredApiGraph();
|
return container.isPreferredApiDocumentDB() || container.isPreferredApiGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createNewCollectionGroup(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createNewCollectionGroup(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = container.addCollectionText();
|
const label = container.addCollectionText();
|
||||||
return {
|
return {
|
||||||
iconSrc: AddCollectionIcon,
|
iconSrc: AddCollectionIcon,
|
||||||
@@ -240,10 +242,15 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createOpenSynapseLinkDialogButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonComponentProps {
|
||||||
if (config.platform === Platform.Emulator) {
|
if (config.platform === Platform.Emulator) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (container.isServerlessEnabled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
container.databaseAccount &&
|
container.databaseAccount &&
|
||||||
container.databaseAccount() &&
|
container.databaseAccount() &&
|
||||||
@@ -275,7 +282,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createNewDatabase(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = container.addDatabaseText();
|
const label = container.addDatabaseText();
|
||||||
return {
|
return {
|
||||||
iconSrc: AddDatabaseIcon,
|
iconSrc: AddDatabaseIcon,
|
||||||
@@ -290,7 +297,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createNewSQLQueryButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createNewSQLQueryButton(container: Explorer): CommandButtonComponentProps {
|
||||||
if (container.isPreferredApiDocumentDB() || container.isPreferredApiGraph()) {
|
if (container.isPreferredApiDocumentDB() || container.isPreferredApiGraph()) {
|
||||||
const label = "New SQL Query";
|
const label = "New SQL Query";
|
||||||
return {
|
return {
|
||||||
@@ -324,15 +331,15 @@ export class CommandBarComponentButtonFactory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createScriptCommandButtons(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig[] {
|
public static createScriptCommandButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
const buttons: ViewModels.NavbarButtonConfig[] = [];
|
const buttons: CommandButtonComponentProps[] = [];
|
||||||
|
|
||||||
const shouldEnableScriptsCommands: boolean =
|
const shouldEnableScriptsCommands: boolean =
|
||||||
!container.isDatabaseNodeOrNoneSelected() && CommandBarComponentButtonFactory.areScriptsSupported(container);
|
!container.isDatabaseNodeOrNoneSelected() && CommandBarComponentButtonFactory.areScriptsSupported(container);
|
||||||
|
|
||||||
if (shouldEnableScriptsCommands) {
|
if (shouldEnableScriptsCommands) {
|
||||||
const label = "New Stored Procedure";
|
const label = "New Stored Procedure";
|
||||||
const newStoredProcedureBtn: ViewModels.NavbarButtonConfig = {
|
const newStoredProcedureBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: AddStoredProcedureIcon,
|
iconSrc: AddStoredProcedureIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
@@ -349,7 +356,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
|
|
||||||
if (shouldEnableScriptsCommands) {
|
if (shouldEnableScriptsCommands) {
|
||||||
const label = "New UDF";
|
const label = "New UDF";
|
||||||
const newUserDefinedFunctionBtn: ViewModels.NavbarButtonConfig = {
|
const newUserDefinedFunctionBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: AddUdfIcon,
|
iconSrc: AddUdfIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
@@ -366,7 +373,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
|
|
||||||
if (shouldEnableScriptsCommands) {
|
if (shouldEnableScriptsCommands) {
|
||||||
const label = "New Trigger";
|
const label = "New Trigger";
|
||||||
const newTriggerBtn: ViewModels.NavbarButtonConfig = {
|
const newTriggerBtn: CommandButtonComponentProps = {
|
||||||
iconSrc: AddTriggerIcon,
|
iconSrc: AddTriggerIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
@@ -384,7 +391,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createScaleAndSettingsButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createScaleAndSettingsButton(container: Explorer): CommandButtonComponentProps {
|
||||||
let isShared = false;
|
let isShared = false;
|
||||||
if (container.isDatabaseNodeSelected()) {
|
if (container.isDatabaseNodeSelected()) {
|
||||||
isShared = container.findSelectedDatabase().isDatabaseShared();
|
isShared = container.findSelectedDatabase().isDatabaseShared();
|
||||||
@@ -409,7 +416,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createNewNotebookButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createNewNotebookButton(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "New Notebook";
|
const label = "New Notebook";
|
||||||
return {
|
return {
|
||||||
iconSrc: NewNotebookIcon,
|
iconSrc: NewNotebookIcon,
|
||||||
@@ -422,7 +429,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createuploadNotebookButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createuploadNotebookButton(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "Upload to Notebook Server";
|
const label = "Upload to Notebook Server";
|
||||||
return {
|
return {
|
||||||
iconSrc: NewNotebookIcon,
|
iconSrc: NewNotebookIcon,
|
||||||
@@ -435,7 +442,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createOpenQueryButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createOpenQueryButton(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "Open Query";
|
const label = "Open Query";
|
||||||
return {
|
return {
|
||||||
iconSrc: BrowseQueriesIcon,
|
iconSrc: BrowseQueriesIcon,
|
||||||
@@ -448,7 +455,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createOpenQueryFromDiskButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createOpenQueryFromDiskButton(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "Open Query From Disk";
|
const label = "Open Query From Disk";
|
||||||
return {
|
return {
|
||||||
iconSrc: OpenQueryFromDiskIcon,
|
iconSrc: OpenQueryFromDiskIcon,
|
||||||
@@ -461,7 +468,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createEnableNotebooksButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createEnableNotebooksButton(container: Explorer): CommandButtonComponentProps {
|
||||||
if (config.platform === Platform.Emulator) {
|
if (config.platform === Platform.Emulator) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -482,7 +489,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createOpenTerminalButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createOpenTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "Open Terminal";
|
const label = "Open Terminal";
|
||||||
return {
|
return {
|
||||||
iconSrc: CosmosTerminalIcon,
|
iconSrc: CosmosTerminalIcon,
|
||||||
@@ -495,7 +502,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createOpenMongoTerminalButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createOpenMongoTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "Open Mongo Shell";
|
const label = "Open Mongo Shell";
|
||||||
const tooltip =
|
const tooltip =
|
||||||
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
||||||
@@ -521,7 +528,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createOpenCassandraTerminalButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createOpenCassandraTerminalButton(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "Open Cassandra Shell";
|
const label = "Open Cassandra Shell";
|
||||||
const tooltip =
|
const tooltip =
|
||||||
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
"This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks.";
|
||||||
@@ -547,7 +554,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createNotebookWorkspaceResetButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createNotebookWorkspaceResetButton(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "Reset Workspace";
|
const label = "Reset Workspace";
|
||||||
return {
|
return {
|
||||||
iconSrc: ResetWorkspaceIcon,
|
iconSrc: ResetWorkspaceIcon,
|
||||||
@@ -560,7 +567,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createManageGitHubAccountButton(container: ViewModels.Explorer): ViewModels.NavbarButtonConfig {
|
private static createManageGitHubAccountButton(container: Explorer): CommandButtonComponentProps {
|
||||||
let connectedToGitHub: boolean = container.notebookManager?.gitHubOAuthService.isLoggedIn();
|
let connectedToGitHub: boolean = container.notebookManager?.gitHubOAuthService.isLoggedIn();
|
||||||
const label = connectedToGitHub ? "Manage GitHub settings" : "Connect to GitHub";
|
const label = connectedToGitHub ? "Manage GitHub settings" : "Connect to GitHub";
|
||||||
return {
|
return {
|
||||||
@@ -583,9 +590,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createStaticCommandBarButtonsForResourceToken(
|
private static createStaticCommandBarButtonsForResourceToken(container: Explorer): CommandButtonComponentProps[] {
|
||||||
container: ViewModels.Explorer
|
|
||||||
): ViewModels.NavbarButtonConfig[] {
|
|
||||||
const newSqlQueryBtn = CommandBarComponentButtonFactory.createNewSQLQueryButton(container);
|
const newSqlQueryBtn = CommandBarComponentButtonFactory.createNewSQLQueryButton(container);
|
||||||
const openQueryBtn = CommandBarComponentButtonFactory.createOpenQueryButton(container);
|
const openQueryBtn = CommandBarComponentButtonFactory.createOpenQueryButton(container);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { CommandBarUtil } from "./CommandBarUtil";
|
import { CommandBarUtil } from "./CommandBarUtil";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { ICommandBarItemProps } from "office-ui-fabric-react/lib/CommandBar";
|
import { ICommandBarItemProps } from "office-ui-fabric-react/lib/CommandBar";
|
||||||
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
|
||||||
describe("CommandBarUtil tests", () => {
|
describe("CommandBarUtil tests", () => {
|
||||||
const createButton = (): ViewModels.NavbarButtonConfig => {
|
const createButton = (): CommandButtonComponentProps => {
|
||||||
return {
|
return {
|
||||||
iconSrc: "icon",
|
iconSrc: "icon",
|
||||||
iconAlt: "label",
|
iconAlt: "label",
|
||||||
@@ -54,7 +55,7 @@ describe("CommandBarUtil tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should create buttons with unique keys", () => {
|
it("should create buttons with unique keys", () => {
|
||||||
const btns: ViewModels.NavbarButtonConfig[] = [];
|
const btns: CommandButtonComponentProps[] = [];
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
btns.push(createButton());
|
btns.push(createButton());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { Observable } from "knockout";
|
import { Observable } from "knockout";
|
||||||
import { IconType } from "office-ui-fabric-react/lib/Icon";
|
import { IconType } from "office-ui-fabric-react/lib/Icon";
|
||||||
import { IComponentAsProps } from "office-ui-fabric-react/lib/Utilities";
|
import { IComponentAsProps } from "office-ui-fabric-react/lib/Utilities";
|
||||||
import { KeyCodes, StyleConstants } from "../../../Common/Constants";
|
import { StyleConstants } from "../../../Common/Constants";
|
||||||
import { ICommandBarItemProps } from "office-ui-fabric-react/lib/CommandBar";
|
import { ICommandBarItemProps } from "office-ui-fabric-react/lib/CommandBar";
|
||||||
import { Dropdown, DropdownMenuItemType, IDropdownStyles, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
|
import { Dropdown, IDropdownStyles, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
|
import ChevronDownIcon from "../../../../images/Chevron_down.svg";
|
||||||
import { ArcadiaMenuPicker } from "../../Controls/Arcadia/ArcadiaMenuPicker";
|
import { ArcadiaMenuPicker } from "../../Controls/Arcadia/ArcadiaMenuPicker";
|
||||||
@@ -21,13 +20,13 @@ export class CommandBarUtil {
|
|||||||
* Convert our NavbarButtonConfig to UI Fabric buttons
|
* Convert our NavbarButtonConfig to UI Fabric buttons
|
||||||
* @param btns
|
* @param btns
|
||||||
*/
|
*/
|
||||||
public static convertButton(btns: ViewModels.NavbarButtonConfig[], backgroundColor: string): ICommandBarItemProps[] {
|
public static convertButton(btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] {
|
||||||
const buttonHeightPx = StyleConstants.CommandBarButtonHeight;
|
const buttonHeightPx = StyleConstants.CommandBarButtonHeight;
|
||||||
|
|
||||||
return btns
|
return btns
|
||||||
.filter(btn => btn)
|
.filter(btn => btn)
|
||||||
.map(
|
.map(
|
||||||
(btn: ViewModels.NavbarButtonConfig, index: number): ICommandBarItemProps => {
|
(btn: CommandButtonComponentProps, index: number): ICommandBarItemProps => {
|
||||||
if (btn.isDivider) {
|
if (btn.isDivider) {
|
||||||
return CommandBarUtil.createDivider(btn.commandButtonLabel);
|
return CommandBarUtil.createDivider(btn.commandButtonLabel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import {
|
||||||
import { CommandButtonComponent } from "../../Controls/CommandButton/CommandButtonComponent";
|
CommandButtonComponent,
|
||||||
|
CommandButtonComponentProps
|
||||||
|
} from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
|
||||||
export interface ControlBarComponentProps {
|
export interface ControlBarComponentProps {
|
||||||
buttons: ViewModels.NavbarButtonConfig[];
|
buttons: CommandButtonComponentProps[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ControlBarComponent extends React.Component<ControlBarComponentProps> {
|
export class ControlBarComponent extends React.Component<ControlBarComponentProps> {
|
||||||
private static renderButtons(commandButtonOptions: ViewModels.NavbarButtonConfig[]): JSX.Element[] {
|
private static renderButtons(commandButtonOptions: CommandButtonComponentProps[]): JSX.Element[] {
|
||||||
return commandButtonOptions.map(
|
return commandButtonOptions.map(
|
||||||
(btn: ViewModels.NavbarButtonConfig, index: number): JSX.Element => {
|
(btn: CommandButtonComponentProps, index: number): JSX.Element => {
|
||||||
// Remove label
|
// Remove label
|
||||||
btn.commandButtonLabel = null;
|
btn.commandButtonLabel = null;
|
||||||
return CommandButtonComponent.renderButton(btn, `${index}`);
|
return CommandButtonComponent.renderButton(btn, `${index}`);
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import * as ko from "knockout";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||||
import { ControlBarComponent } from "./ControlBarComponent";
|
import { ControlBarComponent } from "./ControlBarComponent";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
|
||||||
export class ControlBarComponentAdapter implements ReactAdapter {
|
export class ControlBarComponentAdapter implements ReactAdapter {
|
||||||
public parameters: ko.Observable<number>;
|
public parameters: ko.Observable<number>;
|
||||||
|
|
||||||
constructor(private buttons: ko.ObservableArray<ViewModels.NavbarButtonConfig>) {
|
constructor(private buttons: ko.ObservableArray<CommandButtonComponentProps>) {
|
||||||
this.buttons.subscribe(() => this.forceRender());
|
this.buttons.subscribe(() => this.forceRender());
|
||||||
this.parameters = ko.observable<number>(Date.now());
|
this.parameters = ko.observable<number>(Date.now());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
|||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { NotificationConsoleComponent } from "./NotificationConsoleComponent";
|
import { NotificationConsoleComponent } from "./NotificationConsoleComponent";
|
||||||
import { ConsoleData } from "./NotificationConsoleComponent";
|
import { ConsoleData } from "./NotificationConsoleComponent";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
|
||||||
export class NotificationConsoleComponentAdapter implements ReactAdapter {
|
export class NotificationConsoleComponentAdapter implements ReactAdapter {
|
||||||
public parameters: ko.Observable<number>;
|
public parameters: ko.Observable<number>;
|
||||||
public container: ViewModels.Explorer;
|
public container: Explorer;
|
||||||
private consoleData: ko.ObservableArray<ConsoleData>;
|
private consoleData: ko.ObservableArray<ConsoleData>;
|
||||||
|
|
||||||
constructor(container: ViewModels.Explorer) {
|
constructor(container: Explorer) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
|
|
||||||
this.consoleData = container.notificationConsoleData;
|
this.consoleData = container.notificationConsoleData;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { StorageKey, LocalStorageUtility } from "../../Shared/StorageUtility";
|
|||||||
|
|
||||||
import CollectionIcon from "../../../images/tree-collection.svg";
|
import CollectionIcon from "../../../images/tree-collection.svg";
|
||||||
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
import NotebookIcon from "../../../images/notebook/Notebook-resource.svg";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
|
||||||
export enum Type {
|
export enum Type {
|
||||||
OpenCollection,
|
OpenCollection,
|
||||||
@@ -36,7 +37,7 @@ export class MostRecentActivity {
|
|||||||
private static readonly schemaVersion: string = "1";
|
private static readonly schemaVersion: string = "1";
|
||||||
private static itemsMaxNumber: number = 5;
|
private static itemsMaxNumber: number = 5;
|
||||||
private storedData: StoredData;
|
private storedData: StoredData;
|
||||||
constructor(private container: ViewModels.Explorer) {
|
constructor(private container: Explorer) {
|
||||||
// Retrieve from local storage
|
// Retrieve from local storage
|
||||||
if (LocalStorageUtility.hasItem(StorageKey.MostRecentActivity)) {
|
if (LocalStorageUtility.hasItem(StorageKey.MostRecentActivity)) {
|
||||||
const rawData = LocalStorageUtility.getEntryString(StorageKey.MostRecentActivity);
|
const rawData = LocalStorageUtility.getEntryString(StorageKey.MostRecentActivity);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { actions, createContentRef, createKernelRef, selectors } from "@nteract/
|
|||||||
import VirtualCommandBarComponent from "./VirtualCommandBarComponent";
|
import VirtualCommandBarComponent from "./VirtualCommandBarComponent";
|
||||||
import { NotebookContentItem } from "../NotebookContentItem";
|
import { NotebookContentItem } from "../NotebookContentItem";
|
||||||
import { NotebookComponentBootstrapper } from "./NotebookComponentBootstrapper";
|
import { NotebookComponentBootstrapper } from "./NotebookComponentBootstrapper";
|
||||||
|
import { CdbAppState } from "./types";
|
||||||
|
|
||||||
export interface NotebookComponentAdapterOptions {
|
export interface NotebookComponentAdapterOptions {
|
||||||
contentItem: NotebookContentItem;
|
contentItem: NotebookContentItem;
|
||||||
@@ -18,6 +19,7 @@ export interface NotebookComponentAdapterOptions {
|
|||||||
|
|
||||||
export class NotebookComponentAdapter extends NotebookComponentBootstrapper implements ReactAdapter {
|
export class NotebookComponentAdapter extends NotebookComponentBootstrapper implements ReactAdapter {
|
||||||
private onUpdateKernelInfo: () => void;
|
private onUpdateKernelInfo: () => void;
|
||||||
|
public getNotebookParentElement: () => HTMLElement;
|
||||||
public parameters: any;
|
public parameters: any;
|
||||||
|
|
||||||
constructor(options: NotebookComponentAdapterOptions) {
|
constructor(options: NotebookComponentAdapterOptions) {
|
||||||
@@ -44,6 +46,11 @@ export class NotebookComponentAdapter extends NotebookComponentBootstrapper impl
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.getNotebookParentElement = () => {
|
||||||
|
const cdbAppState = this.getStore().getState() as CdbAppState;
|
||||||
|
return cdbAppState.cdb.currentNotebookParentElements.get(this.contentRef);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderExtraComponent = (): JSX.Element => {
|
protected renderExtraComponent = (): JSX.Element => {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
} from "@nteract/core";
|
} from "@nteract/core";
|
||||||
import * as Immutable from "immutable";
|
import * as Immutable from "immutable";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { CellType, CellId, toJS } from "@nteract/commutable";
|
import { CellType, CellId, ImmutableNotebook } from "@nteract/commutable";
|
||||||
import { Store, AnyAction } from "redux";
|
import { Store, AnyAction } from "redux";
|
||||||
|
|
||||||
import "./NotebookComponent.less";
|
import "./NotebookComponent.less";
|
||||||
@@ -71,14 +71,14 @@ export class NotebookComponentBootstrapper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getContent(): { name: string; content: string } {
|
public getContent(): { name: string; content: string | ImmutableNotebook } {
|
||||||
const record = this.getStore()
|
const record = this.getStore()
|
||||||
.getState()
|
.getState()
|
||||||
.core.entities.contents.byRef.get(this.contentRef);
|
.core.entities.contents.byRef.get(this.contentRef);
|
||||||
let content: string;
|
let content: string | ImmutableNotebook;
|
||||||
switch (record.model.type) {
|
switch (record.model.type) {
|
||||||
case "notebook":
|
case "notebook":
|
||||||
content = JSON.stringify(toJS(record.model.notebook));
|
content = record.model.notebook;
|
||||||
break;
|
break;
|
||||||
case "file":
|
case "file":
|
||||||
content = record.model.text;
|
content = record.model.text;
|
||||||
|
|||||||
@@ -84,3 +84,22 @@ export const traceNotebookTelemetry = (payload: {
|
|||||||
payload
|
payload
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const UPDATE_NOTEBOOK_PARENT_DOM_ELTS = "UPDATE_NOTEBOOK_PARENT_DOM_ELTS";
|
||||||
|
export interface UpdateNotebookParentDomEltAction {
|
||||||
|
type: "UPDATE_NOTEBOOK_PARENT_DOM_ELTS";
|
||||||
|
payload: {
|
||||||
|
contentRef: ContentRef;
|
||||||
|
parentElt: HTMLElement;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UpdateNotebookParentDomElt = (payload: {
|
||||||
|
contentRef: ContentRef;
|
||||||
|
parentElt: HTMLElement;
|
||||||
|
}): UpdateNotebookParentDomEltAction => {
|
||||||
|
return {
|
||||||
|
type: UPDATE_NOTEBOOK_PARENT_DOM_ELTS,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import { sessions, kernels } from "rx-jupyter";
|
|||||||
import { RecordOf } from "immutable";
|
import { RecordOf } from "immutable";
|
||||||
|
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { NotificationConsoleUtils } from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import * as CdbActions from "./actions";
|
import * as CdbActions from "./actions";
|
||||||
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
|||||||
@@ -82,6 +82,19 @@ export const cdbReducer = (state: CdbRecord, action: Action) => {
|
|||||||
});
|
});
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case cdbActions.UPDATE_NOTEBOOK_PARENT_DOM_ELTS: {
|
||||||
|
const typedAction = action as cdbActions.UpdateNotebookParentDomEltAction;
|
||||||
|
var parentEltsMap = state.get("currentNotebookParentElements");
|
||||||
|
const contentRef = typedAction.payload.contentRef;
|
||||||
|
const parentElt = typedAction.payload.parentElt;
|
||||||
|
if (parentElt) {
|
||||||
|
parentEltsMap.set(contentRef, parentElt);
|
||||||
|
} else {
|
||||||
|
parentEltsMap.delete(contentRef);
|
||||||
|
}
|
||||||
|
return state.set("currentNotebookParentElements", parentEltsMap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as Immutable from "immutable";
|
import * as Immutable from "immutable";
|
||||||
import { AppState } from "@nteract/core";
|
import { AppState, ContentRef } from "@nteract/core";
|
||||||
|
|
||||||
import { Notebook } from "../../../Common/Constants";
|
import { Notebook } from "../../../Common/Constants";
|
||||||
import { CellId } from "@nteract/commutable";
|
import { CellId } from "@nteract/commutable";
|
||||||
@@ -9,6 +9,7 @@ export interface CdbRecordProps {
|
|||||||
defaultExperience: string | undefined;
|
defaultExperience: string | undefined;
|
||||||
kernelRestartDelayMs: number;
|
kernelRestartDelayMs: number;
|
||||||
hoveredCellId: CellId | undefined;
|
hoveredCellId: CellId | undefined;
|
||||||
|
currentNotebookParentElements: Map<ContentRef, HTMLElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CdbRecord = Immutable.RecordOf<CdbRecordProps>;
|
export type CdbRecord = Immutable.RecordOf<CdbRecordProps>;
|
||||||
@@ -21,5 +22,6 @@ export const makeCdbRecord = Immutable.Record<CdbRecordProps>({
|
|||||||
databaseAccountName: undefined,
|
databaseAccountName: undefined,
|
||||||
defaultExperience: undefined,
|
defaultExperience: undefined,
|
||||||
kernelRestartDelayMs: Notebook.kernelRestartInitialDelayMs,
|
kernelRestartDelayMs: Notebook.kernelRestartInitialDelayMs,
|
||||||
hoveredCellId: undefined
|
hoveredCellId: undefined,
|
||||||
|
currentNotebookParentElements: new Map<ContentRef, HTMLElement>()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
* Notebook container related stuff
|
* Notebook container related stuff
|
||||||
*/
|
*/
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as Logger from "../../Common/Logger";
|
import * as Logger from "../../Common/Logger";
|
||||||
|
|
||||||
export class NotebookContainerClient implements ViewModels.INotebookContainerClient {
|
export class NotebookContainerClient {
|
||||||
private reconnectingNotificationId: string;
|
private reconnectingNotificationId: string;
|
||||||
private isResettingWorkspace: boolean;
|
private isResettingWorkspace: boolean;
|
||||||
|
|
||||||
@@ -131,7 +130,7 @@ export class NotebookContainerClient implements ViewModels.INotebookContainerCli
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async recreateNotebookWorkspaceAsync(): Promise<void> {
|
private async recreateNotebookWorkspaceAsync(): Promise<void> {
|
||||||
const explorer = window.dataExplorer as ViewModels.Explorer;
|
const explorer = window.dataExplorer;
|
||||||
if (!explorer || !explorer.databaseAccount() || !explorer.databaseAccount().id) {
|
if (!explorer || !explorer.databaseAccount() || !explorer.databaseAccount().id) {
|
||||||
throw new Error("DataExplorer not initialized");
|
throw new Error("DataExplorer not initialized");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem";
|
import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem";
|
||||||
import { StringUtils } from "../../Utils/StringUtils";
|
import { StringUtils } from "../../Utils/StringUtils";
|
||||||
import { FileSystemUtil } from "./FileSystemUtil";
|
import { FileSystemUtil } from "./FileSystemUtil";
|
||||||
@@ -9,7 +8,7 @@ import { ServerConfig, IContent, IContentProvider, FileType, IEmptyContent } fro
|
|||||||
import { AjaxResponse } from "rxjs/ajax";
|
import { AjaxResponse } from "rxjs/ajax";
|
||||||
import { stringifyNotebook } from "@nteract/commutable";
|
import { stringifyNotebook } from "@nteract/commutable";
|
||||||
|
|
||||||
export class NotebookContentClient implements ViewModels.INotebookContentClient {
|
export class NotebookContentClient {
|
||||||
constructor(
|
constructor(
|
||||||
private notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>,
|
private notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>,
|
||||||
private notebookBasePath: ko.Observable<string>,
|
private notebookBasePath: ko.Observable<string>,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { JunoClient } from "../../Juno/JunoClient";
|
import { JunoClient } from "../../Juno/JunoClient";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService";
|
import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService";
|
||||||
import { GitHubClient } from "../../GitHub/GitHubClient";
|
import { GitHubClient } from "../../GitHub/GitHubClient";
|
||||||
import * as Logger from "../../Common/Logger";
|
import * as Logger from "../../Common/Logger";
|
||||||
@@ -23,9 +22,12 @@ import { DialogProps } from "../Controls/DialogReactComponent/DialogComponent";
|
|||||||
import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter";
|
import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter";
|
||||||
import { PublishNotebookPaneAdapter } from "../Panes/PublishNotebookPaneAdapter";
|
import { PublishNotebookPaneAdapter } from "../Panes/PublishNotebookPaneAdapter";
|
||||||
import { getFullName } from "../../Utils/UserUtils";
|
import { getFullName } from "../../Utils/UserUtils";
|
||||||
|
import { ImmutableNotebook } from "@nteract/commutable";
|
||||||
|
import Explorer from "../Explorer";
|
||||||
|
import { ContextualPaneBase } from "../Panes/ContextualPaneBase";
|
||||||
|
|
||||||
export interface NotebookManagerOptions {
|
export interface NotebookManagerOptions {
|
||||||
container: ViewModels.Explorer;
|
container: Explorer;
|
||||||
notebookBasePath: ko.Observable<string>;
|
notebookBasePath: ko.Observable<string>;
|
||||||
dialogProps: ko.Observable<DialogProps>;
|
dialogProps: ko.Observable<DialogProps>;
|
||||||
resourceTree: ResourceTreeAdapter;
|
resourceTree: ResourceTreeAdapter;
|
||||||
@@ -38,14 +40,14 @@ export default class NotebookManager {
|
|||||||
public junoClient: JunoClient;
|
public junoClient: JunoClient;
|
||||||
|
|
||||||
public notebookContentProvider: IContentProvider;
|
public notebookContentProvider: IContentProvider;
|
||||||
public notebookClient: ViewModels.INotebookContainerClient;
|
public notebookClient: NotebookContainerClient;
|
||||||
public notebookContentClient: ViewModels.INotebookContentClient;
|
public notebookContentClient: NotebookContentClient;
|
||||||
|
|
||||||
private gitHubContentProvider: GitHubContentProvider;
|
private gitHubContentProvider: GitHubContentProvider;
|
||||||
public gitHubOAuthService: GitHubOAuthService;
|
public gitHubOAuthService: GitHubOAuthService;
|
||||||
private gitHubClient: GitHubClient;
|
private gitHubClient: GitHubClient;
|
||||||
|
|
||||||
public gitHubReposPane: ViewModels.ContextualPane;
|
public gitHubReposPane: ContextualPaneBase;
|
||||||
public publishNotebookPaneAdapter: PublishNotebookPaneAdapter;
|
public publishNotebookPaneAdapter: PublishNotebookPaneAdapter;
|
||||||
|
|
||||||
public initialize(params: NotebookManagerOptions): void {
|
public initialize(params: NotebookManagerOptions): void {
|
||||||
@@ -55,7 +57,6 @@ export default class NotebookManager {
|
|||||||
this.gitHubOAuthService = new GitHubOAuthService(this.junoClient);
|
this.gitHubOAuthService = new GitHubOAuthService(this.junoClient);
|
||||||
this.gitHubClient = new GitHubClient(this.onGitHubClientError);
|
this.gitHubClient = new GitHubClient(this.onGitHubClientError);
|
||||||
this.gitHubReposPane = new GitHubReposPane({
|
this.gitHubReposPane = new GitHubReposPane({
|
||||||
documentClientUtility: this.params.container.documentClientUtility,
|
|
||||||
id: "gitHubReposPane",
|
id: "gitHubReposPane",
|
||||||
visible: ko.observable<boolean>(false),
|
visible: ko.observable<boolean>(false),
|
||||||
container: this.params.container,
|
container: this.params.container,
|
||||||
@@ -107,8 +108,12 @@ export default class NotebookManager {
|
|||||||
this.junoClient.getPinnedRepos(this.gitHubOAuthService.getTokenObservable()()?.scope);
|
this.junoClient.getPinnedRepos(this.gitHubOAuthService.getTokenObservable()()?.scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public openPublishNotebookPane(name: string, content: string): void {
|
public openPublishNotebookPane(
|
||||||
this.publishNotebookPaneAdapter.open(name, getFullName(), content);
|
name: string,
|
||||||
|
content: string | ImmutableNotebook,
|
||||||
|
parentDomElement: HTMLElement
|
||||||
|
): void {
|
||||||
|
this.publishNotebookPaneAdapter.open(name, getFullName(), content, parentDomElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Octokit's error handler uses any
|
// Octokit's error handler uses any
|
||||||
|
|||||||
@@ -49,8 +49,7 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
|||||||
editor: {
|
editor: {
|
||||||
codemirror: (props: PassedEditorProps) =>
|
codemirror: (props: PassedEditorProps) =>
|
||||||
this.props.hideInputs ? <></> : <CodeMirrorEditor {...props} readOnly={"nocursor"} />
|
this.props.hideInputs ? <></> : <CodeMirrorEditor {...props} readOnly={"nocursor"} />
|
||||||
},
|
}
|
||||||
prompt: ({ id, contentRef }) => <></>
|
|
||||||
}}
|
}}
|
||||||
</CodeCell>
|
</CodeCell>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -30,11 +30,18 @@ import { CellType } from "@nteract/commutable/src";
|
|||||||
import "./NotebookRenderer.less";
|
import "./NotebookRenderer.less";
|
||||||
import HoverableCell from "./decorators/HoverableCell";
|
import HoverableCell from "./decorators/HoverableCell";
|
||||||
import CellLabeler from "./decorators/CellLabeler";
|
import CellLabeler from "./decorators/CellLabeler";
|
||||||
|
import * as cdbActions from "../NotebookComponent/actions";
|
||||||
|
|
||||||
export interface NotebookRendererProps {
|
export interface NotebookRendererBaseProps {
|
||||||
contentRef: any;
|
contentRef: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface NotebookRendererDispatchProps {
|
||||||
|
updateNotebookParentDomElt: (contentRef: ContentRef, parentElt: HTMLElement) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotebookRendererProps = NotebookRendererBaseProps & NotebookRendererDispatchProps;
|
||||||
|
|
||||||
interface PassedEditorProps {
|
interface PassedEditorProps {
|
||||||
id: string;
|
id: string;
|
||||||
contentRef: ContentRef;
|
contentRef: ContentRef;
|
||||||
@@ -68,6 +75,8 @@ const decorate = (id: string, contentRef: ContentRef, cell_type: CellType, child
|
|||||||
};
|
};
|
||||||
|
|
||||||
class BaseNotebookRenderer extends React.Component<NotebookRendererProps> {
|
class BaseNotebookRenderer extends React.Component<NotebookRendererProps> {
|
||||||
|
private notebookRendererRef = React.createRef<HTMLDivElement>();
|
||||||
|
|
||||||
constructor(props: NotebookRendererProps) {
|
constructor(props: NotebookRendererProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@@ -78,13 +87,22 @@ class BaseNotebookRenderer extends React.Component<NotebookRendererProps> {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
loadTransform(this.props as any);
|
loadTransform(this.props as any);
|
||||||
|
this.props.updateNotebookParentDomElt(this.props.contentRef, this.notebookRendererRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.props.updateNotebookParentDomElt(this.props.contentRef, this.notebookRendererRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.props.updateNotebookParentDomElt(this.props.contentRef, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="NotebookRendererContainer">
|
<div className="NotebookRendererContainer">
|
||||||
<div className="NotebookRenderer">
|
<div className="NotebookRenderer" ref={this.notebookRendererRef}>
|
||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
<KeyboardShortcuts contentRef={this.props.contentRef}>
|
<KeyboardShortcuts contentRef={this.props.contentRef}>
|
||||||
<Cells contentRef={this.props.contentRef}>
|
<Cells contentRef={this.props.contentRef}>
|
||||||
@@ -146,7 +164,7 @@ class BaseNotebookRenderer extends React.Component<NotebookRendererProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeMapDispatchToProps = (initialDispatch: Dispatch, initialProps: NotebookRendererProps) => {
|
const makeMapDispatchToProps = (initialDispatch: Dispatch, initialProps: NotebookRendererBaseProps) => {
|
||||||
const mapDispatchToProps = (dispatch: Dispatch) => {
|
const mapDispatchToProps = (dispatch: Dispatch) => {
|
||||||
return {
|
return {
|
||||||
addTransform: (transform: React.ComponentType & { MIMETYPE: string }) => {
|
addTransform: (transform: React.ComponentType & { MIMETYPE: string }) => {
|
||||||
@@ -156,6 +174,14 @@ const makeMapDispatchToProps = (initialDispatch: Dispatch, initialProps: Noteboo
|
|||||||
component: transform
|
component: transform
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
updateNotebookParentDomElt: (contentRef: ContentRef, parentElt: HTMLElement) => {
|
||||||
|
return dispatch(
|
||||||
|
cdbActions.UpdateNotebookParentDomElt({
|
||||||
|
contentRef,
|
||||||
|
parentElt
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
import { NotebookUtil } from "./NotebookUtil";
|
import { NotebookUtil } from "./NotebookUtil";
|
||||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||||
|
import {
|
||||||
|
ImmutableNotebook,
|
||||||
|
MediaBundle,
|
||||||
|
CodeCellParams,
|
||||||
|
MarkdownCellParams,
|
||||||
|
makeCodeCell,
|
||||||
|
makeMarkdownCell,
|
||||||
|
makeNotebookRecord
|
||||||
|
} from "@nteract/commutable";
|
||||||
|
import { List, Map } from "immutable";
|
||||||
|
|
||||||
const fileName = "file";
|
const fileName = "file";
|
||||||
const notebookName = "file.ipynb";
|
const notebookName = "file.ipynb";
|
||||||
@@ -7,6 +17,57 @@ const filePath = `folder/${fileName}`;
|
|||||||
const notebookPath = `folder/${notebookName}`;
|
const notebookPath = `folder/${notebookName}`;
|
||||||
const gitHubFileUri = GitHubUtils.toContentUri("owner", "repo", "branch", filePath);
|
const gitHubFileUri = GitHubUtils.toContentUri("owner", "repo", "branch", filePath);
|
||||||
const gitHubNotebookUri = GitHubUtils.toContentUri("owner", "repo", "branch", notebookPath);
|
const gitHubNotebookUri = GitHubUtils.toContentUri("owner", "repo", "branch", notebookPath);
|
||||||
|
const notebookRecord = makeNotebookRecord({
|
||||||
|
cellOrder: List.of("0", "1", "2", "3"),
|
||||||
|
cellMap: Map({
|
||||||
|
"0": makeMarkdownCell({
|
||||||
|
cell_type: "markdown",
|
||||||
|
source: "abc",
|
||||||
|
metadata: undefined
|
||||||
|
} as MarkdownCellParams),
|
||||||
|
"1": makeCodeCell({
|
||||||
|
cell_type: "code",
|
||||||
|
execution_count: undefined,
|
||||||
|
metadata: undefined,
|
||||||
|
source: "print(5)",
|
||||||
|
outputs: List.of({
|
||||||
|
name: "stdout",
|
||||||
|
output_type: "stream",
|
||||||
|
text: "5"
|
||||||
|
})
|
||||||
|
} as CodeCellParams),
|
||||||
|
"2": makeCodeCell({
|
||||||
|
cell_type: "code",
|
||||||
|
execution_count: undefined,
|
||||||
|
metadata: undefined,
|
||||||
|
source: 'display(HTML("<h1>Sample html</h1>"))',
|
||||||
|
outputs: List.of({
|
||||||
|
data: Object.freeze({
|
||||||
|
data: {
|
||||||
|
"text/html": "<h1>Sample output</h1>",
|
||||||
|
"text/plain": "<IPython.core.display.HTML object>"
|
||||||
|
}
|
||||||
|
} as MediaBundle),
|
||||||
|
output_type: "display_data",
|
||||||
|
metadata: undefined
|
||||||
|
})
|
||||||
|
} as CodeCellParams),
|
||||||
|
"3": makeCodeCell({
|
||||||
|
cell_type: "code",
|
||||||
|
execution_count: undefined,
|
||||||
|
metadata: undefined,
|
||||||
|
source: 'print("hello world")',
|
||||||
|
outputs: List.of({
|
||||||
|
name: "stdout",
|
||||||
|
output_type: "stream",
|
||||||
|
text: "hello world"
|
||||||
|
})
|
||||||
|
} as CodeCellParams)
|
||||||
|
}),
|
||||||
|
nbformat_minor: 2,
|
||||||
|
nbformat: 2,
|
||||||
|
metadata: undefined
|
||||||
|
});
|
||||||
|
|
||||||
describe("NotebookUtil", () => {
|
describe("NotebookUtil", () => {
|
||||||
describe("isNotebookFile", () => {
|
describe("isNotebookFile", () => {
|
||||||
@@ -46,4 +107,11 @@ describe("NotebookUtil", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("findFirstCodeCellWithDisplay", () => {
|
||||||
|
it("works for Notebook file", () => {
|
||||||
|
const notebookObject = notebookRecord as ImmutableNotebook;
|
||||||
|
expect(NotebookUtil.findFirstCodeCellWithDisplay(notebookObject)).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { ImmutableNotebook } from "@nteract/commutable";
|
import { ImmutableNotebook, ImmutableCodeCell, ImmutableOutput } from "@nteract/commutable";
|
||||||
import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem";
|
import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem";
|
||||||
import { StringUtils } from "../../Utils/StringUtils";
|
import { StringUtils } from "../../Utils/StringUtils";
|
||||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||||
@@ -100,4 +100,30 @@ export class NotebookUtil {
|
|||||||
const basePath = path.split(contentName).shift();
|
const basePath = path.split(contentName).shift();
|
||||||
return `${basePath}${newName}`;
|
return `${basePath}${newName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static findFirstCodeCellWithDisplay(notebookObject: ImmutableNotebook): number {
|
||||||
|
let codeCellCount = -1;
|
||||||
|
for (let i = 0; i < notebookObject.cellOrder.size; i++) {
|
||||||
|
const cellId = notebookObject.cellOrder.get(i);
|
||||||
|
if (cellId) {
|
||||||
|
const cell = notebookObject.cellMap.get(cellId);
|
||||||
|
if (cell && cell.cell_type === "code") {
|
||||||
|
codeCellCount++;
|
||||||
|
const codeCell = cell as ImmutableCodeCell;
|
||||||
|
if (codeCell.outputs) {
|
||||||
|
const displayOutput = codeCell.outputs.find((output: ImmutableOutput) => {
|
||||||
|
if (output.output_type === "display_data" || output.output_type === "execute_result") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (displayOutput) {
|
||||||
|
return codeCellCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("Output does not exist for any of the cells.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,43 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import { handleOpenAction } from "./OpenActions";
|
import { handleOpenAction } from "./OpenActions";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import {
|
|
||||||
ExplorerStub,
|
|
||||||
DatabaseStub,
|
|
||||||
CollectionStub,
|
|
||||||
AddCollectionPaneStub,
|
|
||||||
CassandraAddCollectionPane
|
|
||||||
} from "./OpenActionsStubs";
|
|
||||||
import { ActionContracts } from "../Contracts/ExplorerContracts";
|
import { ActionContracts } from "../Contracts/ExplorerContracts";
|
||||||
|
import Explorer from "./Explorer";
|
||||||
|
import CassandraAddCollectionPane from "./Panes/CassandraAddCollectionPane";
|
||||||
|
import AddCollectionPane from "./Panes/AddCollectionPane";
|
||||||
|
|
||||||
describe("OpenActions", () => {
|
describe("OpenActions", () => {
|
||||||
describe("handleOpenAction", () => {
|
describe("handleOpenAction", () => {
|
||||||
let explorer: ViewModels.Explorer;
|
let explorer: Explorer;
|
||||||
let database: ViewModels.Database;
|
let database: ViewModels.Database;
|
||||||
let collection: ViewModels.Collection;
|
let collection: ViewModels.Collection;
|
||||||
let databases: ViewModels.Database[];
|
let databases: ViewModels.Database[];
|
||||||
|
|
||||||
let expandCollection: jasmine.Spy;
|
|
||||||
let onDocumentDBDocumentsClick: jasmine.Spy;
|
|
||||||
let onMongoDBDocumentsClick: jasmine.Spy;
|
|
||||||
let onTableEntitiesClick: jasmine.Spy;
|
|
||||||
let onGraphDocumentsClick: jasmine.Spy;
|
|
||||||
let onNewQueryClick: jasmine.Spy;
|
|
||||||
let onSettingsClick: jasmine.Spy;
|
|
||||||
let openAddCollectionPane: jasmine.Spy;
|
|
||||||
let openCassandraAddCollectionPane: jasmine.Spy;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
explorer = new ExplorerStub();
|
explorer = {} as Explorer;
|
||||||
explorer.addCollectionPane = new AddCollectionPaneStub();
|
explorer.addCollectionPane = {} as AddCollectionPane;
|
||||||
explorer.cassandraAddCollectionPane = new CassandraAddCollectionPane();
|
explorer.addCollectionPane.open = jest.fn();
|
||||||
|
explorer.cassandraAddCollectionPane = {} as CassandraAddCollectionPane;
|
||||||
|
explorer.cassandraAddCollectionPane.open = jest.fn();
|
||||||
|
explorer.closeAllPanes = () => {};
|
||||||
|
explorer.isConnectExplorerVisible = () => false;
|
||||||
|
|
||||||
database = new DatabaseStub({
|
database = {
|
||||||
id: ko.observable("db"),
|
id: ko.observable("db"),
|
||||||
collections: ko.observableArray<ViewModels.Collection>([])
|
collections: ko.observableArray<ViewModels.Collection>([])
|
||||||
});
|
} as ViewModels.Database;
|
||||||
databases = [database];
|
databases = [database];
|
||||||
collection = new CollectionStub({
|
collection = {
|
||||||
id: ko.observable("coll")
|
id: ko.observable("coll")
|
||||||
});
|
} as ViewModels.Collection;
|
||||||
|
|
||||||
expandCollection = spyOn(collection, "expandCollection");
|
collection.expandCollection = jest.fn();
|
||||||
onDocumentDBDocumentsClick = spyOn(collection, "onDocumentDBDocumentsClick");
|
collection.onDocumentDBDocumentsClick = jest.fn();
|
||||||
onMongoDBDocumentsClick = spyOn(collection, "onMongoDBDocumentsClick");
|
collection.onMongoDBDocumentsClick = jest.fn();
|
||||||
onTableEntitiesClick = spyOn(collection, "onTableEntitiesClick");
|
collection.onTableEntitiesClick = jest.fn();
|
||||||
onGraphDocumentsClick = spyOn(collection, "onGraphDocumentsClick");
|
collection.onGraphDocumentsClick = jest.fn();
|
||||||
onNewQueryClick = spyOn(collection, "onNewQueryClick");
|
collection.onNewQueryClick = jest.fn();
|
||||||
onSettingsClick = spyOn(collection, "onSettingsClick");
|
collection.onSettingsClick = jest.fn();
|
||||||
openAddCollectionPane = spyOn(explorer.addCollectionPane, "open");
|
|
||||||
openCassandraAddCollectionPane = spyOn(explorer.cassandraAddCollectionPane, "open");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("unknown action type", () => {
|
describe("unknown action type", () => {
|
||||||
@@ -87,7 +75,7 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const actionHandled = handleOpenAction(action, [], explorer);
|
const actionHandled = handleOpenAction(action, [], explorer);
|
||||||
expect(openCassandraAddCollectionPane).toHaveBeenCalled();
|
expect(explorer.cassandraAddCollectionPane.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call cassandraAddCollectionPane.open", () => {
|
it("enum value should call cassandraAddCollectionPane.open", () => {
|
||||||
@@ -97,7 +85,7 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const actionHandled = handleOpenAction(action, [], explorer);
|
const actionHandled = handleOpenAction(action, [], explorer);
|
||||||
expect(openCassandraAddCollectionPane).toHaveBeenCalled();
|
expect(explorer.cassandraAddCollectionPane.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -109,7 +97,7 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const actionHandled = handleOpenAction(action, [], explorer);
|
const actionHandled = handleOpenAction(action, [], explorer);
|
||||||
expect(openAddCollectionPane).toHaveBeenCalled();
|
expect(explorer.addCollectionPane.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call addCollectionPane.open", () => {
|
it("enum value should call addCollectionPane.open", () => {
|
||||||
@@ -119,7 +107,7 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const actionHandled = handleOpenAction(action, [], explorer);
|
const actionHandled = handleOpenAction(action, [], explorer);
|
||||||
expect(openAddCollectionPane).toHaveBeenCalled();
|
expect(explorer.addCollectionPane.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -149,10 +137,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(expandCollection).not.toHaveBeenCalled();
|
expect(collection.expandCollection).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(expandCollection).toHaveBeenCalled();
|
expect(collection.expandCollection).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should expand collection node when handleOpenAction is called", () => {
|
it("should expand collection node when handleOpenAction is called", () => {
|
||||||
@@ -164,7 +152,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(expandCollection).toHaveBeenCalled();
|
expect(collection.expandCollection).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("SQLDocuments tab kind", () => {
|
describe("SQLDocuments tab kind", () => {
|
||||||
@@ -177,10 +165,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onDocumentDBDocumentsClick).not.toHaveBeenCalled();
|
expect(collection.onDocumentDBDocumentsClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onDocumentDBDocumentsClick).toHaveBeenCalled();
|
expect(collection.onDocumentDBDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("string value should call onDocumentDBDocumentsClick", () => {
|
it("string value should call onDocumentDBDocumentsClick", () => {
|
||||||
@@ -193,7 +181,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onDocumentDBDocumentsClick).toHaveBeenCalled();
|
expect(collection.onDocumentDBDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onDocumentDBDocumentsClick before collections are fetched", () => {
|
it("enum value should call onDocumentDBDocumentsClick before collections are fetched", () => {
|
||||||
@@ -205,10 +193,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onDocumentDBDocumentsClick).not.toHaveBeenCalled();
|
expect(collection.onDocumentDBDocumentsClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onDocumentDBDocumentsClick).toHaveBeenCalled();
|
expect(collection.onDocumentDBDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onDocumentDBDocumentsClick", () => {
|
it("enum value should call onDocumentDBDocumentsClick", () => {
|
||||||
@@ -221,7 +209,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onDocumentDBDocumentsClick).toHaveBeenCalled();
|
expect(collection.onDocumentDBDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -235,10 +223,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onMongoDBDocumentsClick).not.toHaveBeenCalled();
|
expect(collection.onMongoDBDocumentsClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onMongoDBDocumentsClick).toHaveBeenCalled();
|
expect(collection.onMongoDBDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("string value should call onMongoDBDocumentsClick", () => {
|
it("string value should call onMongoDBDocumentsClick", () => {
|
||||||
@@ -251,7 +239,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onMongoDBDocumentsClick).toHaveBeenCalled();
|
expect(collection.onMongoDBDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onMongoDBDocumentsClick before collections are fetched", () => {
|
it("enum value should call onMongoDBDocumentsClick before collections are fetched", () => {
|
||||||
@@ -263,10 +251,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onMongoDBDocumentsClick).not.toHaveBeenCalled();
|
expect(collection.onMongoDBDocumentsClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onMongoDBDocumentsClick).toHaveBeenCalled();
|
expect(collection.onMongoDBDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onMongoDBDocumentsClick", () => {
|
it("enum value should call onMongoDBDocumentsClick", () => {
|
||||||
@@ -279,7 +267,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onMongoDBDocumentsClick).toHaveBeenCalled();
|
expect(collection.onMongoDBDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -293,10 +281,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onTableEntitiesClick).not.toHaveBeenCalled();
|
expect(collection.onTableEntitiesClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onTableEntitiesClick).toHaveBeenCalled();
|
expect(collection.onTableEntitiesClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("string value should call onTableEntitiesClick", () => {
|
it("string value should call onTableEntitiesClick", () => {
|
||||||
@@ -309,7 +297,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onTableEntitiesClick).toHaveBeenCalled();
|
expect(collection.onTableEntitiesClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onTableEntitiesClick before collections are fetched", () => {
|
it("enum value should call onTableEntitiesClick before collections are fetched", () => {
|
||||||
@@ -322,7 +310,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onTableEntitiesClick).toHaveBeenCalled();
|
expect(collection.onTableEntitiesClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onTableEntitiesClick", () => {
|
it("enum value should call onTableEntitiesClick", () => {
|
||||||
@@ -334,10 +322,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onTableEntitiesClick).not.toHaveBeenCalled();
|
expect(collection.onTableEntitiesClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onTableEntitiesClick).toHaveBeenCalled();
|
expect(collection.onTableEntitiesClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -351,10 +339,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onGraphDocumentsClick).not.toHaveBeenCalled();
|
expect(collection.onGraphDocumentsClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onGraphDocumentsClick).toHaveBeenCalled();
|
expect(collection.onGraphDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("string value should call onGraphDocumentsClick", () => {
|
it("string value should call onGraphDocumentsClick", () => {
|
||||||
@@ -367,7 +355,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onGraphDocumentsClick).toHaveBeenCalled();
|
expect(collection.onGraphDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onGraphDocumentsClick before collections are fetched", () => {
|
it("enum value should call onGraphDocumentsClick before collections are fetched", () => {
|
||||||
@@ -379,10 +367,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onGraphDocumentsClick).not.toHaveBeenCalled();
|
expect(collection.onGraphDocumentsClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onGraphDocumentsClick).toHaveBeenCalled();
|
expect(collection.onGraphDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onGraphDocumentsClick", () => {
|
it("enum value should call onGraphDocumentsClick", () => {
|
||||||
@@ -395,7 +383,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onGraphDocumentsClick).toHaveBeenCalled();
|
expect(collection.onGraphDocumentsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -409,10 +397,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onNewQueryClick).not.toHaveBeenCalled();
|
expect(collection.onNewQueryClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onNewQueryClick).toHaveBeenCalled();
|
expect(collection.onNewQueryClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("string value should call onNewQueryClick", () => {
|
it("string value should call onNewQueryClick", () => {
|
||||||
@@ -425,7 +413,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onNewQueryClick).toHaveBeenCalled();
|
expect(collection.onNewQueryClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onNewQueryClick before collections are fetched", () => {
|
it("enum value should call onNewQueryClick before collections are fetched", () => {
|
||||||
@@ -437,10 +425,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onNewQueryClick).not.toHaveBeenCalled();
|
expect(collection.onNewQueryClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onNewQueryClick).toHaveBeenCalled();
|
expect(collection.onNewQueryClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onNewQueryClick", () => {
|
it("enum value should call onNewQueryClick", () => {
|
||||||
@@ -453,7 +441,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onNewQueryClick).toHaveBeenCalled();
|
expect(collection.onNewQueryClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -467,10 +455,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onSettingsClick).not.toHaveBeenCalled();
|
expect(collection.onSettingsClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onSettingsClick).toHaveBeenCalled();
|
expect(collection.onSettingsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("string value should call onSettingsClick", () => {
|
it("string value should call onSettingsClick", () => {
|
||||||
@@ -483,7 +471,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onSettingsClick).toHaveBeenCalled();
|
expect(collection.onSettingsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onSettingsClick before collections are fetched", () => {
|
it("enum value should call onSettingsClick before collections are fetched", () => {
|
||||||
@@ -495,10 +483,10 @@ describe("OpenActions", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onSettingsClick).not.toHaveBeenCalled();
|
expect(collection.onSettingsClick).not.toHaveBeenCalled();
|
||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
expect(onSettingsClick).toHaveBeenCalled();
|
expect(collection.onSettingsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("enum value should call onSettingsClick", () => {
|
it("enum value should call onSettingsClick", () => {
|
||||||
@@ -511,7 +499,7 @@ describe("OpenActions", () => {
|
|||||||
|
|
||||||
database.collections([collection]);
|
database.collections([collection]);
|
||||||
handleOpenAction(action, [database], explorer);
|
handleOpenAction(action, [database], explorer);
|
||||||
expect(onSettingsClick).toHaveBeenCalled();
|
expect(collection.onSettingsClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import { ActionContracts } from "../Contracts/ExplorerContracts";
|
import { ActionContracts } from "../Contracts/ExplorerContracts";
|
||||||
|
import Explorer from "./Explorer";
|
||||||
|
|
||||||
export function handleOpenAction(
|
export function handleOpenAction(
|
||||||
action: ActionContracts.DataExplorerAction,
|
action: ActionContracts.DataExplorerAction,
|
||||||
databases: ViewModels.Database[],
|
databases: ViewModels.Database[],
|
||||||
explorer: ViewModels.Explorer
|
explorer: Explorer
|
||||||
): boolean {
|
): boolean {
|
||||||
if (
|
if (
|
||||||
action.actionType === ActionContracts.ActionType.OpenCollectionTab ||
|
action.actionType === ActionContracts.ActionType.OpenCollectionTab ||
|
||||||
@@ -126,7 +127,7 @@ function openCollectionTab(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openPane(action: ActionContracts.OpenPane, explorer: ViewModels.Explorer) {
|
function openPane(action: ActionContracts.OpenPane, explorer: Explorer) {
|
||||||
if (
|
if (
|
||||||
action.paneKind === ActionContracts.PaneKind.AddCollection ||
|
action.paneKind === ActionContracts.PaneKind.AddCollection ||
|
||||||
(<any>action).paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.AddCollection]
|
(<any>action).paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.AddCollection]
|
||||||
@@ -154,7 +155,7 @@ function openPane(action: ActionContracts.OpenPane, explorer: ViewModels.Explore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openFile(action: ActionContracts.OpenSampleNotebook, explorer: ViewModels.Explorer) {
|
function openFile(action: ActionContracts.OpenSampleNotebook, explorer: Explorer) {
|
||||||
explorer.handleOpenFileAction(decodeURIComponent(action.path));
|
explorer.handleOpenFileAction(decodeURIComponent(action.path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user