mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-23 10:51:30 +00:00
Compare commits
1 Commits
replace-co
...
upgrade-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1636f20978 |
@@ -1,6 +1,5 @@
|
|||||||
**/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
|
||||||
@@ -138,6 +137,7 @@ 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,9 @@ 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/ManageSparkClusterPane.ts
|
||||||
src/Explorer/Panes/NewVertexPane.ts
|
src/Explorer/Panes/NewVertexPane.ts
|
||||||
src/Explorer/Panes/PaneComponents.ts
|
src/Explorer/Panes/PaneComponents.ts
|
||||||
src/Explorer/Panes/RenewAdHocAccessPane.ts
|
src/Explorer/Panes/RenewAdHocAccessPane.ts
|
||||||
@@ -153,6 +155,7 @@ src/Explorer/Panes/SaveQueryPane.ts
|
|||||||
src/Explorer/Panes/SettingsPane.test.ts
|
src/Explorer/Panes/SettingsPane.test.ts
|
||||||
src/Explorer/Panes/SettingsPane.ts
|
src/Explorer/Panes/SettingsPane.ts
|
||||||
src/Explorer/Panes/SetupNotebooksPane.ts
|
src/Explorer/Panes/SetupNotebooksPane.ts
|
||||||
|
src/Explorer/Panes/SetupSparkClusterPane.ts
|
||||||
src/Explorer/Panes/StringInputPane.ts
|
src/Explorer/Panes/StringInputPane.ts
|
||||||
src/Explorer/Panes/SwitchDirectoryPane.ts
|
src/Explorer/Panes/SwitchDirectoryPane.ts
|
||||||
src/Explorer/Panes/Tables/AddTableEntityPane.ts
|
src/Explorer/Panes/Tables/AddTableEntityPane.ts
|
||||||
@@ -270,6 +273,7 @@ src/Shared/AddCollectionUtility.test.ts
|
|||||||
src/Shared/AddCollectionUtility.ts
|
src/Shared/AddCollectionUtility.ts
|
||||||
src/Shared/AddDatabaseUtility.test.ts
|
src/Shared/AddDatabaseUtility.test.ts
|
||||||
src/Shared/AddDatabaseUtility.ts
|
src/Shared/AddDatabaseUtility.ts
|
||||||
|
src/Shared/Ajax.ts
|
||||||
src/Shared/Constants.ts
|
src/Shared/Constants.ts
|
||||||
src/Shared/DefaultExperienceUtility.test.ts
|
src/Shared/DefaultExperienceUtility.test.ts
|
||||||
src/Shared/DefaultExperienceUtility.ts
|
src/Shared/DefaultExperienceUtility.ts
|
||||||
@@ -298,9 +302,11 @@ 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
|
||||||
@@ -328,6 +334,10 @@ 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
|
||||||
@@ -401,6 +411,7 @@ src/Explorer/Tabs/NotebookViewerTab.tsx
|
|||||||
src/Explorer/Tabs/TerminalTab.tsx
|
src/Explorer/Tabs/TerminalTab.tsx
|
||||||
src/Explorer/Tree/ResourceTreeAdapter.tsx
|
src/Explorer/Tree/ResourceTreeAdapter.tsx
|
||||||
src/Explorer/Tree/ResourceTreeAdapterForResourceToken.tsx
|
src/Explorer/Tree/ResourceTreeAdapterForResourceToken.tsx
|
||||||
|
src/GalleryViewer/Cards/CardStyleConstants.tsx
|
||||||
src/GalleryViewer/Cards/GalleryCardComponent.tsx
|
src/GalleryViewer/Cards/GalleryCardComponent.tsx
|
||||||
src/GalleryViewer/GalleryViewer.tsx
|
src/GalleryViewer/GalleryViewer.tsx
|
||||||
src/GalleryViewer/GalleryViewerComponent.tsx
|
src/GalleryViewer/GalleryViewerComponent.tsx
|
||||||
|
|||||||
25
.eslintrc.js
25
.eslintrc.js
@@ -1,47 +1,44 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es6: true
|
es6: true,
|
||||||
},
|
},
|
||||||
plugins: ["@typescript-eslint", "no-null", "prefer-arrow"],
|
plugins: ["@typescript-eslint", "no-null"],
|
||||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||||
globals: {
|
globals: {
|
||||||
Atomics: "readonly",
|
Atomics: "readonly",
|
||||||
SharedArrayBuffer: "readonly"
|
SharedArrayBuffer: "readonly",
|
||||||
},
|
},
|
||||||
parser: "@typescript-eslint/parser",
|
parser: "@typescript-eslint/parser",
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
jsx: true
|
jsx: true,
|
||||||
},
|
},
|
||||||
ecmaVersion: 2018,
|
ecmaVersion: 2018,
|
||||||
sourceType: "module"
|
sourceType: "module",
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ["**/*.tsx"],
|
files: ["**/*.tsx"],
|
||||||
env: {
|
env: {
|
||||||
jest: true
|
jest: true,
|
||||||
},
|
},
|
||||||
extends: ["plugin:react/recommended"],
|
extends: ["plugin:react/recommended"],
|
||||||
plugins: ["react"]
|
plugins: ["react"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: ["**/*.test.{ts,tsx}"],
|
files: ["**/*.test.{ts,tsx}"],
|
||||||
env: {
|
env: {
|
||||||
jest: true
|
jest: true,
|
||||||
},
|
},
|
||||||
extends: ["plugin:jest/recommended"],
|
extends: ["plugin:jest/recommended"],
|
||||||
plugins: ["jest"]
|
plugins: ["jest"],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
curly: "error",
|
curly: "error",
|
||||||
"@typescript-eslint/no-unused-vars": "error",
|
"@typescript-eslint/no-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 }],
|
|
||||||
eqeqeq: "error"
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -1 +1 @@
|
|||||||
* @Azure/cosmos-explorer-owners @Azure/azure-cosmos-explorer-developers
|
* @Azure/cosmos-explorer-owners
|
||||||
|
|||||||
49
.github/workflows/ci.yml
vendored
49
.github/workflows/ci.yml
vendored
@@ -1,13 +1,9 @@
|
|||||||
name: CI
|
name: CI
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [master]
|
||||||
- master
|
|
||||||
- hotfix/*
|
|
||||||
- release/*
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches: [master]
|
||||||
- master
|
|
||||||
jobs:
|
jobs:
|
||||||
compile:
|
compile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -56,7 +52,6 @@ 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
|
||||||
@@ -80,7 +75,6 @@ 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
|
||||||
@@ -107,7 +101,6 @@ 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
|
||||||
@@ -134,14 +127,8 @@ jobs:
|
|||||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||||
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
||||||
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
|
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
name: videos
|
|
||||||
if: ${{ failure() }}
|
|
||||||
with:
|
|
||||||
path: "**/*.mp4"
|
|
||||||
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
|
||||||
@@ -168,37 +155,8 @@ jobs:
|
|||||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||||
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
|
||||||
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
|
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
if: ${{ failure() }}
|
|
||||||
name: videos
|
|
||||||
with:
|
|
||||||
path: "**/*.mp4"
|
|
||||||
accessibility:
|
|
||||||
name: "Accessibility | Hosted"
|
|
||||||
needs: [lint, format, compile, unittest]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Use Node.js 12.x
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 12.x
|
|
||||||
- name: Accessibility Check
|
|
||||||
run: |
|
|
||||||
# Ubuntu gets mad when webpack runs too many files watchers
|
|
||||||
cat /proc/sys/fs/inotify/max_user_watches
|
|
||||||
sudo sysctl fs.inotify.max_user_watches=524288
|
|
||||||
sudo sysctl -p
|
|
||||||
npm ci
|
|
||||||
npm start &
|
|
||||||
npx wait-on -i 5000 https-get://0.0.0.0:1234/
|
|
||||||
node utils/accesibilityCheck.js
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
|
||||||
nuget:
|
nuget:
|
||||||
name: Publish Nuget
|
name: Publish Nuget
|
||||||
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
|
||||||
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
@@ -217,12 +175,10 @@ jobs:
|
|||||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||||
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
name: packages
|
|
||||||
with:
|
with:
|
||||||
path: "*.nupkg"
|
path: "*.nupkg"
|
||||||
nugetmpac:
|
nugetmpac:
|
||||||
name: Publish Nuget MPAC
|
name: Publish Nuget MPAC
|
||||||
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
|
||||||
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
@@ -242,6 +198,5 @@ jobs:
|
|||||||
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
- run: nuget pack -Version "2.0.0-github-${GITHUB_SHA}"
|
||||||
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
- run: nuget push -Source "$NUGET_SOURCE" -ApiKey Az *.nupkg
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
name: packages
|
|
||||||
with:
|
with:
|
||||||
path: "*.nupkg"
|
path: "*.nupkg"
|
||||||
|
|||||||
7
.github/workflows/runners.yml
vendored
7
.github/workflows/runners.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
name: Runners
|
name: Runners
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 * 1 * *"
|
- cron: "*/10 * * * *"
|
||||||
jobs:
|
jobs:
|
||||||
sqlcreatecollection:
|
sqlcreatecollection:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -18,8 +18,3 @@ jobs:
|
|||||||
PORTAL_RUNNER_SUBSCRIPTION: 69e02f2d-f059-4409-9eac-97e8a276ae2c
|
PORTAL_RUNNER_SUBSCRIPTION: 69e02f2d-f059-4409-9eac-97e8a276ae2c
|
||||||
PORTAL_RUNNER_RESOURCE_GROUP: runners
|
PORTAL_RUNNER_RESOURCE_GROUP: runners
|
||||||
PORTAL_RUNNER_DATABASE_ACCOUNT: portal-sql-runner
|
PORTAL_RUNNER_DATABASE_ACCOUNT: portal-sql-runner
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
if: failure()
|
|
||||||
with:
|
|
||||||
name: screenshots
|
|
||||||
path: failure.png
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,5 +16,4 @@ notebookapp/*
|
|||||||
Contracts/*
|
Contracts/*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.cache/
|
.cache/
|
||||||
.env
|
.env
|
||||||
failure.png
|
|
||||||
@@ -30,13 +30,12 @@ For IE support, polyfill is preferred over new usage of lodash or underscore. We
|
|||||||
### Typescript
|
### Typescript
|
||||||
* Follow this [typescript style guide](https://github.com/excelmicro/typescript) which is based on [airbnb's style guide](https://github.com/airbnb/javascript).
|
* Follow this [typescript style guide](https://github.com/excelmicro/typescript) which is based on [airbnb's style guide](https://github.com/airbnb/javascript).
|
||||||
* Conventions speficic to this project:
|
* Conventions speficic to this project:
|
||||||
- Use double-quotes for string
|
* Use double-quotes for string
|
||||||
- Don't use `null`, use `undefined`
|
* Don't use null, use undefined
|
||||||
- Pascal case for private static readonly fields
|
* Pascal case for private static readonly fields
|
||||||
- Camel case for classnames in markup
|
* Camel case for classnames in markup
|
||||||
* Don't use class unless necessary
|
* Don't use class unless necessary
|
||||||
* Code related to notebooks should be dynamically imported so that it is loaded from a separate bundle only if the account is notebook-enabled. There are already top-level notebook components which are dynamically imported and their dependencies can be statically imported from these files.
|
* Code related to notebooks should be dynamically imported so that it is loaded from a separate bundle only if the account is notebook-enabled. There are already top-level notebook components which are dynamically imported and their dependencies can be statically imported from these files.
|
||||||
* Prefer using [Fluent UI controls](https://developer.microsoft.com/en-us/fluentui#/controls/web) over creating your own, in order to maintain consistency and support a11y.
|
|
||||||
|
|
||||||
### React
|
### React
|
||||||
* Prefer using React class components over function components and hooks unless you have a simple component and require no nested functions:
|
* Prefer using React class components over function components and hooks unless you have a simple component and require no nested functions:
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
# 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`
|
||||||
@@ -91,10 +87,6 @@ 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).
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"]
|
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"pluginsFile": false,
|
"pluginsFile": false,
|
||||||
"fixturesFolder": false,
|
"fixturesFolder": false,
|
||||||
"supportFile": "./support/index.js",
|
"supportFile": "./support/index.js",
|
||||||
"defaultCommandTimeout": 90000,
|
"defaultCommandTimeout": 60000,
|
||||||
"chromeWebSecurity": false,
|
"chromeWebSecurity": false,
|
||||||
"reporter": "mochawesome",
|
"reporter": "mochawesome",
|
||||||
"reporterOptions": {
|
"reporterOptions": {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ context("Cassandra API Test - createDatabase", () => {
|
|||||||
const keyspaceId = `KeyspaceId${crypt.randomBytes(8).toString("hex")}`;
|
const keyspaceId = `KeyspaceId${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const tableId = `TableId112`;
|
const tableId = `TableId112`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -32,27 +32,15 @@ context("Cassandra API Test - createDatabase", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[id="keyspace-id"]').should("be.visible").type(keyspaceId);
|
||||||
.find('input[id="keyspace-id"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.type(keyspaceId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[class="textfontclr"]').type(tableId);
|
||||||
.find('input[class="textfontclr"]')
|
|
||||||
.type(tableId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('data-test="addCollection-createCollection"').click();
|
||||||
.find('data-test="addCollection-createCollection"')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ context("Graph API Test", () => {
|
|||||||
const graphId = `TestGraph${crypt.randomBytes(8).toString("hex")}`;
|
const graphId = `TestGraph${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const partitionKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const partitionKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -33,39 +33,21 @@ context("Graph API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').should("be.visible").type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(graphId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(graphId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(partitionKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(partitionKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ context("Mongo API Test - createDatabase", () => {
|
|||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -33,38 +33,21 @@ context("Mongo API Test - createDatabase", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find("#submitBtnAddCollection").click();
|
||||||
.find("#submitBtnAddCollection")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ context("Mongo API Test", () => {
|
|||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -33,34 +33,23 @@ context("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="throughputModeContainer"]')
|
.find('div[class="throughputModeContainer"]')
|
||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.and(input => {
|
.and((input) => {
|
||||||
expect(input.get(0).textContent, "first item").contains("Autopilot (preview)");
|
expect(input.get(0).textContent, "first item").contains("Autopilot (preview)");
|
||||||
expect(input.get(1).textContent, "second item").contains("Manual");
|
expect(input.get(1).textContent, "second item").contains("Manual");
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[id="newContainer-databaseThroughput-autoPilotRadio"]').check();
|
||||||
.find('input[id="newContainer-databaseThroughput-autoPilotRadio"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('select[name="autoPilotTiers"]')
|
.find('select[name="autoPilotTiers"]')
|
||||||
@@ -68,19 +57,13 @@ context("Mongo API Test", () => {
|
|||||||
// // .select('4,000 RU/s').should('have.value', '1');
|
// // .select('4,000 RU/s').should('have.value', '1');
|
||||||
|
|
||||||
.find('option[value="2"]')
|
.find('option[value="2"]')
|
||||||
.then($element => $element.get(1).setAttribute("selected", "selected"));
|
.then(($element) => $element.get(1).setAttribute("selected", "selected"));
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ context("Mongo API Test", () => {
|
|||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('span[class="nodeLabel"]')
|
.find('span[class="nodeLabel"]')
|
||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.then($span => {
|
.then(($span) => {
|
||||||
const dbId1 = $span.text();
|
const dbId1 = $span.text();
|
||||||
cy.log("DBBB", dbId1);
|
cy.log("DBBB", dbId1);
|
||||||
|
|
||||||
@@ -28,30 +28,17 @@ context("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-existingDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-existingDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-existingDatabase"]').type(dbId1);
|
||||||
.find('input[data-test="addCollection-existingDatabase"]')
|
|
||||||
.type(dbId1);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ context.skip("Mongo API Test", () => {
|
|||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -21,50 +21,31 @@ context.skip("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find(".createNewDatabaseOrUseExisting")
|
.find(".createNewDatabaseOrUseExisting")
|
||||||
.should("have.length", 2)
|
.should("have.length", 2)
|
||||||
.and(input => {
|
.and((input) => {
|
||||||
expect(input.get(0).textContent, "first item").contains("Create new");
|
expect(input.get(0).textContent, "first item").contains("Create new");
|
||||||
expect(input.get(1).textContent, "second item").contains("Use existing");
|
expect(input.get(1).textContent, "second item").contains("Use existing");
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
@@ -84,7 +65,7 @@ context.skip("Mongo API Test", () => {
|
|||||||
const collectionIdTitle = `Add Collection`;
|
const collectionIdTitle = `Add Collection`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -93,42 +74,23 @@ context.skip("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').uncheck();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.uncheck();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[id="tab2"]').check({ force: true });
|
||||||
.find('input[id="tab2"]')
|
|
||||||
.check({ force: true });
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
@@ -147,7 +109,7 @@ context.skip("Mongo API Test", () => {
|
|||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -156,38 +118,21 @@ context.skip("Mongo API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').uncheck();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.uncheck();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[id="tab1"]').check({ force: true });
|
||||||
.find('input[id="tab1"]')
|
|
||||||
.check({ force: true });
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ context("SQL API Test", () => {
|
|||||||
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
const sharedKey = `SharedKey${crypt.randomBytes(8).toString("hex")}`;
|
||||||
connectionString.loginUsingConnectionString();
|
connectionString.loginUsingConnectionString();
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -34,38 +34,21 @@ context("SQL API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createNewDatabase"]').check();
|
||||||
.find('input[data-test="addCollection-createNewDatabase"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollectionPane-databaseSharedThroughput"]').check();
|
||||||
.find('input[data-test="addCollectionPane-databaseSharedThroughput"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-newDatabaseId"]').type(dbId);
|
||||||
.find('input[data-test="addCollection-newDatabaseId"]')
|
|
||||||
.type(dbId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-partitionKeyValue"]').type(sharedKey);
|
||||||
.find('input[data-test="addCollection-partitionKeyValue"]')
|
|
||||||
.type(sharedKey);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find("#submitBtnAddCollection").click();
|
||||||
.find("#submitBtnAddCollection")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ context("Table API Test", () => {
|
|||||||
it("Create a new table in Table API", () => {
|
it("Create a new table in Table API", () => {
|
||||||
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
const collectionId = `TestCollection${crypt.randomBytes(8).toString("hex")}`;
|
||||||
|
|
||||||
cy.get("iframe").then($element => {
|
cy.get("iframe").then(($element) => {
|
||||||
const $body = $element.contents().find("body");
|
const $body = $element.contents().find("body");
|
||||||
cy.wrap($body)
|
cy.wrap($body)
|
||||||
.find('div[class="commandBarContainer"]')
|
.find('div[class="commandBarContainer"]')
|
||||||
@@ -31,22 +31,13 @@ context("Table API Test", () => {
|
|||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('div[class="contextual-pane-in"]').should("be.visible").find('span[id="containerTitle"]');
|
||||||
.find('div[class="contextual-pane-in"]')
|
|
||||||
.should("be.visible")
|
|
||||||
.find('span[id="containerTitle"]');
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-collectionId"]').type(collectionId);
|
||||||
.find('input[data-test="addCollection-collectionId"]')
|
|
||||||
.type(collectionId);
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="databaseThroughputValue"]').should("have.value", "400");
|
||||||
.find('input[data-test="databaseThroughputValue"]')
|
|
||||||
.should("have.value", "400");
|
|
||||||
|
|
||||||
cy.wrap($body)
|
cy.wrap($body).find('input[data-test="addCollection-createCollection"]').click();
|
||||||
.find('input[data-test="addCollection-createCollection"]')
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.wait(10000);
|
cy.wait(10000);
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ context("Emulator - createDatabase", () => {
|
|||||||
|
|
||||||
cy.get(".createNewDatabaseOrUseExisting")
|
cy.get(".createNewDatabaseOrUseExisting")
|
||||||
.should("have.length", 2)
|
.should("have.length", 2)
|
||||||
.and(input => {
|
.and((input) => {
|
||||||
expect(input.get(0).textContent, "first item").contains("Create new");
|
expect(input.get(0).textContent, "first item").contains("Create new");
|
||||||
expect(input.get(1).textContent, "second item").contains("Use existing");
|
expect(input.get(1).textContent, "second item").contains("Use existing");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,27 +38,15 @@ context("Emulator - Create database -> container -> item", () => {
|
|||||||
cy.get("[data-test=addCollection-partitionKeyValue]").type("/pk");
|
cy.get("[data-test=addCollection-partitionKeyValue]").type("/pk");
|
||||||
cy.get('input[name="createCollection"]').click();
|
cy.get('input[name="createCollection"]').click();
|
||||||
cy.get(".dataResourceTree").should("contain", databaseId);
|
cy.get(".dataResourceTree").should("contain", databaseId);
|
||||||
cy.get(".dataResourceTree")
|
cy.get(".dataResourceTree").contains(databaseId).click();
|
||||||
.contains(databaseId)
|
|
||||||
.click();
|
|
||||||
cy.get(".dataResourceTree").should("contain", collectionId);
|
cy.get(".dataResourceTree").should("contain", collectionId);
|
||||||
cy.get(".dataResourceTree")
|
cy.get(".dataResourceTree").contains(collectionId).click();
|
||||||
.contains(collectionId)
|
cy.get(".dataResourceTree").contains("Items").click();
|
||||||
.click();
|
cy.get(".dataResourceTree").contains("Items").click();
|
||||||
cy.get(".dataResourceTree")
|
|
||||||
.contains("Items")
|
|
||||||
.click();
|
|
||||||
cy.get(".dataResourceTree")
|
|
||||||
.contains("Items")
|
|
||||||
.click();
|
|
||||||
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
||||||
cy.get(".commandBarContainer")
|
cy.get(".commandBarContainer").contains("New Item").click();
|
||||||
.contains("New Item")
|
|
||||||
.click();
|
|
||||||
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
||||||
cy.get(".commandBarContainer")
|
cy.get(".commandBarContainer").contains("Save").click();
|
||||||
.contains("Save")
|
|
||||||
.click();
|
|
||||||
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
cy.wait(1000); // React rendering inside KO causes some weird async rendering that makes this test flaky without waiting
|
||||||
cy.get(".documentsGridHeaderContainer").should("contain", "replace_with_new_document_id");
|
cy.get(".documentsGridHeaderContainer").should("contain", "replace_with_new_document_id");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,25 +14,18 @@ context("Emulator - deleteCollection", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Delete a collection", () => {
|
it("Delete a collection", () => {
|
||||||
cy.get(".databaseId")
|
cy.get(".databaseId").last().click();
|
||||||
.last()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get(".collectionList")
|
cy.get(".collectionList")
|
||||||
.last()
|
.last()
|
||||||
.then($id => {
|
.then(($id) => {
|
||||||
const collectionId = $id.text();
|
const collectionId = $id.text();
|
||||||
|
|
||||||
cy.get('span[data-test="collectionEllipsisMenu"]').should("exist");
|
cy.get('span[data-test="collectionEllipsisMenu"]').should("exist");
|
||||||
|
|
||||||
cy.get('span[data-test="collectionEllipsisMenu"]')
|
cy.get('span[data-test="collectionEllipsisMenu"]').invoke("show").last().click();
|
||||||
.invoke("show")
|
|
||||||
.last()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('div[data-test="collectionContextMenu"]')
|
cy.get('div[data-test="collectionContextMenu"]').contains("Delete Container").click({ force: true });
|
||||||
.contains("Delete Container")
|
|
||||||
.click({ force: true });
|
|
||||||
|
|
||||||
cy.get('input[data-test="confirmCollectionId"]').type(collectionId.trim());
|
cy.get('input[data-test="confirmCollectionId"]').type(collectionId.trim());
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ context("Emulator - deleteDatabase", () => {
|
|||||||
url: "https://localhost:8081/_explorer/authorization/post/dbs/",
|
url: "https://localhost:8081/_explorer/authorization/post/dbs/",
|
||||||
headers: {
|
headers: {
|
||||||
"x-ms-date": date,
|
"x-ms-date": date,
|
||||||
authorization: "-"
|
authorization: "-",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
authToken = response.body.Token; // Getting auth token for collection creation
|
authToken = response.body.Token; // Getting auth token for collection creation
|
||||||
return new Cypress.Promise((resolve, reject) => {
|
return new Cypress.Promise((resolve, reject) => {
|
||||||
return resolve();
|
return resolve();
|
||||||
@@ -38,12 +38,12 @@ context("Emulator - deleteDatabase", () => {
|
|||||||
headers: {
|
headers: {
|
||||||
"x-ms-date": date,
|
"x-ms-date": date,
|
||||||
authorization: authToken,
|
authorization: authToken,
|
||||||
"x-ms-version": "2018-12-31"
|
"x-ms-version": "2018-12-31",
|
||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
id: dbId
|
id: dbId,
|
||||||
}
|
},
|
||||||
}).then(response => {
|
}).then((response) => {
|
||||||
cy.log("Response", response);
|
cy.log("Response", response);
|
||||||
db_rid = response.body._rid;
|
db_rid = response.body._rid;
|
||||||
return new Cypress.Promise((resolve, reject) => {
|
return new Cypress.Promise((resolve, reject) => {
|
||||||
@@ -59,19 +59,14 @@ context("Emulator - deleteDatabase", () => {
|
|||||||
|
|
||||||
cy.get(".databaseId")
|
cy.get(".databaseId")
|
||||||
.last()
|
.last()
|
||||||
.then($id => {
|
.then(($id) => {
|
||||||
const dbId = $id.text();
|
const dbId = $id.text();
|
||||||
|
|
||||||
cy.get('span[data-test="databaseEllipsisMenu"]').should("exist");
|
cy.get('span[data-test="databaseEllipsisMenu"]').should("exist");
|
||||||
|
|
||||||
cy.get('span[data-test="databaseEllipsisMenu"]')
|
cy.get('span[data-test="databaseEllipsisMenu"]').invoke("show").last().click();
|
||||||
.invoke("show")
|
|
||||||
.last()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('div[data-test="databaseContextMenu"]')
|
cy.get('div[data-test="databaseContextMenu"]').contains("Delete Database").click({ force: true });
|
||||||
.contains("Delete Database")
|
|
||||||
.click({ force: true });
|
|
||||||
|
|
||||||
cy.get('input[data-test="confirmDatabaseId"]').type(dbId.trim());
|
cy.get('input[data-test="confirmDatabaseId"]').type(dbId.trim());
|
||||||
|
|
||||||
|
|||||||
@@ -21,29 +21,25 @@ context("New Notebook smoke test", () => {
|
|||||||
cy.contains("New Notebook").click();
|
cy.contains("New Notebook").click();
|
||||||
|
|
||||||
// Check tab name
|
// Check tab name
|
||||||
cy.get("li.tabList .tabNavText").should($span => {
|
cy.get("li.tabList .tabNavText").should(($span) => {
|
||||||
const text = $span.text();
|
const text = $span.text();
|
||||||
expect(text).to.match(/^Untitled.*\.ipynb$/);
|
expect(text).to.match(/^Untitled.*\.ipynb$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for python3 | idle status
|
// Wait for python3 | idle status
|
||||||
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should($p => {
|
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should(($p) => {
|
||||||
const text = $p.text();
|
const text = $p.text();
|
||||||
expect(text).to.match(/^python3.*idle$/);
|
expect(text).to.match(/^python3.*idle$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Click on a cell
|
// Click on a cell
|
||||||
cy.get(".cell-container")
|
cy.get(".cell-container").as("cellContainer").click();
|
||||||
.as("cellContainer")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Type in some code
|
// Type in some code
|
||||||
cy.get("@cellContainer").type("2+4");
|
cy.get("@cellContainer").type("2+4");
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
cy.get('[data-test="Run"]')
|
cy.get('[data-test="Run"]').first().click();
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Verify results
|
// Verify results
|
||||||
cy.get("@cellContainer").within(() => {
|
cy.get("@cellContainer").within(() => {
|
||||||
@@ -51,39 +47,29 @@ context("New Notebook smoke test", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Restart kernel
|
// Restart kernel
|
||||||
cy.get('[data-test="Run"] button')
|
cy.get('[data-test="Run"] button').eq(-1).click();
|
||||||
.eq(-1)
|
cy.get("li").contains("Restart Kernel").click();
|
||||||
.click();
|
|
||||||
cy.get("li")
|
|
||||||
.contains("Restart Kernel")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Wait for python3 | restarting status
|
// Wait for python3 | restarting status
|
||||||
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should($p => {
|
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should(($p) => {
|
||||||
const text = $p.text();
|
const text = $p.text();
|
||||||
expect(text).to.match(/^python3.*restarting$/);
|
expect(text).to.match(/^python3.*restarting$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for python3 | idle status
|
// Wait for python3 | idle status
|
||||||
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should($p => {
|
cy.get('[data-test="notebookStatusBar"] [data-test="kernelStatus"]', { timeout }).should(($p) => {
|
||||||
const text = $p.text();
|
const text = $p.text();
|
||||||
expect(text).to.match(/^python3.*idle$/);
|
expect(text).to.match(/^python3.*idle$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Click on a cell
|
// Click on a cell
|
||||||
cy.get(".cell-container")
|
cy.get(".cell-container").as("cellContainer").find(".input").as("codeInput").click();
|
||||||
.as("cellContainer")
|
|
||||||
.find(".input")
|
|
||||||
.as("codeInput")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Type in some code
|
// Type in some code
|
||||||
cy.get("@codeInput").type("{backspace}{backspace}{backspace}4+5");
|
cy.get("@codeInput").type("{backspace}{backspace}{backspace}4+5");
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
cy.get('[data-test="Run"]')
|
cy.get('[data-test="Run"]').first().click();
|
||||||
.first()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Verify results
|
// Verify results
|
||||||
cy.get("@cellContainer").within(() => {
|
cy.get("@cellContainer").within(() => {
|
||||||
|
|||||||
@@ -11,15 +11,11 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const clickContextMenuAndSelectOption = (nodeLabel, option) => {
|
const clickContextMenuAndSelectOption = (nodeLabel, option) => {
|
||||||
cy.get(`.treeNodeHeader[data-test="${nodeLabel}"]`)
|
cy.get(`.treeNodeHeader[data-test="${nodeLabel}"]`).find("button.treeMenuEllipsis").click();
|
||||||
.find("button.treeMenuEllipsis")
|
cy.get('[data-test="treeComponentMenuItemContainer"]').contains(option).click();
|
||||||
.click();
|
|
||||||
cy.get('[data-test="treeComponentMenuItemContainer"]')
|
|
||||||
.contains(option)
|
|
||||||
.click();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createFolder = folder => {
|
const createFolder = (folder) => {
|
||||||
clickContextMenuAndSelectOption("My Notebooks/", "New Directory");
|
clickContextMenuAndSelectOption("My Notebooks/", "New Directory");
|
||||||
|
|
||||||
cy.get("#stringInputPane").within(() => {
|
cy.get("#stringInputPane").within(() => {
|
||||||
@@ -28,11 +24,9 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteItem = nodeName => {
|
const deleteItem = (nodeName) => {
|
||||||
clickContextMenuAndSelectOption(`${nodeName}`, "Delete");
|
clickContextMenuAndSelectOption(`${nodeName}`, "Delete");
|
||||||
cy.get(".ms-Dialog-main")
|
cy.get(".ms-Dialog-main").contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -56,9 +50,7 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
// Rename
|
// Rename
|
||||||
clickContextMenuAndSelectOption(`${folder}/`, "Rename");
|
clickContextMenuAndSelectOption(`${folder}/`, "Rename");
|
||||||
cy.get("#stringInputPane").within(() => {
|
cy.get("#stringInputPane").within(() => {
|
||||||
cy.get('input[name="collectionIdConfirmation"]')
|
cy.get('input[name="collectionIdConfirmation"]').clear().type(renamedFolder);
|
||||||
.clear()
|
|
||||||
.type(renamedFolder);
|
|
||||||
cy.get("form").submit();
|
cy.get("form").submit();
|
||||||
});
|
});
|
||||||
cy.get(`.treeNodeHeader[data-test="${renamedFolder}/"]`).should("exist");
|
cy.get(`.treeNodeHeader[data-test="${renamedFolder}/"]`).should("exist");
|
||||||
@@ -75,16 +67,12 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
|
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
|
||||||
|
|
||||||
// Verify tab is open
|
// Verify tab is open
|
||||||
cy.get(".tabList")
|
cy.get(".tabList").contains(newNotebookName).should("exist");
|
||||||
.contains(newNotebookName)
|
|
||||||
.should("exist");
|
|
||||||
|
|
||||||
// Close tab
|
// Close tab
|
||||||
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`)
|
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`).find(".cancelButton").click();
|
||||||
.find(".cancelButton")
|
|
||||||
.click();
|
|
||||||
// When running from command line, closing the tab is too fast
|
// When running from command line, closing the tab is too fast
|
||||||
cy.get("body").then($body => {
|
cy.get("body").then(($body) => {
|
||||||
if ($body.find(".ms-Dialog-main").length) {
|
if ($body.find(".ms-Dialog-main").length) {
|
||||||
// For some reason, this does not work
|
// For some reason, this does not work
|
||||||
// cy.get(".ms-Dialog-main").contains("Close").click();
|
// cy.get(".ms-Dialog-main").contains("Close").click();
|
||||||
@@ -100,14 +88,10 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
|
||||||
.find("button.treeMenuEllipsis")
|
.find("button.treeMenuEllipsis")
|
||||||
.click();
|
.click();
|
||||||
cy.get('[data-test="treeComponentMenuItemContainer"]')
|
cy.get('[data-test="treeComponentMenuItemContainer"]').contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Confirm
|
// Confirm
|
||||||
cy.get(".ms-Dialog-main")
|
cy.get(".ms-Dialog-main").contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
|
||||||
|
|
||||||
deleteItem(`${folder}/`);
|
deleteItem(`${folder}/`);
|
||||||
@@ -121,10 +105,8 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
|
clickContextMenuAndSelectOption(`${folder}/`, "New Notebook");
|
||||||
|
|
||||||
// Close tab
|
// Close tab
|
||||||
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`)
|
cy.get(`.tabList[title="notebooks/${folder}/${newNotebookName}"]`).find(".cancelButton").click();
|
||||||
.find(".cancelButton")
|
cy.get("body").then(($body) => {
|
||||||
.click();
|
|
||||||
cy.get("body").then($body => {
|
|
||||||
if ($body.find(".ms-Dialog-main").length) {
|
if ($body.find(".ms-Dialog-main").length) {
|
||||||
// For some reason, this does not work
|
// For some reason, this does not work
|
||||||
// cy.get(".ms-Dialog-main").contains("Close").click();
|
// cy.get(".ms-Dialog-main").contains("Close").click();
|
||||||
@@ -140,14 +122,10 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`)
|
||||||
.find("button.treeMenuEllipsis")
|
.find("button.treeMenuEllipsis")
|
||||||
.click();
|
.click();
|
||||||
cy.get('[data-test="treeComponentMenuItemContainer"]')
|
cy.get('[data-test="treeComponentMenuItemContainer"]').contains("Rename").click();
|
||||||
.contains("Rename")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get("#stringInputPane").within(() => {
|
cy.get("#stringInputPane").within(() => {
|
||||||
cy.get('input[name="collectionIdConfirmation"]')
|
cy.get('input[name="collectionIdConfirmation"]').clear().type(renamedNotebookName);
|
||||||
.clear()
|
|
||||||
.type(renamedNotebookName);
|
|
||||||
cy.get("form").submit();
|
cy.get("form").submit();
|
||||||
});
|
});
|
||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${newNotebookName}"]`).should("not.exist");
|
||||||
@@ -157,14 +135,10 @@ context("Resource tree notebook file manipulation", () => {
|
|||||||
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${renamedNotebookName}"]`)
|
cy.get(`.nodeChildren[data-test="${folder}/"] .treeNodeHeader[data-test="${renamedNotebookName}"]`)
|
||||||
.find("button.treeMenuEllipsis")
|
.find("button.treeMenuEllipsis")
|
||||||
.click();
|
.click();
|
||||||
cy.get('[data-test="treeComponentMenuItemContainer"]')
|
cy.get('[data-test="treeComponentMenuItemContainer"]').contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Confirm
|
// Confirm
|
||||||
cy.get(".ms-Dialog-main")
|
cy.get(".ms-Dialog-main").contains("Delete").click();
|
||||||
.contains("Delete")
|
|
||||||
.click();
|
|
||||||
// Give it time to settle
|
// Give it time to settle
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
deleteItem(`${folder}/`);
|
deleteItem(`${folder}/`);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "cypress run",
|
"test": "cypress run",
|
||||||
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",
|
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",
|
||||||
"test:sql": "cypress run --browser chrome --spec \"./integration/dataexplorer/SQL/*\"",
|
"test:sql": "cypress run --browser chrome --headless --spec \"./integration/dataexplorer/SQL/*\"",
|
||||||
"test:ci": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/ https-get://0.0.0.0:8081/_explorer/index.html && cypress run --browser chrome --headless",
|
"test:ci": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/ https-get://0.0.0.0:8081/_explorer/index.html && cypress run --browser chrome --headless",
|
||||||
"test:debug": "cypress open"
|
"test:debug": "cypress open"
|
||||||
},
|
},
|
||||||
|
|||||||
7
externals/bootstrap.js
vendored
Normal file
7
externals/bootstrap.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
externals/bootstrap.min.js
vendored
Normal file
7
externals/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
externals/bootstrap_ase.css
vendored
Normal file
6
externals/bootstrap_ase.css
vendored
Normal file
File diff suppressed because one or more lines are too long
595
externals/bops.js
vendored
Normal file
595
externals/bops.js
vendored
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
(function(e){if("function"==typeof bootstrap)bootstrap("bops",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeBops=e}else"undefined"!=typeof window?window.bops=e():global.bops=e()})(function(){var define,ses,bootstrap,module,exports;
|
||||||
|
return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
|
||||||
|
var proto = {}
|
||||||
|
module.exports = proto
|
||||||
|
|
||||||
|
proto.from = require('./from.js')
|
||||||
|
proto.to = require('./to.js')
|
||||||
|
proto.is = require('./is.js')
|
||||||
|
proto.subarray = require('./subarray.js')
|
||||||
|
proto.join = require('./join.js')
|
||||||
|
proto.copy = require('./copy.js')
|
||||||
|
proto.create = require('./create.js')
|
||||||
|
|
||||||
|
mix(require('./read.js'), proto)
|
||||||
|
mix(require('./write.js'), proto)
|
||||||
|
|
||||||
|
function mix(from, into) {
|
||||||
|
for(var key in from) {
|
||||||
|
into[key] = from[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},{"./from.js":2,"./to.js":3,"./is.js":4,"./subarray.js":5,"./join.js":6,"./copy.js":7,"./create.js":8,"./read.js":9,"./write.js":10}],4:[function(require,module,exports){
|
||||||
|
|
||||||
|
module.exports = function(buffer) {
|
||||||
|
return buffer instanceof Uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
},{}],5:[function(require,module,exports){
|
||||||
|
module.exports = subarray
|
||||||
|
|
||||||
|
function subarray(buf, from, to) {
|
||||||
|
return buf.subarray(from || 0, to || buf.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
},{}],6:[function(require,module,exports){
|
||||||
|
module.exports = join
|
||||||
|
|
||||||
|
function join(targets, hint) {
|
||||||
|
if(!targets.length) {
|
||||||
|
return new Uint8Array(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var len = hint !== undefined ? hint : get_length(targets)
|
||||||
|
, out = new Uint8Array(len)
|
||||||
|
, cur = targets[0]
|
||||||
|
, curlen = cur.length
|
||||||
|
, curidx = 0
|
||||||
|
, curoff = 0
|
||||||
|
, i = 0
|
||||||
|
|
||||||
|
while(i < len) {
|
||||||
|
if(curoff === curlen) {
|
||||||
|
curoff = 0
|
||||||
|
++curidx
|
||||||
|
cur = targets[curidx]
|
||||||
|
curlen = cur && cur.length
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[i++] = cur[curoff++]
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_length(targets) {
|
||||||
|
var size = 0
|
||||||
|
for(var i = 0, len = targets.length; i < len; ++i) {
|
||||||
|
size += targets[i].byteLength
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
},{}],7:[function(require,module,exports){
|
||||||
|
module.exports = copy
|
||||||
|
|
||||||
|
var slice = [].slice
|
||||||
|
|
||||||
|
function copy(source, target, target_start, source_start, source_end) {
|
||||||
|
target_start = arguments.length < 3 ? 0 : target_start
|
||||||
|
source_start = arguments.length < 4 ? 0 : source_start
|
||||||
|
source_end = arguments.length < 5 ? source.length : source_end
|
||||||
|
|
||||||
|
if(source_end === source_start) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(target.length === 0 || source.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(source_end > source.length) {
|
||||||
|
source_end = source.length
|
||||||
|
}
|
||||||
|
|
||||||
|
if(target.length - target_start < source_end - source_start) {
|
||||||
|
source_end = target.length - target_start + start
|
||||||
|
}
|
||||||
|
|
||||||
|
if(source.buffer !== target.buffer) {
|
||||||
|
return fast_copy(source, target, target_start, source_start, source_end)
|
||||||
|
}
|
||||||
|
return slow_copy(source, target, target_start, source_start, source_end)
|
||||||
|
}
|
||||||
|
|
||||||
|
function fast_copy(source, target, target_start, source_start, source_end) {
|
||||||
|
var len = (source_end - source_start) + target_start
|
||||||
|
|
||||||
|
for(var i = target_start, j = source_start;
|
||||||
|
i < len;
|
||||||
|
++i,
|
||||||
|
++j) {
|
||||||
|
target[i] = source[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function slow_copy(from, to, j, i, jend) {
|
||||||
|
// the buffers could overlap.
|
||||||
|
var iend = jend + i
|
||||||
|
, tmp = new Uint8Array(slice.call(from, i, iend))
|
||||||
|
, x = 0
|
||||||
|
|
||||||
|
for(; i < iend; ++i, ++x) {
|
||||||
|
to[j++] = tmp[x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},{}],8:[function(require,module,exports){
|
||||||
|
module.exports = function(size) {
|
||||||
|
return new Uint8Array(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
},{}],9:[function(require,module,exports){
|
||||||
|
module.exports = {
|
||||||
|
readUInt8: read_uint8
|
||||||
|
, readInt8: read_int8
|
||||||
|
, readUInt16LE: read_uint16_le
|
||||||
|
, readUInt32LE: read_uint32_le
|
||||||
|
, readInt16LE: read_int16_le
|
||||||
|
, readInt32LE: read_int32_le
|
||||||
|
, readFloatLE: read_float_le
|
||||||
|
, readDoubleLE: read_double_le
|
||||||
|
, readUInt16BE: read_uint16_be
|
||||||
|
, readUInt32BE: read_uint32_be
|
||||||
|
, readInt16BE: read_int16_be
|
||||||
|
, readInt32BE: read_int32_be
|
||||||
|
, readFloatBE: read_float_be
|
||||||
|
, readDoubleBE: read_double_be
|
||||||
|
}
|
||||||
|
|
||||||
|
var map = require('./mapped.js')
|
||||||
|
|
||||||
|
function read_uint8(target, at) {
|
||||||
|
return target[at]
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_int8(target, at) {
|
||||||
|
var v = target[at];
|
||||||
|
return v < 0x80 ? v : v - 0x100
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_uint16_le(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getUint16(at + target.byteOffset, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_uint32_le(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getUint32(at + target.byteOffset, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_int16_le(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getInt16(at + target.byteOffset, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_int32_le(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getInt32(at + target.byteOffset, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_float_le(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getFloat32(at + target.byteOffset, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_double_le(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getFloat64(at + target.byteOffset, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_uint16_be(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getUint16(at + target.byteOffset, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_uint32_be(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getUint32(at + target.byteOffset, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_int16_be(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getInt16(at + target.byteOffset, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_int32_be(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getInt32(at + target.byteOffset, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_float_be(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getFloat32(at + target.byteOffset, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_double_be(target, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.getFloat64(at + target.byteOffset, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
},{"./mapped.js":11}],10:[function(require,module,exports){
|
||||||
|
module.exports = {
|
||||||
|
writeUInt8: write_uint8
|
||||||
|
, writeInt8: write_int8
|
||||||
|
, writeUInt16LE: write_uint16_le
|
||||||
|
, writeUInt32LE: write_uint32_le
|
||||||
|
, writeInt16LE: write_int16_le
|
||||||
|
, writeInt32LE: write_int32_le
|
||||||
|
, writeFloatLE: write_float_le
|
||||||
|
, writeDoubleLE: write_double_le
|
||||||
|
, writeUInt16BE: write_uint16_be
|
||||||
|
, writeUInt32BE: write_uint32_be
|
||||||
|
, writeInt16BE: write_int16_be
|
||||||
|
, writeInt32BE: write_int32_be
|
||||||
|
, writeFloatBE: write_float_be
|
||||||
|
, writeDoubleBE: write_double_be
|
||||||
|
}
|
||||||
|
|
||||||
|
var map = require('./mapped.js')
|
||||||
|
|
||||||
|
function write_uint8(target, value, at) {
|
||||||
|
return target[at] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_int8(target, value, at) {
|
||||||
|
return target[at] = value < 0 ? value + 0x100 : value
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_uint16_le(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setUint16(at + target.byteOffset, value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_uint32_le(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setUint32(at + target.byteOffset, value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_int16_le(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setInt16(at + target.byteOffset, value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_int32_le(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setInt32(at + target.byteOffset, value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_float_le(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setFloat32(at + target.byteOffset, value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_double_le(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setFloat64(at + target.byteOffset, value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_uint16_be(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setUint16(at + target.byteOffset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_uint32_be(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setUint32(at + target.byteOffset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_int16_be(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setInt16(at + target.byteOffset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_int32_be(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setInt32(at + target.byteOffset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_float_be(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setFloat32(at + target.byteOffset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_double_be(target, value, at) {
|
||||||
|
var dv = map.get(target);
|
||||||
|
return dv.setFloat64(at + target.byteOffset, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
},{"./mapped.js":11}],11:[function(require,module,exports){
|
||||||
|
var proto
|
||||||
|
, map
|
||||||
|
|
||||||
|
module.exports = proto = {}
|
||||||
|
|
||||||
|
map = typeof WeakMap === 'undefined' ? null : new WeakMap
|
||||||
|
|
||||||
|
proto.get = !map ? no_weakmap_get : get
|
||||||
|
|
||||||
|
function no_weakmap_get(target) {
|
||||||
|
return new DataView(target.buffer, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(target) {
|
||||||
|
var out = map.get(target.buffer)
|
||||||
|
if(!out) {
|
||||||
|
map.set(target.buffer, out = new DataView(target.buffer, 0))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
},{}],2:[function(require,module,exports){
|
||||||
|
module.exports = from
|
||||||
|
|
||||||
|
var base64 = require('base64-js')
|
||||||
|
|
||||||
|
var decoders = {
|
||||||
|
hex: from_hex
|
||||||
|
, utf8: from_utf
|
||||||
|
, base64: from_base64
|
||||||
|
}
|
||||||
|
|
||||||
|
function from(source, encoding) {
|
||||||
|
if(Array.isArray(source)) {
|
||||||
|
return new Uint8Array(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoders[encoding || 'utf8'](source)
|
||||||
|
}
|
||||||
|
|
||||||
|
function from_hex(str) {
|
||||||
|
var size = str.length / 2
|
||||||
|
, buf = new Uint8Array(size)
|
||||||
|
, character = ''
|
||||||
|
|
||||||
|
for(var i = 0, len = str.length; i < len; ++i) {
|
||||||
|
character += str.charAt(i)
|
||||||
|
|
||||||
|
if(i > 0 && (i % 2) === 1) {
|
||||||
|
buf[i>>>1] = parseInt(character, 16)
|
||||||
|
character = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
function from_utf(str) {
|
||||||
|
var bytes = []
|
||||||
|
, tmp
|
||||||
|
, ch
|
||||||
|
|
||||||
|
for(var i = 0, len = str.length; i < len; ++i) {
|
||||||
|
ch = str.charCodeAt(i)
|
||||||
|
if(ch & 0x80) {
|
||||||
|
tmp = encodeURIComponent(str.charAt(i)).substr(1).split('%')
|
||||||
|
for(var j = 0, jlen = tmp.length; j < jlen; ++j) {
|
||||||
|
bytes[bytes.length] = parseInt(tmp[j], 16)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bytes[bytes.length] = ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Uint8Array(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
function from_base64(str) {
|
||||||
|
return new Uint8Array(base64.toByteArray(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
},{"base64-js":12}],3:[function(require,module,exports){
|
||||||
|
module.exports = to
|
||||||
|
|
||||||
|
var base64 = require('base64-js')
|
||||||
|
, toutf8 = require('to-utf8')
|
||||||
|
|
||||||
|
var encoders = {
|
||||||
|
hex: to_hex
|
||||||
|
, utf8: to_utf
|
||||||
|
, base64: to_base64
|
||||||
|
}
|
||||||
|
|
||||||
|
function to(buf, encoding) {
|
||||||
|
return encoders[encoding || 'utf8'](buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
function to_hex(buf) {
|
||||||
|
var str = ''
|
||||||
|
, byt
|
||||||
|
|
||||||
|
for(var i = 0, len = buf.length; i < len; ++i) {
|
||||||
|
byt = buf[i]
|
||||||
|
str += ((byt & 0xF0) >>> 4).toString(16)
|
||||||
|
str += (byt & 0x0F).toString(16)
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
function to_utf(buf) {
|
||||||
|
return toutf8(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
function to_base64(buf) {
|
||||||
|
return base64.fromByteArray(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},{"base64-js":12,"to-utf8":13}],12:[function(require,module,exports){
|
||||||
|
(function (exports) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||||
|
|
||||||
|
function b64ToByteArray(b64) {
|
||||||
|
var i, j, l, tmp, placeHolders, arr;
|
||||||
|
|
||||||
|
if (b64.length % 4 > 0) {
|
||||||
|
throw 'Invalid string. Length must be a multiple of 4';
|
||||||
|
}
|
||||||
|
|
||||||
|
// the number of equal signs (place holders)
|
||||||
|
// if there are two placeholders, than the two characters before it
|
||||||
|
// represent one byte
|
||||||
|
// if there is only one, then the three characters before it represent 2 bytes
|
||||||
|
// this is just a cheap hack to not do indexOf twice
|
||||||
|
placeHolders = b64.indexOf('=');
|
||||||
|
placeHolders = placeHolders > 0 ? b64.length - placeHolders : 0;
|
||||||
|
|
||||||
|
// base64 is 4/3 + up to two characters of the original data
|
||||||
|
arr = [];//new Uint8Array(b64.length * 3 / 4 - placeHolders);
|
||||||
|
|
||||||
|
// if there are placeholders, only get up to the last complete 4 chars
|
||||||
|
l = placeHolders > 0 ? b64.length - 4 : b64.length;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; i < l; i += 4, j += 3) {
|
||||||
|
tmp = (lookup.indexOf(b64[i]) << 18) | (lookup.indexOf(b64[i + 1]) << 12) | (lookup.indexOf(b64[i + 2]) << 6) | lookup.indexOf(b64[i + 3]);
|
||||||
|
arr.push((tmp & 0xFF0000) >> 16);
|
||||||
|
arr.push((tmp & 0xFF00) >> 8);
|
||||||
|
arr.push(tmp & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (placeHolders === 2) {
|
||||||
|
tmp = (lookup.indexOf(b64[i]) << 2) | (lookup.indexOf(b64[i + 1]) >> 4);
|
||||||
|
arr.push(tmp & 0xFF);
|
||||||
|
} else if (placeHolders === 1) {
|
||||||
|
tmp = (lookup.indexOf(b64[i]) << 10) | (lookup.indexOf(b64[i + 1]) << 4) | (lookup.indexOf(b64[i + 2]) >> 2);
|
||||||
|
arr.push((tmp >> 8) & 0xFF);
|
||||||
|
arr.push(tmp & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uint8ToBase64(uint8) {
|
||||||
|
var i,
|
||||||
|
extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
|
||||||
|
output = "",
|
||||||
|
temp, length;
|
||||||
|
|
||||||
|
function tripletToBase64 (num) {
|
||||||
|
return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F];
|
||||||
|
};
|
||||||
|
|
||||||
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
||||||
|
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
|
||||||
|
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]);
|
||||||
|
output += tripletToBase64(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
||||||
|
switch (extraBytes) {
|
||||||
|
case 1:
|
||||||
|
temp = uint8[uint8.length - 1];
|
||||||
|
output += lookup[temp >> 2];
|
||||||
|
output += lookup[(temp << 4) & 0x3F];
|
||||||
|
output += '==';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]);
|
||||||
|
output += lookup[temp >> 10];
|
||||||
|
output += lookup[(temp >> 4) & 0x3F];
|
||||||
|
output += lookup[(temp << 2) & 0x3F];
|
||||||
|
output += '=';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.toByteArray = b64ToByteArray;
|
||||||
|
module.exports.fromByteArray = uint8ToBase64;
|
||||||
|
}());
|
||||||
|
|
||||||
|
},{}],13:[function(require,module,exports){
|
||||||
|
module.exports = to_utf8
|
||||||
|
|
||||||
|
var out = []
|
||||||
|
, col = []
|
||||||
|
, fcc = String.fromCharCode
|
||||||
|
, mask = [0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01]
|
||||||
|
, unmask = [
|
||||||
|
0x00
|
||||||
|
, 0x01
|
||||||
|
, 0x02 | 0x01
|
||||||
|
, 0x04 | 0x02 | 0x01
|
||||||
|
, 0x08 | 0x04 | 0x02 | 0x01
|
||||||
|
, 0x10 | 0x08 | 0x04 | 0x02 | 0x01
|
||||||
|
, 0x20 | 0x10 | 0x08 | 0x04 | 0x02 | 0x01
|
||||||
|
, 0x40 | 0x20 | 0x10 | 0x08 | 0x04 | 0x02 | 0x01
|
||||||
|
]
|
||||||
|
|
||||||
|
function to_utf8(bytes, start, end) {
|
||||||
|
start = start === undefined ? 0 : start
|
||||||
|
end = end === undefined ? bytes.length : end
|
||||||
|
|
||||||
|
var idx = 0
|
||||||
|
, hi = 0x80
|
||||||
|
, collecting = 0
|
||||||
|
, pos
|
||||||
|
, by
|
||||||
|
|
||||||
|
col.length =
|
||||||
|
out.length = 0
|
||||||
|
|
||||||
|
while(idx < bytes.length) {
|
||||||
|
by = bytes[idx]
|
||||||
|
if(!collecting && by & hi) {
|
||||||
|
pos = find_pad_position(by)
|
||||||
|
collecting += pos
|
||||||
|
if(pos < 8) {
|
||||||
|
col[col.length] = by & unmask[6 - pos]
|
||||||
|
}
|
||||||
|
} else if(collecting) {
|
||||||
|
col[col.length] = by & unmask[6]
|
||||||
|
--collecting
|
||||||
|
if(!collecting && col.length) {
|
||||||
|
out[out.length] = fcc(reduced(col, pos))
|
||||||
|
col.length = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out[out.length] = fcc(by)
|
||||||
|
}
|
||||||
|
++idx
|
||||||
|
}
|
||||||
|
if(col.length && !collecting) {
|
||||||
|
out[out.length] = fcc(reduced(col, pos))
|
||||||
|
col.length = 0
|
||||||
|
}
|
||||||
|
return out.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function find_pad_position(byt) {
|
||||||
|
for(var i = 0; i < 7; ++i) {
|
||||||
|
if(!(byt & mask[i])) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
function reduced(list) {
|
||||||
|
var out = 0
|
||||||
|
for(var i = 0, len = list.length; i < len; ++i) {
|
||||||
|
out |= list[i] << ((len - i - 1) * 6)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
},{}]},{},[1])(1)
|
||||||
|
});
|
||||||
|
;
|
||||||
723
externals/crossroads.js
vendored
Normal file
723
externals/crossroads.js
vendored
Normal file
@@ -0,0 +1,723 @@
|
|||||||
|
/** @license
|
||||||
|
* crossroads <http://millermedeiros.github.com/crossroads.js/>
|
||||||
|
* Author: Miller Medeiros | MIT License
|
||||||
|
* v0.12.2 (2015/07/31 18:37)
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var factory = function (signals) {
|
||||||
|
|
||||||
|
var crossroads,
|
||||||
|
_hasOptionalGroupBug,
|
||||||
|
UNDEF;
|
||||||
|
|
||||||
|
// Helpers -----------
|
||||||
|
//====================
|
||||||
|
|
||||||
|
// IE 7-8 capture optional groups as empty strings while other browsers
|
||||||
|
// capture as `undefined`
|
||||||
|
_hasOptionalGroupBug = (/t(.+)?/).exec('t')[1] === '';
|
||||||
|
|
||||||
|
function arrayIndexOf(arr, val) {
|
||||||
|
if (arr.indexOf) {
|
||||||
|
return arr.indexOf(val);
|
||||||
|
} else {
|
||||||
|
//Array.indexOf doesn't work on IE 6-7
|
||||||
|
var n = arr.length;
|
||||||
|
while (n--) {
|
||||||
|
if (arr[n] === val) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayRemove(arr, item) {
|
||||||
|
var i = arrayIndexOf(arr, item);
|
||||||
|
if (i !== -1) {
|
||||||
|
arr.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isKind(val, kind) {
|
||||||
|
return '[object '+ kind +']' === Object.prototype.toString.call(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRegExp(val) {
|
||||||
|
return isKind(val, 'RegExp');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isArray(val) {
|
||||||
|
return isKind(val, 'Array');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFunction(val) {
|
||||||
|
return typeof val === 'function';
|
||||||
|
}
|
||||||
|
|
||||||
|
//borrowed from AMD-utils
|
||||||
|
function typecastValue(val) {
|
||||||
|
var r;
|
||||||
|
if (val === null || val === 'null') {
|
||||||
|
r = null;
|
||||||
|
} else if (val === 'true') {
|
||||||
|
r = true;
|
||||||
|
} else if (val === 'false') {
|
||||||
|
r = false;
|
||||||
|
} else if (val === UNDEF || val === 'undefined') {
|
||||||
|
r = UNDEF;
|
||||||
|
} else if (val === '' || isNaN(val)) {
|
||||||
|
r = val;
|
||||||
|
} else {
|
||||||
|
r = parseFloat(val);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
function typecastArrayValues(values) {
|
||||||
|
var n = values.length,
|
||||||
|
result = [];
|
||||||
|
while (n--) {
|
||||||
|
result[n] = typecastValue(values[n]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// borrowed from MOUT
|
||||||
|
function decodeQueryString(queryStr, shouldTypecast) {
|
||||||
|
var queryArr = (queryStr || '').replace('?', '').split('&'),
|
||||||
|
reg = /([^=]+)=(.+)/,
|
||||||
|
i = -1,
|
||||||
|
obj = {},
|
||||||
|
equalIndex, cur, pValue, pName;
|
||||||
|
|
||||||
|
while ((cur = queryArr[++i])) {
|
||||||
|
equalIndex = cur.indexOf('=');
|
||||||
|
pName = cur.substring(0, equalIndex);
|
||||||
|
pValue = decodeURIComponent(cur.substring(equalIndex + 1));
|
||||||
|
if (shouldTypecast !== false) {
|
||||||
|
pValue = typecastValue(pValue);
|
||||||
|
}
|
||||||
|
if (pName in obj){
|
||||||
|
if(isArray(obj[pName])){
|
||||||
|
obj[pName].push(pValue);
|
||||||
|
} else {
|
||||||
|
obj[pName] = [obj[pName], pValue];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
obj[pName] = pValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Crossroads --------
|
||||||
|
//====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function Crossroads() {
|
||||||
|
this.bypassed = new signals.Signal();
|
||||||
|
this.routed = new signals.Signal();
|
||||||
|
this._routes = [];
|
||||||
|
this._prevRoutes = [];
|
||||||
|
this._piped = [];
|
||||||
|
this.resetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Crossroads.prototype = {
|
||||||
|
|
||||||
|
greedy : false,
|
||||||
|
|
||||||
|
greedyEnabled : true,
|
||||||
|
|
||||||
|
ignoreCase : true,
|
||||||
|
|
||||||
|
ignoreState : false,
|
||||||
|
|
||||||
|
shouldTypecast : false,
|
||||||
|
|
||||||
|
normalizeFn : null,
|
||||||
|
|
||||||
|
resetState : function(){
|
||||||
|
this._prevRoutes.length = 0;
|
||||||
|
this._prevMatchedRequest = null;
|
||||||
|
this._prevBypassedRequest = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
create : function () {
|
||||||
|
return new Crossroads();
|
||||||
|
},
|
||||||
|
|
||||||
|
addRoute : function (pattern, callback, priority) {
|
||||||
|
var route = new Route(pattern, callback, priority, this);
|
||||||
|
this._sortedInsert(route);
|
||||||
|
return route;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeRoute : function (route) {
|
||||||
|
arrayRemove(this._routes, route);
|
||||||
|
route._destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAllRoutes : function () {
|
||||||
|
var n = this.getNumRoutes();
|
||||||
|
while (n--) {
|
||||||
|
this._routes[n]._destroy();
|
||||||
|
}
|
||||||
|
this._routes.length = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
parse : function (request, defaultArgs) {
|
||||||
|
request = request || '';
|
||||||
|
defaultArgs = defaultArgs || [];
|
||||||
|
|
||||||
|
// should only care about different requests if ignoreState isn't true
|
||||||
|
if ( !this.ignoreState &&
|
||||||
|
(request === this._prevMatchedRequest ||
|
||||||
|
request === this._prevBypassedRequest) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var routes = this._getMatchedRoutes(request),
|
||||||
|
i = 0,
|
||||||
|
n = routes.length,
|
||||||
|
cur;
|
||||||
|
|
||||||
|
if (n) {
|
||||||
|
this._prevMatchedRequest = request;
|
||||||
|
|
||||||
|
this._notifyPrevRoutes(routes, request);
|
||||||
|
this._prevRoutes = routes;
|
||||||
|
//should be incremental loop, execute routes in order
|
||||||
|
while (i < n) {
|
||||||
|
cur = routes[i];
|
||||||
|
cur.route.matched.dispatch.apply(cur.route.matched, defaultArgs.concat(cur.params));
|
||||||
|
cur.isFirst = !i;
|
||||||
|
this.routed.dispatch.apply(this.routed, defaultArgs.concat([request, cur]));
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._prevBypassedRequest = request;
|
||||||
|
this.bypassed.dispatch.apply(this.bypassed, defaultArgs.concat([request]));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pipeParse(request, defaultArgs);
|
||||||
|
},
|
||||||
|
|
||||||
|
_notifyPrevRoutes : function(matchedRoutes, request) {
|
||||||
|
var i = 0, prev;
|
||||||
|
while (prev = this._prevRoutes[i++]) {
|
||||||
|
//check if switched exist since route may be disposed
|
||||||
|
if(prev.route.switched && this._didSwitch(prev.route, matchedRoutes)) {
|
||||||
|
prev.route.switched.dispatch(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_didSwitch : function (route, matchedRoutes){
|
||||||
|
var matched,
|
||||||
|
i = 0;
|
||||||
|
while (matched = matchedRoutes[i++]) {
|
||||||
|
// only dispatch switched if it is going to a different route
|
||||||
|
if (matched.route === route) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_pipeParse : function(request, defaultArgs) {
|
||||||
|
var i = 0, route;
|
||||||
|
while (route = this._piped[i++]) {
|
||||||
|
route.parse(request, defaultArgs);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getNumRoutes : function () {
|
||||||
|
return this._routes.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
_sortedInsert : function (route) {
|
||||||
|
//simplified insertion sort
|
||||||
|
var routes = this._routes,
|
||||||
|
n = routes.length;
|
||||||
|
do { --n; } while (routes[n] && route._priority <= routes[n]._priority);
|
||||||
|
routes.splice(n+1, 0, route);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getMatchedRoutes : function (request) {
|
||||||
|
var res = [],
|
||||||
|
routes = this._routes,
|
||||||
|
n = routes.length,
|
||||||
|
route;
|
||||||
|
//should be decrement loop since higher priorities are added at the end of array
|
||||||
|
while (route = routes[--n]) {
|
||||||
|
if ((!res.length || this.greedy || route.greedy) && route.match(request)) {
|
||||||
|
res.push({
|
||||||
|
route : route,
|
||||||
|
params : route._getParamsArray(request)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!this.greedyEnabled && res.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
|
||||||
|
pipe : function (otherRouter) {
|
||||||
|
this._piped.push(otherRouter);
|
||||||
|
},
|
||||||
|
|
||||||
|
unpipe : function (otherRouter) {
|
||||||
|
arrayRemove(this._piped, otherRouter);
|
||||||
|
},
|
||||||
|
|
||||||
|
toString : function () {
|
||||||
|
return '[crossroads numRoutes:'+ this.getNumRoutes() +']';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//"static" instance
|
||||||
|
crossroads = new Crossroads();
|
||||||
|
crossroads.VERSION = '0.12.2';
|
||||||
|
|
||||||
|
crossroads.NORM_AS_ARRAY = function (req, vals) {
|
||||||
|
return [vals.vals_];
|
||||||
|
};
|
||||||
|
|
||||||
|
crossroads.NORM_AS_OBJECT = function (req, vals) {
|
||||||
|
return [vals];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Route --------------
|
||||||
|
//=====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function Route(pattern, callback, priority, router) {
|
||||||
|
var isRegexPattern = isRegExp(pattern),
|
||||||
|
patternLexer = router.patternLexer;
|
||||||
|
this._router = router;
|
||||||
|
this._pattern = pattern;
|
||||||
|
this._paramsIds = isRegexPattern? null : patternLexer.getParamIds(pattern);
|
||||||
|
this._optionalParamsIds = isRegexPattern? null : patternLexer.getOptionalParamsIds(pattern);
|
||||||
|
this._matchRegexp = isRegexPattern? pattern : patternLexer.compilePattern(pattern, router.ignoreCase);
|
||||||
|
this.matched = new signals.Signal();
|
||||||
|
this.switched = new signals.Signal();
|
||||||
|
if (callback) {
|
||||||
|
this.matched.add(callback);
|
||||||
|
}
|
||||||
|
this._priority = priority || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Route.prototype = {
|
||||||
|
|
||||||
|
greedy : false,
|
||||||
|
|
||||||
|
rules : void(0),
|
||||||
|
|
||||||
|
match : function (request) {
|
||||||
|
request = request || '';
|
||||||
|
return this._matchRegexp.test(request) && this._validateParams(request); //validate params even if regexp because of `request_` rule.
|
||||||
|
},
|
||||||
|
|
||||||
|
_validateParams : function (request) {
|
||||||
|
var rules = this.rules,
|
||||||
|
values = this._getParamsObject(request),
|
||||||
|
key;
|
||||||
|
for (key in rules) {
|
||||||
|
// normalize_ isn't a validation rule... (#39)
|
||||||
|
if(key !== 'normalize_' && rules.hasOwnProperty(key) && ! this._isValidParam(request, key, values)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_isValidParam : function (request, prop, values) {
|
||||||
|
var validationRule = this.rules[prop],
|
||||||
|
val = values[prop],
|
||||||
|
isValid = false,
|
||||||
|
isQuery = (prop.indexOf('?') === 0);
|
||||||
|
|
||||||
|
if (val == null && this._optionalParamsIds && arrayIndexOf(this._optionalParamsIds, prop) !== -1) {
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
else if (isRegExp(validationRule)) {
|
||||||
|
if (isQuery) {
|
||||||
|
val = values[prop +'_']; //use raw string
|
||||||
|
}
|
||||||
|
isValid = validationRule.test(val);
|
||||||
|
}
|
||||||
|
else if (isArray(validationRule)) {
|
||||||
|
if (isQuery) {
|
||||||
|
val = values[prop +'_']; //use raw string
|
||||||
|
}
|
||||||
|
isValid = this._isValidArrayRule(validationRule, val);
|
||||||
|
}
|
||||||
|
else if (isFunction(validationRule)) {
|
||||||
|
isValid = validationRule(val, request, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid; //fail silently if validationRule is from an unsupported type
|
||||||
|
},
|
||||||
|
|
||||||
|
_isValidArrayRule : function (arr, val) {
|
||||||
|
if (! this._router.ignoreCase) {
|
||||||
|
return arrayIndexOf(arr, val) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
val = val.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = arr.length,
|
||||||
|
item,
|
||||||
|
compareVal;
|
||||||
|
|
||||||
|
while (n--) {
|
||||||
|
item = arr[n];
|
||||||
|
compareVal = (typeof item === 'string')? item.toLowerCase() : item;
|
||||||
|
if (compareVal === val) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getParamsObject : function (request) {
|
||||||
|
var shouldTypecast = this._router.shouldTypecast,
|
||||||
|
values = this._router.patternLexer.getParamValues(request, this._matchRegexp, shouldTypecast),
|
||||||
|
o = {},
|
||||||
|
n = values.length,
|
||||||
|
param, val;
|
||||||
|
while (n--) {
|
||||||
|
val = values[n];
|
||||||
|
if (this._paramsIds) {
|
||||||
|
param = this._paramsIds[n];
|
||||||
|
if (param.indexOf('?') === 0 && val) {
|
||||||
|
//make a copy of the original string so array and
|
||||||
|
//RegExp validation can be applied properly
|
||||||
|
o[param +'_'] = val;
|
||||||
|
//update vals_ array as well since it will be used
|
||||||
|
//during dispatch
|
||||||
|
val = decodeQueryString(val, shouldTypecast);
|
||||||
|
values[n] = val;
|
||||||
|
}
|
||||||
|
// IE will capture optional groups as empty strings while other
|
||||||
|
// browsers will capture `undefined` so normalize behavior.
|
||||||
|
// see: #gh-58, #gh-59, #gh-60
|
||||||
|
if ( _hasOptionalGroupBug && val === '' && arrayIndexOf(this._optionalParamsIds, param) !== -1 ) {
|
||||||
|
val = void(0);
|
||||||
|
values[n] = val;
|
||||||
|
}
|
||||||
|
o[param] = val;
|
||||||
|
}
|
||||||
|
//alias to paths and for RegExp pattern
|
||||||
|
o[n] = val;
|
||||||
|
}
|
||||||
|
o.request_ = shouldTypecast? typecastValue(request) : request;
|
||||||
|
o.vals_ = values;
|
||||||
|
return o;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getParamsArray : function (request) {
|
||||||
|
var norm = this.rules? this.rules.normalize_ : null,
|
||||||
|
params;
|
||||||
|
norm = norm || this._router.normalizeFn; // default normalize
|
||||||
|
if (norm && isFunction(norm)) {
|
||||||
|
params = norm(request, this._getParamsObject(request));
|
||||||
|
} else {
|
||||||
|
params = this._getParamsObject(request).vals_;
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
},
|
||||||
|
|
||||||
|
interpolate : function(replacements) {
|
||||||
|
var str = this._router.patternLexer.interpolate(this._pattern, replacements);
|
||||||
|
if (! this._validateParams(str) ) {
|
||||||
|
throw new Error('Generated string doesn\'t validate against `Route.rules`.');
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
|
||||||
|
dispose : function () {
|
||||||
|
this._router.removeRoute(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
_destroy : function () {
|
||||||
|
this.matched.dispose();
|
||||||
|
this.switched.dispose();
|
||||||
|
this.matched = this.switched = this._pattern = this._matchRegexp = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
toString : function () {
|
||||||
|
return '[Route pattern:"'+ this._pattern +'", numListeners:'+ this.matched.getNumListeners() +']';
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Pattern Lexer ------
|
||||||
|
//=====================
|
||||||
|
|
||||||
|
Crossroads.prototype.patternLexer = (function () {
|
||||||
|
|
||||||
|
var
|
||||||
|
//match chars that should be escaped on string regexp
|
||||||
|
ESCAPE_CHARS_REGEXP = /[\\.+*?\^$\[\](){}\/'#]/g,
|
||||||
|
|
||||||
|
//trailing slashes (begin/end of string)
|
||||||
|
LOOSE_SLASHES_REGEXP = /^\/|\/$/g,
|
||||||
|
LEGACY_SLASHES_REGEXP = /\/$/g,
|
||||||
|
|
||||||
|
//params - everything between `{ }` or `: :`
|
||||||
|
PARAMS_REGEXP = /(?:\{|:)([^}:]+)(?:\}|:)/g,
|
||||||
|
|
||||||
|
//used to save params during compile (avoid escaping things that
|
||||||
|
//shouldn't be escaped).
|
||||||
|
TOKENS = {
|
||||||
|
'OS' : {
|
||||||
|
//optional slashes
|
||||||
|
//slash between `::` or `}:` or `\w:` or `:{?` or `}{?` or `\w{?`
|
||||||
|
rgx : /([:}]|\w(?=\/))\/?(:|(?:\{\?))/g,
|
||||||
|
save : '$1{{id}}$2',
|
||||||
|
res : '\\/?'
|
||||||
|
},
|
||||||
|
'RS' : {
|
||||||
|
//required slashes
|
||||||
|
//used to insert slash between `:{` and `}{`
|
||||||
|
rgx : /([:}])\/?(\{)/g,
|
||||||
|
save : '$1{{id}}$2',
|
||||||
|
res : '\\/'
|
||||||
|
},
|
||||||
|
'RQ' : {
|
||||||
|
//required query string - everything in between `{? }`
|
||||||
|
rgx : /\{\?([^}]+)\}/g,
|
||||||
|
//everything from `?` till `#` or end of string
|
||||||
|
res : '\\?([^#]+)'
|
||||||
|
},
|
||||||
|
'OQ' : {
|
||||||
|
//optional query string - everything in between `:? :`
|
||||||
|
rgx : /:\?([^:]+):/g,
|
||||||
|
//everything from `?` till `#` or end of string
|
||||||
|
res : '(?:\\?([^#]*))?'
|
||||||
|
},
|
||||||
|
'OR' : {
|
||||||
|
//optional rest - everything in between `: *:`
|
||||||
|
rgx : /:([^:]+)\*:/g,
|
||||||
|
res : '(.*)?' // optional group to avoid passing empty string as captured
|
||||||
|
},
|
||||||
|
'RR' : {
|
||||||
|
//rest param - everything in between `{ *}`
|
||||||
|
rgx : /\{([^}]+)\*\}/g,
|
||||||
|
res : '(.+)'
|
||||||
|
},
|
||||||
|
// required/optional params should come after rest segments
|
||||||
|
'RP' : {
|
||||||
|
//required params - everything between `{ }`
|
||||||
|
rgx : /\{([^}]+)\}/g,
|
||||||
|
res : '([^\\/?]+)'
|
||||||
|
},
|
||||||
|
'OP' : {
|
||||||
|
//optional params - everything between `: :`
|
||||||
|
rgx : /:([^:]+):/g,
|
||||||
|
res : '([^\\/?]+)?\/?'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
LOOSE_SLASH = 1,
|
||||||
|
STRICT_SLASH = 2,
|
||||||
|
LEGACY_SLASH = 3,
|
||||||
|
|
||||||
|
_slashMode = LOOSE_SLASH;
|
||||||
|
|
||||||
|
|
||||||
|
function precompileTokens(){
|
||||||
|
var key, cur;
|
||||||
|
for (key in TOKENS) {
|
||||||
|
if (TOKENS.hasOwnProperty(key)) {
|
||||||
|
cur = TOKENS[key];
|
||||||
|
cur.id = '__CR_'+ key +'__';
|
||||||
|
cur.save = ('save' in cur)? cur.save.replace('{{id}}', cur.id) : cur.id;
|
||||||
|
cur.rRestore = new RegExp(cur.id, 'g');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
precompileTokens();
|
||||||
|
|
||||||
|
|
||||||
|
function captureVals(regex, pattern) {
|
||||||
|
var vals = [], match;
|
||||||
|
// very important to reset lastIndex since RegExp can have "g" flag
|
||||||
|
// and multiple runs might affect the result, specially if matching
|
||||||
|
// same string multiple times on IE 7-8
|
||||||
|
regex.lastIndex = 0;
|
||||||
|
while (match = regex.exec(pattern)) {
|
||||||
|
vals.push(match[1]);
|
||||||
|
}
|
||||||
|
return vals;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParamIds(pattern) {
|
||||||
|
return captureVals(PARAMS_REGEXP, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOptionalParamsIds(pattern) {
|
||||||
|
return captureVals(TOKENS.OP.rgx, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compilePattern(pattern, ignoreCase) {
|
||||||
|
pattern = pattern || '';
|
||||||
|
|
||||||
|
if(pattern){
|
||||||
|
if (_slashMode === LOOSE_SLASH) {
|
||||||
|
pattern = pattern.replace(LOOSE_SLASHES_REGEXP, '');
|
||||||
|
}
|
||||||
|
else if (_slashMode === LEGACY_SLASH) {
|
||||||
|
pattern = pattern.replace(LEGACY_SLASHES_REGEXP, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
//save tokens
|
||||||
|
pattern = replaceTokens(pattern, 'rgx', 'save');
|
||||||
|
//regexp escape
|
||||||
|
pattern = pattern.replace(ESCAPE_CHARS_REGEXP, '\\$&');
|
||||||
|
//restore tokens
|
||||||
|
pattern = replaceTokens(pattern, 'rRestore', 'res');
|
||||||
|
|
||||||
|
if (_slashMode === LOOSE_SLASH) {
|
||||||
|
pattern = '\\/?'+ pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_slashMode !== STRICT_SLASH) {
|
||||||
|
//single slash is treated as empty and end slash is optional
|
||||||
|
pattern += '\\/?';
|
||||||
|
}
|
||||||
|
return new RegExp('^'+ pattern + '$', ignoreCase? 'i' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceTokens(pattern, regexpName, replaceName) {
|
||||||
|
var cur, key;
|
||||||
|
for (key in TOKENS) {
|
||||||
|
if (TOKENS.hasOwnProperty(key)) {
|
||||||
|
cur = TOKENS[key];
|
||||||
|
pattern = pattern.replace(cur[regexpName], cur[replaceName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParamValues(request, regexp, shouldTypecast) {
|
||||||
|
var vals = regexp.exec(request);
|
||||||
|
if (vals) {
|
||||||
|
vals.shift();
|
||||||
|
if (shouldTypecast) {
|
||||||
|
vals = typecastArrayValues(vals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vals;
|
||||||
|
}
|
||||||
|
|
||||||
|
function interpolate(pattern, replacements) {
|
||||||
|
// default to an empty object because pattern might have just
|
||||||
|
// optional arguments
|
||||||
|
replacements = replacements || {};
|
||||||
|
if (typeof pattern !== 'string') {
|
||||||
|
throw new Error('Route pattern should be a string.');
|
||||||
|
}
|
||||||
|
|
||||||
|
var replaceFn = function(match, prop){
|
||||||
|
var val;
|
||||||
|
prop = (prop.substr(0, 1) === '?')? prop.substr(1) : prop;
|
||||||
|
if (replacements[prop] != null) {
|
||||||
|
if (typeof replacements[prop] === 'object') {
|
||||||
|
var queryParts = [], rep;
|
||||||
|
for(var key in replacements[prop]) {
|
||||||
|
rep = replacements[prop][key];
|
||||||
|
if (isArray(rep)) {
|
||||||
|
for (var k in rep) {
|
||||||
|
if ( key.slice(-2) == '[]' ) {
|
||||||
|
queryParts.push(encodeURI(key.slice(0, -2)) + '[]=' + encodeURI(rep[k]));
|
||||||
|
} else {
|
||||||
|
queryParts.push(encodeURI(key + '=' + rep[k]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
queryParts.push(encodeURI(key + '=' + rep));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val = '?' + queryParts.join('&');
|
||||||
|
} else {
|
||||||
|
// make sure value is a string see #gh-54
|
||||||
|
val = String(replacements[prop]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match.indexOf('*') === -1 && val.indexOf('/') !== -1) {
|
||||||
|
throw new Error('Invalid value "'+ val +'" for segment "'+ match +'".');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (match.indexOf('{') !== -1) {
|
||||||
|
throw new Error('The segment '+ match +' is required.');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val = '';
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (! TOKENS.OS.trail) {
|
||||||
|
TOKENS.OS.trail = new RegExp('(?:'+ TOKENS.OS.id +')+$');
|
||||||
|
}
|
||||||
|
|
||||||
|
return pattern
|
||||||
|
.replace(TOKENS.OS.rgx, TOKENS.OS.save)
|
||||||
|
.replace(PARAMS_REGEXP, replaceFn)
|
||||||
|
.replace(TOKENS.OS.trail, '') // remove trailing
|
||||||
|
.replace(TOKENS.OS.rRestore, '/'); // add slash between segments
|
||||||
|
}
|
||||||
|
|
||||||
|
//API
|
||||||
|
return {
|
||||||
|
strict : function(){
|
||||||
|
_slashMode = STRICT_SLASH;
|
||||||
|
},
|
||||||
|
loose : function(){
|
||||||
|
_slashMode = LOOSE_SLASH;
|
||||||
|
},
|
||||||
|
legacy : function(){
|
||||||
|
_slashMode = LEGACY_SLASH;
|
||||||
|
},
|
||||||
|
getParamIds : getParamIds,
|
||||||
|
getOptionalParamsIds : getOptionalParamsIds,
|
||||||
|
getParamValues : getParamValues,
|
||||||
|
compilePattern : compilePattern,
|
||||||
|
interpolate : interpolate
|
||||||
|
};
|
||||||
|
|
||||||
|
}());
|
||||||
|
|
||||||
|
|
||||||
|
return crossroads;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
define(['signals'], factory);
|
||||||
|
} else if (typeof module !== 'undefined' && module.exports) { //Node
|
||||||
|
module.exports = factory(require('signals'));
|
||||||
|
} else {
|
||||||
|
/*jshint sub:true */
|
||||||
|
window['crossroads'] = factory(window['signals']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}());
|
||||||
|
|
||||||
1254
externals/dataTables.colReorder.js
vendored
Normal file
1254
externals/dataTables.colReorder.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
846
externals/dataTables.colResize.js
vendored
Normal file
846
externals/dataTables.colResize.js
vendored
Normal file
@@ -0,0 +1,846 @@
|
|||||||
|
/*! ColResize 0.0.10
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary ColResize
|
||||||
|
* @description Provide the ability to resize columns in a DataTable
|
||||||
|
* @version 0.0.10
|
||||||
|
* @file dataTables.colResize.js
|
||||||
|
* @author Silvacom Ltd.
|
||||||
|
*
|
||||||
|
* For details please refer to: http://www.datatables.net
|
||||||
|
*
|
||||||
|
* Special thank to everyone who has contributed to this plug in
|
||||||
|
* - dykstrad
|
||||||
|
* - tdillan (for 0.0.3 and 0.0.5 bug fixes)
|
||||||
|
* - kylealonius (for 0.0.8 bug fix)
|
||||||
|
* - the86freak (for 0.0.9 bug fix)
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function (window, document, undefined) {
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* DataTables plug-in API functions test
|
||||||
|
*
|
||||||
|
* This are required by ColResize in order to perform the tasks required, and also keep this
|
||||||
|
* code portable, to be used for other column resize projects with DataTables, if needed.
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
var factory = function ($, DataTable) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plug-in for DataTables which will resize the columns depending on the handle clicked
|
||||||
|
* @method $.fn.dataTableExt.oApi.fnColResize
|
||||||
|
* @param object oSettings DataTables settings object - automatically added by DataTables!
|
||||||
|
* @param int iCol Take the column to be resized
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
$.fn.dataTableExt.oApi.fnColResize = function (oSettings, iCol) {
|
||||||
|
var v110 = $.fn.dataTable.Api ? true : false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update DataTables' event handlers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Fire an event so other plug-ins can update */
|
||||||
|
$(oSettings.oInstance).trigger('column-resize', [ oSettings, {
|
||||||
|
"iCol": iCol
|
||||||
|
} ]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ColResize provides column resize control for DataTables
|
||||||
|
* @class ColResize
|
||||||
|
* @constructor
|
||||||
|
* @param {object} dt DataTables settings object
|
||||||
|
* @param {object} opts ColResize options
|
||||||
|
*/
|
||||||
|
var ColResize = function (dt, opts) {
|
||||||
|
var oDTSettings;
|
||||||
|
|
||||||
|
if ($.fn.dataTable.Api) {
|
||||||
|
oDTSettings = new $.fn.dataTable.Api(dt).settings()[0];
|
||||||
|
}
|
||||||
|
// 1.9 compatibility
|
||||||
|
else if (dt.fnSettings) {
|
||||||
|
// DataTables object, convert to the settings object
|
||||||
|
oDTSettings = dt.fnSettings();
|
||||||
|
}
|
||||||
|
else if (typeof dt === 'string') {
|
||||||
|
// jQuery selector
|
||||||
|
if ($.fn.dataTable.fnIsDataTable($(dt)[0])) {
|
||||||
|
oDTSettings = $(dt).eq(0).dataTable().fnSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dt.nodeName && dt.nodeName.toLowerCase() === 'table') {
|
||||||
|
// Table node
|
||||||
|
if ($.fn.dataTable.fnIsDataTable(dt.nodeName)) {
|
||||||
|
oDTSettings = $(dt.nodeName).dataTable().fnSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dt instanceof jQuery) {
|
||||||
|
// jQuery object
|
||||||
|
if ($.fn.dataTable.fnIsDataTable(dt[0])) {
|
||||||
|
oDTSettings = dt.eq(0).dataTable().fnSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// DataTables settings object
|
||||||
|
oDTSettings = dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from camelCase to Hungarian, just as DataTables does
|
||||||
|
if ($.fn.dataTable.camelToHungarian) {
|
||||||
|
$.fn.dataTable.camelToHungarian(ColResize.defaults, opts || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Public class variables
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace Settings object which contains customizable information for ColResize instance
|
||||||
|
*/
|
||||||
|
this.s = {
|
||||||
|
/**
|
||||||
|
* DataTables settings object
|
||||||
|
* @property dt
|
||||||
|
* @type Object
|
||||||
|
* @default null
|
||||||
|
*/
|
||||||
|
"dt": null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialisation object used for this instance
|
||||||
|
* @property init
|
||||||
|
* @type object
|
||||||
|
* @default {}
|
||||||
|
*/
|
||||||
|
"init": $.extend(true, {}, ColResize.defaults, opts),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace Information used for the mouse drag
|
||||||
|
*/
|
||||||
|
"mouse": {
|
||||||
|
"startX": -1,
|
||||||
|
"startY": -1,
|
||||||
|
"targetIndex": -1,
|
||||||
|
"targetColumn": -1,
|
||||||
|
"neighbourIndex": -1,
|
||||||
|
"neighbourColumn": -1
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status variable keeping track of mouse down status
|
||||||
|
* @property isMousedown
|
||||||
|
* @type boolean
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
"isMousedown": false
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace Common and useful DOM elements for the class instance
|
||||||
|
*/
|
||||||
|
this.dom = {
|
||||||
|
/**
|
||||||
|
* Resizing element (the one the mouse is resizing)
|
||||||
|
* @property resize
|
||||||
|
* @type element
|
||||||
|
* @default null
|
||||||
|
*/
|
||||||
|
"resizeCol": null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizing element neighbour (the column next to the one the mouse is resizing)
|
||||||
|
* This is for fixed table resizing.
|
||||||
|
* @property resize
|
||||||
|
* @type element
|
||||||
|
* @default null
|
||||||
|
*/
|
||||||
|
"resizeColNeighbour": null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of events to be restored, used for overriding existing events from other plugins for a time.
|
||||||
|
* @property restoreEvents
|
||||||
|
* @type array
|
||||||
|
* @default []
|
||||||
|
*/
|
||||||
|
"restoreEvents": []
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Constructor logic */
|
||||||
|
this.s.dt = oDTSettings.oInstance.fnSettings();
|
||||||
|
this.s.dt._colResize = this;
|
||||||
|
this._fnConstruct();
|
||||||
|
|
||||||
|
/* Add destroy callback */
|
||||||
|
oDTSettings.oApi._fnCallbackReg(oDTSettings, 'aoDestroyCallback', $.proxy(this._fnDestroy, this), 'ColResize');
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ColResize.prototype = {
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Public methods
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the column widths to the original widths that was detected on
|
||||||
|
* start up.
|
||||||
|
* @return {this} Returns `this` for chaining.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // DataTables initialisation with ColResize
|
||||||
|
* var table = $('#example').dataTable( {
|
||||||
|
* "sDom": 'Zlfrtip'
|
||||||
|
* } );
|
||||||
|
*
|
||||||
|
* // Add click event to a button to reset the ordering
|
||||||
|
* $('#resetOrdering').click( function (e) {
|
||||||
|
* e.preventDefault();
|
||||||
|
* $.fn.dataTable.ColResize( table ).fnReset();
|
||||||
|
* } );
|
||||||
|
*/
|
||||||
|
"fnReset": function () {
|
||||||
|
var a = [];
|
||||||
|
for (var i = 0, iLen = this.s.dt.aoColumns.length; i < iLen; i++) {
|
||||||
|
this.s.dt.aoColumns[i].width = this.s.dt.aoColumns[i]._ColResize_iOrigWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.s.dt.adjust().draw();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Private methods (they are of course public in JS, but recommended as private)
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor logic
|
||||||
|
* @method _fnConstruct
|
||||||
|
* @returns void
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
"_fnConstruct": function () {
|
||||||
|
var that = this;
|
||||||
|
var iLen = that.s.dt.aoColumns.length;
|
||||||
|
var i;
|
||||||
|
|
||||||
|
that._fnSetupMouseListeners();
|
||||||
|
|
||||||
|
/* Add event handlers for the resize handles */
|
||||||
|
for (i = 0; i < iLen; i++) {
|
||||||
|
/* Mark the original column width for later reference */
|
||||||
|
this.s.dt.aoColumns[i]._ColResize_iOrigWidth = this.s.dt.aoColumns[i].width;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._fnSetColumnIndexes();
|
||||||
|
|
||||||
|
/* State saving */
|
||||||
|
this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {
|
||||||
|
that._fnStateSave.call(that, oData);
|
||||||
|
}, "ColResize_State" );
|
||||||
|
|
||||||
|
// State loading
|
||||||
|
this._fnStateLoad();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method _fnStateSave
|
||||||
|
* @param object oState DataTables state
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
"_fnStateSave": function (oState) {
|
||||||
|
this.s.dt.aoColumns.forEach(function(col, index) {
|
||||||
|
oState.columns[index].width = col.sWidthOrig;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If state has been loaded, apply the saved widths to the columns
|
||||||
|
* @method _fnStateLoad
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
"_fnStateLoad": function() {
|
||||||
|
var that = this,
|
||||||
|
loadedState = this.s.dt.oLoadedState;
|
||||||
|
if (loadedState && loadedState.columns) {
|
||||||
|
var colStates = loadedState.columns,
|
||||||
|
currCols = this.s.dt.aoColumns;
|
||||||
|
// Only apply the saved widths if the number of columns is the same.
|
||||||
|
// Otherwise, we don't know if we're applying the width to the correct column.
|
||||||
|
if (colStates.length > 0 && colStates.length === currCols.length) {
|
||||||
|
colStates.forEach(function(state, index) {
|
||||||
|
var col = that.s.dt.aoColumns[index];
|
||||||
|
if (state.width) {
|
||||||
|
col.sWidthOrig = col.sWidth = state.width;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove events of type from obj add it to restoreEvents array to be restored at a later time
|
||||||
|
* @param until string flag when to restore the event
|
||||||
|
* @param obj Object to remove events from
|
||||||
|
* @param type type of event to remove
|
||||||
|
* @param namespace namespace of the event being removed
|
||||||
|
*/
|
||||||
|
"_fnDelayEvents": function (until, obj, type, namespace) {
|
||||||
|
var that = this;
|
||||||
|
//Get the events for the object
|
||||||
|
var events = $._data($(obj).get(0), 'events');
|
||||||
|
$.each(events, function (i, o) {
|
||||||
|
//If the event type matches
|
||||||
|
if (i == type) {
|
||||||
|
//Loop through the possible many events with that type
|
||||||
|
$.each(o, function (k, v) {
|
||||||
|
//Somehow it is possible for the event to be undefined make sure it is defined first
|
||||||
|
if (v) {
|
||||||
|
if (namespace) {
|
||||||
|
//Add the event to the array of events to be restored later
|
||||||
|
that.dom.restoreEvents.push({"until": until, "obj": obj, "type": v.type, "namespace": v.namespace, "handler": v.handler});
|
||||||
|
//If the namespace matches
|
||||||
|
if (v.namespace == namespace) {
|
||||||
|
//Turn off/unbind the event
|
||||||
|
$(obj).off(type + "." + namespace);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Add the event to the array of events to be restored later
|
||||||
|
that.dom.restoreEvents.push({"until": until, "obj": obj, "type": v.type, "namespace": null, "handler": v.handler});
|
||||||
|
//Turn off/unbind the event
|
||||||
|
$(obj).off(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop through restoreEvents array and restore the events on the elements provided
|
||||||
|
*/
|
||||||
|
"_fnRestoreEvents": function (until) {
|
||||||
|
var that = this;
|
||||||
|
//Loop through the events to be restored
|
||||||
|
var i;
|
||||||
|
for (i = that.dom.restoreEvents.length; i--;) {
|
||||||
|
if (that.dom.restoreEvents[i].until == undefined || that.dom.restoreEvents[i].until == null || that.dom.restoreEvents[i].until == until) {
|
||||||
|
if (that.dom.restoreEvents[i].namespace) {
|
||||||
|
//Turn on the event for the object provided
|
||||||
|
$(that.dom.restoreEvents[i].obj).off(that.dom.restoreEvents[i].type + "." + that.dom.restoreEvents[i].namespace).on(that.dom.restoreEvents[i].type + "." + that.dom.restoreEvents[i].namespace, that.dom.restoreEvents[i].handler);
|
||||||
|
that.dom.restoreEvents.splice(i, 1);
|
||||||
|
} else {
|
||||||
|
//Turn on the event for the object provided
|
||||||
|
$(that.dom.restoreEvents[i].obj).off(that.dom.restoreEvents[i].type).on(that.dom.restoreEvents[i].type, that.dom.restoreEvents[i].handler);
|
||||||
|
that.dom.restoreEvents.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Mouse drop and drag
|
||||||
|
*/
|
||||||
|
|
||||||
|
"_fnSetupMouseListeners":function() {
|
||||||
|
var that = this;
|
||||||
|
$(that.s.dt.nTableWrapper).off("mouseenter.ColResize").on("mouseenter.ColResize","th",function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
that._fnMouseEnter.call(that, e, this);
|
||||||
|
});
|
||||||
|
$(that.s.dt.nTableWrapper).off("mouseleave.ColResize").on("mouseleave.ColResize","th",function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
that._fnMouseLeave.call(that, e, this);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add mouse listeners to the resize handle on TH element
|
||||||
|
* @method _fnMouseListener
|
||||||
|
* @param i Column index
|
||||||
|
* @param nTh TH resize handle element clicked on
|
||||||
|
* @returns void
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
"_fnMouseListener": function (i, nTh) {
|
||||||
|
var that = this;
|
||||||
|
$(nTh).off('mouseenter.ColResize').on('mouseenter.ColResize', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
that._fnMouseEnter.call(that, e, nTh);
|
||||||
|
});
|
||||||
|
$(nTh).off('mouseleave.ColResize').on('mouseleave.ColResize', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
that._fnMouseLeave.call(that, e, nTh);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param e Mouse event
|
||||||
|
* @param nTh TH element that the mouse is over
|
||||||
|
*/
|
||||||
|
"_fnMouseEnter": function (e, nTh) {
|
||||||
|
var that = this;
|
||||||
|
if(!that.s.isMousedown) {
|
||||||
|
//Once the mouse has entered the cell add mouse move event to see if the mouse is over resize handle
|
||||||
|
$(nTh).off('mousemove.ColResizeHandle').on('mousemove.ColResizeHandle', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
that._fnResizeHandleCheck.call(that, e, nTh);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear mouse events when the mouse has left the th
|
||||||
|
* @param e Mouse event
|
||||||
|
* @param nTh TH element that the mouse has just left
|
||||||
|
*/
|
||||||
|
"_fnMouseLeave": function (e, nTh) {
|
||||||
|
//Once the mouse has left the TH make suure to remove the mouse move listener
|
||||||
|
$(nTh).off('mousemove.ColResizeHandle');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mouse down on a TH element in the table header
|
||||||
|
* @method _fnMouseDown
|
||||||
|
* @param event e Mouse event
|
||||||
|
* @param element nTh TH element to be resized
|
||||||
|
* @returns void
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
"_fnMouseDown": function (e, nTh) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.s.isMousedown = true;
|
||||||
|
|
||||||
|
/* Store information about the mouse position */
|
||||||
|
var target = $(e.target).closest('th, td');
|
||||||
|
var offset = target.offset();
|
||||||
|
|
||||||
|
/* Store information about the mouse position for resize calculations in mouse move function */
|
||||||
|
this.s.mouse.startX = e.pageX;
|
||||||
|
this.s.mouse.startY = e.pageY;
|
||||||
|
|
||||||
|
//Store the indexes of the columns the mouse is down on
|
||||||
|
var idx = that.dom.resizeCol[0].cellIndex;
|
||||||
|
|
||||||
|
// the last column has no 'right-side' neighbour
|
||||||
|
// with fixed this can make the table smaller
|
||||||
|
if (that.dom.resizeColNeighbour[0] === undefined){
|
||||||
|
var idxNeighbour = 0;
|
||||||
|
} else {
|
||||||
|
var idxNeighbour = that.dom.resizeColNeighbour[0].cellIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (idx === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.s.mouse.targetIndex = idx;
|
||||||
|
this.s.mouse.targetColumn = this.s.dt.aoColumns[ idx ];
|
||||||
|
|
||||||
|
this.s.mouse.neighbourIndex = idxNeighbour;
|
||||||
|
this.s.mouse.neighbourColumn = this.s.dt.aoColumns[ idxNeighbour ];
|
||||||
|
|
||||||
|
/* Add event handlers to the document */
|
||||||
|
$(document)
|
||||||
|
.off('mousemove.ColResize').on('mousemove.ColResize', function (e) {
|
||||||
|
that._fnMouseMove.call(that, e);
|
||||||
|
})
|
||||||
|
.off('mouseup.ColResize').on('mouseup.ColResize', function (e) {
|
||||||
|
that._fnMouseUp.call(that, e);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deal with a mouse move event while dragging to resize a column
|
||||||
|
* @method _fnMouseMove
|
||||||
|
* @param e Mouse event
|
||||||
|
* @returns void
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
"_fnMouseMove": function (e) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
var offset = $(that.s.mouse.targetColumn.nTh).offset();
|
||||||
|
var relativeX = (e.pageX - offset.left);
|
||||||
|
var distFromLeft = relativeX;
|
||||||
|
var distFromRight = $(that.s.mouse.targetColumn.nTh).outerWidth() - relativeX - 1;
|
||||||
|
|
||||||
|
//Change in mouse x position
|
||||||
|
var dx = e.pageX - that.s.mouse.startX;
|
||||||
|
//Get the minimum width of the column (default minimum 10px)
|
||||||
|
var minColumnWidth = Math.max(parseInt($(that.s.mouse.targetColumn.nTh).css('min-width')), 10);
|
||||||
|
//Store the previous width of the column
|
||||||
|
var prevWidth = $(that.s.mouse.targetColumn.nTh).width();
|
||||||
|
//As long as the cursor is past the handle, resize the columns
|
||||||
|
if ((dx > 0 && distFromRight <= 0) || (dx < 0 && distFromRight >= 0)) {
|
||||||
|
if (!that.s.init.tableWidthFixed) {
|
||||||
|
//As long as the width is larger than the minimum
|
||||||
|
var newColWidth = Math.max(minColumnWidth, prevWidth + dx);
|
||||||
|
//Get the width difference (take into account the columns minimum width)
|
||||||
|
var widthDiff = newColWidth - prevWidth;
|
||||||
|
var colResizeIdx = parseInt(that.dom.resizeCol.attr("data-column-index"));
|
||||||
|
//Set datatable column widths
|
||||||
|
that.s.mouse.targetColumn.sWidthOrig = that.s.mouse.targetColumn.sWidth = that.s.mouse.targetColumn.width = newColWidth + "px";
|
||||||
|
var domCols = $(that.s.dt.nTableWrapper).find("th[data-column-index='"+colResizeIdx+"']");
|
||||||
|
//For each table expand the width by the same amount as the column
|
||||||
|
//This accounts for other datatable plugins like FixedColumns
|
||||||
|
domCols.parents("table").each(function() {
|
||||||
|
if(!$(this).parent().hasClass("DTFC_LeftBodyLiner")) {
|
||||||
|
var newWidth = $(this).width() + widthDiff;
|
||||||
|
$(this).width(newWidth);
|
||||||
|
} else {
|
||||||
|
var newWidth =$(that.s.dt.nTableWrapper).find(".DTFC_LeftHeadWrapper").children("table").width();
|
||||||
|
$(this).parents(".DTFC_LeftWrapper").width(newWidth);
|
||||||
|
$(this).parent().width(newWidth+15);
|
||||||
|
$(this).width(newWidth);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//Apply the new width to the columns after the table has been resized
|
||||||
|
domCols.width(that.s.mouse.targetColumn.width);
|
||||||
|
} else {
|
||||||
|
//A neighbour column must exist in order to resize a column in a table with a fixed width
|
||||||
|
if (that.s.mouse.neighbourColumn) {
|
||||||
|
//Get the minimum width of the neighbor column (default minimum 10px)
|
||||||
|
var minColumnNeighbourWidth = Math.max(parseInt($(that.s.mouse.neighbourColumn.nTh).css('min-width')), 10);
|
||||||
|
//Store the previous width of the neighbour column
|
||||||
|
var prevNeighbourWidth = $(that.s.mouse.neighbourColumn.nTh).width();
|
||||||
|
//As long as the width is larger than the minimum
|
||||||
|
var newColWidth = Math.max(minColumnWidth, prevWidth + dx);
|
||||||
|
var newColNeighbourWidth = Math.max(minColumnNeighbourWidth, prevNeighbourWidth - dx);
|
||||||
|
//Get the width difference (take into account the columns minimum width)
|
||||||
|
var widthDiff = newColWidth - prevWidth;
|
||||||
|
var widthDiffNeighbour = newColNeighbourWidth - prevNeighbourWidth;
|
||||||
|
//Get the column index for the column being changed
|
||||||
|
var colResizeIdx = parseInt(that.dom.resizeCol.attr("data-column-index"));
|
||||||
|
var neighbourColResizeIdx = parseInt(that.dom.resizeColNeighbour.attr("data-column-index"));
|
||||||
|
//Set datatable column widths
|
||||||
|
that.s.mouse.neighbourColumn.sWidthOrig = that.s.mouse.neighbourColumn.sWidth = that.s.mouse.neighbourColumn.width = newColNeighbourWidth + "px";
|
||||||
|
that.s.mouse.targetColumn.sWidthOrig = that.s.mouse.targetColumn.sWidth = that.s.mouse.targetColumn.width = newColWidth + "px";
|
||||||
|
//Get list of columns based on column index in all affected tables tables. This accounts for other plugins like FixedColumns
|
||||||
|
var domNeighbourCols = $(that.s.dt.nTableWrapper).find("th[data-column-index='" + neighbourColResizeIdx + "']");
|
||||||
|
var domCols = $(that.s.dt.nTableWrapper).find("th[data-column-index='" + colResizeIdx + "']");
|
||||||
|
//If dx if positive (the width is getting larger) shrink the neighbour columns first
|
||||||
|
if(dx>0) {
|
||||||
|
domNeighbourCols.width(that.s.mouse.neighbourColumn.width);
|
||||||
|
domCols.width(that.s.mouse.targetColumn.width);
|
||||||
|
} else {
|
||||||
|
//Apply the new width to the columns then to the neighbour columns
|
||||||
|
domCols.width(that.s.mouse.targetColumn.width);
|
||||||
|
domNeighbourCols.width(that.s.mouse.neighbourColumn.width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
that.s.mouse.startX = e.pageX;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if the mouse is over the resize handle area
|
||||||
|
* @param e
|
||||||
|
* @param nTh
|
||||||
|
*/
|
||||||
|
"_fnResizeHandleCheck": function (e, nTh) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
var offset = $(nTh).offset();
|
||||||
|
var relativeX = (e.pageX - offset.left);
|
||||||
|
var relativeY = (e.pageY - offset.top);
|
||||||
|
var distFromLeft = relativeX;
|
||||||
|
var distFromRight = $(nTh).outerWidth() - relativeX - 1;
|
||||||
|
|
||||||
|
var handleBuffer = this.s.init.handleWidth / 2;
|
||||||
|
var leftHandleOn = distFromLeft < handleBuffer;
|
||||||
|
var rightHandleOn = distFromRight < handleBuffer;
|
||||||
|
|
||||||
|
//If this is the first table cell
|
||||||
|
if ($(nTh).prev("th").length == 0) {
|
||||||
|
if(this.s.init.rtl)
|
||||||
|
rightHandleOn = false;
|
||||||
|
else
|
||||||
|
leftHandleOn = false;
|
||||||
|
}
|
||||||
|
//If this is the last cell and the table is fixed width don't let them expand the last cell directly
|
||||||
|
if ($(nTh).next("th").length == 0 && this.s.init.tableWidthFixed) {
|
||||||
|
if(this.s.init.rtl)
|
||||||
|
leftHandleOn = false;
|
||||||
|
else
|
||||||
|
rightHandleOn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resizeAvailable = leftHandleOn||rightHandleOn;
|
||||||
|
|
||||||
|
//If table is in right to left mode flip which TH is being resized
|
||||||
|
if (that.s.init.rtl) {
|
||||||
|
//Handle is to the left
|
||||||
|
if (leftHandleOn) {
|
||||||
|
that.dom.resizeCol = $(nTh);
|
||||||
|
that.dom.resizeColNeighbour = $(nTh).next();
|
||||||
|
} else if (rightHandleOn) {
|
||||||
|
that.dom.resizeCol = $(nTh).prev();
|
||||||
|
that.dom.resizeColNeighbour = $(nTh);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Handle is to the right
|
||||||
|
if (rightHandleOn) {
|
||||||
|
that.dom.resizeCol = $(nTh);
|
||||||
|
that.dom.resizeColNeighbour = $(nTh).next();
|
||||||
|
} else if (leftHandleOn) {
|
||||||
|
that.dom.resizeCol = $(nTh).prev();
|
||||||
|
that.dom.resizeColNeighbour = $(nTh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If table width is fixed make sure both columns are resizable else just check the one.
|
||||||
|
if(this.s.init.tableWidthFixed)
|
||||||
|
resizeAvailable &= this.s.init.exclude.indexOf(parseInt($(that.dom.resizeCol).attr("data-column-index"))) == -1 && this.s.init.exclude.indexOf(parseInt($(that.dom.resizeColNeighbour).attr("data-column-index"))) == -1;
|
||||||
|
else
|
||||||
|
resizeAvailable &= this.s.init.exclude.indexOf(parseInt($(that.dom.resizeCol).attr("data-column-index"))) == -1;
|
||||||
|
|
||||||
|
$(nTh).off('mousedown.ColResize');
|
||||||
|
if (resizeAvailable) {
|
||||||
|
$(nTh).css("cursor", "ew-resize");
|
||||||
|
|
||||||
|
//Delay other mousedown events from the Reorder plugin
|
||||||
|
that._fnDelayEvents(null, nTh, "mousedown", "ColReorder");
|
||||||
|
that._fnDelayEvents("click", nTh, "click", "DT");
|
||||||
|
|
||||||
|
$(nTh).off('mousedown.ColResize').on('mousedown.ColResize', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
that._fnMouseDown.call(that, e, nTh);
|
||||||
|
})
|
||||||
|
.off('click.ColResize').on('click.ColResize', function (e) {
|
||||||
|
that._fnClick.call(that, e);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$(nTh).css("cursor", "pointer");
|
||||||
|
$(nTh).off('mousedown.ColResize click.ColResize');
|
||||||
|
//Restore any events that were removed
|
||||||
|
that._fnRestoreEvents();
|
||||||
|
//This is to restore column sorting on click functionality
|
||||||
|
if (!that.s.isMousedown)
|
||||||
|
//Restore click event if mouse is not down
|
||||||
|
this._fnRestoreEvents("click");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"_fnClick": function (e) {
|
||||||
|
var that = this;
|
||||||
|
that.s.isMousedown = false;
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish off the mouse drag
|
||||||
|
* @method _fnMouseUp
|
||||||
|
* @param e Mouse event
|
||||||
|
* @returns void
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
"_fnMouseUp": function (e) {
|
||||||
|
var that = this;
|
||||||
|
that.s.isMousedown = false;
|
||||||
|
|
||||||
|
//Fix width of column to be the size the dom is limited to (for when user sets min-width on a column)
|
||||||
|
that.s.mouse.targetColumn.width = that.dom.resizeCol.width();
|
||||||
|
|
||||||
|
$(document).off('mousemove.ColResize mouseup.ColResize');
|
||||||
|
this.s.dt.oInstance.fnAdjustColumnSizing();
|
||||||
|
//Table width fix, prevents extra gaps between tables
|
||||||
|
var LeftWrapper = $(that.s.dt.nTableWrapper).find(".DTFC_LeftWrapper");
|
||||||
|
var DTFC_LeftWidth = LeftWrapper.width();
|
||||||
|
LeftWrapper.children(".DTFC_LeftHeadWrapper").children("table").width(DTFC_LeftWidth);
|
||||||
|
|
||||||
|
if (that.s.init.resizeCallback) {
|
||||||
|
that.s.init.resizeCallback.call(that, that.s.mouse.targetColumn);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up ColResize memory references and event handlers
|
||||||
|
* @method _fnDestroy
|
||||||
|
* @returns void
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
"_fnDestroy": function () {
|
||||||
|
var i, iLen;
|
||||||
|
|
||||||
|
for (i = 0, iLen = this.s.dt.aoDrawCallback.length; i < iLen; i++) {
|
||||||
|
if (this.s.dt.aoDrawCallback[i].sName === 'ColResize_Pre') {
|
||||||
|
this.s.dt.aoDrawCallback.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this.s.dt.nTHead).find('*').off('.ColResize');
|
||||||
|
|
||||||
|
$.each(this.s.dt.aoColumns, function (i, column) {
|
||||||
|
$(column.nTh).removeAttr('data-column-index');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.s.dt._colResize = null;
|
||||||
|
this.s = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a data attribute to the column headers, so we know the index of
|
||||||
|
* the row to be reordered. This allows fast detection of the index, and
|
||||||
|
* for this plug-in to work with FixedHeader which clones the nodes.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
"_fnSetColumnIndexes": function () {
|
||||||
|
$.each(this.s.dt.aoColumns, function (i, column) {
|
||||||
|
$(column.nTh).attr('data-column-index', i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Static parameters
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ColResize default settings for initialisation
|
||||||
|
* @namespace
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
ColResize.defaults = {
|
||||||
|
/**
|
||||||
|
* Callback function that is fired when columns are resized
|
||||||
|
* @type function():void
|
||||||
|
* @default null
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
"resizeCallback": null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude array for columns that are not resizable
|
||||||
|
* @property exclude
|
||||||
|
* @type array of indexes that are excluded from resizing
|
||||||
|
* @default []
|
||||||
|
*/
|
||||||
|
"exclude": [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if user is using a fixed table width or dynamic
|
||||||
|
* if true:
|
||||||
|
* -Columns will resize themselves and their neighbour
|
||||||
|
* -If neighbour is excluded resize will not occur
|
||||||
|
* if false:
|
||||||
|
* -Columns will resize themselves and increase or decrease the width of the table accordingly
|
||||||
|
*/
|
||||||
|
"tableWidthFixed": true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Width of the resize handle in pixels
|
||||||
|
* @property handleWidth
|
||||||
|
* @type int (pixels)
|
||||||
|
* @default 10
|
||||||
|
*/
|
||||||
|
"handleWidth": 10,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right to left support, when true flips which column they are resizing on mouse down
|
||||||
|
* @property rtl
|
||||||
|
* @type bool
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
"rtl": false
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* Constants
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ColResize version
|
||||||
|
* @constant version
|
||||||
|
* @type String
|
||||||
|
* @default As code
|
||||||
|
*/
|
||||||
|
ColResize.version = "0.0.10";
|
||||||
|
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* DataTables interfaces
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
// Expose
|
||||||
|
$.fn.dataTable.ColResize = ColResize;
|
||||||
|
$.fn.DataTable.ColResize = ColResize;
|
||||||
|
|
||||||
|
|
||||||
|
// Register a new feature with DataTables
|
||||||
|
if (typeof $.fn.dataTable == "function" &&
|
||||||
|
typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
|
||||||
|
$.fn.dataTableExt.fnVersionCheck('1.9.3')) {
|
||||||
|
$.fn.dataTableExt.aoFeatures.push({
|
||||||
|
"fnInit": function (settings) {
|
||||||
|
var table = settings.oInstance;
|
||||||
|
|
||||||
|
if (!settings._colResize) {
|
||||||
|
var dtInit = settings.oInit;
|
||||||
|
var opts = dtInit.colResize || dtInit.oColResize || {};
|
||||||
|
|
||||||
|
new ColResize(settings, opts);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
table.oApi._fnLog(settings, 1, "ColResize attempted to initialise twice. Ignoring second");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
/* No node for DataTables to insert */
|
||||||
|
},
|
||||||
|
"cFeature": "Z",
|
||||||
|
"sFeature": "ColResize"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alert("Warning: ColResize requires DataTables 1.9.3 or greater - www.datatables.net/download");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// API augmentation
|
||||||
|
if ($.fn.dataTable.Api) {
|
||||||
|
$.fn.dataTable.Api.register('colResize.reset()', function () {
|
||||||
|
return this.iterator('table', function (ctx) {
|
||||||
|
ctx._colResize.fnReset();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ColResize;
|
||||||
|
}; // /factory
|
||||||
|
|
||||||
|
|
||||||
|
// Define as an AMD module if possible
|
||||||
|
if ( typeof define === 'function' && define.amd ) {
|
||||||
|
define( ['jquery', 'datatables'], factory );
|
||||||
|
}
|
||||||
|
else if ( typeof exports === 'object' ) {
|
||||||
|
// Node/CommonJS
|
||||||
|
factory( require('jquery'), require('datatables') );
|
||||||
|
}
|
||||||
|
else if (jQuery && !jQuery.fn.dataTable.ColResize) {
|
||||||
|
// Otherwise simply initialise as normal, stopping multiple evaluation
|
||||||
|
factory(jQuery, jQuery.fn.dataTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})(window, document);
|
||||||
15321
externals/datatables.js
vendored
Normal file
15321
externals/datatables.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
externals/jquery.dataTables.min_ase.css
vendored
Normal file
1
externals/jquery.dataTables.min_ase.css
vendored
Normal file
File diff suppressed because one or more lines are too long
160
externals/jquery.dataTables.min_ase.js
vendored
Normal file
160
externals/jquery.dataTables.min_ase.js
vendored
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/*! DataTables 1.10.7
|
||||||
|
* ©2008-2015 SpryMedia Ltd - datatables.net/license
|
||||||
|
*/
|
||||||
|
(function(Ea,Q,k){var P=function(h){function W(a){var b,c,e={};h.each(a,function(d){if((b=d.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=d.replace(b[0],b[2].toLowerCase()),e[c]=d,"o"===b[1]&&W(a[d])});a._hungarianMap=e}function H(a,b,c){a._hungarianMap||W(a);var e;h.each(b,function(d){e=a._hungarianMap[d];if(e!==k&&(c||b[e]===k))"o"===e.charAt(0)?(b[e]||(b[e]={}),h.extend(!0,b[e],b[d]),H(a[e],b[e],c)):b[e]=b[d]})}function P(a){var b=m.defaults.oLanguage,c=a.sZeroRecords;
|
||||||
|
!a.sEmptyTable&&(c&&"No data available in table"===b.sEmptyTable)&&E(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(c&&"Loading..."===b.sLoadingRecords)&&E(a,a,"sZeroRecords","sLoadingRecords");a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&db(a)}function eb(a){A(a,"ordering","bSort");A(a,"orderMulti","bSortMulti");A(a,"orderClasses","bSortClasses");A(a,"orderCellsTop","bSortCellsTop");A(a,"order","aaSorting");A(a,"orderFixed","aaSortingFixed");A(a,"paging","bPaginate");
|
||||||
|
A(a,"pagingType","sPaginationType");A(a,"pageLength","iDisplayLength");A(a,"searching","bFilter");if(a=a.aoSearchCols)for(var b=0,c=a.length;b<c;b++)a[b]&&H(m.models.oSearch,a[b])}function fb(a){A(a,"orderable","bSortable");A(a,"orderData","aDataSort");A(a,"orderSequence","asSorting");A(a,"orderDataType","sortDataType");var b=a.aDataSort;b&&!h.isArray(b)&&(a.aDataSort=[b])}function gb(a){var a=a.oBrowser,b=h("<div/>").css({position:"absolute",top:0,left:0,height:1,width:1,overflow:"hidden"}).append(h("<div/>").css({position:"absolute",
|
||||||
|
top:1,left:1,width:100,overflow:"scroll"}).append(h('<div class="test"/>').css({width:"100%",height:10}))).appendTo("body"),c=b.find(".test");a.bScrollOversize=100===c[0].offsetWidth;a.bScrollbarLeft=1!==Math.round(c.offset().left);b.remove()}function hb(a,b,c,e,d,f){var g,j=!1;c!==k&&(g=c,j=!0);for(;e!==d;)a.hasOwnProperty(e)&&(g=j?b(g,a[e],e,a):a[e],j=!0,e+=f);return g}function Fa(a,b){var c=m.defaults.column,e=a.aoColumns.length,c=h.extend({},m.models.oColumn,c,{nTh:b?b:Q.createElement("th"),sTitle:c.sTitle?
|
||||||
|
c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[e],mData:c.mData?c.mData:e,idx:e});a.aoColumns.push(c);c=a.aoPreSearchCols;c[e]=h.extend({},m.models.oSearch,c[e]);ka(a,e,h(b).data())}function ka(a,b,c){var b=a.aoColumns[b],e=a.oClasses,d=h(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=d.attr("width")||null;var f=(d.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==k&&null!==c&&(fb(c),H(m.defaults.column,c),c.mDataProp!==k&&!c.mData&&(c.mData=c.mDataProp),c.sType&&
|
||||||
|
(b._sManualType=c.sType),c.className&&!c.sClass&&(c.sClass=c.className),h.extend(b,c),E(b,c,"sWidth","sWidthOrig"),c.iDataSort!==k&&(b.aDataSort=[c.iDataSort]),E(b,c,"aDataSort"));var g=b.mData,j=R(g),i=b.mRender?R(b.mRender):null,c=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};b._bAttrSrc=h.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b.fnGetData=function(a,b,c){var e=j(a,b,k,c);return i&&b?i(e,b,a,c):e};b.fnSetData=function(a,b,c){return S(g)(a,b,c)};"number"!==typeof g&&
|
||||||
|
(a._rowReadObject=!0);a.oFeatures.bSort||(b.bSortable=!1,d.addClass(e.sSortableNone));a=-1!==h.inArray("asc",b.asSorting);c=-1!==h.inArray("desc",b.asSorting);!b.bSortable||!a&&!c?(b.sSortingClass=e.sSortableNone,b.sSortingClassJUI=""):a&&!c?(b.sSortingClass=e.sSortableAsc,b.sSortingClassJUI=e.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=e.sSortableDesc,b.sSortingClassJUI=e.sSortJUIDescAllowed):(b.sSortingClass=e.sSortable,b.sSortingClassJUI=e.sSortJUI)}function X(a){if(!1!==a.oFeatures.bAutoWidth){var b=
|
||||||
|
a.aoColumns;Ga(a);for(var c=0,e=b.length;c<e;c++)b[c].nTh.style.width=b[c].sWidth}b=a.oScroll;(""!==b.sY||""!==b.sX)&&Y(a);w(a,null,"column-sizing",[a])}function la(a,b){var c=Z(a,"bVisible");return"number"===typeof c[b]?c[b]:null}function $(a,b){var c=Z(a,"bVisible"),c=h.inArray(b,c);return-1!==c?c:null}function aa(a){return Z(a,"bVisible").length}function Z(a,b){var c=[];h.map(a.aoColumns,function(a,d){a[b]&&c.push(d)});return c}function Ha(a){var b=a.aoColumns,c=a.aoData,e=m.ext.type.detect,d,
|
||||||
|
f,g,j,i,h,l,q,n;d=0;for(f=b.length;d<f;d++)if(l=b[d],n=[],!l.sType&&l._sManualType)l.sType=l._sManualType;else if(!l.sType){g=0;for(j=e.length;g<j;g++){i=0;for(h=c.length;i<h;i++){n[i]===k&&(n[i]=x(a,i,d,"type"));q=e[g](n[i],a);if(!q&&g!==e.length-1)break;if("html"===q)break}if(q){l.sType=q;break}}l.sType||(l.sType="string")}}function ib(a,b,c,e){var d,f,g,j,i,o,l=a.aoColumns;if(b)for(d=b.length-1;0<=d;d--){o=b[d];var q=o.targets!==k?o.targets:o.aTargets;h.isArray(q)||(q=[q]);f=0;for(g=q.length;f<
|
||||||
|
g;f++)if("number"===typeof q[f]&&0<=q[f]){for(;l.length<=q[f];)Fa(a);e(q[f],o)}else if("number"===typeof q[f]&&0>q[f])e(l.length+q[f],o);else if("string"===typeof q[f]){j=0;for(i=l.length;j<i;j++)("_all"==q[f]||h(l[j].nTh).hasClass(q[f]))&&e(j,o)}}if(c){d=0;for(a=c.length;d<a;d++)e(d,c[d])}}function K(a,b,c,e){var d=a.aoData.length,f=h.extend(!0,{},m.models.oRow,{src:c?"dom":"data"});f._aData=b;a.aoData.push(f);for(var b=a.aoColumns,f=0,g=b.length;f<g;f++)c&&Ia(a,d,f,x(a,d,f)),b[f].sType=null;a.aiDisplayMaster.push(d);
|
||||||
|
(c||!a.oFeatures.bDeferRender)&&Ja(a,d,c,e);return d}function ma(a,b){var c;b instanceof h||(b=h(b));return b.map(function(b,d){c=na(a,d);return K(a,c.data,d,c.cells)})}function x(a,b,c,e){var d=a.iDraw,f=a.aoColumns[c],g=a.aoData[b]._aData,j=f.sDefaultContent,c=f.fnGetData(g,e,{settings:a,row:b,col:c});if(c===k)return a.iDrawError!=d&&null===j&&(I(a,0,"Requested unknown parameter "+("function"==typeof f.mData?"{function}":"'"+f.mData+"'")+" for row "+b,4),a.iDrawError=d),j;if((c===g||null===c)&&
|
||||||
|
null!==j)c=j;else if("function"===typeof c)return c.call(g);return null===c&&"display"==e?"":c}function Ia(a,b,c,e){a.aoColumns[c].fnSetData(a.aoData[b]._aData,e,{settings:a,row:b,col:c})}function Ka(a){return h.map(a.match(/(\\.|[^\.])+/g),function(a){return a.replace(/\\./g,".")})}function R(a){if(h.isPlainObject(a)){var b={};h.each(a,function(a,c){c&&(b[a]=R(c))});return function(a,c,f,g){var j=b[c]||b._;return j!==k?j(a,c,f,g):a}}if(null===a)return function(a){return a};if("function"===typeof a)return function(b,
|
||||||
|
c,f,g){return a(b,c,f,g)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var c=function(a,b,f){var g,j;if(""!==f){j=Ka(f);for(var i=0,h=j.length;i<h;i++){f=j[i].match(ba);g=j[i].match(T);if(f){j[i]=j[i].replace(ba,"");""!==j[i]&&(a=a[j[i]]);g=[];j.splice(0,i+1);j=j.join(".");i=0;for(h=a.length;i<h;i++)g.push(c(a[i],b,j));a=f[0].substring(1,f[0].length-1);a=""===a?g:g.join(a);break}else if(g){j[i]=j[i].replace(T,"");a=a[j[i]]();continue}if(null===a||a[j[i]]===
|
||||||
|
k)return k;a=a[j[i]]}}return a};return function(b,d){return c(b,d,a)}}return function(b){return b[a]}}function S(a){if(h.isPlainObject(a))return S(a._);if(null===a)return function(){};if("function"===typeof a)return function(b,e,d){a(b,"set",e,d)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var b=function(a,e,d){var d=Ka(d),f;f=d[d.length-1];for(var g,j,i=0,h=d.length-1;i<h;i++){g=d[i].match(ba);j=d[i].match(T);if(g){d[i]=d[i].replace(ba,"");a[d[i]]=[];
|
||||||
|
f=d.slice();f.splice(0,i+1);g=f.join(".");j=0;for(h=e.length;j<h;j++)f={},b(f,e[j],g),a[d[i]].push(f);return}j&&(d[i]=d[i].replace(T,""),a=a[d[i]](e));if(null===a[d[i]]||a[d[i]]===k)a[d[i]]={};a=a[d[i]]}if(f.match(T))a[f.replace(T,"")](e);else a[f.replace(ba,"")]=e};return function(c,e){return b(c,e,a)}}return function(b,e){b[a]=e}}function La(a){return D(a.aoData,"_aData")}function oa(a){a.aoData.length=0;a.aiDisplayMaster.length=0;a.aiDisplay.length=0}function pa(a,b,c){for(var e=-1,d=0,f=a.length;d<
|
||||||
|
f;d++)a[d]==b?e=d:a[d]>b&&a[d]--; -1!=e&&c===k&&a.splice(e,1)}function ca(a,b,c,e){var d=a.aoData[b],f,g=function(c,f){for(;c.childNodes.length;)c.removeChild(c.firstChild);c.innerHTML=x(a,b,f,"display")};if("dom"===c||(!c||"auto"===c)&&"dom"===d.src)d._aData=na(a,d,e,e===k?k:d._aData).data;else{var j=d.anCells;if(j)if(e!==k)g(j[e],e);else{c=0;for(f=j.length;c<f;c++)g(j[c],c)}}d._aSortData=null;d._aFilterData=null;g=a.aoColumns;if(e!==k)g[e].sType=null;else{c=0;for(f=g.length;c<f;c++)g[c].sType=null;
|
||||||
|
Ma(d)}}function na(a,b,c,e){var d=[],f=b.firstChild,g,j=0,i,o=a.aoColumns,l=a._rowReadObject,e=e||l?{}:[],q=function(a,b){if("string"===typeof a){var c=a.indexOf("@");-1!==c&&(c=a.substring(c+1),S(a)(e,b.getAttribute(c)))}},a=function(a){if(c===k||c===j)g=o[j],i=h.trim(a.innerHTML),g&&g._bAttrSrc?(S(g.mData._)(e,i),q(g.mData.sort,a),q(g.mData.type,a),q(g.mData.filter,a)):l?(g._setter||(g._setter=S(g.mData)),g._setter(e,i)):e[j]=i;j++};if(f)for(;f;){b=f.nodeName.toUpperCase();if("TD"==b||"TH"==b)a(f),
|
||||||
|
d.push(f);f=f.nextSibling}else{d=b.anCells;f=0;for(b=d.length;f<b;f++)a(d[f])}return{data:e,cells:d}}function Ja(a,b,c,e){var d=a.aoData[b],f=d._aData,g=[],j,i,h,l,q;if(null===d.nTr){j=c||Q.createElement("tr");d.nTr=j;d.anCells=g;j._DT_RowIndex=b;Ma(d);l=0;for(q=a.aoColumns.length;l<q;l++){h=a.aoColumns[l];i=c?e[l]:Q.createElement(h.sCellType);g.push(i);if(!c||h.mRender||h.mData!==l)i.innerHTML=x(a,b,l,"display");h.sClass&&(i.className+=" "+h.sClass);h.bVisible&&!c?j.appendChild(i):!h.bVisible&&c&&
|
||||||
|
i.parentNode.removeChild(i);h.fnCreatedCell&&h.fnCreatedCell.call(a.oInstance,i,x(a,b,l),f,b,l)}w(a,"aoRowCreatedCallback",null,[j,f,b])}d.nTr.setAttribute("role","row")}function Ma(a){var b=a.nTr,c=a._aData;if(b){c.DT_RowId&&(b.id=c.DT_RowId);if(c.DT_RowClass){var e=c.DT_RowClass.split(" ");a.__rowc=a.__rowc?Na(a.__rowc.concat(e)):e;h(b).removeClass(a.__rowc.join(" ")).addClass(c.DT_RowClass)}c.DT_RowAttr&&h(b).attr(c.DT_RowAttr);c.DT_RowData&&h(b).data(c.DT_RowData)}}function jb(a){var b,c,e,d,
|
||||||
|
f,g=a.nTHead,j=a.nTFoot,i=0===h("th, td",g).length,o=a.oClasses,l=a.aoColumns;i&&(d=h("<tr/>").appendTo(g));b=0;for(c=l.length;b<c;b++)f=l[b],e=h(f.nTh).addClass(f.sClass),i&&e.appendTo(d),a.oFeatures.bSort&&(e.addClass(f.sSortingClass),!1!==f.bSortable&&(e.attr("tabindex",a.iTabIndex).attr("aria-controls",a.sTableId),Oa(a,f.nTh,b))),f.sTitle!=e.html()&&e.html(f.sTitle),Pa(a,"header")(a,e,f,o);i&&da(a.aoHeader,g);h(g).find(">tr").attr("role","row");h(g).find(">tr>th, >tr>td").addClass(o.sHeaderTH);
|
||||||
|
h(j).find(">tr>th, >tr>td").addClass(o.sFooterTH);if(null!==j){a=a.aoFooter[0];b=0;for(c=a.length;b<c;b++)f=l[b],f.nTf=a[b].cell,f.sClass&&h(f.nTf).addClass(f.sClass)}}function ea(a,b,c){var e,d,f,g=[],j=[],i=a.aoColumns.length,o;if(b){c===k&&(c=!1);e=0;for(d=b.length;e<d;e++){g[e]=b[e].slice();g[e].nTr=b[e].nTr;for(f=i-1;0<=f;f--)!a.aoColumns[f].bVisible&&!c&&g[e].splice(f,1);j.push([])}e=0;for(d=g.length;e<d;e++){if(a=g[e].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=g[e].length;f<b;f++)if(o=
|
||||||
|
i=1,j[e][f]===k){a.appendChild(g[e][f].cell);for(j[e][f]=1;g[e+i]!==k&&g[e][f].cell==g[e+i][f].cell;)j[e+i][f]=1,i++;for(;g[e][f+o]!==k&&g[e][f].cell==g[e][f+o].cell;){for(c=0;c<i;c++)j[e+c][f+o]=1;o++}h(g[e][f].cell).attr("rowspan",i).attr("colspan",o)}}}}function M(a){var b=w(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==h.inArray(!1,b))C(a,!1);else{var b=[],c=0,e=a.asStripeClasses,d=e.length,f=a.oLanguage,g=a.iInitDisplayStart,j="ssp"==B(a),i=a.aiDisplay;a.bDrawing=!0;g!==k&&-1!==g&&(a._iDisplayStart=
|
||||||
|
j?g:g>=a.fnRecordsDisplay()?0:g,a.iInitDisplayStart=-1);var g=a._iDisplayStart,o=a.fnDisplayEnd();if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,C(a,!1);else if(j){if(!a.bDestroying&&!kb(a))return}else a.iDraw++;if(0!==i.length){f=j?a.aoData.length:o;for(j=j?0:g;j<f;j++){var l=i[j],q=a.aoData[l];null===q.nTr&&Ja(a,l);l=q.nTr;if(0!==d){var n=e[c%d];q._sRowStripe!=n&&(h(l).removeClass(q._sRowStripe).addClass(n),q._sRowStripe=n)}w(a,"aoRowCallback",null,[l,q._aData,c,j]);b.push(l);c++}}else c=f.sZeroRecords,
|
||||||
|
1==a.iDraw&&"ajax"==B(a)?c=f.sLoadingRecords:f.sEmptyTable&&0===a.fnRecordsTotal()&&(c=f.sEmptyTable),b[0]=h("<tr/>",{"class":d?e[0]:""}).append(h("<td />",{valign:"top",colSpan:aa(a),"class":a.oClasses.sRowEmpty}).html(c))[0];w(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],La(a),g,o,i]);w(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],La(a),g,o,i]);e=h(a.nTBody);e.children().detach();e.append(h(b));w(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=
|
||||||
|
!1}}function N(a,b){var c=a.oFeatures,e=c.bFilter;c.bSort&&lb(a);e?fa(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;M(a);a._drawHold=!1}function mb(a){var b=a.oClasses,c=h(a.nTable),c=h("<div/>").insertBefore(c),e=a.oFeatures,d=h("<div/>",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=d[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,j,i,o,l,q,n=0;n<f.length;n++){g=
|
||||||
|
null;j=f[n];if("<"==j){i=h("<div/>")[0];o=f[n+1];if("'"==o||'"'==o){l="";for(q=2;f[n+q]!=o;)l+=f[n+q],q++;"H"==l?l=b.sJUIHeader:"F"==l&&(l=b.sJUIFooter);-1!=l.indexOf(".")?(o=l.split("."),i.id=o[0].substr(1,o[0].length-1),i.className=o[1]):"#"==l.charAt(0)?i.id=l.substr(1,l.length-1):i.className=l;n+=q}d.append(i);d=h(i)}else if(">"==j)d=d.parent();else if("l"==j&&e.bPaginate&&e.bLengthChange)g=nb(a);else if("f"==j&&e.bFilter)g=ob(a);else if("r"==j&&e.bProcessing)g=pb(a);else if("t"==j)g=qb(a);else if("i"==
|
||||||
|
j&&e.bInfo)g=rb(a);else if("p"==j&&e.bPaginate)g=sb(a);else if(0!==m.ext.feature.length){i=m.ext.feature;q=0;for(o=i.length;q<o;q++)if(j==i[q].cFeature){g=i[q].fnInit(a);break}}g&&(i=a.aanFeatures,i[j]||(i[j]=[]),i[j].push(g),d.append(g))}c.replaceWith(d)}function da(a,b){var c=h(b).children("tr"),e,d,f,g,j,i,o,l,q,n;a.splice(0,a.length);f=0;for(i=c.length;f<i;f++)a.push([]);f=0;for(i=c.length;f<i;f++){e=c[f];for(d=e.firstChild;d;){if("TD"==d.nodeName.toUpperCase()||"TH"==d.nodeName.toUpperCase()){l=
|
||||||
|
1*d.getAttribute("colspan");q=1*d.getAttribute("rowspan");l=!l||0===l||1===l?1:l;q=!q||0===q||1===q?1:q;g=0;for(j=a[f];j[g];)g++;o=g;n=1===l?!0:!1;for(j=0;j<l;j++)for(g=0;g<q;g++)a[f+g][o+j]={cell:d,unique:n},a[f+g].nTr=e}d=d.nextSibling}}}function qa(a,b,c){var e=[];c||(c=a.aoHeader,b&&(c=[],da(c,b)));for(var b=0,d=c.length;b<d;b++)for(var f=0,g=c[b].length;f<g;f++)if(c[b][f].unique&&(!e[f]||!a.bSortCellsTop))e[f]=c[b][f].cell;return e}function ra(a,b,c){w(a,"aoServerParams","serverParams",[b]);
|
||||||
|
if(b&&h.isArray(b)){var e={},d=/(.*?)\[\]$/;h.each(b,function(a,b){var c=b.name.match(d);c?(c=c[0],e[c]||(e[c]=[]),e[c].push(b.value)):e[b.name]=b.value});b=e}var f,g=a.ajax,j=a.oInstance,i=function(b){w(a,null,"xhr",[a,b,a.jqXHR]);c(b)};if(h.isPlainObject(g)&&g.data){f=g.data;var o=h.isFunction(f)?f(b,a):f,b=h.isFunction(f)&&o?o:h.extend(!0,b,o);delete g.data}o={data:b,success:function(b){var c=b.error||b.sError;c&&I(a,0,c);a.json=b;i(b)},dataType:"json",cache:!1,type:a.sServerMethod,error:function(b,
|
||||||
|
c){var f=w(a,null,"xhr",[a,null,a.jqXHR]);-1===h.inArray(!0,f)&&("parsererror"==c?I(a,0,"Invalid JSON response",1):4===b.readyState&&I(a,0,"Ajax error",7));C(a,!1)}};a.oAjaxData=b;w(a,null,"preXhr",[a,b]);a.fnServerData?a.fnServerData.call(j,a.sAjaxSource,h.map(b,function(a,b){return{name:b,value:a}}),i,a):a.sAjaxSource||"string"===typeof g?a.jqXHR=h.ajax(h.extend(o,{url:g||a.sAjaxSource})):h.isFunction(g)?a.jqXHR=g.call(j,b,i,a):(a.jqXHR=h.ajax(h.extend(o,g)),g.data=f)}function kb(a){return a.bAjaxDataGet?
|
||||||
|
(a.iDraw++,C(a,!0),ra(a,tb(a),function(b){ub(a,b)}),!1):!0}function tb(a){var b=a.aoColumns,c=b.length,e=a.oFeatures,d=a.oPreviousSearch,f=a.aoPreSearchCols,g,j=[],i,o,l,q=U(a);g=a._iDisplayStart;i=!1!==e.bPaginate?a._iDisplayLength:-1;var n=function(a,b){j.push({name:a,value:b})};n("sEcho",a.iDraw);n("iColumns",c);n("sColumns",D(b,"sName").join(","));n("iDisplayStart",g);n("iDisplayLength",i);var k={draw:a.iDraw,columns:[],order:[],start:g,length:i,search:{value:d.sSearch,regex:d.bRegex}};for(g=
|
||||||
|
0;g<c;g++)o=b[g],l=f[g],i="function"==typeof o.mData?"function":o.mData,k.columns.push({data:i,name:o.sName,searchable:o.bSearchable,orderable:o.bSortable,search:{value:l.sSearch,regex:l.bRegex}}),n("mDataProp_"+g,i),e.bFilter&&(n("sSearch_"+g,l.sSearch),n("bRegex_"+g,l.bRegex),n("bSearchable_"+g,o.bSearchable)),e.bSort&&n("bSortable_"+g,o.bSortable);e.bFilter&&(n("sSearch",d.sSearch),n("bRegex",d.bRegex));e.bSort&&(h.each(q,function(a,b){k.order.push({column:b.col,dir:b.dir});n("iSortCol_"+a,b.col);
|
||||||
|
n("sSortDir_"+a,b.dir)}),n("iSortingCols",q.length));b=m.ext.legacy.ajax;return null===b?a.sAjaxSource?j:k:b?j:k}function ub(a,b){var c=sa(a,b),e=b.sEcho!==k?b.sEcho:b.draw,d=b.iTotalRecords!==k?b.iTotalRecords:b.recordsTotal,f=b.iTotalDisplayRecords!==k?b.iTotalDisplayRecords:b.recordsFiltered;if(e){if(1*e<a.iDraw)return;a.iDraw=1*e}oa(a);a._iRecordsTotal=parseInt(d,10);a._iRecordsDisplay=parseInt(f,10);e=0;for(d=c.length;e<d;e++)K(a,c[e]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;
|
||||||
|
M(a);a._bInitComplete||ta(a,b);a.bAjaxDataGet=!0;C(a,!1)}function sa(a,b){var c=h.isPlainObject(a.ajax)&&a.ajax.dataSrc!==k?a.ajax.dataSrc:a.sAjaxDataProp;return"data"===c?b.aaData||b[c]:""!==c?R(c)(b):b}function ob(a){var b=a.oClasses,c=a.sTableId,e=a.oLanguage,d=a.oPreviousSearch,f=a.aanFeatures,g='<input type="search" class="'+b.sFilterInput+'"/>',j=e.sSearch,j=j.match(/_INPUT_/)?j.replace("_INPUT_",g):j+g,b=h("<div/>",{id:!f.f?c+"_filter":null,"class":b.sFilter}).append(h("<label/>").append(j)),
|
||||||
|
f=function(){var b=!this.value?"":this.value;b!=d.sSearch&&(fa(a,{sSearch:b,bRegex:d.bRegex,bSmart:d.bSmart,bCaseInsensitive:d.bCaseInsensitive}),a._iDisplayStart=0,M(a))},g=null!==a.searchDelay?a.searchDelay:"ssp"===B(a)?400:0,i=h("input",b).val(d.sSearch).attr("placeholder",e.sSearchPlaceholder).bind("keyup.DT search.DT input.DT paste.DT cut.DT",g?ua(f,g):f).bind("keypress.DT",function(a){if(13==a.keyCode)return!1}).attr("aria-controls",c);h(a.nTable).on("search.dt.DT",function(b,c){if(a===c)try{i[0]!==
|
||||||
|
Q.activeElement&&i.val(d.sSearch)}catch(f){}});return b[0]}function fa(a,b,c){var e=a.oPreviousSearch,d=a.aoPreSearchCols,f=function(a){e.sSearch=a.sSearch;e.bRegex=a.bRegex;e.bSmart=a.bSmart;e.bCaseInsensitive=a.bCaseInsensitive};Ha(a);if("ssp"!=B(a)){vb(a,b.sSearch,c,b.bEscapeRegex!==k?!b.bEscapeRegex:b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<d.length;b++)wb(a,d[b].sSearch,b,d[b].bEscapeRegex!==k?!d[b].bEscapeRegex:d[b].bRegex,d[b].bSmart,d[b].bCaseInsensitive);xb(a)}else f(b);a.bFiltered=
|
||||||
|
!0;w(a,null,"search",[a])}function xb(a){for(var b=m.ext.search,c=a.aiDisplay,e,d,f=0,g=b.length;f<g;f++){for(var j=[],i=0,h=c.length;i<h;i++)d=c[i],e=a.aoData[d],b[f](a,e._aFilterData,d,e._aData,i)&&j.push(d);c.length=0;c.push.apply(c,j)}}function wb(a,b,c,e,d,f){if(""!==b)for(var g=a.aiDisplay,e=Qa(b,e,d,f),d=g.length-1;0<=d;d--)b=a.aoData[g[d]]._aFilterData[c],e.test(b)||g.splice(d,1)}function vb(a,b,c,e,d,f){var e=Qa(b,e,d,f),d=a.oPreviousSearch.sSearch,f=a.aiDisplayMaster,g;0!==m.ext.search.length&&
|
||||||
|
(c=!0);g=yb(a);if(0>=b.length)a.aiDisplay=f.slice();else{if(g||c||d.length>b.length||0!==b.indexOf(d)||a.bSorted)a.aiDisplay=f.slice();b=a.aiDisplay;for(c=b.length-1;0<=c;c--)e.test(a.aoData[b[c]]._sFilterRow)||b.splice(c,1)}}function Qa(a,b,c,e){a=b?a:va(a);c&&(a="^(?=.*?"+h.map(a.match(/"[^"]+"|[^ ]+/g)||[""],function(a){if('"'===a.charAt(0))var b=a.match(/^"(.*)"$/),a=b?b[1]:a;return a.replace('"',"")}).join(")(?=.*?")+").*$");return RegExp(a,e?"i":"")}function va(a){return a.replace(Yb,"\\$1")}
|
||||||
|
function yb(a){var b=a.aoColumns,c,e,d,f,g,j,i,h,l=m.ext.type.search;c=!1;e=0;for(f=a.aoData.length;e<f;e++)if(h=a.aoData[e],!h._aFilterData){j=[];d=0;for(g=b.length;d<g;d++)c=b[d],c.bSearchable?(i=x(a,e,d,"filter"),l[c.sType]&&(i=l[c.sType](i)),null===i&&(i=""),"string"!==typeof i&&i.toString&&(i=i.toString())):i="",i.indexOf&&-1!==i.indexOf("&")&&(wa.innerHTML=i,i=Zb?wa.textContent:wa.innerText),i.replace&&(i=i.replace(/[\r\n]/g,"")),j.push(i);h._aFilterData=j;h._sFilterRow=j.join(" ");c=!0}return c}
|
||||||
|
function zb(a){return{search:a.sSearch,smart:a.bSmart,regex:a.bRegex,caseInsensitive:a.bCaseInsensitive}}function Ab(a){return{sSearch:a.search,bSmart:a.smart,bRegex:a.regex,bCaseInsensitive:a.caseInsensitive}}function rb(a){var b=a.sTableId,c=a.aanFeatures.i,e=h("<div/>",{"class":a.oClasses.sInfo,id:!c?b+"_info":null});c||(a.aoDrawCallback.push({fn:Bb,sName:"information"}),e.attr("role","status").attr("aria-live","polite"),h(a.nTable).attr("aria-describedby",b+"_info"));return e[0]}function Bb(a){var b=
|
||||||
|
a.aanFeatures.i;if(0!==b.length){var c=a.oLanguage,e=a._iDisplayStart+1,d=a.fnDisplayEnd(),f=a.fnRecordsTotal(),g=a.fnRecordsDisplay(),j=g?c.sInfo:c.sInfoEmpty;g!==f&&(j+=" "+c.sInfoFiltered);j+=c.sInfoPostFix;j=Cb(a,j);c=c.fnInfoCallback;null!==c&&(j=c.call(a.oInstance,a,e,d,f,g,j));h(b).html(j)}}function Cb(a,b){var c=a.fnFormatNumber,e=a._iDisplayStart+1,d=a._iDisplayLength,f=a.fnRecordsDisplay(),g=-1===d;return b.replace(/_START_/g,c.call(a,e)).replace(/_END_/g,c.call(a,a.fnDisplayEnd())).replace(/_MAX_/g,
|
||||||
|
c.call(a,a.fnRecordsTotal())).replace(/_TOTAL_/g,c.call(a,f)).replace(/_PAGE_/g,c.call(a,g?1:Math.ceil(e/d))).replace(/_PAGES_/g,c.call(a,g?1:Math.ceil(f/d)))}function ga(a){var b,c,e=a.iInitDisplayStart,d=a.aoColumns,f;c=a.oFeatures;if(a.bInitialised){mb(a);jb(a);ea(a,a.aoHeader);ea(a,a.aoFooter);C(a,!0);c.bAutoWidth&&Ga(a);b=0;for(c=d.length;b<c;b++)f=d[b],f.sWidth&&(f.nTh.style.width=s(f.sWidth));N(a);d=B(a);"ssp"!=d&&("ajax"==d?ra(a,[],function(c){var f=sa(a,c);for(b=0;b<f.length;b++)K(a,f[b]);
|
||||||
|
a.iInitDisplayStart=e;N(a);C(a,!1);ta(a,c)},a):(C(a,!1),ta(a)))}else setTimeout(function(){ga(a)},200)}function ta(a,b){a._bInitComplete=!0;b&&X(a);w(a,"aoInitComplete","init",[a,b])}function Ra(a,b){var c=parseInt(b,10);a._iDisplayLength=c;Sa(a);w(a,null,"length",[a,c])}function nb(a){for(var b=a.oClasses,c=a.sTableId,e=a.aLengthMenu,d=h.isArray(e[0]),f=d?e[0]:e,e=d?e[1]:e,d=h("<select/>",{name:c+"_length","aria-controls":c,"class":b.sLengthSelect}),g=0,j=f.length;g<j;g++)d[0][g]=new Option(e[g],
|
||||||
|
f[g]);var i=h("<div><label/></div>").addClass(b.sLength);a.aanFeatures.l||(i[0].id=c+"_length");i.children().append(a.oLanguage.sLengthMenu.replace("_MENU_",d[0].outerHTML));h("select",i).val(a._iDisplayLength).bind("change.DT",function(){Ra(a,h(this).val());M(a)});h(a.nTable).bind("length.dt.DT",function(b,c,f){a===c&&h("select",i).val(f)});return i[0]}function sb(a){var b=a.sPaginationType,c=m.ext.pager[b],e="function"===typeof c,d=function(a){M(a)},b=h("<div/>").addClass(a.oClasses.sPaging+b)[0],
|
||||||
|
f=a.aanFeatures;e||c.fnInit(a,b,d);f.p||(b.id=a.sTableId+"_paginate",a.aoDrawCallback.push({fn:function(a){if(e){var b=a._iDisplayStart,i=a._iDisplayLength,h=a.fnRecordsDisplay(),l=-1===i,b=l?0:Math.ceil(b/i),i=l?1:Math.ceil(h/i),h=c(b,i),q,l=0;for(q=f.p.length;l<q;l++)Pa(a,"pageButton")(a,f.p[l],l,h,b,i)}else c.fnUpdate(a,d)},sName:"pagination"}));return b}function Ta(a,b,c){var e=a._iDisplayStart,d=a._iDisplayLength,f=a.fnRecordsDisplay();0===f||-1===d?e=0:"number"===typeof b?(e=b*d,e>f&&(e=0)):
|
||||||
|
"first"==b?e=0:"previous"==b?(e=0<=d?e-d:0,0>e&&(e=0)):"next"==b?e+d<f&&(e+=d):"last"==b?e=Math.floor((f-1)/d)*d:I(a,0,"Unknown paging action: "+b,5);b=a._iDisplayStart!==e;a._iDisplayStart=e;b&&(w(a,null,"page",[a]),c&&M(a));return b}function pb(a){return h("<div/>",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function C(a,b){a.oFeatures.bProcessing&&h(a.aanFeatures.r).css("display",b?"block":"none");w(a,
|
||||||
|
null,"processing",[a,b])}function qb(a){var b=h(a.nTable);b.attr("role","grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var e=c.sX,d=c.sY,f=a.oClasses,g=b.children("caption"),j=g.length?g[0]._captionSide:null,i=h(b[0].cloneNode(!1)),o=h(b[0].cloneNode(!1)),l=b.children("tfoot");c.sX&&"100%"===b.attr("width")&&b.removeAttr("width");l.length||(l=null);c=h("<div/>",{"class":f.sScrollWrapper}).append(h("<div/>",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,
|
||||||
|
width:e?!e?null:s(e):"100%"}).append(h("<div/>",{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",width:c.sXInner||"100%"}).append(i.removeAttr("id").css("margin-left",0).append("top"===j?g:null).append(b.children("thead"))))).append(h("<div/>",{"class":f.sScrollBody}).css({overflow:"auto",height:!d?null:s(d),width:!e?null:s(e)}).append(b));l&&c.append(h("<div/>",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:e?!e?null:s(e):"100%"}).append(h("<div/>",{"class":f.sScrollFootInner}).append(o.removeAttr("id").css("margin-left",
|
||||||
|
0).append("bottom"===j?g:null).append(b.children("tfoot")))));var b=c.children(),q=b[0],f=b[1],n=l?b[2]:null;if(e)h(f).on("scroll.DT",function(){var a=this.scrollLeft;q.scrollLeft=a;l&&(n.scrollLeft=a)});a.nScrollHead=q;a.nScrollBody=f;a.nScrollFoot=n;a.aoDrawCallback.push({fn:Y,sName:"scrolling"});return c[0]}function Y(a){var b=a.oScroll,c=b.sX,e=b.sXInner,d=b.sY,f=b.iBarWidth,g=h(a.nScrollHead),j=g[0].style,i=g.children("div"),o=i[0].style,l=i.children("table"),i=a.nScrollBody,q=h(i),n=i.style,
|
||||||
|
k=h(a.nScrollFoot).children("div"),p=k.children("table"),m=h(a.nTHead),r=h(a.nTable),t=r[0],O=t.style,L=a.nTFoot?h(a.nTFoot):null,ha=a.oBrowser,w=ha.bScrollOversize,v,u,y,x,z,A=[],B=[],C=[],D,E=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};r.children("thead, tfoot").remove();z=m.clone().prependTo(r);v=m.find("tr");y=z.find("tr");z.find("th, td").removeAttr("tabindex");L&&(x=L.clone().prependTo(r),u=L.find("tr"),x=x.find("tr"));
|
||||||
|
c||(n.width="100%",g[0].style.width="100%");h.each(qa(a,z),function(b,c){D=la(a,b);c.style.width=a.aoColumns[D].sWidth});L&&G(function(a){a.style.width=""},x);b.bCollapse&&""!==d&&(n.height=q[0].offsetHeight+m[0].offsetHeight+"px");g=r.outerWidth();if(""===c){if(O.width="100%",w&&(r.find("tbody").height()>i.offsetHeight||"scroll"==q.css("overflow-y")))O.width=s(r.outerWidth()-f)}else""!==e?O.width=s(e):g==q.width()&&q.height()<r.height()?(O.width=s(g-f),r.outerWidth()>g-f&&(O.width=s(g))):O.width=
|
||||||
|
s(g);g=r.outerWidth();G(E,y);G(function(a){C.push(a.innerHTML);A.push(s(h(a).css("width")))},y);G(function(a,b){a.style.width=A[b]},v);h(y).height(0);L&&(G(E,x),G(function(a){B.push(s(h(a).css("width")))},x),G(function(a,b){a.style.width=B[b]},u),h(x).height(0));G(function(a,b){a.innerHTML='<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+C[b]+"</div>";a.style.width=A[b]},y);L&&G(function(a,b){a.innerHTML="";a.style.width=B[b]},x);if(r.outerWidth()<g){u=i.scrollHeight>i.offsetHeight||
|
||||||
|
"scroll"==q.css("overflow-y")?g+f:g;if(w&&(i.scrollHeight>i.offsetHeight||"scroll"==q.css("overflow-y")))O.width=s(u-f);(""===c||""!==e)&&I(a,1,"Possible column misalignment",6)}else u="100%";n.width=s(u);j.width=s(u);L&&(a.nScrollFoot.style.width=s(u));!d&&w&&(n.height=s(t.offsetHeight+f));d&&b.bCollapse&&(n.height=s(d),b=c&&t.offsetWidth>i.offsetWidth?f:0,t.offsetHeight<i.offsetHeight&&(n.height=s(t.offsetHeight+b)));b=r.outerWidth();l[0].style.width=s(b);o.width=s(b);l=r.height()>i.clientHeight||
|
||||||
|
"scroll"==q.css("overflow-y");ha="padding"+(ha.bScrollbarLeft?"Left":"Right");o[ha]=l?f+"px":"0px";L&&(p[0].style.width=s(b),k[0].style.width=s(b),k[0].style[ha]=l?f+"px":"0px");q.scroll();if((a.bSorted||a.bFiltered)&&!a._drawHold)i.scrollTop=0}function G(a,b,c){for(var e=0,d=0,f=b.length,g,j;d<f;){g=b[d].firstChild;for(j=c?c[d].firstChild:null;g;)1===g.nodeType&&(c?a(g,j,e):a(g,e),e++),g=g.nextSibling,j=c?j.nextSibling:null;d++}}function Ga(a){var b=a.nTable,c=a.aoColumns,e=a.oScroll,d=e.sY,f=e.sX,
|
||||||
|
g=e.sXInner,j=c.length,e=Z(a,"bVisible"),i=h("th",a.nTHead),o=b.getAttribute("width"),l=b.parentNode,k=!1,n,m;(n=b.style.width)&&-1!==n.indexOf("%")&&(o=n);for(n=0;n<e.length;n++)m=c[e[n]],null!==m.sWidth&&(m.sWidth=Db(m.sWidthOrig,l),k=!0);if(!k&&!f&&!d&&j==aa(a)&&j==i.length)for(n=0;n<j;n++)c[n].sWidth=s(i.eq(n).width());else{j=h(b).clone().css("visibility","hidden").removeAttr("id");j.find("tbody tr").remove();var p=h("<tr/>").appendTo(j.find("tbody"));j.find("tfoot th, tfoot td").css("width",
|
||||||
|
"");i=qa(a,j.find("thead")[0]);for(n=0;n<e.length;n++)m=c[e[n]],i[n].style.width=null!==m.sWidthOrig&&""!==m.sWidthOrig?s(m.sWidthOrig):"";if(a.aoData.length)for(n=0;n<e.length;n++)k=e[n],m=c[k],h(Eb(a,k)).clone(!1).append(m.sContentPadding).appendTo(p);j.appendTo(l);f&&g?j.width(g):f?(j.css("width","auto"),j.width()<l.offsetWidth&&j.width(l.offsetWidth)):d?j.width(l.offsetWidth):o&&j.width(o);Fb(a,j[0]);if(f){for(n=g=0;n<e.length;n++)m=c[e[n]],d=h(i[n]).outerWidth(),g+=null===m.sWidthOrig?d:parseInt(m.sWidth,
|
||||||
|
10)+d-h(i[n]).width();j.width(s(g));b.style.width=s(g)}for(n=0;n<e.length;n++)if(m=c[e[n]],d=h(i[n]).width())m.sWidth=s(d);b.style.width=s(j.css("width"));j.remove()}o&&(b.style.width=s(o));if((o||f)&&!a._reszEvt)b=function(){h(Ea).bind("resize.DT-"+a.sInstance,ua(function(){X(a)}))},a.oBrowser.bScrollOversize?setTimeout(b,1E3):b(),a._reszEvt=!0}function ua(a,b){var c=b!==k?b:200,e,d;return function(){var b=this,g=+new Date,j=arguments;e&&g<e+c?(clearTimeout(d),d=setTimeout(function(){e=k;a.apply(b,
|
||||||
|
j)},c)):(e=g,a.apply(b,j))}}function Db(a,b){if(!a)return 0;var c=h("<div/>").css("width",s(a)).appendTo(b||Q.body),e=c[0].offsetWidth;c.remove();return e}function Fb(a,b){var c=a.oScroll;if(c.sX||c.sY)c=!c.sX?c.iBarWidth:0,b.style.width=s(h(b).outerWidth()-c)}function Eb(a,b){var c=Gb(a,b);if(0>c)return null;var e=a.aoData[c];return!e.nTr?h("<td/>").html(x(a,c,b,"display"))[0]:e.anCells[b]}function Gb(a,b){for(var c,e=-1,d=-1,f=0,g=a.aoData.length;f<g;f++)c=x(a,f,b,"display")+"",c=c.replace($b,""),
|
||||||
|
c.length>e&&(e=c.length,d=f);return d}function s(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function Hb(){var a=m.__scrollbarWidth;if(a===k){var b=h("<p/>").css({position:"absolute",top:0,left:0,width:"100%",height:150,padding:0,overflow:"scroll",visibility:"hidden"}).appendTo("body"),a=b[0].offsetWidth-b[0].clientWidth;m.__scrollbarWidth=a;b.remove()}return a}function U(a){var b,c,e=[],d=a.aoColumns,f,g,j,i;b=a.aaSortingFixed;c=h.isPlainObject(b);var o=[];
|
||||||
|
f=function(a){a.length&&!h.isArray(a[0])?o.push(a):o.push.apply(o,a)};h.isArray(b)&&f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;a<o.length;a++){i=o[a][0];f=d[i].aDataSort;b=0;for(c=f.length;b<c;b++)g=f[b],j=d[g].sType||"string",o[a]._idx===k&&(o[a]._idx=h.inArray(o[a][1],d[g].asSorting)),e.push({src:i,col:g,dir:o[a][1],index:o[a]._idx,type:j,formatter:m.ext.type.order[j+"-pre"]})}return e}function lb(a){var b,c,e=[],d=m.ext.type.order,f=a.aoData,g=0,j,i=a.aiDisplayMaster,h;
|
||||||
|
Ha(a);h=U(a);b=0;for(c=h.length;b<c;b++)j=h[b],j.formatter&&g++,Ib(a,j.col);if("ssp"!=B(a)&&0!==h.length){b=0;for(c=i.length;b<c;b++)e[i[b]]=b;g===h.length?i.sort(function(a,b){var c,d,g,j,i=h.length,k=f[a]._aSortData,m=f[b]._aSortData;for(g=0;g<i;g++)if(j=h[g],c=k[j.col],d=m[j.col],c=c<d?-1:c>d?1:0,0!==c)return"asc"===j.dir?c:-c;c=e[a];d=e[b];return c<d?-1:c>d?1:0}):i.sort(function(a,b){var c,g,j,i,k=h.length,m=f[a]._aSortData,r=f[b]._aSortData;for(j=0;j<k;j++)if(i=h[j],c=m[i.col],g=r[i.col],i=d[i.type+
|
||||||
|
"-"+i.dir]||d["string-"+i.dir],c=i(c,g),0!==c)return c;c=e[a];g=e[b];return c<g?-1:c>g?1:0})}a.bSorted=!0}function Jb(a){for(var b,c,e=a.aoColumns,d=U(a),a=a.oLanguage.oAria,f=0,g=e.length;f<g;f++){c=e[f];var j=c.asSorting;b=c.sTitle.replace(/<.*?>/g,"");var i=c.nTh;i.removeAttribute("aria-sort");c.bSortable&&(0<d.length&&d[0].col==f?(i.setAttribute("aria-sort","asc"==d[0].dir?"ascending":"descending"),c=j[d[0].index+1]||j[0]):c=j[0],b+="asc"===c?a.sSortAscending:a.sSortDescending);i.setAttribute("aria-label",
|
||||||
|
b)}}function Ua(a,b,c,e){var d=a.aaSorting,f=a.aoColumns[b].asSorting,g=function(a,b){var c=a._idx;c===k&&(c=h.inArray(a[1],f));return c+1<f.length?c+1:b?null:0};"number"===typeof d[0]&&(d=a.aaSorting=[d]);c&&a.oFeatures.bSortMulti?(c=h.inArray(b,D(d,"0")),-1!==c?(b=g(d[c],!0),null===b&&1===d.length&&(b=0),null===b?d.splice(c,1):(d[c][1]=f[b],d[c]._idx=b)):(d.push([b,f[0],0]),d[d.length-1]._idx=0)):d.length&&d[0][0]==b?(b=g(d[0]),d.length=1,d[0][1]=f[b],d[0]._idx=b):(d.length=0,d.push([b,f[0]]),d[0]._idx=
|
||||||
|
0);N(a);"function"==typeof e&&e(a)}function Oa(a,b,c,e){var d=a.aoColumns[c];Va(b,{},function(b){!1!==d.bSortable&&(a.oFeatures.bProcessing?(C(a,!0),setTimeout(function(){Ua(a,c,b.shiftKey,e);"ssp"!==B(a)&&C(a,!1)},0)):Ua(a,c,b.shiftKey,e))})}function xa(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,e=U(a),d=a.oFeatures,f,g;if(d.bSort&&d.bSortClasses){d=0;for(f=b.length;d<f;d++)g=b[d].src,h(D(a.aoData,"anCells",g)).removeClass(c+(2>d?d+1:3));d=0;for(f=e.length;d<f;d++)g=e[d].src,h(D(a.aoData,"anCells",
|
||||||
|
g)).addClass(c+(2>d?d+1:3))}a.aLastSort=e}function Ib(a,b){var c=a.aoColumns[b],e=m.ext.order[c.sSortDataType],d;e&&(d=e.call(a.oInstance,a,b,$(a,b)));for(var f,g=m.ext.type.order[c.sType+"-pre"],j=0,i=a.aoData.length;j<i;j++)if(c=a.aoData[j],c._aSortData||(c._aSortData=[]),!c._aSortData[b]||e)f=e?d[j]:x(a,j,b,"sort"),c._aSortData[b]=g?g(f):f}function ya(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b={time:+new Date,start:a._iDisplayStart,length:a._iDisplayLength,order:h.extend(!0,[],a.aaSorting),
|
||||||
|
search:zb(a.oPreviousSearch),columns:h.map(a.aoColumns,function(b,e){return{visible:b.bVisible,search:zb(a.aoPreSearchCols[e])}})};w(a,"aoStateSaveParams","stateSaveParams",[a,b]);a.oSavedState=b;a.fnStateSaveCallback.call(a.oInstance,a,b)}}function Kb(a){var b,c,e=a.aoColumns;if(a.oFeatures.bStateSave){var d=a.fnStateLoadCallback.call(a.oInstance,a);if(d&&d.time&&(b=w(a,"aoStateLoadParams","stateLoadParams",[a,d]),-1===h.inArray(!1,b)&&(b=a.iStateDuration,!(0<b&&d.time<+new Date-1E3*b)&&e.length===
|
||||||
|
d.columns.length))){a.oLoadedState=h.extend(!0,{},d);d.start!==k&&(a._iDisplayStart=d.start,a.iInitDisplayStart=d.start);d.length!==k&&(a._iDisplayLength=d.length);d.order!==k&&(a.aaSorting=[],h.each(d.order,function(b,c){a.aaSorting.push(c[0]>=e.length?[0,c[1]]:c)}));d.search!==k&&h.extend(a.oPreviousSearch,Ab(d.search));b=0;for(c=d.columns.length;b<c;b++){var f=d.columns[b];f.visible!==k&&(e[b].bVisible=f.visible);f.search!==k&&h.extend(a.aoPreSearchCols[b],Ab(f.search))}w(a,"aoStateLoaded","stateLoaded",
|
||||||
|
[a,d])}}}function za(a){var b=m.settings,a=h.inArray(a,D(b,"nTable"));return-1!==a?b[a]:null}function I(a,b,c,e){c="DataTables warning: "+(null!==a?"table id="+a.sTableId+" - ":"")+c;e&&(c+=". For more information about this error, please see http://datatables.net/tn/"+e);if(b)Ea.console&&console.log&&console.log(c);else if(b=m.ext,b=b.sErrMode||b.errMode,w(a,null,"error",[a,e,c]),"alert"==b)alert(c);else{if("throw"==b)throw Error(c);"function"==typeof b&&b(a,e,c)}}function E(a,b,c,e){h.isArray(c)?
|
||||||
|
h.each(c,function(c,f){h.isArray(f)?E(a,b,f[0],f[1]):E(a,b,f)}):(e===k&&(e=c),b[c]!==k&&(a[e]=b[c]))}function Lb(a,b,c){var e,d;for(d in b)b.hasOwnProperty(d)&&(e=b[d],h.isPlainObject(e)?(h.isPlainObject(a[d])||(a[d]={}),h.extend(!0,a[d],e)):a[d]=c&&"data"!==d&&"aaData"!==d&&h.isArray(e)?e.slice():e);return a}function Va(a,b,c){h(a).bind("click.DT",b,function(b){a.blur();c(b)}).bind("keypress.DT",b,function(a){13===a.which&&(a.preventDefault(),c(a))}).bind("selectstart.DT",function(){return!1})}function z(a,
|
||||||
|
b,c,e){c&&a[b].push({fn:c,sName:e})}function w(a,b,c,e){var d=[];b&&(d=h.map(a[b].slice().reverse(),function(b){return b.fn.apply(a.oInstance,e)}));null!==c&&(b=h.Event(c+".dt"),h(a.nTable).trigger(b,e),d.push(b.result));return d}function Sa(a){var b=a._iDisplayStart,c=a.fnDisplayEnd(),e=a._iDisplayLength;b>=c&&(b=c-e);b-=b%e;if(-1===e||0>b)b=0;a._iDisplayStart=b}function Pa(a,b){var c=a.renderer,e=m.ext.renderer[b];return h.isPlainObject(c)&&c[b]?e[c[b]]||e._:"string"===typeof c?e[c]||e._:e._}function B(a){return a.oFeatures.bServerSide?
|
||||||
|
"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function Wa(a,b){var c=[],c=Mb.numbers_length,e=Math.floor(c/2);b<=c?c=V(0,b):a<=e?(c=V(0,c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-e?c=V(b-(c-2),b):(c=V(a-e+2,a+e-1),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function db(a){h.each({num:function(b){return Aa(b,a)},"num-fmt":function(b){return Aa(b,a,Xa)},"html-num":function(b){return Aa(b,a,Ba)},"html-num-fmt":function(b){return Aa(b,a,Ba,Xa)}},function(b,
|
||||||
|
c){u.type.order[b+a+"-pre"]=c;b.match(/^html\-/)&&(u.type.search[b+a]=u.type.search.html)})}function Nb(a){return function(){var b=[za(this[m.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return m.ext.internal[a].apply(this,b)}}var m,u,t,r,v,Ya={},Ob=/[\r\n]/g,Ba=/<.*?>/g,ac=/^[\w\+\-]/,bc=/[\w\+\-]$/,Yb=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Xa=/[',$\u00a3\u20ac\u00a5%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi,J=function(a){return!a||!0===a||
|
||||||
|
"-"===a?!0:!1},Pb=function(a){var b=parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},Qb=function(a,b){Ya[b]||(Ya[b]=RegExp(va(b),"g"));return"string"===typeof a&&"."!==b?a.replace(/\./g,"").replace(Ya[b],"."):a},Za=function(a,b,c){var e="string"===typeof a;if(J(a))return!0;b&&e&&(a=Qb(a,b));c&&e&&(a=a.replace(Xa,""));return!isNaN(parseFloat(a))&&isFinite(a)},Rb=function(a,b,c){return J(a)?!0:!(J(a)||"string"===typeof a)?null:Za(a.replace(Ba,""),b,c)?!0:null},D=function(a,b,c){var e=[],d=0,f=a.length;
|
||||||
|
if(c!==k)for(;d<f;d++)a[d]&&a[d][b]&&e.push(a[d][b][c]);else for(;d<f;d++)a[d]&&e.push(a[d][b]);return e},ia=function(a,b,c,e){var d=[],f=0,g=b.length;if(e!==k)for(;f<g;f++)a[b[f]][c]&&d.push(a[b[f]][c][e]);else for(;f<g;f++)d.push(a[b[f]][c]);return d},V=function(a,b){var c=[],e;b===k?(b=0,e=a):(e=b,b=a);for(var d=b;d<e;d++)c.push(d);return c},Sb=function(a){for(var b=[],c=0,e=a.length;c<e;c++)a[c]&&b.push(a[c]);return b},Na=function(a){var b=[],c,e,d=a.length,f,g=0;e=0;a:for(;e<d;e++){c=a[e];for(f=
|
||||||
|
0;f<g;f++)if(b[f]===c)continue a;b.push(c);g++}return b},A=function(a,b,c){a[b]!==k&&(a[c]=a[b])},ba=/\[.*?\]$/,T=/\(\)$/,wa=h("<div>")[0],Zb=wa.textContent!==k,$b=/<.*?>/g;m=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new t(za(this[u.iApiIndex])):new t(this)};this.fnAddData=function(a,b){var c=this.api(!0),e=h.isArray(a)&&(h.isArray(a[0])||h.isPlainObject(a[0]))?c.rows.add(a):c.row.add(a);(b===
|
||||||
|
k||b)&&c.draw();return e.flatten().toArray()};this.fnAdjustColumnSizing=function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],e=c.oScroll;a===k||a?b.draw(!1):(""!==e.sX||""!==e.sY)&&Y(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===k||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var e=this.api(!0),a=e.rows(a),d=a.settings()[0],h=d.aoData[a[0][0]];a.remove();b&&b.call(this,d,h);(c===k||c)&&e.draw();return h};
|
||||||
|
this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(a)};this.fnFilter=function(a,b,c,e,d,h){d=this.api(!0);null===b||b===k?d.search(a,c,e,h):d.column(b).search(a,c,e,h);d.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==k){var e=a.nodeName?a.nodeName.toLowerCase():"";return b!==k||"td"==e||"th"==e?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};this.fnGetNodes=function(a){var b=this.api(!0);return a!==k?b.row(a).node():b.rows().nodes().flatten().toArray()};
|
||||||
|
this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase();return"TR"==c?b.row(a).index():"TD"==c||"TH"==c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);(b===k||b)&&c.draw(!1)};this.fnSetColumnVis=function(a,b,c){a=this.api(!0).column(a).visible(b);(c===
|
||||||
|
k||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return za(this[u.iApiIndex])};this.fnSort=function(a){this.api(!0).order(a).draw()};this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,e,d){var h=this.api(!0);c===k||null===c?h.row(b).data(a):h.cell(b,c).data(a);(d===k||d)&&h.columns.adjust();(e===k||e)&&h.draw();return 0};this.fnVersionCheck=u.fnVersionCheck;var b=this,c=a===k,e=this.length;c&&(a={});this.oApi=this.internal=u.internal;for(var d in m.ext.internal)d&&
|
||||||
|
(this[d]=Nb(d));this.each(function(){var d={},d=1<e?Lb(d,a,!0):a,g=0,j,i=this.getAttribute("id"),o=!1,l=m.defaults,q=h(this);if("table"!=this.nodeName.toLowerCase())I(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{eb(l);fb(l.column);H(l,l,!0);H(l.column,l.column,!0);H(l,h.extend(d,q.data()));var n=m.settings,g=0;for(j=n.length;g<j;g++){var r=n[g];if(r.nTable==this||r.nTHead.parentNode==this||r.nTFoot&&r.nTFoot.parentNode==this){g=d.bRetrieve!==k?d.bRetrieve:l.bRetrieve;if(c||g)return r.oInstance;
|
||||||
|
if(d.bDestroy!==k?d.bDestroy:l.bDestroy){r.oInstance.fnDestroy();break}else{I(r,0,"Cannot reinitialise DataTable",3);return}}if(r.sTableId==this.id){n.splice(g,1);break}}if(null===i||""===i)this.id=i="DataTables_Table_"+m.ext._unique++;var p=h.extend(!0,{},m.models.oSettings,{sDestroyWidth:q[0].style.width,sInstance:i,sTableId:i});p.nTable=this;p.oApi=b.internal;p.oInit=d;n.push(p);p.oInstance=1===b.length?b:q.dataTable();eb(d);d.oLanguage&&P(d.oLanguage);d.aLengthMenu&&!d.iDisplayLength&&(d.iDisplayLength=
|
||||||
|
h.isArray(d.aLengthMenu[0])?d.aLengthMenu[0][0]:d.aLengthMenu[0]);d=Lb(h.extend(!0,{},l),d);E(p.oFeatures,d,"bPaginate bLengthChange bFilter bSort bSortMulti bInfo bProcessing bAutoWidth bSortClasses bServerSide bDeferRender".split(" "));E(p,d,["asStripeClasses","ajax","fnServerData","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","sAjaxSource","sAjaxDataProp","iStateDuration","sDom","bSortCellsTop","iTabIndex","fnStateLoadCallback","fnStateSaveCallback",
|
||||||
|
"renderer","searchDelay",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"],["bJQueryUI","bJUI"]]);E(p.oScroll,d,[["sScrollX","sX"],["sScrollXInner","sXInner"],["sScrollY","sY"],["bScrollCollapse","bCollapse"]]);E(p.oLanguage,d,"fnInfoCallback");z(p,"aoDrawCallback",d.fnDrawCallback,"user");z(p,"aoServerParams",d.fnServerParams,"user");z(p,"aoStateSaveParams",d.fnStateSaveParams,"user");z(p,"aoStateLoadParams",
|
||||||
|
d.fnStateLoadParams,"user");z(p,"aoStateLoaded",d.fnStateLoaded,"user");z(p,"aoRowCallback",d.fnRowCallback,"user");z(p,"aoRowCreatedCallback",d.fnCreatedRow,"user");z(p,"aoHeaderCallback",d.fnHeaderCallback,"user");z(p,"aoFooterCallback",d.fnFooterCallback,"user");z(p,"aoInitComplete",d.fnInitComplete,"user");z(p,"aoPreDrawCallback",d.fnPreDrawCallback,"user");i=p.oClasses;d.bJQueryUI?(h.extend(i,m.ext.oJUIClasses,d.oClasses),d.sDom===l.sDom&&"lfrtip"===l.sDom&&(p.sDom='<"H"lfr>t<"F"ip>'),p.renderer)?
|
||||||
|
h.isPlainObject(p.renderer)&&!p.renderer.header&&(p.renderer.header="jqueryui"):p.renderer="jqueryui":h.extend(i,m.ext.classes,d.oClasses);q.addClass(i.sTable);if(""!==p.oScroll.sX||""!==p.oScroll.sY)p.oScroll.iBarWidth=Hb();!0===p.oScroll.sX&&(p.oScroll.sX="100%");p.iInitDisplayStart===k&&(p.iInitDisplayStart=d.iDisplayStart,p._iDisplayStart=d.iDisplayStart);null!==d.iDeferLoading&&(p.bDeferLoading=!0,g=h.isArray(d.iDeferLoading),p._iRecordsDisplay=g?d.iDeferLoading[0]:d.iDeferLoading,p._iRecordsTotal=
|
||||||
|
g?d.iDeferLoading[1]:d.iDeferLoading);var t=p.oLanguage;h.extend(!0,t,d.oLanguage);""!==t.sUrl&&(h.ajax({dataType:"json",url:t.sUrl,success:function(a){P(a);H(l.oLanguage,a);h.extend(true,t,a);ga(p)},error:function(){ga(p)}}),o=!0);null===d.asStripeClasses&&(p.asStripeClasses=[i.sStripeOdd,i.sStripeEven]);var g=p.asStripeClasses,s=q.children("tbody").find("tr").eq(0);-1!==h.inArray(!0,h.map(g,function(a){return s.hasClass(a)}))&&(h("tbody tr",this).removeClass(g.join(" ")),p.asDestroyStripes=g.slice());
|
||||||
|
n=[];g=this.getElementsByTagName("thead");0!==g.length&&(da(p.aoHeader,g[0]),n=qa(p));if(null===d.aoColumns){r=[];g=0;for(j=n.length;g<j;g++)r.push(null)}else r=d.aoColumns;g=0;for(j=r.length;g<j;g++)Fa(p,n?n[g]:null);ib(p,d.aoColumnDefs,r,function(a,b){ka(p,a,b)});if(s.length){var u=function(a,b){return a.getAttribute("data-"+b)!==null?b:null};h.each(na(p,s[0]).cells,function(a,b){var c=p.aoColumns[a];if(c.mData===a){var d=u(b,"sort")||u(b,"order"),e=u(b,"filter")||u(b,"search");if(d!==null||e!==
|
||||||
|
null){c.mData={_:a+".display",sort:d!==null?a+".@data-"+d:k,type:d!==null?a+".@data-"+d:k,filter:e!==null?a+".@data-"+e:k};ka(p,a)}}})}var v=p.oFeatures;d.bStateSave&&(v.bStateSave=!0,Kb(p,d),z(p,"aoDrawCallback",ya,"state_save"));if(d.aaSorting===k){n=p.aaSorting;g=0;for(j=n.length;g<j;g++)n[g][1]=p.aoColumns[g].asSorting[0]}xa(p);v.bSort&&z(p,"aoDrawCallback",function(){if(p.bSorted){var a=U(p),b={};h.each(a,function(a,c){b[c.src]=c.dir});w(p,null,"order",[p,a,b]);Jb(p)}});z(p,"aoDrawCallback",
|
||||||
|
function(){(p.bSorted||B(p)==="ssp"||v.bDeferRender)&&xa(p)},"sc");gb(p);g=q.children("caption").each(function(){this._captionSide=q.css("caption-side")});j=q.children("thead");0===j.length&&(j=h("<thead/>").appendTo(this));p.nTHead=j[0];j=q.children("tbody");0===j.length&&(j=h("<tbody/>").appendTo(this));p.nTBody=j[0];j=q.children("tfoot");if(0===j.length&&0<g.length&&(""!==p.oScroll.sX||""!==p.oScroll.sY))j=h("<tfoot/>").appendTo(this);0===j.length||0===j.children().length?q.addClass(i.sNoFooter):
|
||||||
|
0<j.length&&(p.nTFoot=j[0],da(p.aoFooter,p.nTFoot));if(d.aaData)for(g=0;g<d.aaData.length;g++)K(p,d.aaData[g]);else(p.bDeferLoading||"dom"==B(p))&&ma(p,h(p.nTBody).children("tr"));p.aiDisplay=p.aiDisplayMaster.slice();p.bInitialised=!0;!1===o&&ga(p)}});b=null;return this};var Tb=[],y=Array.prototype,cc=function(a){var b,c,e=m.settings,d=h.map(e,function(a){return a.nTable});if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&"table"===a.nodeName.toLowerCase())return b=h.inArray(a,d),-1!==b?[e[b]]:
|
||||||
|
null;if(a&&"function"===typeof a.settings)return a.settings().toArray();"string"===typeof a?c=h(a):a instanceof h&&(c=a)}else return[];if(c)return c.map(function(){b=h.inArray(this,d);return-1!==b?e[b]:null}).toArray()};t=function(a,b){if(!(this instanceof t))return new t(a,b);var c=[],e=function(a){(a=cc(a))&&c.push.apply(c,a)};if(h.isArray(a))for(var d=0,f=a.length;d<f;d++)e(a[d]);else e(a);this.context=Na(c);b&&this.push.apply(this,b.toArray?b.toArray():b);this.selector={rows:null,cols:null,opts:null};
|
||||||
|
t.extend(this,this,Tb)};m.Api=t;t.prototype={any:function(){return 0!==this.flatten().length},concat:y.concat,context:[],each:function(a){for(var b=0,c=this.length;b<c;b++)a.call(this,this[b],b,this);return this},eq:function(a){var b=this.context;return b.length>a?new t(b[a],this[a]):null},filter:function(a){var b=[];if(y.filter)b=y.filter.call(this,a,this);else for(var c=0,e=this.length;c<e;c++)a.call(this,this[c],c,this)&&b.push(this[c]);return new t(this.context,b)},flatten:function(){var a=[];
|
||||||
|
return new t(this.context,a.concat.apply(a,this.toArray()))},join:y.join,indexOf:y.indexOf||function(a,b){for(var c=b||0,e=this.length;c<e;c++)if(this[c]===a)return c;return-1},iterator:function(a,b,c,e){var d=[],f,g,h,i,o,l=this.context,q,n,m=this.selector;"string"===typeof a&&(e=c,c=b,b=a,a=!1);g=0;for(h=l.length;g<h;g++){var p=new t(l[g]);if("table"===b)f=c.call(p,l[g],g),f!==k&&d.push(f);else if("columns"===b||"rows"===b)f=c.call(p,l[g],this[g],g),f!==k&&d.push(f);else if("column"===b||"column-rows"===
|
||||||
|
b||"row"===b||"cell"===b){n=this[g];"column-rows"===b&&(q=Ca(l[g],m.opts));i=0;for(o=n.length;i<o;i++)f=n[i],f="cell"===b?c.call(p,l[g],f.row,f.column,g,i):c.call(p,l[g],f,g,i,q),f!==k&&d.push(f)}}return d.length||e?(a=new t(l,a?d.concat.apply([],d):d),b=a.selector,b.rows=m.rows,b.cols=m.cols,b.opts=m.opts,a):this},lastIndexOf:y.lastIndexOf||function(a,b){return this.indexOf.apply(this.toArray.reverse(),arguments)},length:0,map:function(a){var b=[];if(y.map)b=y.map.call(this,a,this);else for(var c=
|
||||||
|
0,e=this.length;c<e;c++)b.push(a.call(this,this[c],c));return new t(this.context,b)},pluck:function(a){return this.map(function(b){return b[a]})},pop:y.pop,push:y.push,reduce:y.reduce||function(a,b){return hb(this,a,b,0,this.length,1)},reduceRight:y.reduceRight||function(a,b){return hb(this,a,b,this.length-1,-1,-1)},reverse:y.reverse,selector:null,shift:y.shift,sort:y.sort,splice:y.splice,toArray:function(){return y.slice.call(this)},to$:function(){return h(this)},toJQuery:function(){return h(this)},
|
||||||
|
unique:function(){return new t(this.context,Na(this))},unshift:y.unshift};t.extend=function(a,b,c){if(c.length&&b&&(b instanceof t||b.__dt_wrapper)){var e,d,f,g=function(a,b,c){return function(){var d=b.apply(a,arguments);t.extend(d,d,c.methodExt);return d}};e=0;for(d=c.length;e<d;e++)f=c[e],b[f.name]="function"===typeof f.val?g(a,f.val,f):h.isPlainObject(f.val)?{}:f.val,b[f.name].__dt_wrapper=!0,t.extend(a,b[f.name],f.propExt)}};t.register=r=function(a,b){if(h.isArray(a))for(var c=0,e=a.length;c<
|
||||||
|
e;c++)t.register(a[c],b);else for(var d=a.split("."),f=Tb,g,j,c=0,e=d.length;c<e;c++){g=(j=-1!==d[c].indexOf("()"))?d[c].replace("()",""):d[c];var i;a:{i=0;for(var o=f.length;i<o;i++)if(f[i].name===g){i=f[i];break a}i=null}i||(i={name:g,val:{},methodExt:[],propExt:[]},f.push(i));c===e-1?i.val=b:f=j?i.methodExt:i.propExt}};t.registerPlural=v=function(a,b,c){t.register(a,c);t.register(b,function(){var a=c.apply(this,arguments);return a===this?this:a instanceof t?a.length?h.isArray(a[0])?new t(a.context,
|
||||||
|
a[0]):a[0]:k:a})};r("tables()",function(a){var b;if(a){b=t;var c=this.context;if("number"===typeof a)a=[c[a]];else var e=h.map(c,function(a){return a.nTable}),a=h(e).filter(a).map(function(){var a=h.inArray(this,e);return c[a]}).toArray();b=new b(a)}else b=this;return b});r("table()",function(a){var a=this.tables(a),b=a.context;return b.length?new t(b[0]):a});v("tables().nodes()","table().node()",function(){return this.iterator("table",function(a){return a.nTable},1)});v("tables().body()","table().body()",
|
||||||
|
function(){return this.iterator("table",function(a){return a.nTBody},1)});v("tables().header()","table().header()",function(){return this.iterator("table",function(a){return a.nTHead},1)});v("tables().footer()","table().footer()",function(){return this.iterator("table",function(a){return a.nTFoot},1)});v("tables().containers()","table().container()",function(){return this.iterator("table",function(a){return a.nTableWrapper},1)});r("draw()",function(a){return this.iterator("table",function(b){N(b,
|
||||||
|
!1===a)})});r("page()",function(a){return a===k?this.page.info().page:this.iterator("table",function(b){Ta(b,a)})});r("page.info()",function(){if(0===this.context.length)return k;var a=this.context[0],b=a._iDisplayStart,c=a._iDisplayLength,e=a.fnRecordsDisplay(),d=-1===c;return{page:d?0:Math.floor(b/c),pages:d?1:Math.ceil(e/c),start:b,end:a.fnDisplayEnd(),length:c,recordsTotal:a.fnRecordsTotal(),recordsDisplay:e}});r("page.len()",function(a){return a===k?0!==this.context.length?this.context[0]._iDisplayLength:
|
||||||
|
k:this.iterator("table",function(b){Ra(b,a)})});var Ub=function(a,b,c){if(c){var e=new t(a);e.one("draw",function(){c(e.ajax.json())})}"ssp"==B(a)?N(a,b):(C(a,!0),ra(a,[],function(c){oa(a);for(var c=sa(a,c),e=0,g=c.length;e<g;e++)K(a,c[e]);N(a,b);C(a,!1)}))};r("ajax.json()",function(){var a=this.context;if(0<a.length)return a[0].json});r("ajax.params()",function(){var a=this.context;if(0<a.length)return a[0].oAjaxData});r("ajax.reload()",function(a,b){return this.iterator("table",function(c){Ub(c,
|
||||||
|
!1===b,a)})});r("ajax.url()",function(a){var b=this.context;if(a===k){if(0===b.length)return k;b=b[0];return b.ajax?h.isPlainObject(b.ajax)?b.ajax.url:b.ajax:b.sAjaxSource}return this.iterator("table",function(b){h.isPlainObject(b.ajax)?b.ajax.url=a:b.ajax=a})});r("ajax.url().load()",function(a,b){return this.iterator("table",function(c){Ub(c,!1===b,a)})});var $a=function(a,b,c,e,d){var f=[],g,j,i,o,l,q;i=typeof b;if(!b||"string"===i||"function"===i||b.length===k)b=[b];i=0;for(o=b.length;i<o;i++){j=
|
||||||
|
b[i]&&b[i].split?b[i].split(","):[b[i]];l=0;for(q=j.length;l<q;l++)(g=c("string"===typeof j[l]?h.trim(j[l]):j[l]))&&g.length&&f.push.apply(f,g)}a=u.selector[a];if(a.length){i=0;for(o=a.length;i<o;i++)f=a[i](e,d,f)}return f},ab=function(a){a||(a={});a.filter&&a.search===k&&(a.search=a.filter);return h.extend({search:"none",order:"current",page:"all"},a)},bb=function(a){for(var b=0,c=a.length;b<c;b++)if(0<a[b].length)return a[0]=a[b],a[0].length=1,a.length=1,a.context=[a.context[b]],a;a.length=0;return a},
|
||||||
|
Ca=function(a,b){var c,e,d,f=[],g=a.aiDisplay;c=a.aiDisplayMaster;var j=b.search;e=b.order;d=b.page;if("ssp"==B(a))return"removed"===j?[]:V(0,c.length);if("current"==d){c=a._iDisplayStart;for(e=a.fnDisplayEnd();c<e;c++)f.push(g[c])}else if("current"==e||"applied"==e)f="none"==j?c.slice():"applied"==j?g.slice():h.map(c,function(a){return-1===h.inArray(a,g)?a:null});else if("index"==e||"original"==e){c=0;for(e=a.aoData.length;c<e;c++)"none"==j?f.push(c):(d=h.inArray(c,g),(-1===d&&"removed"==j||0<=d&&
|
||||||
|
"applied"==j)&&f.push(c))}return f};r("rows()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=ab(b),c=this.iterator("table",function(c){var d=b;return $a("row",a,function(a){var b=Pb(a);if(b!==null&&!d)return[b];var j=Ca(c,d);if(b!==null&&h.inArray(b,j)!==-1)return[b];if(!a)return j;if(typeof a==="function")return h.map(j,function(b){var d=c.aoData[b];return a(b,d._aData,d.nTr)?b:null});b=Sb(ia(c.aoData,j,"nTr"));return a.nodeName&&h.inArray(a,b)!==-1?[a._DT_RowIndex]:h(b).filter(a).map(function(){return this._DT_RowIndex}).toArray()},
|
||||||
|
c,d)},1);c.selector.rows=a;c.selector.opts=b;return c});r("rows().nodes()",function(){return this.iterator("row",function(a,b){return a.aoData[b].nTr||k},1)});r("rows().data()",function(){return this.iterator(!0,"rows",function(a,b){return ia(a.aoData,b,"_aData")},1)});v("rows().cache()","row().cache()",function(a){return this.iterator("row",function(b,c){var e=b.aoData[c];return"search"===a?e._aFilterData:e._aSortData},1)});v("rows().invalidate()","row().invalidate()",function(a){return this.iterator("row",
|
||||||
|
function(b,c){ca(b,c,a)})});v("rows().indexes()","row().index()",function(){return this.iterator("row",function(a,b){return b},1)});v("rows().remove()","row().remove()",function(){var a=this;return this.iterator("row",function(b,c,e){var d=b.aoData;d.splice(c,1);for(var f=0,g=d.length;f<g;f++)null!==d[f].nTr&&(d[f].nTr._DT_RowIndex=f);h.inArray(c,b.aiDisplay);pa(b.aiDisplayMaster,c);pa(b.aiDisplay,c);pa(a[e],c,!1);Sa(b)})});r("rows.add()",function(a){var b=this.iterator("table",function(b){var c,
|
||||||
|
f,g,h=[];f=0;for(g=a.length;f<g;f++)c=a[f],c.nodeName&&"TR"===c.nodeName.toUpperCase()?h.push(ma(b,c)[0]):h.push(K(b,c));return h},1),c=this.rows(-1);c.pop();c.push.apply(c,b.toArray());return c});r("row()",function(a,b){return bb(this.rows(a,b))});r("row().data()",function(a){var b=this.context;if(a===k)return b.length&&this.length?b[0].aoData[this[0]]._aData:k;b[0].aoData[this[0]]._aData=a;ca(b[0],this[0],"data");return this});r("row().node()",function(){var a=this.context;return a.length&&this.length?
|
||||||
|
a[0].aoData[this[0]].nTr||null:null});r("row.add()",function(a){a instanceof h&&a.length&&(a=a[0]);var b=this.iterator("table",function(b){return a.nodeName&&"TR"===a.nodeName.toUpperCase()?ma(b,a)[0]:K(b,a)});return this.row(b[0])});var cb=function(a,b){var c=a.context;c.length&&(c=c[0].aoData[b!==k?b:a[0]],c._details&&(c._details.remove(),c._detailsShow=k,c._details=k))},Vb=function(a,b){var c=a.context;if(c.length&&a.length){var e=c[0].aoData[a[0]];if(e._details){(e._detailsShow=b)?e._details.insertAfter(e.nTr):
|
||||||
|
e._details.detach();var d=c[0],f=new t(d),g=d.aoData;f.off("draw.dt.DT_details column-visibility.dt.DT_details destroy.dt.DT_details");0<D(g,"_details").length&&(f.on("draw.dt.DT_details",function(a,b){d===b&&f.rows({page:"current"}).eq(0).each(function(a){a=g[a];a._detailsShow&&a._details.insertAfter(a.nTr)})}),f.on("column-visibility.dt.DT_details",function(a,b){if(d===b)for(var c,e=aa(b),f=0,h=g.length;f<h;f++)c=g[f],c._details&&c._details.children("td[colspan]").attr("colspan",e)}),f.on("destroy.dt.DT_details",
|
||||||
|
function(a,b){if(d===b)for(var c=0,e=g.length;c<e;c++)g[c]._details&&cb(f,c)}))}}};r("row().child()",function(a,b){var c=this.context;if(a===k)return c.length&&this.length?c[0].aoData[this[0]]._details:k;if(!0===a)this.child.show();else if(!1===a)cb(this);else if(c.length&&this.length){var e=c[0],c=c[0].aoData[this[0]],d=[],f=function(a,b){if(h.isArray(a)||a instanceof h)for(var c=0,k=a.length;c<k;c++)f(a[c],b);else a.nodeName&&"tr"===a.nodeName.toLowerCase()?d.push(a):(c=h("<tr><td/></tr>").addClass(b),
|
||||||
|
h("td",c).addClass(b).html(a)[0].colSpan=aa(e),d.push(c[0]))};f(a,b);c._details&&c._details.remove();c._details=h(d);c._detailsShow&&c._details.insertAfter(c.nTr)}return this});r(["row().child.show()","row().child().show()"],function(){Vb(this,!0);return this});r(["row().child.hide()","row().child().hide()"],function(){Vb(this,!1);return this});r(["row().child.remove()","row().child().remove()"],function(){cb(this);return this});r("row().child.isShown()",function(){var a=this.context;return a.length&&
|
||||||
|
this.length?a[0].aoData[this[0]]._detailsShow||!1:!1});var dc=/^(.+):(name|visIdx|visible)$/,Wb=function(a,b,c,e,d){for(var c=[],e=0,f=d.length;e<f;e++)c.push(x(a,d[e],b));return c};r("columns()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=ab(b),c=this.iterator("table",function(c){var d=a,f=b,g=c.aoColumns,j=D(g,"sName"),i=D(g,"nTh");return $a("column",d,function(a){var b=Pb(a);if(a==="")return V(g.length);if(b!==null)return[b>=0?b:g.length+b];if(typeof a==="function"){var d=Ca(c,
|
||||||
|
f);return h.map(g,function(b,f){return a(f,Wb(c,f,0,0,d),i[f])?f:null})}var k=typeof a==="string"?a.match(dc):"";if(k)switch(k[2]){case "visIdx":case "visible":b=parseInt(k[1],10);if(b<0){var m=h.map(g,function(a,b){return a.bVisible?b:null});return[m[m.length+b]]}return[la(c,b)];case "name":return h.map(j,function(a,b){return a===k[1]?b:null})}else return h(i).filter(a).map(function(){return h.inArray(this,i)}).toArray()},c,f)},1);c.selector.cols=a;c.selector.opts=b;return c});v("columns().header()",
|
||||||
|
"column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh},1)});v("columns().footer()","column().footer()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTf},1)});v("columns().data()","column().data()",function(){return this.iterator("column-rows",Wb,1)});v("columns().dataSrc()","column().dataSrc()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].mData},1)});v("columns().cache()","column().cache()",
|
||||||
|
function(a){return this.iterator("column-rows",function(b,c,e,d,f){return ia(b.aoData,f,"search"===a?"_aFilterData":"_aSortData",c)},1)});v("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,e,d){return ia(a.aoData,d,"anCells",b)},1)});v("columns().visible()","column().visible()",function(a,b){return this.iterator("column",function(c,e){if(a===k)return c.aoColumns[e].bVisible;var d=c.aoColumns,f=d[e],g=c.aoData,j,i,m;if(a!==k&&f.bVisible!==a){if(a){var l=
|
||||||
|
h.inArray(!0,D(d,"bVisible"),e+1);j=0;for(i=g.length;j<i;j++)m=g[j].nTr,d=g[j].anCells,m&&m.insertBefore(d[e],d[l]||null)}else h(D(c.aoData,"anCells",e)).detach();f.bVisible=a;ea(c,c.aoHeader);ea(c,c.aoFooter);if(b===k||b)X(c),(c.oScroll.sX||c.oScroll.sY)&&Y(c);w(c,null,"column-visibility",[c,e,a]);ya(c)}})});v("columns().indexes()","column().index()",function(a){return this.iterator("column",function(b,c){return"visible"===a?$(b,c):c},1)});r("columns.adjust()",function(){return this.iterator("table",
|
||||||
|
function(a){X(a)},1)});r("column.index()",function(a,b){if(0!==this.context.length){var c=this.context[0];if("fromVisible"===a||"toData"===a)return la(c,b);if("fromData"===a||"toVisible"===a)return $(c,b)}});r("column()",function(a,b){return bb(this.columns(a,b))});r("cells()",function(a,b,c){h.isPlainObject(a)&&(a.row===k?(c=a,a=null):(c=b,b=null));h.isPlainObject(b)&&(c=b,b=null);if(null===b||b===k)return this.iterator("table",function(b){var d=a,e=ab(c),f=b.aoData,g=Ca(b,e),i=Sb(ia(f,g,"anCells")),
|
||||||
|
j=h([].concat.apply([],i)),l,m=b.aoColumns.length,o,r,t,s,u,v;return $a("cell",d,function(a){var c=typeof a==="function";if(a===null||a===k||c){o=[];r=0;for(t=g.length;r<t;r++){l=g[r];for(s=0;s<m;s++){u={row:l,column:s};if(c){v=b.aoData[l];a(u,x(b,l,s),v.anCells?v.anCells[s]:null)&&o.push(u)}else o.push(u)}}return o}return h.isPlainObject(a)?[a]:j.filter(a).map(function(a,b){l=b.parentNode._DT_RowIndex;return{row:l,column:h.inArray(b,f[l].anCells)}}).toArray()},b,e)});var e=this.columns(b,c),d=this.rows(a,
|
||||||
|
c),f,g,j,i,m,l=this.iterator("table",function(a,b){f=[];g=0;for(j=d[b].length;g<j;g++){i=0;for(m=e[b].length;i<m;i++)f.push({row:d[b][g],column:e[b][i]})}return f},1);h.extend(l.selector,{cols:b,rows:a,opts:c});return l});v("cells().nodes()","cell().node()",function(){return this.iterator("cell",function(a,b,c){return(a=a.aoData[b].anCells)?a[c]:k},1)});r("cells().data()",function(){return this.iterator("cell",function(a,b,c){return x(a,b,c)},1)});v("cells().cache()","cell().cache()",function(a){a=
|
||||||
|
"search"===a?"_aFilterData":"_aSortData";return this.iterator("cell",function(b,c,e){return b.aoData[c][a][e]},1)});v("cells().render()","cell().render()",function(a){return this.iterator("cell",function(b,c,e){return x(b,c,e,a)},1)});v("cells().indexes()","cell().index()",function(){return this.iterator("cell",function(a,b,c){return{row:b,column:c,columnVisible:$(a,c)}},1)});v("cells().invalidate()","cell().invalidate()",function(a){return this.iterator("cell",function(b,c,e){ca(b,c,a,e)})});r("cell()",
|
||||||
|
function(a,b,c){return bb(this.cells(a,b,c))});r("cell().data()",function(a){var b=this.context,c=this[0];if(a===k)return b.length&&c.length?x(b[0],c[0].row,c[0].column):k;Ia(b[0],c[0].row,c[0].column,a);ca(b[0],c[0].row,"data",c[0].column);return this});r("order()",function(a,b){var c=this.context;if(a===k)return 0!==c.length?c[0].aaSorting:k;"number"===typeof a?a=[[a,b]]:h.isArray(a[0])||(a=Array.prototype.slice.call(arguments));return this.iterator("table",function(b){b.aaSorting=a.slice()})});
|
||||||
|
r("order.listener()",function(a,b,c){return this.iterator("table",function(e){Oa(e,a,b,c)})});r(["columns().order()","column().order()"],function(a){var b=this;return this.iterator("table",function(c,e){var d=[];h.each(b[e],function(b,c){d.push([c,a])});c.aaSorting=d})});r("search()",function(a,b,c,e){var d=this.context;return a===k?0!==d.length?d[0].oPreviousSearch.sSearch:k:this.iterator("table",function(d){d.oFeatures.bFilter&&fa(d,h.extend({},d.oPreviousSearch,{sSearch:a+"",bRegex:null===b?!1:
|
||||||
|
b,bSmart:null===c?!0:c,bCaseInsensitive:null===e?!0:e}),1)})});v("columns().search()","column().search()",function(a,b,c,e){return this.iterator("column",function(d,f){var g=d.aoPreSearchCols;if(a===k)return g[f].sSearch;d.oFeatures.bFilter&&(h.extend(g[f],{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===e?!0:e}),fa(d,d.oPreviousSearch,1))})});r("state()",function(){return this.context.length?this.context[0].oSavedState:null});r("state.clear()",function(){return this.iterator("table",
|
||||||
|
function(a){a.fnStateSaveCallback.call(a.oInstance,a,{})})});r("state.loaded()",function(){return this.context.length?this.context[0].oLoadedState:null});r("state.save()",function(){return this.iterator("table",function(a){ya(a)})});m.versionCheck=m.fnVersionCheck=function(a){for(var b=m.version.split("."),a=a.split("."),c,e,d=0,f=a.length;d<f;d++)if(c=parseInt(b[d],10)||0,e=parseInt(a[d],10)||0,c!==e)return c>e;return!0};m.isDataTable=m.fnIsDataTable=function(a){var b=h(a).get(0),c=!1;h.each(m.settings,
|
||||||
|
function(a,d){var f=d.nScrollHead?h("table",d.nScrollHead)[0]:null,g=d.nScrollFoot?h("table",d.nScrollFoot)[0]:null;if(d.nTable===b||f===b||g===b)c=!0});return c};m.tables=m.fnTables=function(a){return h.map(m.settings,function(b){if(!a||a&&h(b.nTable).is(":visible"))return b.nTable})};m.util={throttle:ua,escapeRegex:va};m.camelToHungarian=H;r("$()",function(a,b){var c=this.rows(b).nodes(),c=h(c);return h([].concat(c.filter(a).toArray(),c.find(a).toArray()))});h.each(["on","one","off"],function(a,
|
||||||
|
b){r(b+"()",function(){var a=Array.prototype.slice.call(arguments);a[0].match(/\.dt\b/)||(a[0]+=".dt");var e=h(this.tables().nodes());e[b].apply(e,a);return this})});r("clear()",function(){return this.iterator("table",function(a){oa(a)})});r("settings()",function(){return new t(this.context,this.context)});r("init()",function(){var a=this.context;return a.length?a[0].oInit:null});r("data()",function(){return this.iterator("table",function(a){return D(a.aoData,"_aData")}).flatten()});r("destroy()",
|
||||||
|
function(a){a=a||!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,e=b.oClasses,d=b.nTable,f=b.nTBody,g=b.nTHead,j=b.nTFoot,i=h(d),f=h(f),k=h(b.nTableWrapper),l=h.map(b.aoData,function(a){return a.nTr}),q;b.bDestroying=!0;w(b,"aoDestroyCallback","destroy",[b]);a||(new t(b)).columns().visible(!0);k.unbind(".DT").find(":not(tbody *)").unbind(".DT");h(Ea).unbind(".DT-"+b.sInstance);d!=g.parentNode&&(i.children("thead").detach(),i.append(g));j&&d!=j.parentNode&&(i.children("tfoot").detach(),
|
||||||
|
i.append(j));i.detach();k.detach();b.aaSorting=[];b.aaSortingFixed=[];xa(b);h(l).removeClass(b.asStripeClasses.join(" "));h("th, td",g).removeClass(e.sSortable+" "+e.sSortableAsc+" "+e.sSortableDesc+" "+e.sSortableNone);b.bJUI&&(h("th span."+e.sSortIcon+", td span."+e.sSortIcon,g).detach(),h("th, td",g).each(function(){var a=h("div."+e.sSortJUIWrapper,this);h(this).append(a.contents());a.detach()}));!a&&c&&c.insertBefore(d,b.nTableReinsertBefore);f.children().detach();f.append(l);i.css("width",b.sDestroyWidth).removeClass(e.sTable);
|
||||||
|
(q=b.asDestroyStripes.length)&&f.children().each(function(a){h(this).addClass(b.asDestroyStripes[a%q])});c=h.inArray(b,m.settings);-1!==c&&m.settings.splice(c,1)})});h.each(["column","row","cell"],function(a,b){r(b+"s().every()",function(a){return this.iterator(b,function(e,d,f){a.call((new t(e))[b](d,f))})})});r("i18n()",function(a,b,c){var e=this.context[0],a=R(a)(e.oLanguage);a===k&&(a=b);c!==k&&h.isPlainObject(a)&&(a=a[c]!==k?a[c]:a._);return a.replace("%d",c)});m.version="1.10.7";m.settings=
|
||||||
|
[];m.models={};m.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};m.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null};m.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",
|
||||||
|
sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};m.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,
|
||||||
|
fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){}},fnStateLoadParams:null,
|
||||||
|
fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},fnStateSaveParams:null,iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},
|
||||||
|
sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:h.extend({},m.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",searchDelay:null,
|
||||||
|
sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null};W(m.defaults);m.defaults.column={aDataSort:null,iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};W(m.defaults.column);m.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,
|
||||||
|
bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],
|
||||||
|
sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,searchDelay:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,
|
||||||
|
bAjaxDataGet:!0,jqXHR:null,json:k,oAjaxData:k,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==B(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==B(this)?1*this._iRecordsDisplay:
|
||||||
|
this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,e=this.aiDisplay.length,d=this.oFeatures,f=d.bPaginate;return d.bServerSide?!1===f||-1===a?b+e:Math.min(b+a,this._iRecordsDisplay):!f||c>e||-1===a?e:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{}};m.ext=u={buttons:{},classes:{},errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},
|
||||||
|
header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:m.fnVersionCheck,iApiIndex:0,oJUIClasses:{},sVersion:m.version};h.extend(u,{afnFiltering:u.search,aTypes:u.type.detect,ofnSearch:u.type.search,oSort:u.type.order,afnSortData:u.order,aoFeatures:u.feature,oApi:u.internal,oStdClasses:u.classes,oPagination:u.pager});h.extend(m.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",
|
||||||
|
sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",
|
||||||
|
sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var Da="",Da="",F=Da+"ui-state-default",ja=Da+"css_right ui-icon ui-icon-",Xb=Da+"fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix";h.extend(m.ext.oJUIClasses,
|
||||||
|
m.ext.classes,{sPageButton:"fg-button ui-button "+F,sPageButtonActive:"ui-state-disabled",sPageButtonDisabled:"ui-state-disabled",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:F+" sorting_asc",sSortDesc:F+" sorting_desc",sSortable:F+" sorting",sSortableAsc:F+" sorting_asc_disabled",sSortableDesc:F+" sorting_desc_disabled",sSortableNone:F+" sorting_disabled",sSortJUIAsc:ja+"triangle-1-n",sSortJUIDesc:ja+"triangle-1-s",sSortJUI:ja+"carat-2-n-s",
|
||||||
|
sSortJUIAscAllowed:ja+"carat-1-n",sSortJUIDescAllowed:ja+"carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead "+F,sScrollFoot:"dataTables_scrollFoot "+F,sHeaderTH:F,sFooterTH:F,sJUIHeader:Xb+" ui-corner-tl ui-corner-tr",sJUIFooter:Xb+" ui-corner-bl ui-corner-br"});var Mb=m.ext.pager;h.extend(Mb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},simple_numbers:function(a,b){return["previous",
|
||||||
|
Wa(a,b),"next"]},full_numbers:function(a,b){return["first","previous",Wa(a,b),"next","last"]},_numbers:Wa,numbers_length:7});h.extend(!0,m.ext.renderer,{pageButton:{_:function(a,b,c,e,d,f){var g=a.oClasses,j=a.oLanguage.oPaginate,i,k,l=0,m=function(b,e){var n,r,t,s,u=function(b){Ta(a,b.data.action,true)};n=0;for(r=e.length;n<r;n++){s=e[n];if(h.isArray(s)){t=h("<"+(s.DT_el||"div")+"/>").appendTo(b);m(t,s)}else{k=i="";switch(s){case "ellipsis":b.append('<span class="ellipsis">…</span>');break;
|
||||||
|
case "first":i=j.sFirst;k=s+(d>0?"":" "+g.sPageButtonDisabled);break;case "previous":i=j.sPrevious;k=s+(d>0?"":" "+g.sPageButtonDisabled);break;case "next":i=j.sNext;k=s+(d<f-1?"":" "+g.sPageButtonDisabled);break;case "last":i=j.sLast;k=s+(d<f-1?"":" "+g.sPageButtonDisabled);break;default:i=s+1;k=d===s?g.sPageButtonActive:""}if(i){t=h("<a>",{"class":g.sPageButton+" "+k,"aria-controls":a.sTableId,"data-dt-idx":l,tabindex:a.iTabIndex,id:c===0&&typeof s==="string"?a.sTableId+"_"+s:null}).html(i).appendTo(b);
|
||||||
|
Va(t,{action:s},u);l++}}}},n;try{n=h(Q.activeElement).data("dt-idx")}catch(r){}m(h(b).empty(),e);n&&h(b).find("[data-dt-idx="+n+"]").focus()}}});h.extend(m.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return Za(a,c)?"num"+c:null},function(a){if(a&&!(a instanceof Date)&&(!ac.test(a)||!bc.test(a)))return null;var b=Date.parse(a);return null!==b&&!isNaN(b)||J(a)?"date":null},function(a,b){var c=b.oLanguage.sDecimal;return Za(a,c,!0)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;
|
||||||
|
return Rb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Rb(a,c,!0)?"html-num-fmt"+c:null},function(a){return J(a)||"string"===typeof a&&-1!==a.indexOf("<")?"html":null}]);h.extend(m.ext.type.search,{html:function(a){return J(a)?a:"string"===typeof a?a.replace(Ob," ").replace(Ba,""):""},string:function(a){return J(a)?a:"string"===typeof a?a.replace(Ob," "):a}});var Aa=function(a,b,c,e){if(0!==a&&(!a||"-"===a))return-Infinity;b&&(a=Qb(a,b));a.replace&&(c&&(a=a.replace(c,"")),
|
||||||
|
e&&(a=a.replace(e,"")));return 1*a};h.extend(u.type.order,{"date-pre":function(a){return Date.parse(a)||0},"html-pre":function(a){return J(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return J(a)?"":"string"===typeof a?a.toLowerCase():!a.toString?"":a.toString()},"string-asc":function(a,b){return a<b?-1:a>b?1:0},"string-desc":function(a,b){return a<b?1:a>b?-1:0}});db("");h.extend(!0,m.ext.renderer,{header:{_:function(a,b,c,e){h(a.nTable).on("order.dt.DT",function(d,
|
||||||
|
f,g,h){if(a===f){d=c.idx;b.removeClass(c.sSortingClass+" "+e.sSortAsc+" "+e.sSortDesc).addClass(h[d]=="asc"?e.sSortAsc:h[d]=="desc"?e.sSortDesc:c.sSortingClass)}})},jqueryui:function(a,b,c,e){h("<div/>").addClass(e.sSortJUIWrapper).append(b.contents()).append(h("<span/>").addClass(e.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);h(a.nTable).on("order.dt.DT",function(d,f,g,h){if(a===f){d=c.idx;b.removeClass(e.sSortAsc+" "+e.sSortDesc).addClass(h[d]=="asc"?e.sSortAsc:h[d]=="desc"?e.sSortDesc:c.sSortingClass);
|
||||||
|
b.find("span."+e.sSortIcon).removeClass(e.sSortJUIAsc+" "+e.sSortJUIDesc+" "+e.sSortJUI+" "+e.sSortJUIAscAllowed+" "+e.sSortJUIDescAllowed).addClass(h[d]=="asc"?e.sSortJUIAsc:h[d]=="desc"?e.sSortJUIDesc:c.sSortingClassJUI)}})}}});m.render={number:function(a,b,c,e){return{display:function(d){if("number"!==typeof d&&"string"!==typeof d)return d;var f=0>d?"-":"",d=Math.abs(parseFloat(d)),g=parseInt(d,10),d=c?b+(d-g).toFixed(c).substring(2):"";return f+(e||"")+g.toString().replace(/\B(?=(\d{3})+(?!\d))/g,
|
||||||
|
a)+d}}}};h.extend(m.ext.internal,{_fnExternApiFunc:Nb,_fnBuildAjax:ra,_fnAjaxUpdate:kb,_fnAjaxParameters:tb,_fnAjaxUpdateDraw:ub,_fnAjaxDataSrc:sa,_fnAddColumn:Fa,_fnColumnOptions:ka,_fnAdjustColumnSizing:X,_fnVisibleToColumnIndex:la,_fnColumnIndexToVisible:$,_fnVisbleColumns:aa,_fnGetColumns:Z,_fnColumnTypes:Ha,_fnApplyColumnDefs:ib,_fnHungarianMap:W,_fnCamelToHungarian:H,_fnLanguageCompat:P,_fnBrowserDetect:gb,_fnAddData:K,_fnAddTr:ma,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==k?b._DT_RowIndex:
|
||||||
|
null},_fnNodeToColumnIndex:function(a,b,c){return h.inArray(c,a.aoData[b].anCells)},_fnGetCellData:x,_fnSetCellData:Ia,_fnSplitObjNotation:Ka,_fnGetObjectDataFn:R,_fnSetObjectDataFn:S,_fnGetDataMaster:La,_fnClearTable:oa,_fnDeleteIndex:pa,_fnInvalidate:ca,_fnGetRowElements:na,_fnCreateTr:Ja,_fnBuildHead:jb,_fnDrawHead:ea,_fnDraw:M,_fnReDraw:N,_fnAddOptionsHtml:mb,_fnDetectHeader:da,_fnGetUniqueThs:qa,_fnFeatureHtmlFilter:ob,_fnFilterComplete:fa,_fnFilterCustom:xb,_fnFilterColumn:wb,_fnFilter:vb,_fnFilterCreateSearch:Qa,
|
||||||
|
_fnEscapeRegex:va,_fnFilterData:yb,_fnFeatureHtmlInfo:rb,_fnUpdateInfo:Bb,_fnInfoMacros:Cb,_fnInitialise:ga,_fnInitComplete:ta,_fnLengthChange:Ra,_fnFeatureHtmlLength:nb,_fnFeatureHtmlPaginate:sb,_fnPageChange:Ta,_fnFeatureHtmlProcessing:pb,_fnProcessingDisplay:C,_fnFeatureHtmlTable:qb,_fnScrollDraw:Y,_fnApplyToChildren:G,_fnCalculateColumnWidths:Ga,_fnThrottle:ua,_fnConvertToWidth:Db,_fnScrollingWidthAdjust:Fb,_fnGetWidestNode:Eb,_fnGetMaxLenString:Gb,_fnStringToCss:s,_fnScrollBarWidth:Hb,_fnSortFlatten:U,
|
||||||
|
_fnSort:lb,_fnSortAria:Jb,_fnSortListener:Ua,_fnSortAttachListener:Oa,_fnSortingClasses:xa,_fnSortData:Ib,_fnSaveState:ya,_fnLoadState:Kb,_fnSettingsFromNode:za,_fnLog:I,_fnMap:E,_fnBindAction:Va,_fnCallbackReg:z,_fnCallbackFire:w,_fnLengthOverflow:Sa,_fnRenderer:Pa,_fnDataSource:B,_fnRowAttributes:Ma,_fnCalculateEnd:function(){}});h.fn.dataTable=m;h.fn.dataTableSettings=m.settings;h.fn.dataTableExt=m.ext;h.fn.DataTable=function(a){return h(this).dataTable(a).api()};h.each(m,function(a,b){h.fn.DataTable[a]=
|
||||||
|
b});return h.fn.dataTable};"function"===typeof define&&define.amd?define("datatables",["jquery"],P):"object"===typeof exports?module.exports=P(require("jquery")):jQuery&&!jQuery.fn.dataTable&&P(jQuery)})(window,document);
|
||||||
14950
externals/jquery.datatables_ase.js
vendored
Normal file
14950
externals/jquery.datatables_ase.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11
externals/jquery.js
vendored
Normal file
11
externals/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
19
externals/msrCrypto.js
vendored
Normal file
19
externals/msrCrypto.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,22 +0,0 @@
|
|||||||
<svg id="b089cfca-0de1-451c-a1ca-6680ea50cb4f" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="b25d0836-964a-4c84-8c20-855f66e8345e" cx="-105.006" cy="-10.409" r="5.954" gradientTransform="translate(117.739 19.644) scale(1.036 1.027)" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0.183" stop-color="#5ea0ef"/>
|
|
||||||
<stop offset="1" stop-color="#0078d4"/>
|
|
||||||
</radialGradient>
|
|
||||||
<clipPath id="b36c7f5d-2ef1-4760-8a25-eeb9661f4e47">
|
|
||||||
<path d="M14.969,7.53A6.137,6.137,0,1,1,7.574,2.987,6.137,6.137,0,0,1,14.969,7.53Z" fill="none"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
<title>Icon-databases-121</title>
|
|
||||||
<path d="M2.954,5.266a.175.175,0,0,1-.176-.176h0A2.012,2.012,0,0,0,.769,3.081a.176.176,0,0,1-.176-.175h0a.176.176,0,0,1,.176-.176A2.012,2.012,0,0,0,2.778.72.175.175,0,0,1,2.954.544h0A.175.175,0,0,1,3.13.72h0A2.012,2.012,0,0,0,5.139,2.729a.175.175,0,0,1,.176.176h0a.175.175,0,0,1-.176.176h0A2.011,2.011,0,0,0,3.13,5.09.177.177,0,0,1,2.954,5.266Z" fill="#50e6ff"/>
|
|
||||||
<path d="M15.611,17.456a.141.141,0,0,1-.141-.141h0a1.609,1.609,0,0,0-1.607-1.607.141.141,0,0,1-.141-.14h0a.141.141,0,0,1,.141-.141h0a1.608,1.608,0,0,0,1.607-1.607.141.141,0,0,1,.141-.141h0a.141.141,0,0,1,.141.141h0a1.608,1.608,0,0,0,1.607,1.607.141.141,0,1,1,0,.282h0a1.609,1.609,0,0,0-1.607,1.607A.141.141,0,0,1,15.611,17.456Z" fill="#50e6ff"/>
|
|
||||||
<g>
|
|
||||||
<path d="M14.969,7.53A6.137,6.137,0,1,1,7.574,2.987,6.137,6.137,0,0,1,14.969,7.53Z" fill="url(#b25d0836-964a-4c84-8c20-855f66e8345e)"/>
|
|
||||||
<g clip-path="url(#b36c7f5d-2ef1-4760-8a25-eeb9661f4e47)">
|
|
||||||
<path d="M5.709,13.115A1.638,1.638,0,1,0,5.714,9.84,1.307,1.307,0,0,0,5.721,9.7,1.651,1.651,0,0,0,4.06,8.064H2.832a6.251,6.251,0,0,0,1.595,5.051Z" fill="#f2f2f2"/>
|
|
||||||
<path d="M15.045,7.815c0-.015,0-.03-.007-.044a5.978,5.978,0,0,0-1.406-2.88,1.825,1.825,0,0,0-.289-.09,1.806,1.806,0,0,0-2.3,1.663,2,2,0,0,0-.2-.013,1.737,1.737,0,0,0-.581,3.374,1.451,1.451,0,0,0,.541.1h2.03A13.453,13.453,0,0,0,15.045,7.815Z" fill="#f2f2f2"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path d="M17.191,3.832c-.629-1.047-2.1-1.455-4.155-1.149a14.606,14.606,0,0,0-2.082.452,6.456,6.456,0,0,1,1.528.767c.241-.053.483-.116.715-.151A7.49,7.49,0,0,1,14.3,3.662a2.188,2.188,0,0,1,1.959.725h0c.383.638.06,1.729-.886,3a16.723,16.723,0,0,1-4.749,4.051A16.758,16.758,0,0,1,4.8,13.7c-1.564.234-2.682,0-3.065-.636s-.06-1.73.886-2.995c.117-.157.146-.234.279-.392a6.252,6.252,0,0,1,.026-1.63A11.552,11.552,0,0,0,1.756,9.419C.517,11.076.181,12.566.809,13.613a3.165,3.165,0,0,0,2.9,1.249,8.434,8.434,0,0,0,1.251-.1,17.855,17.855,0,0,0,6.219-2.4,17.808,17.808,0,0,0,5.061-4.332C17.483,6.369,17.819,4.88,17.191,3.832Z" fill="#50e6ff"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -4,6 +4,6 @@ module.exports = {
|
|||||||
launch: {
|
launch: {
|
||||||
headless: isCI,
|
headless: isCI,
|
||||||
slowMo: isCI ? null : 20,
|
slowMo: isCI ? null : 20,
|
||||||
defaultViewport: null
|
defaultViewport: null,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
preset: "jest-puppeteer",
|
preset: "jest-puppeteer",
|
||||||
testMatch: ["<rootDir>/test/**/*.spec.[jt]s?(x)"],
|
testMatch: ["<rootDir>/test/**/*.spec.[jt]s?(x)"],
|
||||||
setupFiles: ["dotenv/config"]
|
setupFiles: ["dotenv/config"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,11 +39,11 @@ 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: 20,
|
branches: 18,
|
||||||
functions: 24,
|
functions: 22,
|
||||||
lines: 30,
|
lines: 28,
|
||||||
statements: 29.0
|
statements: 27,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Make calling deprecated APIs throw helpful error messages
|
// Make calling deprecated APIs throw helpful error messages
|
||||||
@@ -76,7 +76,7 @@ module.exports = {
|
|||||||
"office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes
|
"office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes
|
||||||
"^dnd-core$": "dnd-core/dist/cjs",
|
"^dnd-core$": "dnd-core/dist/cjs",
|
||||||
"^react-dnd$": "react-dnd/dist/cjs",
|
"^react-dnd$": "react-dnd/dist/cjs",
|
||||||
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs"
|
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs",
|
||||||
},
|
},
|
||||||
|
|
||||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||||
@@ -164,11 +164,11 @@ module.exports = {
|
|||||||
// A map from regular expressions to paths to transformers
|
// A map from regular expressions to paths to transformers
|
||||||
transform: {
|
transform: {
|
||||||
"^.+\\.html?$": "html-loader-jest",
|
"^.+\\.html?$": "html-loader-jest",
|
||||||
"^.+\\.[t|j]sx?$": "babel-jest"
|
"^.+\\.[t|j]sx?$": "babel-jest",
|
||||||
},
|
},
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||||
transformIgnorePatterns: ["/node_modules/", "/externals/"]
|
transformIgnorePatterns: ["/node_modules/", "/externals/"],
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||||
// unmockedModulePathPatterns: undefined,
|
// unmockedModulePathPatterns: undefined,
|
||||||
|
|||||||
1910
less/StorageExplorer.less
Normal file
1910
less/StorageExplorer.less
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2349,12 +2349,6 @@ a:link {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabsManagerContainer {
|
|
||||||
height: 100%;
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabs {
|
.tabs {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 15px 0 25px 0;
|
margin: 15px 0 25px 0;
|
||||||
|
|||||||
17
less/forms_ase.less
Normal file
17
less/forms_ase.less
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@import "./Common/Constants";
|
||||||
|
@import "./forms";
|
||||||
|
|
||||||
|
.formTree {
|
||||||
|
border: 1px solid inherit;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0px 12px 1px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formTree:hover {
|
||||||
|
border: 1px solid inherit;
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formTree:active {
|
||||||
|
border: 1px solid inherit;
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
.infoBoxMessage {
|
.infoBoxMessage {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: normal;
|
white-space: nowrap;
|
||||||
width: 320px;
|
width: 320px;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
color: @BaseHigh;
|
color: @BaseHigh;
|
||||||
|
|||||||
3805
package-lock.json
generated
3805
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
@@ -4,7 +4,7 @@
|
|||||||
"description": "Cosmos Explorer",
|
"description": "Cosmos Explorer",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/cosmos": "3.9.0",
|
"@azure/cosmos": "3.7.4",
|
||||||
"@azure/cosmos-language-service": "0.0.4",
|
"@azure/cosmos-language-service": "0.0.4",
|
||||||
"@jupyterlab/services": "4.2.0",
|
"@jupyterlab/services": "4.2.0",
|
||||||
"@jupyterlab/terminal": "1.2.1",
|
"@jupyterlab/terminal": "1.2.1",
|
||||||
@@ -34,15 +34,13 @@
|
|||||||
"@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",
|
"@uifabric/react-cards": "0.109.53",
|
||||||
"@types/node-fetch": "2.5.7",
|
"@uifabric/styling": "7.11.2",
|
||||||
"@uifabric/react-cards": "0.109.110",
|
|
||||||
"@uifabric/styling": "7.13.7",
|
|
||||||
"abort-controller": "3.0.0",
|
"abort-controller": "3.0.0",
|
||||||
"applicationinsights": "1.8.0",
|
"applicationinsights": "1.8.0",
|
||||||
"babel-polyfill": "6.26.0",
|
"babel-polyfill": "6.26.0",
|
||||||
"bootstrap": "3.4.1",
|
"bootstrap": "3.3.7",
|
||||||
"canvas": "2.6.1",
|
"canvas": "2.6.0",
|
||||||
"clean-webpack-plugin": "0.1.19",
|
"clean-webpack-plugin": "0.1.19",
|
||||||
"copy-webpack-plugin": "6.0.2",
|
"copy-webpack-plugin": "6.0.2",
|
||||||
"crossroads": "0.12.2",
|
"crossroads": "0.12.2",
|
||||||
@@ -56,18 +54,15 @@
|
|||||||
"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.4.0",
|
||||||
"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.115.0",
|
||||||
"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",
|
||||||
@@ -114,14 +109,13 @@
|
|||||||
"@types/react-notification-system": "0.2.39",
|
"@types/react-notification-system": "0.2.39",
|
||||||
"@types/react-redux": "7.1.7",
|
"@types/react-redux": "7.1.7",
|
||||||
"@types/sinon": "2.3.3",
|
"@types/sinon": "2.3.3",
|
||||||
"@types/styled-components": "5.1.1",
|
"@types/styled-components": "4.1.8",
|
||||||
"@types/text-encoding": "0.0.33",
|
"@types/text-encoding": "0.0.33",
|
||||||
"@types/underscore": "1.7.36",
|
"@types/underscore": "1.7.36",
|
||||||
"@types/webfontloader": "1.6.29",
|
"@types/webfontloader": "1.6.29",
|
||||||
"@typescript-eslint/eslint-plugin": "3.2.0",
|
"@typescript-eslint/eslint-plugin": "3.2.0",
|
||||||
"@typescript-eslint/parser": "3.2.0",
|
"@typescript-eslint/parser": "3.2.0",
|
||||||
"adal-angular": "1.0.15",
|
"adal-angular": "1.0.15",
|
||||||
"axe-puppeteer": "1.1.0",
|
|
||||||
"babel-jest": "24.9.0",
|
"babel-jest": "24.9.0",
|
||||||
"babel-loader": "8.1.0",
|
"babel-loader": "8.1.0",
|
||||||
"buffer": "5.1.0",
|
"buffer": "5.1.0",
|
||||||
@@ -135,7 +129,6 @@
|
|||||||
"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",
|
||||||
@@ -153,8 +146,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": "2.0.5",
|
||||||
"prettier": "1.19.1",
|
|
||||||
"puppeteer": "4.0.0",
|
"puppeteer": "4.0.0",
|
||||||
"raw-loader": "0.5.1",
|
"raw-loader": "0.5.1",
|
||||||
"rimraf": "3.0.0",
|
"rimraf": "3.0.0",
|
||||||
@@ -164,7 +156,7 @@
|
|||||||
"ts-loader": "6.2.2",
|
"ts-loader": "6.2.2",
|
||||||
"tslint": "5.11.0",
|
"tslint": "5.11.0",
|
||||||
"tslint-microsoft-contrib": "6.0.0",
|
"tslint-microsoft-contrib": "6.0.0",
|
||||||
"typescript": "3.9.6",
|
"typescript": "3.8.3",
|
||||||
"url-loader": "1.1.1",
|
"url-loader": "1.1.1",
|
||||||
"webpack": "4.43.0",
|
"webpack": "4.43.0",
|
||||||
"webpack-bundle-analyzer": "3.6.1",
|
"webpack-bundle-analyzer": "3.6.1",
|
||||||
@@ -194,8 +186,7 @@
|
|||||||
"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",
|
||||||
|
|||||||
13
src/Api/Apis.ts
Normal file
13
src/Api/Apis.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export enum AuthType {
|
export enum AuthType {
|
||||||
AAD = "aad",
|
AAD = "aad",
|
||||||
EncryptedToken = "encryptedtoken",
|
EncryptedToken = "encryptedtoken",
|
||||||
MasterKey = "masterkey",
|
MasterKey = "masterkey",
|
||||||
ResourceToken = "resourcetoken"
|
ResourceToken = "resourcetoken",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ReactBindingHandler from "./ReactBindingHandler";
|
import * as ReactBindingHandler from "./ReactBindingHandler";
|
||||||
|
|
||||||
export class BindingHandlersRegisterer {
|
interface RestorePoint {
|
||||||
public static registerBindingHandlers() {
|
readonly element: JQuery;
|
||||||
ko.bindingHandlers.setTemplateReady = {
|
readonly width: number;
|
||||||
init(
|
}
|
||||||
element: any,
|
|
||||||
wrappedValueAccessor: () => any,
|
export class BindingHandlersRegisterer {
|
||||||
allBindings?: ko.AllBindings,
|
public static registerBindingHandlers() {
|
||||||
viewModel?: any,
|
ko.bindingHandlers.setTemplateReady = {
|
||||||
bindingContext?: ko.BindingContext
|
init(
|
||||||
) {
|
element: any,
|
||||||
const value = ko.unwrap(wrappedValueAccessor());
|
wrappedValueAccessor: () => any,
|
||||||
bindingContext?.$data.isTemplateReady(value);
|
allBindings?: ko.AllBindings,
|
||||||
}
|
viewModel?: any,
|
||||||
} as ko.BindingHandler;
|
bindingContext?: ko.BindingContext
|
||||||
|
) {
|
||||||
ReactBindingHandler.Registerer.register();
|
const value = ko.unwrap(wrappedValueAccessor());
|
||||||
}
|
bindingContext.$data.isTemplateReady(value);
|
||||||
}
|
},
|
||||||
|
} as ko.BindingHandler;
|
||||||
|
|
||||||
|
ReactBindingHandler.Registerer.register();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export class Registerer {
|
|||||||
|
|
||||||
// Initial rendering at mount point
|
// Initial rendering at mount point
|
||||||
ReactDOM.render(adapter.renderComponent(), element);
|
ReactDOM.render(adapter.renderComponent(), element);
|
||||||
}
|
},
|
||||||
} as ko.BindingHandler;
|
} as ko.BindingHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class ArrayHashMap<T> {
|
|||||||
public forEach(key: string, iteratorFct: (value: T) => void) {
|
public forEach(key: string, iteratorFct: (value: T) => void) {
|
||||||
const values = this.store.get(key);
|
const values = this.store.get(key);
|
||||||
if (values) {
|
if (values) {
|
||||||
values.forEach(value => iteratorFct(value));
|
values.forEach((value) => iteratorFct(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
|||||||
|
import { CosmosClient, tokenProvider, endpoint, requestPlugin, getTokenFromAuthService } from "./CosmosClient";
|
||||||
import { ResourceType } from "@azure/cosmos/dist-esm/common/constants";
|
import { ResourceType } from "@azure/cosmos/dist-esm/common/constants";
|
||||||
import { configContext, Platform, updateConfigContext, resetConfigContext } from "../ConfigContext";
|
import { config, Platform } from "../Config";
|
||||||
import { updateUserContext } from "../UserContext";
|
|
||||||
import { endpoint, getTokenFromAuthService, requestPlugin, tokenProvider } from "./CosmosClient";
|
|
||||||
|
|
||||||
describe("tokenProvider", () => {
|
describe("tokenProvider", () => {
|
||||||
const options = {
|
const options = {
|
||||||
@@ -10,7 +9,7 @@ describe("tokenProvider", () => {
|
|||||||
resourceId: "",
|
resourceId: "",
|
||||||
resourceType: "dbs" as ResourceType,
|
resourceType: "dbs" as ResourceType,
|
||||||
headers: {},
|
headers: {},
|
||||||
getAuthorizationTokenUsingMasterKey: () => ""
|
getAuthorizationTokenUsingMasterKey: () => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -18,7 +17,7 @@ describe("tokenProvider", () => {
|
|||||||
window.fetch = jest.fn().mockImplementation(() => {
|
window.fetch = jest.fn().mockImplementation(() => {
|
||||||
return {
|
return {
|
||||||
json: () => "{}",
|
json: () => "{}",
|
||||||
headers: new Map()
|
headers: new Map(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -33,9 +32,7 @@ describe("tokenProvider", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("does not call the auth service if a master key is set", async () => {
|
it("does not call the auth service if a master key is set", async () => {
|
||||||
updateUserContext({
|
CosmosClient.masterKey("foo");
|
||||||
masterKey: "foo"
|
|
||||||
});
|
|
||||||
await tokenProvider(options);
|
await tokenProvider(options);
|
||||||
expect((window.fetch as any).mock.calls.length).toBe(0);
|
expect((window.fetch as any).mock.calls.length).toBe(0);
|
||||||
});
|
});
|
||||||
@@ -44,11 +41,11 @@ describe("tokenProvider", () => {
|
|||||||
describe("getTokenFromAuthService", () => {
|
describe("getTokenFromAuthService", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
delete window.dataExplorer;
|
delete window.dataExplorer;
|
||||||
resetConfigContext();
|
delete config.BACKEND_ENDPOINT;
|
||||||
window.fetch = jest.fn().mockImplementation(() => {
|
window.fetch = jest.fn().mockImplementation(() => {
|
||||||
return {
|
return {
|
||||||
json: () => "{}",
|
json: () => "{}",
|
||||||
headers: new Map()
|
headers: new Map(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -67,9 +64,7 @@ describe("getTokenFromAuthService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("builds the correct URL in dev", () => {
|
it("builds the correct URL in dev", () => {
|
||||||
updateConfigContext({
|
config.BACKEND_ENDPOINT = "https://localhost:1234";
|
||||||
BACKEND_ENDPOINT: "https://localhost:1234"
|
|
||||||
});
|
|
||||||
getTokenFromAuthService("GET", "dbs", "foo");
|
getTokenFromAuthService("GET", "dbs", "foo");
|
||||||
expect(window.fetch).toHaveBeenCalledWith(
|
expect(window.fetch).toHaveBeenCalledWith(
|
||||||
"https://localhost:1234/api/guest/runtimeproxy/authorizationTokens",
|
"https://localhost:1234/api/guest/runtimeproxy/authorizationTokens",
|
||||||
@@ -80,28 +75,24 @@ describe("getTokenFromAuthService", () => {
|
|||||||
|
|
||||||
describe("endpoint", () => {
|
describe("endpoint", () => {
|
||||||
it("falls back to _databaseAccount", () => {
|
it("falls back to _databaseAccount", () => {
|
||||||
updateUserContext({
|
CosmosClient.databaseAccount({
|
||||||
databaseAccount: {
|
id: "foo",
|
||||||
id: "foo",
|
name: "foo",
|
||||||
name: "foo",
|
location: "foo",
|
||||||
location: "foo",
|
type: "foo",
|
||||||
type: "foo",
|
kind: "foo",
|
||||||
kind: "foo",
|
tags: [],
|
||||||
tags: [],
|
properties: {
|
||||||
properties: {
|
documentEndpoint: "bar",
|
||||||
documentEndpoint: "bar",
|
gremlinEndpoint: "foo",
|
||||||
gremlinEndpoint: "foo",
|
tableEndpoint: "foo",
|
||||||
tableEndpoint: "foo",
|
cassandraEndpoint: "foo",
|
||||||
cassandraEndpoint: "foo"
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
expect(endpoint()).toEqual("bar");
|
expect(endpoint()).toEqual("bar");
|
||||||
});
|
});
|
||||||
it("uses _endpoint if set", () => {
|
it("uses _endpoint if set", () => {
|
||||||
updateUserContext({
|
CosmosClient.endpoint("baz");
|
||||||
endpoint: "baz"
|
|
||||||
});
|
|
||||||
expect(endpoint()).toEqual("baz");
|
expect(endpoint()).toEqual("baz");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -109,17 +100,17 @@ describe("endpoint", () => {
|
|||||||
describe("requestPlugin", () => {
|
describe("requestPlugin", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
delete window.dataExplorerPlatform;
|
delete window.dataExplorerPlatform;
|
||||||
resetConfigContext();
|
delete config.PROXY_PATH;
|
||||||
|
delete config.BACKEND_ENDPOINT;
|
||||||
|
delete config.PROXY_PATH;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Hosted", () => {
|
describe("Hosted", () => {
|
||||||
it("builds a proxy URL in development", () => {
|
it("builds a proxy URL in development", () => {
|
||||||
const next = jest.fn();
|
const next = jest.fn();
|
||||||
updateConfigContext({
|
config.platform = Platform.Hosted;
|
||||||
platform: Platform.Hosted,
|
config.BACKEND_ENDPOINT = "https://localhost:1234";
|
||||||
BACKEND_ENDPOINT: "https://localhost:1234",
|
config.PROXY_PATH = "/proxy";
|
||||||
PROXY_PATH: "/proxy"
|
|
||||||
});
|
|
||||||
const headers = {};
|
const headers = {};
|
||||||
const endpoint = "https://docs.azure.com";
|
const endpoint = "https://docs.azure.com";
|
||||||
const path = "/dbs/foo";
|
const path = "/dbs/foo";
|
||||||
@@ -131,7 +122,8 @@ describe("requestPlugin", () => {
|
|||||||
describe("Emulator", () => {
|
describe("Emulator", () => {
|
||||||
it("builds a url for emulator proxy via webpack", () => {
|
it("builds a url for emulator proxy via webpack", () => {
|
||||||
const next = jest.fn();
|
const next = jest.fn();
|
||||||
updateConfigContext({ platform: Platform.Emulator, PROXY_PATH: "/proxy" });
|
config.platform = Platform.Emulator;
|
||||||
|
config.PROXY_PATH = "/proxy";
|
||||||
const headers = {};
|
const headers = {};
|
||||||
const endpoint = "";
|
const endpoint = "";
|
||||||
const path = "/dbs/foo";
|
const path = "/dbs/foo";
|
||||||
|
|||||||
@@ -1,28 +1,39 @@
|
|||||||
import * as Cosmos from "@azure/cosmos";
|
import * as Cosmos from "@azure/cosmos";
|
||||||
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
|
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
|
||||||
import { configContext, Platform } from "../ConfigContext";
|
import { DatabaseAccount } from "../Contracts/DataModels";
|
||||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
import { HttpHeaders, EmulatorMasterKey } from "./Constants";
|
||||||
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
||||||
import { userContext } from "../UserContext";
|
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
|
import { config, Platform } from "../Config";
|
||||||
|
|
||||||
|
let _client: Cosmos.CosmosClient;
|
||||||
|
let _masterKey: string;
|
||||||
|
let _endpoint: string;
|
||||||
|
let _authorizationToken: string;
|
||||||
|
let _accessToken: string;
|
||||||
|
let _databaseAccount: DatabaseAccount;
|
||||||
|
let _subscriptionId: string;
|
||||||
|
let _resourceGroup: string;
|
||||||
|
let _resourceToken: string;
|
||||||
|
|
||||||
const _global = typeof self === "undefined" ? window : self;
|
const _global = typeof self === "undefined" ? window : self;
|
||||||
|
|
||||||
export const tokenProvider = async (requestInfo: RequestInfo) => {
|
export const tokenProvider = async (requestInfo: RequestInfo) => {
|
||||||
const { verb, resourceId, resourceType, headers } = requestInfo;
|
const { verb, resourceId, resourceType, headers } = requestInfo;
|
||||||
if (configContext.platform === Platform.Emulator) {
|
if (config.platform === Platform.Emulator) {
|
||||||
// TODO This SDK method mutates the headers object. Find a better one or fix the SDK.
|
// TODO This SDK method mutates the headers object. Find a better one or fix the SDK.
|
||||||
await setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, EmulatorMasterKey);
|
await setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, EmulatorMasterKey);
|
||||||
return decodeURIComponent(headers.authorization);
|
return decodeURIComponent(headers.authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.masterKey) {
|
if (_masterKey) {
|
||||||
// TODO This SDK method mutates the headers object. Find a better one or fix the SDK.
|
// TODO This SDK method mutates the headers object. Find a better one or fix the SDK.
|
||||||
await setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, EmulatorMasterKey);
|
await setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, EmulatorMasterKey);
|
||||||
return decodeURIComponent(headers.authorization);
|
return decodeURIComponent(headers.authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userContext.resourceToken) {
|
if (_resourceToken) {
|
||||||
return userContext.resourceToken;
|
return _resourceToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await getTokenFromAuthService(verb, resourceType, resourceId);
|
const result = await getTokenFromAuthService(verb, resourceType, resourceId);
|
||||||
@@ -31,63 +42,139 @@ export const tokenProvider = async (requestInfo: RequestInfo) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const requestPlugin: Cosmos.Plugin<any> = async (requestContext, next) => {
|
export const requestPlugin: Cosmos.Plugin<any> = async (requestContext, next) => {
|
||||||
requestContext.endpoint = configContext.PROXY_PATH;
|
requestContext.endpoint = config.PROXY_PATH;
|
||||||
requestContext.headers["x-ms-proxy-target"] = endpoint();
|
requestContext.headers["x-ms-proxy-target"] = endpoint();
|
||||||
return next(requestContext);
|
return next(requestContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const endpoint = () => {
|
export const endpoint = () => {
|
||||||
if (configContext.platform === Platform.Emulator) {
|
if (config.platform === Platform.Emulator) {
|
||||||
// In worker scope, _global(self).parent does not exist
|
// In worker scope, _global(self).parent does not exist
|
||||||
const location = _global.parent ? _global.parent.location : _global.location;
|
const location = _global.parent ? _global.parent.location : _global.location;
|
||||||
return configContext.EMULATOR_ENDPOINT || location.origin;
|
return config.EMULATOR_ENDPOINT || location.origin;
|
||||||
}
|
}
|
||||||
return (
|
return _endpoint || (_databaseAccount && _databaseAccount.properties && _databaseAccount.properties.documentEndpoint);
|
||||||
userContext.endpoint ||
|
|
||||||
(userContext.databaseAccount &&
|
|
||||||
userContext.databaseAccount.properties &&
|
|
||||||
userContext.databaseAccount.properties.documentEndpoint)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getTokenFromAuthService(verb: string, resourceType: string, resourceId?: string): Promise<any> {
|
export async function getTokenFromAuthService(verb: string, resourceType: string, resourceId?: string): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const host = configContext.BACKEND_ENDPOINT || _global.dataExplorer.extensionEndpoint();
|
const host = config.BACKEND_ENDPOINT || _global.dataExplorer.extensionEndpoint();
|
||||||
const response = await _global.fetch(host + "/api/guest/runtimeproxy/authorizationTokens", {
|
const response = await _global.fetch(host + "/api/guest/runtimeproxy/authorizationTokens", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"x-ms-encrypted-auth-token": userContext.accessToken
|
"x-ms-encrypted-auth-token": _accessToken,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
verb,
|
verb,
|
||||||
resourceType,
|
resourceType,
|
||||||
resourceId
|
resourceId,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
//TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json()
|
//TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json()
|
||||||
const result = JSON.parse(await response.json());
|
const result = JSON.parse(await response.json());
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logConsoleError(`Failed to get authorization headers for ${resourceType}: ${JSON.stringify(error)}`);
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
|
ConsoleDataType.Error,
|
||||||
|
`Failed to get authorization headers for ${resourceType}: ${JSON.stringify(error)}`
|
||||||
|
);
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function client(): Cosmos.CosmosClient {
|
export const CosmosClient = {
|
||||||
const options: Cosmos.CosmosClientOptions = {
|
client(): Cosmos.CosmosClient {
|
||||||
endpoint: endpoint() || " ", // CosmosClient gets upset if we pass a falsy value here
|
if (_client) {
|
||||||
key: userContext.masterKey,
|
return _client;
|
||||||
tokenProvider,
|
}
|
||||||
connectionPolicy: {
|
const options: Cosmos.CosmosClientOptions = {
|
||||||
enableEndpointDiscovery: false
|
endpoint: endpoint() || " ", // CosmosClient gets upset if we pass a falsy value here
|
||||||
},
|
key: _masterKey,
|
||||||
userAgentSuffix: "Azure Portal"
|
tokenProvider,
|
||||||
};
|
connectionPolicy: {
|
||||||
|
enableEndpointDiscovery: false,
|
||||||
|
},
|
||||||
|
userAgentSuffix: "Azure Portal",
|
||||||
|
};
|
||||||
|
|
||||||
// In development we proxy requests to the backend via webpack. This is removed in production bundles.
|
// In development we proxy requests to the backend via webpack. This is removed in production bundles.
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
(options as any).plugins = [{ on: "request", plugin: requestPlugin }];
|
(options as any).plugins = [{ on: "request", plugin: requestPlugin }];
|
||||||
}
|
}
|
||||||
return new Cosmos.CosmosClient(options);
|
_client = new Cosmos.CosmosClient(options);
|
||||||
}
|
return _client;
|
||||||
|
},
|
||||||
|
|
||||||
|
authorizationToken(value?: string): string {
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
return _authorizationToken;
|
||||||
|
}
|
||||||
|
_authorizationToken = value;
|
||||||
|
_client = null;
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
accessToken(value?: string): string {
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
return _accessToken;
|
||||||
|
}
|
||||||
|
_accessToken = value;
|
||||||
|
_client = null;
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
masterKey(value?: string): string {
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
return _masterKey;
|
||||||
|
}
|
||||||
|
_client = null;
|
||||||
|
_masterKey = value;
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
endpoint(value?: string): string {
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
return _endpoint;
|
||||||
|
}
|
||||||
|
_client = null;
|
||||||
|
_endpoint = value;
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
databaseAccount(value?: DatabaseAccount): DatabaseAccount {
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
return _databaseAccount || ({} as any);
|
||||||
|
}
|
||||||
|
_client = null;
|
||||||
|
_databaseAccount = value;
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
subscriptionId(value?: string): string {
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
return _subscriptionId;
|
||||||
|
}
|
||||||
|
_client = null;
|
||||||
|
_subscriptionId = value;
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
resourceGroup(value?: string): string {
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
return _resourceGroup;
|
||||||
|
}
|
||||||
|
_client = null;
|
||||||
|
_resourceGroup = value;
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
resourceToken(value?: string): string {
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
return _resourceToken;
|
||||||
|
}
|
||||||
|
_client = null;
|
||||||
|
_resourceToken = value;
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { getCommonQueryOptions } from "./DataAccessUtilityBase";
|
import { getCommonQueryOptions } from "./DataAccessUtilityBase";
|
||||||
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||||
|
|
||||||
describe("getCommonQueryOptions", () => {
|
describe("getCommonQueryOptions", () => {
|
||||||
it("builds the correct default options objects", () => {
|
it("builds the correct default options objects", () => {
|
||||||
expect(getCommonQueryOptions({})).toMatchSnapshot();
|
expect(getCommonQueryOptions({})).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
it("reads from localStorage", () => {
|
it("reads from localStorage", () => {
|
||||||
LocalStorageUtility.setEntryNumber(StorageKey.ActualItemPerPage, 37);
|
LocalStorageUtility.setEntryNumber(StorageKey.ActualItemPerPage, 37);
|
||||||
LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, 17);
|
LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, 17);
|
||||||
expect(getCommonQueryOptions({})).toMatchSnapshot();
|
expect(getCommonQueryOptions({})).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,94 +1,94 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
|
|
||||||
export default class EditableUtility {
|
export default class EditableUtility {
|
||||||
public static observable<T>(initialValue?: T): ViewModels.Editable<T> {
|
public static observable<T>(initialValue?: T): ViewModels.Editable<T> {
|
||||||
var observable: ViewModels.Editable<T> = <ViewModels.Editable<T>>ko.observable<T>(initialValue);
|
var observable: ViewModels.Editable<T> = <ViewModels.Editable<T>>ko.observable<T>(initialValue);
|
||||||
|
|
||||||
observable.edits = ko.observableArray<T>([initialValue]);
|
observable.edits = ko.observableArray<T>([initialValue]);
|
||||||
observable.validations = ko.observableArray<(value: T) => boolean>([]);
|
observable.validations = ko.observableArray<(value: T) => boolean>([]);
|
||||||
|
|
||||||
observable.setBaseline = (baseline: T) => {
|
observable.setBaseline = (baseline: T) => {
|
||||||
observable(baseline);
|
observable(baseline);
|
||||||
observable.edits([baseline]);
|
observable.edits([baseline]);
|
||||||
};
|
};
|
||||||
|
|
||||||
observable.getEditableCurrentValue = ko.computed<T>(() => {
|
observable.getEditableCurrentValue = ko.computed<T>(() => {
|
||||||
const edits = (observable.edits && observable.edits()) || [];
|
const edits = (observable.edits && observable.edits()) || [];
|
||||||
if (edits.length === 0) {
|
if (edits.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return edits[edits.length - 1];
|
return edits[edits.length - 1];
|
||||||
});
|
});
|
||||||
|
|
||||||
observable.getEditableOriginalValue = ko.computed<T>(() => {
|
observable.getEditableOriginalValue = ko.computed<T>(() => {
|
||||||
const edits = (observable.edits && observable.edits()) || [];
|
const edits = (observable.edits && observable.edits()) || [];
|
||||||
if (edits.length === 0) {
|
if (edits.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return edits[0];
|
return edits[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
observable.editableIsDirty = ko.computed<boolean>(() => {
|
observable.editableIsDirty = ko.computed<boolean>(() => {
|
||||||
const edits = (observable.edits && observable.edits()) || [];
|
const edits = (observable.edits && observable.edits()) || [];
|
||||||
if (edits.length <= 1) {
|
if (edits.length <= 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let current: any = observable.getEditableCurrentValue();
|
let current: any = observable.getEditableCurrentValue();
|
||||||
let original: any = observable.getEditableOriginalValue();
|
let original: any = observable.getEditableOriginalValue();
|
||||||
|
|
||||||
switch (typeof current) {
|
switch (typeof current) {
|
||||||
case "string":
|
case "string":
|
||||||
case "undefined":
|
case "undefined":
|
||||||
case "number":
|
case "number":
|
||||||
case "boolean":
|
case "boolean":
|
||||||
current = current && current.toString();
|
current = current && current.toString();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
current = JSON.stringify(current);
|
current = JSON.stringify(current);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (typeof original) {
|
switch (typeof original) {
|
||||||
case "string":
|
case "string":
|
||||||
case "undefined":
|
case "undefined":
|
||||||
case "number":
|
case "number":
|
||||||
case "boolean":
|
case "boolean":
|
||||||
original = original && original.toString();
|
original = original && original.toString();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
original = JSON.stringify(original);
|
original = JSON.stringify(original);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current !== original) {
|
if (current !== original) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
observable.subscribe(edit => {
|
observable.subscribe((edit) => {
|
||||||
var edits = observable.edits && observable.edits();
|
var edits = observable.edits && observable.edits();
|
||||||
if (!edits) {
|
if (!edits) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
edits.push(edit);
|
edits.push(edit);
|
||||||
observable.edits(edits);
|
observable.edits(edits);
|
||||||
});
|
});
|
||||||
|
|
||||||
observable.editableIsValid = ko.observable<boolean>(true);
|
observable.editableIsValid = ko.observable<boolean>(true);
|
||||||
observable.subscribe(value => {
|
observable.subscribe((value) => {
|
||||||
const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || [];
|
const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || [];
|
||||||
const isValid = validations.every(validate => validate(value));
|
const isValid = validations.every((validate) => validate(value));
|
||||||
observable.editableIsValid(isValid);
|
observable.editableIsValid(isValid);
|
||||||
});
|
});
|
||||||
|
|
||||||
return observable;
|
return observable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,48 @@
|
|||||||
import * as Constants from "../Common/Constants";
|
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 {
|
const defaultEnvironment: string = "default";
|
||||||
const defaultEnvironment: string = "default";
|
const defaultLocation: string = "default";
|
||||||
const defaultLocation: string = "default";
|
let environment: string = serverId;
|
||||||
let environment: string = serverId;
|
const endpointType: Constants.MongoBackendEndpointType =
|
||||||
const endpointType: Constants.MongoBackendEndpointType =
|
Constants.MongoBackend.endpointsByEnvironment[environment] ||
|
||||||
Constants.MongoBackend.endpointsByEnvironment[environment] ||
|
Constants.MongoBackend.endpointsByEnvironment[defaultEnvironment];
|
||||||
Constants.MongoBackend.endpointsByEnvironment[defaultEnvironment];
|
if (endpointType === Constants.MongoBackendEndpointType.local) {
|
||||||
if (endpointType === Constants.MongoBackendEndpointType.local) {
|
return `${extensionEndpoint}${Constants.MongoBackend.localhostEndpoint}`;
|
||||||
return `${extensionEndpoint}${Constants.MongoBackend.localhostEndpoint}`;
|
}
|
||||||
}
|
|
||||||
|
const normalizedLocation = EnvironmentUtility.normalizeRegionName(location);
|
||||||
const normalizedLocation = EnvironmentUtility.normalizeRegionName(location);
|
return (
|
||||||
return (
|
Constants.MongoBackend.endpointsByRegion[normalizedLocation] ||
|
||||||
Constants.MongoBackend.endpointsByRegion[normalizedLocation] ||
|
Constants.MongoBackend.endpointsByRegion[defaultLocation]
|
||||||
Constants.MongoBackend.endpointsByRegion[defaultLocation]
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
public static isAadUser(): boolean {
|
||||||
public static isAadUser(): boolean {
|
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 (
|
Constants.CassandraBackend.endpointsByRegion[location] ||
|
||||||
Constants.CassandraBackend.endpointsByRegion[location] ||
|
Constants.CassandraBackend.endpointsByRegion[defaultLocation]
|
||||||
Constants.CassandraBackend.endpointsByRegion[defaultLocation]
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
public static normalizeArmEndpointUri(uri: string): string {
|
||||||
public static normalizeArmEndpointUri(uri: string): string {
|
if (uri && uri.slice(-1) !== "/") {
|
||||||
if (uri && uri.slice(-1) !== "/") {
|
return `${uri}/`;
|
||||||
return `${uri}/`;
|
}
|
||||||
}
|
return uri;
|
||||||
return uri;
|
}
|
||||||
}
|
|
||||||
|
private static normalizeRegionName(region: string): string {
|
||||||
private static normalizeRegionName(region: string): string {
|
return region && StringUtils.stripSpacesFromString(region.toLocaleLowerCase());
|
||||||
return region && StringUtils.stripSpacesFromString(region.toLocaleLowerCase());
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ describe("Error Parser Utility", () => {
|
|||||||
err.code = 400;
|
err.code = 400;
|
||||||
err.body = {
|
err.body = {
|
||||||
code: "BadRequest",
|
code: "BadRequest",
|
||||||
message
|
message,
|
||||||
};
|
};
|
||||||
err.headers = {};
|
err.headers = {};
|
||||||
err.activityId = "97b2e684-7505-4921-85f6-2513b9b28220";
|
err.activityId = "97b2e684-7505-4921-85f6-2513b9b28220";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
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 {
|
||||||
@@ -7,8 +7,6 @@ export function replaceKnownError(err: string): string {
|
|||||||
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;
|
||||||
@@ -28,7 +26,7 @@ function _parse(err: any): DataModels.ErrorDataModel[] {
|
|||||||
normalizedErrors.push(err);
|
normalizedErrors.push(err);
|
||||||
} else {
|
} else {
|
||||||
const innerErrors: any[] = _getInnerErrors(err.message);
|
const innerErrors: any[] = _getInnerErrors(err.message);
|
||||||
normalizedErrors = innerErrors.map(innerError =>
|
normalizedErrors = innerErrors.map((innerError) =>
|
||||||
typeof innerError === "string" ? { message: innerError } : innerError
|
typeof innerError === "string" ? { message: innerError } : innerError
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import * as HeadersUtility from "./HeadersUtility";
|
import * as HeadersUtility from "./HeadersUtility";
|
||||||
import { ExplorerSettings } from "../Shared/ExplorerSettings";
|
import { ExplorerSettings } from "../Shared/ExplorerSettings";
|
||||||
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||||
|
|
||||||
describe("Headers Utility", () => {
|
describe("Headers Utility", () => {
|
||||||
describe("shouldEnableCrossPartitionKeyForResourceWithPartitionKey()", () => {
|
describe("shouldEnableCrossPartitionKeyForResourceWithPartitionKey()", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ExplorerSettings.createDefaultSettings();
|
ExplorerSettings.createDefaultSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return true by default", () => {
|
it("should return true by default", () => {
|
||||||
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
|
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return false if the enable cross partition key feed option is false", () => {
|
it("should return false if the enable cross partition key feed option is false", () => {
|
||||||
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "false");
|
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "false");
|
||||||
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(false);
|
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return true if the enable cross partition key feed option is true", () => {
|
it("should return true if the enable cross partition key feed option is true", () => {
|
||||||
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "true");
|
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, "true");
|
||||||
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
|
expect(HeadersUtility.shouldEnableCrossPartitionKey()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import * as Constants from "./Constants";
|
import * as Constants from "./Constants";
|
||||||
|
|
||||||
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||||
|
|
||||||
// x-ms-resource-quota: databases = 100; collections = 5000; users = 500000; permissions = 2000000;
|
// x-ms-resource-quota: databases = 100; collections = 5000; users = 500000; permissions = 2000000;
|
||||||
export function getQuota(responseHeaders: any): any {
|
export function getQuota(responseHeaders: any): any {
|
||||||
return responseHeaders && responseHeaders[Constants.HttpHeaders.resourceQuota]
|
return responseHeaders && responseHeaders[Constants.HttpHeaders.resourceQuota]
|
||||||
? parseStringIntoObject(responseHeaders[Constants.HttpHeaders.resourceQuota])
|
? parseStringIntoObject(responseHeaders[Constants.HttpHeaders.resourceQuota])
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shouldEnableCrossPartitionKey(): boolean {
|
export function shouldEnableCrossPartitionKey(): boolean {
|
||||||
return LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true";
|
return LocalStorageUtility.getEntryString(StorageKey.IsCrossPartitionQueryEnabled) === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStringIntoObject(resourceString: string) {
|
function parseStringIntoObject(resourceString: string) {
|
||||||
var entityObject: any = {};
|
var entityObject: any = {};
|
||||||
|
|
||||||
if (resourceString) {
|
if (resourceString) {
|
||||||
var entitiesArray: string[] = resourceString.split(";");
|
var entitiesArray: string[] = resourceString.split(";");
|
||||||
for (var i: any = 0; i < entitiesArray.length; i++) {
|
for (var i: any = 0; i < entitiesArray.length; i++) {
|
||||||
var entity: string[] = entitiesArray[i].split("=");
|
var entity: string[] = entitiesArray[i].split("=");
|
||||||
entityObject[entity[0]] = entity[1];
|
entityObject[entity[0]] = entity[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return entityObject;
|
return entityObject;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ describe("nextPage", () => {
|
|||||||
queryMetrics: {},
|
queryMetrics: {},
|
||||||
requestCharge: 1,
|
requestCharge: 1,
|
||||||
headers: {},
|
headers: {},
|
||||||
activityId: "foo"
|
activityId: "foo",
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(await nextPage(fakeIterator, 10)).toMatchSnapshot();
|
expect(await nextPage(fakeIterator, 10)).toMatchSnapshot();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export interface MinimalQueryIterator {
|
|||||||
// Pick<QueryIterator<any>, "fetchNext">;
|
// Pick<QueryIterator<any>, "fetchNext">;
|
||||||
|
|
||||||
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
|
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
|
||||||
return documentsIterator.fetchNext().then(response => {
|
return documentsIterator.fetchNext().then((response) => {
|
||||||
const documents = response.resources;
|
const documents = response.resources;
|
||||||
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
|
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
|
||||||
const itemCount = (documents && documents.length) || 0;
|
const itemCount = (documents && documents.length) || 0;
|
||||||
@@ -26,7 +26,7 @@ export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex
|
|||||||
lastItemIndex: Number(firstItemIndex) + Number(itemCount),
|
lastItemIndex: Number(firstItemIndex) + Number(itemCount),
|
||||||
headers,
|
headers,
|
||||||
activityId: response.activityId,
|
activityId: response.activityId,
|
||||||
requestCharge: response.requestCharge
|
requestCharge: response.requestCharge,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,46 @@
|
|||||||
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(() => {
|
|
||||||
jest.resetAllMocks();
|
beforeEach(() => {
|
||||||
});
|
sendMessageSpy = spyOn(MessageHandler, "sendMessage");
|
||||||
|
});
|
||||||
it("should log info messages", () => {
|
|
||||||
Logger.logInfo("Test info", "DocDB");
|
afterEach(() => {
|
||||||
expect(sendMessage).toBeCalled();
|
sendMessageSpy = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should log error messages", () => {
|
it("should log info messages", () => {
|
||||||
Logger.logError("Test error", "DocDB");
|
Logger.logInfo("Test info", "DocDB");
|
||||||
expect(sendMessage).toBeCalled();
|
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
||||||
});
|
|
||||||
|
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
||||||
it("should log warnings", () => {
|
expect(spyArgs.data).toContain(LogEntryLevel.Verbose);
|
||||||
Logger.logWarning("Test warning", "DocDB");
|
expect(spyArgs.data).toContain("DocDB");
|
||||||
expect(sendMessage).toBeCalled();
|
expect(spyArgs.data).toContain("Test info");
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
it("should log error messages", () => {
|
||||||
|
Logger.logError("Test error", "DocDB");
|
||||||
|
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
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", () => {
|
||||||
|
Logger.logWarning("Test warning", "DocDB");
|
||||||
|
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
||||||
|
|
||||||
|
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 { sendMessage } from "./MessageHandler";
|
import { MessageHandler } 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,9 +33,9 @@ export function logError(message: string | Error, area: string, code?: number):
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _logEntry(entry: Diagnostics.LogEntry): void {
|
function _logEntry(entry: Diagnostics.LogEntry): void {
|
||||||
sendMessage({
|
MessageHandler.sendMessage({
|
||||||
type: MessageTypes.LogInfo,
|
type: MessageTypes.LogInfo,
|
||||||
data: JSON.stringify(entry)
|
data: JSON.stringify(entry),
|
||||||
});
|
});
|
||||||
|
|
||||||
const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => {
|
const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => {
|
||||||
@@ -59,13 +59,13 @@ function _generateLogEntry(
|
|||||||
level: Diagnostics.LogEntryLevel,
|
level: Diagnostics.LogEntryLevel,
|
||||||
message: string,
|
message: string,
|
||||||
area: string,
|
area: string,
|
||||||
code?: number
|
code: number
|
||||||
): Diagnostics.LogEntry {
|
): Diagnostics.LogEntry {
|
||||||
return {
|
return {
|
||||||
timestamp: new Date().getUTCSeconds(),
|
timestamp: new Date().getUTCSeconds(),
|
||||||
level,
|
level: level,
|
||||||
message,
|
message: message,
|
||||||
area,
|
area: area,
|
||||||
code
|
code: code,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,65 @@
|
|||||||
import Q from "q";
|
import Q from "q";
|
||||||
import * as MessageHandler from "./MessageHandler";
|
import { CachedDataPromise, MessageHandler } from "./MessageHandler";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
|
|
||||||
describe("Message Handler", () => {
|
class MockMessageHandler extends MessageHandler {
|
||||||
it("should handle cached message", async () => {
|
public static addToMap(key: string, value: CachedDataPromise<any>): void {
|
||||||
let mockPromise = {
|
MessageHandler.RequestMap[key] = value;
|
||||||
id: "123",
|
}
|
||||||
startTime: new Date(),
|
|
||||||
deferred: Q.defer<any>()
|
public static mapContainsKey(key: string): boolean {
|
||||||
};
|
return MessageHandler.RequestMap[key] != null;
|
||||||
let mockMessage = { message: { id: "123", data: "{}" } };
|
}
|
||||||
MessageHandler.RequestMap[mockPromise.id] = mockPromise;
|
|
||||||
MessageHandler.handleCachedDataMessage(mockMessage);
|
public static clearAllEntries(): void {
|
||||||
expect(mockPromise.deferred.promise.isFulfilled()).toBe(true);
|
MessageHandler.RequestMap = {};
|
||||||
});
|
}
|
||||||
|
|
||||||
it("should delete fulfilled promises on running the garbage collector", async () => {
|
public static runGarbageCollector(): void {
|
||||||
let message = {
|
MessageHandler.runGarbageCollector();
|
||||||
id: "123",
|
}
|
||||||
startTime: new Date(),
|
}
|
||||||
deferred: Q.defer<any>()
|
|
||||||
};
|
describe("Message Handler", () => {
|
||||||
|
beforeEach(() => {
|
||||||
MessageHandler.handleCachedDataMessage(message);
|
MockMessageHandler.clearAllEntries();
|
||||||
MessageHandler.runGarbageCollector();
|
});
|
||||||
expect(MessageHandler.RequestMap["123"]).toBeUndefined();
|
|
||||||
});
|
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",
|
||||||
|
startTime: new Date(),
|
||||||
|
deferred: Q.defer<any>(),
|
||||||
|
};
|
||||||
|
let mockMessage = { message: { id: "123", data: "{}" } };
|
||||||
|
|
||||||
|
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
||||||
|
MockMessageHandler.handleCachedDataMessage(mockMessage);
|
||||||
|
expect(mockPromise.deferred.promise.isFulfilled()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should delete fulfilled promises on running the garbage collector", () => {
|
||||||
|
let mockPromise: CachedDataPromise<any> = {
|
||||||
|
id: "123",
|
||||||
|
startTime: new Date(),
|
||||||
|
deferred: Q.defer<any>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
||||||
|
mockPromise.deferred.reject("some error");
|
||||||
|
MockMessageHandler.runGarbageCollector();
|
||||||
|
expect(MockMessageHandler.mapContainsKey(mockPromise.id)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -9,65 +9,77 @@ 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 function handleCachedDataMessage(message: any): void {
|
export class MessageHandler {
|
||||||
const messageContent = message && message.message;
|
protected static RequestMap: Map = {};
|
||||||
if (message == null || messageContent == null || messageContent.id == null || !RequestMap[messageContent.id]) {
|
|
||||||
return;
|
public static handleCachedDataMessage(message: any): void {
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
const cachedDataPromise = RequestMap[messageContent.id];
|
public static sendCachedDataMessage<TResponseDataModel>(
|
||||||
if (messageContent.error != null) {
|
messageType: MessageTypes,
|
||||||
cachedDataPromise.deferred.reject(messageContent.error);
|
params: Object[],
|
||||||
} else {
|
timeoutInMs?: number
|
||||||
cachedDataPromise.deferred.resolve(JSON.parse(messageContent.data));
|
): Q.Promise<TResponseDataModel> {
|
||||||
}
|
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
|
||||||
runGarbageCollector();
|
deferred: Q.defer<TResponseDataModel>(),
|
||||||
}
|
startTime: new Date(),
|
||||||
|
id: _.uniqueId(),
|
||||||
|
};
|
||||||
|
MessageHandler.RequestMap[cachedDataPromise.id] = cachedDataPromise;
|
||||||
|
MessageHandler.sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
|
||||||
|
|
||||||
export function sendCachedDataMessage<TResponseDataModel>(
|
//TODO: Use telemetry to measure optimal time to resolve/reject promises
|
||||||
messageType: MessageTypes,
|
return cachedDataPromise.deferred.promise.timeout(
|
||||||
params: Object[],
|
timeoutInMs || Constants.ClientDefaults.requestTimeoutMs,
|
||||||
timeoutInMs?: number
|
"Timed out while waiting for response from portal"
|
||||||
): 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
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function canSendMessage(): boolean {
|
public static sendMessage(data: any): void {
|
||||||
return window.parent !== window;
|
if (MessageHandler.canSendMessage()) {
|
||||||
}
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
// TODO: This is exported just for testing. It should not be.
|
signature: "pcIframe",
|
||||||
export function runGarbageCollector() {
|
data: data,
|
||||||
Object.keys(RequestMap).forEach((key: string) => {
|
},
|
||||||
const promise: Q.Promise<any> = RequestMap[key].deferred.promise;
|
window.document.referrer
|
||||||
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];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
import { AuthType } from "../AuthType";
|
|
||||||
import { configContext, resetConfigContext, updateConfigContext } from "../ConfigContext";
|
|
||||||
import { DatabaseAccount } from "../Contracts/DataModels";
|
|
||||||
import { Collection } from "../Contracts/ViewModels";
|
|
||||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
|
||||||
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
|
|
||||||
import { updateUserContext } from "../UserContext";
|
|
||||||
import {
|
import {
|
||||||
|
_createMongoCollectionWithARM,
|
||||||
deleteDocument,
|
deleteDocument,
|
||||||
getEndpoint,
|
getEndpoint,
|
||||||
queryDocuments,
|
queryDocuments,
|
||||||
readDocument,
|
readDocument,
|
||||||
updateDocument,
|
updateDocument,
|
||||||
_createMongoCollectionWithARM
|
|
||||||
} from "./MongoProxyClient";
|
} from "./MongoProxyClient";
|
||||||
|
import { AuthType } from "../AuthType";
|
||||||
|
import { Collection, DatabaseAccount, DocumentId } from "../Contracts/ViewModels";
|
||||||
|
import { config } from "../Config";
|
||||||
|
import { CosmosClient } from "./CosmosClient";
|
||||||
|
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
|
||||||
jest.mock("../ResourceProvider/ResourceProviderClient.ts");
|
jest.mock("../ResourceProvider/ResourceProviderClient.ts");
|
||||||
|
|
||||||
const databaseId = "testDB";
|
const databaseId = "testDB";
|
||||||
@@ -22,7 +20,7 @@ const fetchMock = () => {
|
|||||||
ok: true,
|
ok: true,
|
||||||
text: () => "{}",
|
text: () => "{}",
|
||||||
json: () => "{}",
|
json: () => "{}",
|
||||||
headers: new Map()
|
headers: new Map(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,8 +33,8 @@ const collection = {
|
|||||||
partitionKey: {
|
partitionKey: {
|
||||||
paths: ["/pk"],
|
paths: ["/pk"],
|
||||||
kind: "Hash",
|
kind: "Hash",
|
||||||
version: 1
|
version: 1,
|
||||||
}
|
},
|
||||||
} as Collection;
|
} as Collection;
|
||||||
|
|
||||||
const documentId = ({
|
const documentId = ({
|
||||||
@@ -46,8 +44,8 @@ const documentId = ({
|
|||||||
partitionKey: {
|
partitionKey: {
|
||||||
paths: ["/pk"],
|
paths: ["/pk"],
|
||||||
kind: "Hash",
|
kind: "Hash",
|
||||||
version: 1
|
version: 1,
|
||||||
}
|
},
|
||||||
} as unknown) as DocumentId;
|
} as unknown) as DocumentId;
|
||||||
|
|
||||||
const databaseAccount = {
|
const databaseAccount = {
|
||||||
@@ -60,20 +58,18 @@ const databaseAccount = {
|
|||||||
documentEndpoint: "bar",
|
documentEndpoint: "bar",
|
||||||
gremlinEndpoint: "foo",
|
gremlinEndpoint: "foo",
|
||||||
tableEndpoint: "foo",
|
tableEndpoint: "foo",
|
||||||
cassandraEndpoint: "foo"
|
cassandraEndpoint: "foo",
|
||||||
}
|
},
|
||||||
} as DatabaseAccount;
|
};
|
||||||
|
|
||||||
describe("MongoProxyClient", () => {
|
describe("MongoProxyClient", () => {
|
||||||
describe("queryDocuments", () => {
|
describe("queryDocuments", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
resetConfigContext();
|
delete config.BACKEND_ENDPOINT;
|
||||||
updateUserContext({
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
databaseAccount
|
|
||||||
});
|
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -90,7 +86,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
updateConfigContext({ MONGO_BACKEND_ENDPOINT: "https://localhost:1234" });
|
config.MONGO_BACKEND_ENDPOINT = "https://localhost:1234";
|
||||||
queryDocuments(databaseId, collection, true, "{}");
|
queryDocuments(databaseId, collection, true, "{}");
|
||||||
expect(window.fetch).toHaveBeenCalledWith(
|
expect(window.fetch).toHaveBeenCalledWith(
|
||||||
"https://localhost:1234/api/mongo/explorer/resourcelist?db=testDB&coll=testCollection&resourceUrl=bardbs%2FtestDB%2Fcolls%2FtestCollection%2Fdocs%2F&rid=testCollectionrid&rtype=docs&sid=&rg=&dba=foo&pk=pk",
|
"https://localhost:1234/api/mongo/explorer/resourcelist?db=testDB&coll=testCollection&resourceUrl=bardbs%2FtestDB%2Fcolls%2FtestCollection%2Fdocs%2F&rid=testCollectionrid&rtype=docs&sid=&rg=&dba=foo&pk=pk",
|
||||||
@@ -100,13 +96,11 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
describe("readDocument", () => {
|
describe("readDocument", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
resetConfigContext();
|
delete config.MONGO_BACKEND_ENDPOINT;
|
||||||
updateUserContext({
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
databaseAccount
|
|
||||||
});
|
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -123,7 +117,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
updateConfigContext({ MONGO_BACKEND_ENDPOINT: "https://localhost:1234" });
|
config.MONGO_BACKEND_ENDPOINT = "https://localhost:1234";
|
||||||
readDocument(databaseId, collection, documentId);
|
readDocument(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%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%2FtestId&rid=testId&rtype=docs&sid=&rg=&dba=foo&pk=pk",
|
||||||
@@ -133,13 +127,11 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
describe("createDocument", () => {
|
describe("createDocument", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
resetConfigContext();
|
delete config.MONGO_BACKEND_ENDPOINT;
|
||||||
updateUserContext({
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
databaseAccount
|
|
||||||
});
|
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -156,7 +148,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
updateConfigContext({ MONGO_BACKEND_ENDPOINT: "https://localhost:1234" });
|
config.MONGO_BACKEND_ENDPOINT = "https://localhost:1234";
|
||||||
readDocument(databaseId, collection, documentId);
|
readDocument(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%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%2FtestId&rid=testId&rtype=docs&sid=&rg=&dba=foo&pk=pk",
|
||||||
@@ -166,13 +158,11 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
describe("updateDocument", () => {
|
describe("updateDocument", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
resetConfigContext();
|
delete config.MONGO_BACKEND_ENDPOINT;
|
||||||
updateUserContext({
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
databaseAccount
|
|
||||||
});
|
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -181,7 +171,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)
|
||||||
@@ -189,8 +179,8 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
updateConfigContext({ 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)
|
||||||
@@ -199,13 +189,11 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
describe("deleteDocument", () => {
|
describe("deleteDocument", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
resetConfigContext();
|
delete config.MONGO_BACKEND_ENDPOINT;
|
||||||
updateUserContext({
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
databaseAccount
|
|
||||||
});
|
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@@ -222,7 +210,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("builds the correct proxy URL in development", () => {
|
it("builds the correct proxy URL in development", () => {
|
||||||
updateConfigContext({ MONGO_BACKEND_ENDPOINT: "https://localhost:1234" });
|
config.MONGO_BACKEND_ENDPOINT = "https://localhost:1234";
|
||||||
deleteDocument(databaseId, collection, documentId);
|
deleteDocument(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",
|
||||||
@@ -232,14 +220,12 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
describe("getEndpoint", () => {
|
describe("getEndpoint", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
resetConfigContext();
|
delete config.MONGO_BACKEND_ENDPOINT;
|
||||||
delete window.authType;
|
delete window.authType;
|
||||||
updateUserContext({
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
databaseAccount
|
|
||||||
});
|
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -249,7 +235,7 @@ describe("MongoProxyClient", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns a development endpoint", () => {
|
it("returns a development endpoint", () => {
|
||||||
updateConfigContext({ MONGO_BACKEND_ENDPOINT: "https://localhost:1234" });
|
config.MONGO_BACKEND_ENDPOINT = "https://localhost:1234";
|
||||||
const endpoint = getEndpoint(databaseAccount as DatabaseAccount);
|
const endpoint = getEndpoint(databaseAccount as DatabaseAccount);
|
||||||
expect(endpoint).toEqual("https://localhost:1234/api/mongo/explorer");
|
expect(endpoint).toEqual("https://localhost:1234/api/mongo/explorer");
|
||||||
});
|
});
|
||||||
@@ -273,7 +259,7 @@ describe("MongoProxyClient", () => {
|
|||||||
sid: "a2",
|
sid: "a2",
|
||||||
rg: "c1",
|
rg: "c1",
|
||||||
dba: "main",
|
dba: "main",
|
||||||
is: false
|
is: false,
|
||||||
};
|
};
|
||||||
_createMongoCollectionWithARM("management.azure.com", properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
|
_createMongoCollectionWithARM("management.azure.com", properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
|
||||||
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
||||||
@@ -282,8 +268,8 @@ describe("MongoProxyClient", () => {
|
|||||||
{
|
{
|
||||||
properties: {
|
properties: {
|
||||||
options: { "x-ms-cosmos-offer-autopilot-tier": "1" },
|
options: { "x-ms-cosmos-offer-autopilot-tier": "1" },
|
||||||
resource: { id: "abc-collection" }
|
resource: { id: "abc-collection" },
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -299,7 +285,7 @@ describe("MongoProxyClient", () => {
|
|||||||
rg: "c1",
|
rg: "c1",
|
||||||
dba: "main",
|
dba: "main",
|
||||||
is: false,
|
is: false,
|
||||||
offerThroughput: 400
|
offerThroughput: 400,
|
||||||
};
|
};
|
||||||
_createMongoCollectionWithARM("management.azure.com", properties, undefined);
|
_createMongoCollectionWithARM("management.azure.com", properties, undefined);
|
||||||
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
||||||
@@ -308,8 +294,8 @@ describe("MongoProxyClient", () => {
|
|||||||
{
|
{
|
||||||
properties: {
|
properties: {
|
||||||
options: { throughput: "400" },
|
options: { throughput: "400" },
|
||||||
resource: { id: "abc-collection" }
|
resource: { id: "abc-collection" },
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,56 +1,55 @@
|
|||||||
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
|
|
||||||
import queryString from "querystring";
|
|
||||||
import { AuthType } from "../AuthType";
|
|
||||||
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 { configContext } from "../ConfigContext";
|
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import { Collection } from "../Contracts/ViewModels";
|
|
||||||
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
|
||||||
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
|
|
||||||
import { AddDbUtilities } from "../Shared/AddDatabaseUtility";
|
|
||||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
|
||||||
import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants";
|
|
||||||
import { userContext } from "../UserContext";
|
|
||||||
import EnvironmentUtility from "./EnvironmentUtility";
|
import EnvironmentUtility from "./EnvironmentUtility";
|
||||||
import { MinimalQueryIterator } from "./IteratorUtilities";
|
import queryString from "querystring";
|
||||||
import { sendMessage } from "./MessageHandler";
|
import { AddDbUtilities } from "../Shared/AddDatabaseUtility";
|
||||||
|
import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants";
|
||||||
|
import { AuthType } from "../AuthType";
|
||||||
|
import { Collection } from "../Contracts/ViewModels";
|
||||||
|
import { config } from "../Config";
|
||||||
|
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
|
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
|
||||||
|
import { CosmosClient } from "./CosmosClient";
|
||||||
|
import { MessageHandler } from "./MessageHandler";
|
||||||
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
|
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
||||||
|
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
|
||||||
|
|
||||||
const defaultHeaders = {
|
const defaultHeaders = {
|
||||||
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
|
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
|
||||||
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
|
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
|
||||||
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15"
|
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15",
|
||||||
};
|
};
|
||||||
|
|
||||||
function authHeaders() {
|
function authHeaders(): any {
|
||||||
if (window.authType === AuthType.EncryptedToken) {
|
if (window.authType === AuthType.EncryptedToken) {
|
||||||
return { [HttpHeaders.guestAccessToken]: userContext.accessToken };
|
return { [HttpHeaders.guestAccessToken]: CosmosClient.accessToken() };
|
||||||
} else {
|
} else {
|
||||||
return { [HttpHeaders.authorization]: userContext.authorizationToken };
|
return { [HttpHeaders.authorization]: CosmosClient.authorizationToken() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queryIterator(databaseId: string, collection: Collection, query: string): MinimalQueryIterator {
|
export function queryIterator(databaseId: string, collection: Collection, query: string): any {
|
||||||
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: { [key: string]: string | number } = {};
|
const headers = {} as any;
|
||||||
response.headers.forEach((value, key) => {
|
response.headers.forEach((value: any, key: any) => {
|
||||||
headers[key] = value;
|
headers[key] = value;
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
resources: response.documents,
|
resources: response.documents,
|
||||||
headers,
|
headers,
|
||||||
requestCharge: Number(headers[CosmosSDKConstants.HttpHeaders.RequestCharge]),
|
requestCharge: headers[CosmosSDKConstants.HttpHeaders.RequestCharge],
|
||||||
activityId: String(headers[CosmosSDKConstants.HttpHeaders.ActivityId]),
|
activityId: headers[CosmosSDKConstants.HttpHeaders.ActivityId],
|
||||||
hasMoreResults: !!continuationToken
|
hasMoreResults: !!continuationToken,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +66,7 @@ export function queryDocuments(
|
|||||||
query: string,
|
query: string,
|
||||||
continuationToken?: string
|
continuationToken?: string
|
||||||
): Promise<QueryResponse> {
|
): Promise<QueryResponse> {
|
||||||
const databaseAccount = userContext.databaseAccount;
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||||
const params = {
|
const params = {
|
||||||
db: databaseId,
|
db: databaseId,
|
||||||
@@ -75,11 +74,13 @@ export function queryDocuments(
|
|||||||
resourceUrl: `${resourceEndpoint}dbs/${databaseId}/colls/${collection.id()}/docs/`,
|
resourceUrl: `${resourceEndpoint}dbs/${databaseId}/colls/${collection.id()}/docs/`,
|
||||||
rid: collection.rid,
|
rid: collection.rid,
|
||||||
rtype: "docs",
|
rtype: "docs",
|
||||||
sid: userContext.subscriptionId,
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: userContext.resourceGroup,
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
collection && collection.partitionKey && !collection.partitionKey.systemKey ? collection.partitionKeyProperty : ""
|
collection && collection.partitionKey && !collection.partitionKey.systemKey
|
||||||
|
? collection.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount) || "";
|
const endpoint = getEndpoint(databaseAccount) || "";
|
||||||
@@ -92,7 +93,7 @@ export function queryDocuments(
|
|||||||
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
|
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
|
||||||
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
|
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
|
||||||
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
|
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
|
||||||
[HttpHeaders.contentType]: "application/query+json"
|
[HttpHeaders.contentType]: "application/query+json",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (continuationToken) {
|
if (continuationToken) {
|
||||||
@@ -105,27 +106,26 @@ export function queryDocuments(
|
|||||||
.fetch(`${endpoint}${path}?${queryString.stringify(params)}`, {
|
.fetch(`${endpoint}${path}?${queryString.stringify(params)}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ query }),
|
body: JSON.stringify({ query }),
|
||||||
headers
|
headers,
|
||||||
})
|
})
|
||||||
.then(async response => {
|
.then(async (response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return {
|
return {
|
||||||
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
|
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
|
||||||
documents: (await response.json()).Documents as DataModels.DocumentId[],
|
documents: (await response.json()).Documents as DataModels.DocumentId[],
|
||||||
headers: response.headers
|
headers: response.headers,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
errorHandling(response, "querying documents", params);
|
return errorHandling(response, "querying documents", params);
|
||||||
return undefined;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readDocument(
|
export function readDocument(
|
||||||
databaseId: string,
|
databaseId: string,
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
documentId: DocumentId
|
documentId: ViewModels.DocumentId
|
||||||
): Promise<DataModels.DocumentId> {
|
): Promise<DataModels.DocumentId> {
|
||||||
const databaseAccount = userContext.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("/");
|
||||||
const path = idComponents.slice(0, 4).join("/");
|
const path = idComponents.slice(0, 4).join("/");
|
||||||
@@ -136,11 +136,13 @@ export function readDocument(
|
|||||||
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
|
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
|
||||||
rid,
|
rid,
|
||||||
rtype: "docs",
|
rtype: "docs",
|
||||||
sid: userContext.subscriptionId,
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: userContext.resourceGroup,
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
|
? documentId.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
@@ -152,10 +154,10 @@ export function readDocument(
|
|||||||
...authHeaders(),
|
...authHeaders(),
|
||||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent(
|
[CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent(
|
||||||
JSON.stringify(documentId.partitionKeyHeader())
|
JSON.stringify(documentId.partitionKeyHeader())
|
||||||
)
|
),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
@@ -167,9 +169,9 @@ export function createDocument(
|
|||||||
databaseId: string,
|
databaseId: string,
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
partitionKeyProperty: string,
|
partitionKeyProperty: string,
|
||||||
documentContent: unknown
|
documentContent: any
|
||||||
): Promise<DataModels.DocumentId> {
|
): Promise<DataModels.DocumentId> {
|
||||||
const databaseAccount = userContext.databaseAccount;
|
const databaseAccount = CosmosClient.databaseAccount();
|
||||||
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
|
||||||
const params = {
|
const params = {
|
||||||
db: databaseId,
|
db: databaseId,
|
||||||
@@ -177,10 +179,10 @@ export function createDocument(
|
|||||||
resourceUrl: `${resourceEndpoint}dbs/${databaseId}/colls/${collection.id()}/docs/`,
|
resourceUrl: `${resourceEndpoint}dbs/${databaseId}/colls/${collection.id()}/docs/`,
|
||||||
rid: collection.rid,
|
rid: collection.rid,
|
||||||
rtype: "docs",
|
rtype: "docs",
|
||||||
sid: userContext.subscriptionId,
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: userContext.resourceGroup,
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : ""
|
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
@@ -191,10 +193,10 @@ export function createDocument(
|
|||||||
body: JSON.stringify(documentContent),
|
body: JSON.stringify(documentContent),
|
||||||
headers: {
|
headers: {
|
||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...authHeaders()
|
...authHeaders(),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
@@ -205,10 +207,10 @@ export function createDocument(
|
|||||||
export function updateDocument(
|
export function updateDocument(
|
||||||
databaseId: string,
|
databaseId: string,
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
documentId: DocumentId,
|
documentId: ViewModels.DocumentId,
|
||||||
documentContent: string
|
documentContent: any
|
||||||
): Promise<DataModels.DocumentId> {
|
): Promise<DataModels.DocumentId> {
|
||||||
const databaseAccount = userContext.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("/");
|
||||||
const path = idComponents.slice(0, 5).join("/");
|
const path = idComponents.slice(0, 5).join("/");
|
||||||
@@ -219,11 +221,13 @@ export function updateDocument(
|
|||||||
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
|
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
|
||||||
rid,
|
rid,
|
||||||
rtype: "docs",
|
rtype: "docs",
|
||||||
sid: userContext.subscriptionId,
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: userContext.resourceGroup,
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
|
? documentId.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
|
|
||||||
@@ -235,10 +239,10 @@ export function updateDocument(
|
|||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...authHeaders(),
|
...authHeaders(),
|
||||||
[HttpHeaders.contentType]: "application/json",
|
[HttpHeaders.contentType]: "application/json",
|
||||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
|
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
@@ -246,8 +250,12 @@ export function updateDocument(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteDocument(databaseId: string, collection: Collection, documentId: DocumentId): Promise<void> {
|
export function deleteDocument(
|
||||||
const databaseAccount = userContext.databaseAccount;
|
databaseId: string,
|
||||||
|
collection: Collection,
|
||||||
|
documentId: ViewModels.DocumentId
|
||||||
|
): Promise<any> {
|
||||||
|
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("/");
|
||||||
const path = idComponents.slice(0, 5).join("/");
|
const path = idComponents.slice(0, 5).join("/");
|
||||||
@@ -258,11 +266,13 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
|
|||||||
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
|
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
|
||||||
rid,
|
rid,
|
||||||
rtype: "docs",
|
rtype: "docs",
|
||||||
sid: userContext.subscriptionId,
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: userContext.resourceGroup,
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
|
? documentId.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
|
|
||||||
@@ -273,10 +283,10 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
|
|||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...authHeaders(),
|
...authHeaders(),
|
||||||
[HttpHeaders.contentType]: "application/json",
|
[HttpHeaders.contentType]: "application/json",
|
||||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
|
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -293,8 +303,8 @@ export function createMongoCollectionWithProxy(
|
|||||||
sharedThroughput: boolean,
|
sharedThroughput: boolean,
|
||||||
isSharded: boolean,
|
isSharded: boolean,
|
||||||
autopilotOptions?: DataModels.RpOptions
|
autopilotOptions?: DataModels.RpOptions
|
||||||
): Promise<DataModels.Collection> {
|
): Promise<any> {
|
||||||
const databaseAccount = userContext.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,
|
||||||
db: databaseId,
|
db: databaseId,
|
||||||
@@ -306,10 +316,10 @@ export function createMongoCollectionWithProxy(
|
|||||||
is: isSharded,
|
is: isSharded,
|
||||||
rid: "",
|
rid: "",
|
||||||
rtype: "colls",
|
rtype: "colls",
|
||||||
sid: userContext.subscriptionId,
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: userContext.resourceGroup,
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
isAutoPilot: false
|
isAutoPilot: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (autopilotOptions) {
|
if (autopilotOptions) {
|
||||||
@@ -327,13 +337,13 @@ export function createMongoCollectionWithProxy(
|
|||||||
headers: {
|
headers: {
|
||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...authHeaders(),
|
...authHeaders(),
|
||||||
[HttpHeaders.contentType]: "application/json"
|
[HttpHeaders.contentType]: "application/json",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json();
|
return undefined;
|
||||||
}
|
}
|
||||||
return errorHandling(response, "creating collection", params);
|
return errorHandling(response, "creating collection", params);
|
||||||
});
|
});
|
||||||
@@ -350,8 +360,8 @@ export function createMongoCollectionWithARM(
|
|||||||
sharedThroughput: boolean,
|
sharedThroughput: boolean,
|
||||||
isSharded: boolean,
|
isSharded: boolean,
|
||||||
additionalOptions?: DataModels.RpOptions
|
additionalOptions?: DataModels.RpOptions
|
||||||
): Promise<DataModels.CreateCollectionWithRpResponse> {
|
): Promise<any> {
|
||||||
const databaseAccount = userContext.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,
|
||||||
db: databaseId,
|
db: databaseId,
|
||||||
@@ -363,10 +373,10 @@ export function createMongoCollectionWithARM(
|
|||||||
is: isSharded,
|
is: isSharded,
|
||||||
rid: "",
|
rid: "",
|
||||||
rtype: "colls",
|
rtype: "colls",
|
||||||
sid: userContext.subscriptionId,
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: userContext.resourceGroup,
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
analyticalStorageTtl
|
analyticalStorageTtl,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (createDatabase) {
|
if (createDatabase) {
|
||||||
@@ -381,11 +391,11 @@ export function createMongoCollectionWithARM(
|
|||||||
return _createMongoCollectionWithARM(armEndpoint, params, additionalOptions);
|
return _createMongoCollectionWithARM(armEndpoint, params, additionalOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEndpoint(databaseAccount: DataModels.DatabaseAccount): string {
|
export function getEndpoint(databaseAccount: ViewModels.DatabaseAccount): string {
|
||||||
const serverId = window.dataExplorer.serverId();
|
const serverId = window.dataExplorer.serverId();
|
||||||
const extensionEndpoint = window.dataExplorer.extensionEndpoint();
|
const extensionEndpoint = window.dataExplorer.extensionEndpoint();
|
||||||
let url = configContext.MONGO_BACKEND_ENDPOINT
|
let url = config.MONGO_BACKEND_ENDPOINT
|
||||||
? configContext.MONGO_BACKEND_ENDPOINT + "/api/mongo/explorer"
|
? config.MONGO_BACKEND_ENDPOINT + "/api/mongo/explorer"
|
||||||
: EnvironmentUtility.getMongoBackendEndpoint(serverId, databaseAccount.location, extensionEndpoint);
|
: EnvironmentUtility.getMongoBackendEndpoint(serverId, databaseAccount.location, extensionEndpoint);
|
||||||
|
|
||||||
if (window.authType === AuthType.EncryptedToken) {
|
if (window.authType === AuthType.EncryptedToken) {
|
||||||
@@ -394,9 +404,7 @@ export function getEndpoint(databaseAccount: DataModels.DatabaseAccount): string
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This function throws most of the time except on Forbidden which is a bit strange
|
async function errorHandling(response: any, action: string, params: any): Promise<any> {
|
||||||
// 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,28 +412,30 @@ async function errorHandling(response: Response, action: string, params: unknown
|
|||||||
`Error ${action}: ${errorMessage}, Payload: ${JSON.stringify(params)}`
|
`Error ${action}: ${errorMessage}, Payload: ${JSON.stringify(params)}`
|
||||||
);
|
);
|
||||||
if (response.status === HttpStatusCodes.Forbidden) {
|
if (response.status === HttpStatusCodes.Forbidden) {
|
||||||
sendMessage({ type: MessageTypes.ForbiddenError, reason: errorMessage });
|
MessageHandler.sendMessage({ type: MessageTypes.ForbiddenError, reason: errorMessage });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new Error(errorMessage);
|
throw new Error(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getARMCreateCollectionEndpoint(params: DataModels.MongoParameters): string {
|
export function getARMCreateCollectionEndpoint(params: DataModels.MongoParameters): string {
|
||||||
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${userContext.databaseAccount.name}/mongodbDatabases/${params.db}/collections/${params.coll}`;
|
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${
|
||||||
|
CosmosClient.databaseAccount().name
|
||||||
|
}/mongodbDatabases/${params.db}/collections/${params.coll}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function _createMongoCollectionWithARM(
|
export async function _createMongoCollectionWithARM(
|
||||||
armEndpoint: string,
|
armEndpoint: string,
|
||||||
params: DataModels.MongoParameters,
|
params: DataModels.MongoParameters,
|
||||||
rpOptions: DataModels.RpOptions
|
rpOptions: DataModels.RpOptions
|
||||||
): Promise<DataModels.CreateCollectionWithRpResponse> {
|
): Promise<any> {
|
||||||
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
|
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
|
||||||
properties: {
|
properties: {
|
||||||
resource: {
|
resource: {
|
||||||
id: params.coll
|
id: params.coll,
|
||||||
},
|
},
|
||||||
options: {}
|
options: {},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (params.is) {
|
if (params.is) {
|
||||||
@@ -446,13 +456,12 @@ export async function _createMongoCollectionWithARM(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new ResourceProviderClient<DataModels.CreateCollectionWithRpResponse>(armEndpoint).putAsync(
|
await new ResourceProviderClient(armEndpoint).putAsync(
|
||||||
getARMCreateCollectionEndpoint(params),
|
getARMCreateCollectionEndpoint(params),
|
||||||
DataExplorerConstants.ArmApiVersions.publicVersion,
|
DataExplorerConstants.ArmApiVersions.publicVersion,
|
||||||
rpPayloadToCreateCollection
|
rpPayloadToCreateCollection
|
||||||
);
|
);
|
||||||
} catch (response) {
|
} catch (response) {
|
||||||
errorHandling(response, "creating collection", undefined);
|
return errorHandling(response, "creating collection", undefined);
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,168 +1,168 @@
|
|||||||
/* Copyright 2013 10gen Inc.
|
/* Copyright 2013 10gen Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class MongoUtility {
|
export default class MongoUtility {
|
||||||
public static tojson = function(x: any, indent: string, nolint: boolean) {
|
public static tojson = function (x: any, indent: string, nolint: boolean) {
|
||||||
if (x === null || x === undefined) {
|
if (x === null || x === undefined) {
|
||||||
return String(x);
|
return String(x);
|
||||||
}
|
}
|
||||||
indent = indent || "";
|
indent = indent || "";
|
||||||
|
|
||||||
switch (typeof x) {
|
switch (typeof x) {
|
||||||
case "string":
|
case "string":
|
||||||
var out = new Array(x.length + 1);
|
var out = new Array(x.length + 1);
|
||||||
out[0] = '"';
|
out[0] = '"';
|
||||||
for (var i = 0; i < x.length; i++) {
|
for (var i = 0; i < x.length; i++) {
|
||||||
if (x[i] === '"') {
|
if (x[i] === '"') {
|
||||||
out[out.length] = '\\"';
|
out[out.length] = '\\"';
|
||||||
} else if (x[i] === "\\") {
|
} else if (x[i] === "\\") {
|
||||||
out[out.length] = "\\\\";
|
out[out.length] = "\\\\";
|
||||||
} else if (x[i] === "\b") {
|
} else if (x[i] === "\b") {
|
||||||
out[out.length] = "\\b";
|
out[out.length] = "\\b";
|
||||||
} else if (x[i] === "\f") {
|
} else if (x[i] === "\f") {
|
||||||
out[out.length] = "\\f";
|
out[out.length] = "\\f";
|
||||||
} else if (x[i] === "\n") {
|
} else if (x[i] === "\n") {
|
||||||
out[out.length] = "\\n";
|
out[out.length] = "\\n";
|
||||||
} else if (x[i] === "\r") {
|
} else if (x[i] === "\r") {
|
||||||
out[out.length] = "\\r";
|
out[out.length] = "\\r";
|
||||||
} else if (x[i] === "\t") {
|
} else if (x[i] === "\t") {
|
||||||
out[out.length] = "\\t";
|
out[out.length] = "\\t";
|
||||||
} else {
|
} else {
|
||||||
var code = x.charCodeAt(i);
|
var code = x.charCodeAt(i);
|
||||||
if (code < 0x20) {
|
if (code < 0x20) {
|
||||||
out[out.length] = (code < 0x10 ? "\\u000" : "\\u00") + code.toString(16);
|
out[out.length] = (code < 0x10 ? "\\u000" : "\\u00") + code.toString(16);
|
||||||
} else {
|
} else {
|
||||||
out[out.length] = x[i];
|
out[out.length] = x[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out.join("") + '"';
|
return out.join("") + '"';
|
||||||
case "number":
|
case "number":
|
||||||
/* falls through */
|
/* falls through */
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return "" + x;
|
return "" + x;
|
||||||
case "object":
|
case "object":
|
||||||
var func = $.isArray(x) ? MongoUtility.tojsonArray : MongoUtility.tojsonObject;
|
var func = $.isArray(x) ? MongoUtility.tojsonArray : MongoUtility.tojsonObject;
|
||||||
var s = func(x, indent, nolint);
|
var s = func(x, indent, nolint);
|
||||||
if (
|
if (
|
||||||
(nolint === null || nolint === undefined || nolint === true) &&
|
(nolint === null || nolint === undefined || nolint === true) &&
|
||||||
s.length < 80 &&
|
s.length < 80 &&
|
||||||
(indent === null || indent.length === 0)
|
(indent === null || indent.length === 0)
|
||||||
) {
|
) {
|
||||||
s = s.replace(/[\t\r\n]+/gm, " ");
|
s = s.replace(/[\t\r\n]+/gm, " ");
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
case "function":
|
case "function":
|
||||||
return x.toString();
|
return x.toString();
|
||||||
default:
|
default:
|
||||||
throw new Error("tojson can't handle type " + typeof x);
|
throw new Error("tojson can't handle type " + typeof x);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static tojsonObject = function(x: any, indent: string, nolint: boolean) {
|
private static tojsonObject = function (x: any, indent: string, nolint: boolean) {
|
||||||
var lineEnding = nolint ? " " : "\n";
|
var lineEnding = nolint ? " " : "\n";
|
||||||
var tabSpace = nolint ? "" : "\t";
|
var tabSpace = nolint ? "" : "\t";
|
||||||
indent = indent || "";
|
indent = indent || "";
|
||||||
|
|
||||||
if (typeof x.tojson === "function" && x.tojson !== MongoUtility.tojson) {
|
if (typeof x.tojson === "function" && x.tojson !== MongoUtility.tojson) {
|
||||||
return x.tojson(indent, nolint);
|
return x.tojson(indent, nolint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x.constructor && typeof x.constructor.tojson === "function" && x.constructor.tojson !== MongoUtility.tojson) {
|
if (x.constructor && typeof x.constructor.tojson === "function" && x.constructor.tojson !== MongoUtility.tojson) {
|
||||||
return x.constructor.tojson(x, indent, nolint);
|
return x.constructor.tojson(x, indent, nolint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MongoUtility.hasDefinedProperty(x, "toString") && !$.isArray(x)) {
|
if (MongoUtility.hasDefinedProperty(x, "toString") && !$.isArray(x)) {
|
||||||
return x.toString();
|
return x.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x instanceof Error) {
|
if (x instanceof Error) {
|
||||||
return x.toString();
|
return x.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MongoUtility.isObjectId(x)) {
|
if (MongoUtility.isObjectId(x)) {
|
||||||
return 'ObjectId("' + x.$oid + '")';
|
return 'ObjectId("' + x.$oid + '")';
|
||||||
}
|
}
|
||||||
|
|
||||||
// push one level of indent
|
// push one level of indent
|
||||||
indent += tabSpace;
|
indent += tabSpace;
|
||||||
var s = "{";
|
var s = "{";
|
||||||
|
|
||||||
var pairs = [];
|
var pairs = [];
|
||||||
for (var k in x) {
|
for (var k in x) {
|
||||||
if (x.hasOwnProperty(k)) {
|
if (x.hasOwnProperty(k)) {
|
||||||
var val = x[k];
|
var val = x[k];
|
||||||
var pair = '"' + k + '" : ' + MongoUtility.tojson(val, indent, nolint);
|
var pair = '"' + k + '" : ' + MongoUtility.tojson(val, indent, nolint);
|
||||||
|
|
||||||
if (k === "_id") {
|
if (k === "_id") {
|
||||||
pairs.unshift(pair);
|
pairs.unshift(pair);
|
||||||
} else {
|
} else {
|
||||||
pairs.push(pair);
|
pairs.push(pair);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add proper line endings, indents, and commas to each line
|
// Add proper line endings, indents, and commas to each line
|
||||||
s += $.map(pairs, function(pair) {
|
s += $.map(pairs, function (pair) {
|
||||||
return lineEnding + indent + pair;
|
return lineEnding + indent + pair;
|
||||||
}).join(",");
|
}).join(",");
|
||||||
s += lineEnding;
|
s += lineEnding;
|
||||||
|
|
||||||
// pop one level of indent
|
// pop one level of indent
|
||||||
indent = indent.substring(1);
|
indent = indent.substring(1);
|
||||||
return s + indent + "}";
|
return s + indent + "}";
|
||||||
};
|
};
|
||||||
|
|
||||||
private static tojsonArray = function(a: any, indent: string, nolint: boolean) {
|
private static tojsonArray = function (a: any, indent: string, nolint: boolean) {
|
||||||
if (a.length === 0) {
|
if (a.length === 0) {
|
||||||
return "[ ]";
|
return "[ ]";
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineEnding = nolint ? " " : "\n";
|
var lineEnding = nolint ? " " : "\n";
|
||||||
if (!indent || nolint) {
|
if (!indent || nolint) {
|
||||||
indent = "";
|
indent = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = "[" + lineEnding;
|
var s = "[" + lineEnding;
|
||||||
indent += "\t";
|
indent += "\t";
|
||||||
for (var i = 0; i < a.length; i++) {
|
for (var i = 0; i < a.length; i++) {
|
||||||
s += indent + MongoUtility.tojson(a[i], indent, nolint);
|
s += indent + MongoUtility.tojson(a[i], indent, nolint);
|
||||||
if (i < a.length - 1) {
|
if (i < a.length - 1) {
|
||||||
s += "," + lineEnding;
|
s += "," + lineEnding;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (a.length === 0) {
|
if (a.length === 0) {
|
||||||
s += indent;
|
s += indent;
|
||||||
}
|
}
|
||||||
|
|
||||||
indent = indent.substring(1);
|
indent = indent.substring(1);
|
||||||
s += lineEnding + indent + "]";
|
s += lineEnding + indent + "]";
|
||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
|
||||||
private static hasDefinedProperty = function(obj: any, prop: string): boolean {
|
private static hasDefinedProperty = function (obj: any, prop: string): boolean {
|
||||||
if (Object.getPrototypeOf === undefined || Object.getPrototypeOf(obj) === null) {
|
if (Object.getPrototypeOf === undefined || Object.getPrototypeOf(obj) === null) {
|
||||||
return false;
|
return false;
|
||||||
} else if (obj.hasOwnProperty(prop)) {
|
} else if (obj.hasOwnProperty(prop)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return MongoUtility.hasDefinedProperty(Object.getPrototypeOf(obj), prop);
|
return MongoUtility.hasDefinedProperty(Object.getPrototypeOf(obj), prop);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static isObjectId(obj: any): boolean {
|
private static isObjectId(obj: any): boolean {
|
||||||
var keys = Object.keys(obj);
|
var keys = Object.keys(obj);
|
||||||
return keys.length === 1 && keys[0] === "$oid" && typeof obj.$oid === "string" && /^[0-9a-f]{24}$/.test(obj.$oid);
|
return keys.length === 1 && keys[0] === "$oid" && typeof obj.$oid === "string" && /^[0-9a-f]{24}$/.test(obj.$oid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,47 @@
|
|||||||
import "jquery";
|
import "jquery";
|
||||||
import * as Q from "q";
|
import * as Q from "q";
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
|
||||||
import { userContext } from "../UserContext";
|
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||||
|
import { CosmosClient } from "./CosmosClient";
|
||||||
export class NotificationsClientBase {
|
|
||||||
private _extensionEndpoint: string;
|
export class NotificationsClientBase implements ViewModels.NotificationsClient {
|
||||||
private _notificationsApiSuffix: string;
|
private _extensionEndpoint: string;
|
||||||
|
private _notificationsApiSuffix: string;
|
||||||
protected constructor(notificationsApiSuffix: string) {
|
|
||||||
this._notificationsApiSuffix = notificationsApiSuffix;
|
protected constructor(notificationsApiSuffix: string) {
|
||||||
}
|
this._notificationsApiSuffix = notificationsApiSuffix;
|
||||||
|
}
|
||||||
public fetchNotifications(): Q.Promise<DataModels.Notification[]> {
|
|
||||||
const deferred: Q.Deferred<DataModels.Notification[]> = Q.defer<DataModels.Notification[]>();
|
public fetchNotifications(): Q.Promise<DataModels.Notification[]> {
|
||||||
const databaseAccount = userContext.databaseAccount;
|
const deferred: Q.Deferred<DataModels.Notification[]> = Q.defer<DataModels.Notification[]>();
|
||||||
const subscriptionId = userContext.subscriptionId;
|
const databaseAccount: ViewModels.DatabaseAccount = CosmosClient.databaseAccount();
|
||||||
const resourceGroup = userContext.resourceGroup;
|
const subscriptionId: string = CosmosClient.subscriptionId();
|
||||||
const url = `${this._extensionEndpoint}${this._notificationsApiSuffix}?accountName=${databaseAccount.name}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
|
const resourceGroup: string = CosmosClient.resourceGroup();
|
||||||
const authorizationHeader: ViewModels.AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
|
const url: string = `${this._extensionEndpoint}${this._notificationsApiSuffix}?accountName=${databaseAccount.name}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
|
||||||
const headers: any = {};
|
const authorizationHeader: ViewModels.AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
|
||||||
headers[authorizationHeader.header] = authorizationHeader.token;
|
const headers: any = {};
|
||||||
|
headers[authorizationHeader.header] = authorizationHeader.token;
|
||||||
$.ajax({
|
|
||||||
url: url,
|
$.ajax({
|
||||||
type: "GET",
|
url: url,
|
||||||
headers: headers,
|
type: "GET",
|
||||||
cache: false
|
headers: headers,
|
||||||
}).then(
|
cache: false,
|
||||||
(notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => {
|
}).then(
|
||||||
deferred.resolve(notifications);
|
(notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => {
|
||||||
},
|
deferred.resolve(notifications);
|
||||||
(xhr: JQueryXHR<any>, textStatus: string, error: any) => {
|
},
|
||||||
deferred.reject(xhr.responseText);
|
(xhr: JQueryXHR<any>, textStatus: string, error: any) => {
|
||||||
}
|
deferred.reject(xhr.responseText);
|
||||||
);
|
}
|
||||||
|
);
|
||||||
return deferred.promise;
|
|
||||||
}
|
return deferred.promise;
|
||||||
|
}
|
||||||
public setExtensionEndpoint(extensionEndpoint: string): void {
|
|
||||||
this._extensionEndpoint = extensionEndpoint;
|
public setExtensionEndpoint(extensionEndpoint: string): void {
|
||||||
}
|
this._extensionEndpoint = extensionEndpoint;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,286 +1,286 @@
|
|||||||
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
import * as _ from "underscore";
|
||||||
import * as _ from "underscore";
|
import * as DataModels from "../Contracts/DataModels";
|
||||||
import * as DataModels from "../Contracts/DataModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import DocumentId from "../Explorer/Tree/DocumentId";
|
||||||
import Explorer from "../Explorer/Explorer";
|
import * as ErrorParserUtility from "./ErrorParserUtility";
|
||||||
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
||||||
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
|
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import DocumentId from "../Explorer/Tree/DocumentId";
|
import { CosmosClient } from "./CosmosClient";
|
||||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
import { QueryUtils } from "../Utils/QueryUtils";
|
import * as Logger from "./Logger";
|
||||||
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
||||||
import { userContext } from "../UserContext";
|
import { QueryUtils } from "../Utils/QueryUtils";
|
||||||
import {
|
|
||||||
createDocument,
|
export class QueriesClient implements ViewModels.QueriesClient {
|
||||||
deleteDocument,
|
private static readonly PartitionKey: DataModels.PartitionKey = {
|
||||||
getOrCreateDatabaseAndCollection,
|
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
||||||
queryDocuments,
|
kind: BackendDefaults.partitionKeyKind,
|
||||||
queryDocumentsPage
|
version: BackendDefaults.partitionKeyVersion,
|
||||||
} from "./DocumentClientUtilityBase";
|
};
|
||||||
import * as ErrorParserUtility from "./ErrorParserUtility";
|
private static readonly FetchQuery: string = "SELECT * FROM c";
|
||||||
import * as Logger from "./Logger";
|
private static readonly FetchMongoQuery: string = "{}";
|
||||||
|
|
||||||
export class QueriesClient {
|
public constructor(private container: ViewModels.Explorer) {}
|
||||||
private static readonly PartitionKey: DataModels.PartitionKey = {
|
|
||||||
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
public async setupQueriesCollection(): Promise<DataModels.Collection> {
|
||||||
kind: BackendDefaults.partitionKeyKind,
|
const queriesCollection: ViewModels.Collection = this.findQueriesCollection();
|
||||||
version: BackendDefaults.partitionKeyVersion
|
if (queriesCollection) {
|
||||||
};
|
return Promise.resolve(queriesCollection.rawDataModel);
|
||||||
private static readonly FetchQuery: string = "SELECT * FROM c";
|
}
|
||||||
private static readonly FetchMongoQuery: string = "{}";
|
|
||||||
|
const id = NotificationConsoleUtils.logConsoleMessage(
|
||||||
public constructor(private container: Explorer) {}
|
ConsoleDataType.InProgress,
|
||||||
|
"Setting up account for saving queries"
|
||||||
public async setupQueriesCollection(): Promise<DataModels.Collection> {
|
);
|
||||||
const queriesCollection: ViewModels.Collection = this.findQueriesCollection();
|
return this.container.documentClientUtility
|
||||||
if (queriesCollection) {
|
.getOrCreateDatabaseAndCollection({
|
||||||
return Promise.resolve(queriesCollection.rawDataModel);
|
collectionId: SavedQueries.CollectionName,
|
||||||
}
|
databaseId: SavedQueries.DatabaseName,
|
||||||
|
partitionKey: QueriesClient.PartitionKey,
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
offerThroughput: SavedQueries.OfferThroughput,
|
||||||
ConsoleDataType.InProgress,
|
databaseLevelThroughput: undefined,
|
||||||
"Setting up account for saving queries"
|
})
|
||||||
);
|
.then(
|
||||||
return getOrCreateDatabaseAndCollection({
|
(collection: DataModels.Collection) => {
|
||||||
collectionId: SavedQueries.CollectionName,
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
databaseId: SavedQueries.DatabaseName,
|
ConsoleDataType.Info,
|
||||||
partitionKey: QueriesClient.PartitionKey,
|
"Successfully set up account for saving queries"
|
||||||
offerThroughput: SavedQueries.OfferThroughput,
|
);
|
||||||
databaseLevelThroughput: undefined
|
return Promise.resolve(collection);
|
||||||
})
|
},
|
||||||
.then(
|
(error: any) => {
|
||||||
(collection: DataModels.Collection) => {
|
const stringifiedError: string = JSON.stringify(error);
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Info,
|
ConsoleDataType.Error,
|
||||||
"Successfully set up account for saving queries"
|
`Failed to set up account for saving queries: ${stringifiedError}`
|
||||||
);
|
);
|
||||||
return Promise.resolve(collection);
|
Logger.logError(stringifiedError, "setupQueriesCollection");
|
||||||
},
|
return Promise.reject(stringifiedError);
|
||||||
(error: any) => {
|
}
|
||||||
const stringifiedError: string = JSON.stringify(error);
|
)
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
ConsoleDataType.Error,
|
}
|
||||||
`Failed to set up account for saving queries: ${stringifiedError}`
|
|
||||||
);
|
public async saveQuery(query: DataModels.Query): Promise<void> {
|
||||||
Logger.logError(stringifiedError, "setupQueriesCollection");
|
const queriesCollection = this.findQueriesCollection();
|
||||||
return Promise.reject(stringifiedError);
|
if (!queriesCollection) {
|
||||||
}
|
const errorMessage: string = "Account not set up to perform saved query operations";
|
||||||
)
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
ConsoleDataType.Error,
|
||||||
}
|
`Failed to save query ${query.queryName}: ${errorMessage}`
|
||||||
|
);
|
||||||
public async saveQuery(query: DataModels.Query): Promise<void> {
|
return Promise.reject(errorMessage);
|
||||||
const queriesCollection = this.findQueriesCollection();
|
}
|
||||||
if (!queriesCollection) {
|
|
||||||
const errorMessage: string = "Account not set up to perform saved query operations";
|
try {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
this.validateQuery(query);
|
||||||
ConsoleDataType.Error,
|
} catch (error) {
|
||||||
`Failed to save query ${query.queryName}: ${errorMessage}`
|
const errorMessage: string = "Invalid query specified";
|
||||||
);
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
return Promise.reject(errorMessage);
|
ConsoleDataType.Error,
|
||||||
}
|
`Failed to save query ${query.queryName}: ${errorMessage}`
|
||||||
|
);
|
||||||
try {
|
return Promise.reject(errorMessage);
|
||||||
this.validateQuery(query);
|
}
|
||||||
} catch (error) {
|
|
||||||
const errorMessage: string = "Invalid query specified";
|
const id = NotificationConsoleUtils.logConsoleMessage(
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
ConsoleDataType.InProgress,
|
||||||
ConsoleDataType.Error,
|
`Saving query ${query.queryName}`
|
||||||
`Failed to save query ${query.queryName}: ${errorMessage}`
|
);
|
||||||
);
|
query.id = query.queryName;
|
||||||
return Promise.reject(errorMessage);
|
return this.container.documentClientUtility
|
||||||
}
|
.createDocument(queriesCollection, query)
|
||||||
|
.then(
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
(savedQuery: DataModels.Query) => {
|
||||||
ConsoleDataType.InProgress,
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
`Saving query ${query.queryName}`
|
ConsoleDataType.Info,
|
||||||
);
|
`Successfully saved query ${query.queryName}`
|
||||||
query.id = query.queryName;
|
);
|
||||||
return createDocument(queriesCollection, query)
|
return Promise.resolve();
|
||||||
.then(
|
},
|
||||||
(savedQuery: DataModels.Query) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
let errorMessage: string;
|
||||||
ConsoleDataType.Info,
|
const parsedError: DataModels.ErrorDataModel = ErrorParserUtility.parse(error)[0];
|
||||||
`Successfully saved query ${query.queryName}`
|
if (parsedError.code === HttpStatusCodes.Conflict.toString()) {
|
||||||
);
|
errorMessage = `Query ${query.queryName} already exists`;
|
||||||
return Promise.resolve();
|
} else {
|
||||||
},
|
errorMessage = parsedError.message;
|
||||||
(error: any) => {
|
}
|
||||||
let errorMessage: string;
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
const parsedError: DataModels.ErrorDataModel = ErrorParserUtility.parse(error)[0];
|
ConsoleDataType.Error,
|
||||||
if (parsedError.code === HttpStatusCodes.Conflict.toString()) {
|
`Failed to save query ${query.queryName}: ${errorMessage}`
|
||||||
errorMessage = `Query ${query.queryName} already exists`;
|
);
|
||||||
} else {
|
Logger.logError(JSON.stringify(parsedError), "saveQuery");
|
||||||
errorMessage = parsedError.message;
|
return Promise.reject(errorMessage);
|
||||||
}
|
}
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
)
|
||||||
ConsoleDataType.Error,
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
`Failed to save query ${query.queryName}: ${errorMessage}`
|
}
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(parsedError), "saveQuery");
|
public async getQueries(): Promise<DataModels.Query[]> {
|
||||||
return Promise.reject(errorMessage);
|
const queriesCollection = this.findQueriesCollection();
|
||||||
}
|
if (!queriesCollection) {
|
||||||
)
|
const errorMessage: string = "Account not set up to perform saved query operations";
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
}
|
ConsoleDataType.Error,
|
||||||
|
`Failed to fetch saved queries: ${errorMessage}`
|
||||||
public async getQueries(): Promise<DataModels.Query[]> {
|
);
|
||||||
const queriesCollection = this.findQueriesCollection();
|
return Promise.reject(errorMessage);
|
||||||
if (!queriesCollection) {
|
}
|
||||||
const errorMessage: string = "Account not set up to perform saved query operations";
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
const options: any = { enableCrossPartitionQuery: true };
|
||||||
ConsoleDataType.Error,
|
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
|
||||||
`Failed to fetch saved queries: ${errorMessage}`
|
return this.container.documentClientUtility
|
||||||
);
|
.queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
|
||||||
return Promise.reject(errorMessage);
|
.then(
|
||||||
}
|
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
|
||||||
|
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
|
||||||
const options: any = { enableCrossPartitionQuery: true };
|
this.container.documentClientUtility.queryDocumentsPage(
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
|
queriesCollection.id(),
|
||||||
return queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
|
queryIterator,
|
||||||
.then(
|
firstItemIndex,
|
||||||
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
|
options
|
||||||
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
|
);
|
||||||
queryDocumentsPage(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) => {
|
if (!document) {
|
||||||
if (!document) {
|
return undefined;
|
||||||
return undefined;
|
}
|
||||||
}
|
const { id, resourceId, query, queryName } = document;
|
||||||
const { id, resourceId, query, queryName } = document;
|
const parsedQuery: DataModels.Query = {
|
||||||
const parsedQuery: DataModels.Query = {
|
resourceId: resourceId,
|
||||||
resourceId: resourceId,
|
queryName: queryName,
|
||||||
queryName: queryName,
|
query: query,
|
||||||
query: query,
|
id: id,
|
||||||
id: id
|
};
|
||||||
};
|
try {
|
||||||
try {
|
this.validateQuery(parsedQuery);
|
||||||
this.validateQuery(parsedQuery);
|
return parsedQuery;
|
||||||
return parsedQuery;
|
} catch (error) {
|
||||||
} catch (error) {
|
return undefined;
|
||||||
return undefined;
|
}
|
||||||
}
|
});
|
||||||
});
|
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
|
||||||
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
|
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, "Successfully fetched saved queries");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, "Successfully fetched saved queries");
|
return Promise.resolve(queries);
|
||||||
return Promise.resolve(queries);
|
},
|
||||||
},
|
(error: any) => {
|
||||||
(error: any) => {
|
const stringifiedError: string = JSON.stringify(error);
|
||||||
const stringifiedError: string = JSON.stringify(error);
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
ConsoleDataType.Error,
|
||||||
ConsoleDataType.Error,
|
`Failed to fetch saved queries: ${stringifiedError}`
|
||||||
`Failed to fetch saved queries: ${stringifiedError}`
|
);
|
||||||
);
|
Logger.logError(stringifiedError, "getSavedQueries");
|
||||||
Logger.logError(stringifiedError, "getSavedQueries");
|
return Promise.reject(stringifiedError);
|
||||||
return Promise.reject(stringifiedError);
|
}
|
||||||
}
|
);
|
||||||
);
|
},
|
||||||
},
|
(error: any) => {
|
||||||
(error: any) => {
|
// should never get into this state but we handle this regardless
|
||||||
// should never get into this state but we handle this regardless
|
const stringifiedError: string = JSON.stringify(error);
|
||||||
const stringifiedError: string = JSON.stringify(error);
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
ConsoleDataType.Error,
|
||||||
ConsoleDataType.Error,
|
`Failed to fetch saved queries: ${stringifiedError}`
|
||||||
`Failed to fetch saved queries: ${stringifiedError}`
|
);
|
||||||
);
|
Logger.logError(stringifiedError, "getSavedQueries");
|
||||||
Logger.logError(stringifiedError, "getSavedQueries");
|
return Promise.reject(stringifiedError);
|
||||||
return Promise.reject(stringifiedError);
|
}
|
||||||
}
|
)
|
||||||
)
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
}
|
||||||
}
|
|
||||||
|
public async deleteQuery(query: DataModels.Query): Promise<void> {
|
||||||
public async deleteQuery(query: DataModels.Query): Promise<void> {
|
const queriesCollection = this.findQueriesCollection();
|
||||||
const queriesCollection = this.findQueriesCollection();
|
if (!queriesCollection) {
|
||||||
if (!queriesCollection) {
|
const errorMessage: string = "Account not set up to perform saved query operations";
|
||||||
const errorMessage: string = "Account not set up to perform saved query operations";
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
ConsoleDataType.Error,
|
||||||
ConsoleDataType.Error,
|
`Failed to fetch saved queries: ${errorMessage}`
|
||||||
`Failed to fetch saved queries: ${errorMessage}`
|
);
|
||||||
);
|
return Promise.reject(errorMessage);
|
||||||
return Promise.reject(errorMessage);
|
}
|
||||||
}
|
|
||||||
|
try {
|
||||||
try {
|
this.validateQuery(query);
|
||||||
this.validateQuery(query);
|
} catch (error) {
|
||||||
} catch (error) {
|
const errorMessage: string = "Invalid query specified";
|
||||||
const errorMessage: string = "Invalid query specified";
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
ConsoleDataType.Error,
|
||||||
ConsoleDataType.Error,
|
`Failed to delete query ${query.queryName}: ${errorMessage}`
|
||||||
`Failed to delete query ${query.queryName}: ${errorMessage}`
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
const id = NotificationConsoleUtils.logConsoleMessage(
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
ConsoleDataType.InProgress,
|
||||||
ConsoleDataType.InProgress,
|
`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(
|
||||||
ConsoleDataType.Info,
|
ConsoleDataType.Info,
|
||||||
`Successfully deleted query ${query.queryName}`
|
`Successfully deleted query ${query.queryName}`
|
||||||
);
|
);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
const stringifiedError: string = JSON.stringify(error);
|
const stringifiedError: string = JSON.stringify(error);
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
`Failed to delete query ${query.queryName}: ${stringifiedError}`
|
`Failed to delete query ${query.queryName}: ${stringifiedError}`
|
||||||
);
|
);
|
||||||
Logger.logError(stringifiedError, "deleteQuery");
|
Logger.logError(stringifiedError, "deleteQuery");
|
||||||
return Promise.reject(stringifiedError);
|
return Promise.reject(stringifiedError);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getResourceId(): string {
|
public getResourceId(): string {
|
||||||
const databaseAccount = userContext.databaseAccount;
|
const databaseAccount: ViewModels.DatabaseAccount = CosmosClient.databaseAccount();
|
||||||
const databaseAccountName = (databaseAccount && databaseAccount.name) || "";
|
const databaseAccountName: string = (databaseAccount && databaseAccount.name) || "";
|
||||||
const subscriptionId = userContext.subscriptionId || "";
|
const subscriptionId: string = CosmosClient.subscriptionId() || "";
|
||||||
const resourceGroup = userContext.resourceGroup || "";
|
const resourceGroup: string = CosmosClient.resourceGroup() || "";
|
||||||
|
|
||||||
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}`;
|
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDb/databaseAccounts/${databaseAccountName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private findQueriesCollection(): ViewModels.Collection {
|
private findQueriesCollection(): ViewModels.Collection {
|
||||||
const queriesDatabase: ViewModels.Database = _.find(
|
const queriesDatabase: ViewModels.Database = _.find(
|
||||||
this.container.databases(),
|
this.container.databases(),
|
||||||
(database: ViewModels.Database) => database.id() === SavedQueries.DatabaseName
|
(database: ViewModels.Database) => database.id() === SavedQueries.DatabaseName
|
||||||
);
|
);
|
||||||
if (!queriesDatabase) {
|
if (!queriesDatabase) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return _.find(
|
return _.find(
|
||||||
queriesDatabase.collections(),
|
queriesDatabase.collections(),
|
||||||
(collection: ViewModels.Collection) => collection.id() === SavedQueries.CollectionName
|
(collection: ViewModels.Collection) => collection.id() === SavedQueries.CollectionName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private validateQuery(query: DataModels.Query): void {
|
private validateQuery(query: DataModels.Query): void {
|
||||||
if (!query || query.queryName == null || query.query == null || query.resourceId == null) {
|
if (!query || query.queryName == null || query.query == null || query.resourceId == null) {
|
||||||
throw new Error("Invalid query specified");
|
throw new Error("Invalid query specified");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchQueriesQuery(): string {
|
private fetchQueriesQuery(): string {
|
||||||
if (this.container.isPreferredApiMongoDB()) {
|
if (this.container.isPreferredApiMongoDB()) {
|
||||||
return QueriesClient.FetchMongoQuery;
|
return QueriesClient.FetchMongoQuery;
|
||||||
}
|
}
|
||||||
return QueriesClient.FetchQuery;
|
return QueriesClient.FetchQuery;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,108 +1,106 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
|
|
||||||
import { SplitterMetrics } from "./Constants";
|
import { SplitterMetrics } from "./Constants";
|
||||||
|
|
||||||
export enum SplitterDirection {
|
export enum SplitterDirection {
|
||||||
Horizontal = "horizontal",
|
Horizontal = "horizontal",
|
||||||
Vertical = "vertical"
|
Vertical = "vertical",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SplitterBounds {
|
export interface SplitterBounds {
|
||||||
max: number;
|
max: number;
|
||||||
min: number;
|
min: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SplitterOptions {
|
export interface SplitterOptions {
|
||||||
splitterId: string;
|
splitterId: string;
|
||||||
leftId: string;
|
leftId: string;
|
||||||
bounds: SplitterBounds;
|
bounds: SplitterBounds;
|
||||||
direction: SplitterDirection;
|
direction: SplitterDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Splitter {
|
export class Splitter {
|
||||||
public splitterId: string;
|
public splitterId: string;
|
||||||
public leftSideId: string;
|
public leftSideId: string;
|
||||||
|
|
||||||
public splitter: HTMLElement;
|
public splitter: HTMLElement;
|
||||||
public leftSide: HTMLElement;
|
public leftSide: HTMLElement;
|
||||||
public lastX: number;
|
public lastX: number;
|
||||||
public lastWidth: number;
|
public lastWidth: number;
|
||||||
|
|
||||||
private isCollapsed: ko.Observable<boolean>;
|
private isCollapsed: ko.Observable<boolean>;
|
||||||
private bounds: SplitterBounds;
|
private bounds: SplitterBounds;
|
||||||
private direction: SplitterDirection;
|
private direction: SplitterDirection;
|
||||||
|
|
||||||
constructor(options: SplitterOptions) {
|
constructor(options: SplitterOptions) {
|
||||||
this.splitterId = options.splitterId;
|
this.splitterId = options.splitterId;
|
||||||
this.leftSideId = options.leftId;
|
this.leftSideId = options.leftId;
|
||||||
this.isCollapsed = ko.observable<boolean>(false);
|
this.isCollapsed = ko.observable<boolean>(false);
|
||||||
this.bounds = options.bounds;
|
this.bounds = options.bounds;
|
||||||
this.direction = options.direction;
|
this.direction = options.direction;
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public initialize() {
|
public initialize() {
|
||||||
this.splitter = document.getElementById(this.splitterId);
|
this.splitter = document.getElementById(this.splitterId);
|
||||||
this.leftSide = document.getElementById(this.leftSideId);
|
this.leftSide = document.getElementById(this.leftSideId);
|
||||||
|
|
||||||
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
|
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
|
||||||
const splitterOptions: JQueryUI.ResizableOptions = {
|
const splitterOptions: JQueryUI.ResizableOptions = {
|
||||||
animate: true,
|
animate: true,
|
||||||
animateDuration: "fast",
|
animateDuration: "fast",
|
||||||
start: this.onResizeStart,
|
start: this.onResizeStart,
|
||||||
stop: this.onResizeStop
|
stop: this.onResizeStop,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isVerticalSplitter) {
|
if (isVerticalSplitter) {
|
||||||
$(this.leftSide).css("width", this.bounds.min);
|
$(this.leftSide).css("width", this.bounds.min);
|
||||||
$(this.splitter).css("height", "100%");
|
$(this.splitter).css("height", "100%");
|
||||||
|
|
||||||
splitterOptions.maxWidth = this.bounds.max;
|
splitterOptions.maxWidth = this.bounds.max;
|
||||||
splitterOptions.minWidth = this.bounds.min;
|
splitterOptions.minWidth = this.bounds.min;
|
||||||
splitterOptions.handles = { e: "#" + this.splitterId };
|
splitterOptions.handles = { e: "#" + this.splitterId };
|
||||||
} else {
|
} else {
|
||||||
$(this.leftSide).css("height", this.bounds.min);
|
$(this.leftSide).css("height", this.bounds.min);
|
||||||
$(this.splitter).css("width", "100%");
|
$(this.splitter).css("width", "100%");
|
||||||
|
|
||||||
splitterOptions.maxHeight = this.bounds.max;
|
splitterOptions.maxHeight = this.bounds.max;
|
||||||
splitterOptions.minHeight = this.bounds.min;
|
splitterOptions.minHeight = this.bounds.min;
|
||||||
splitterOptions.handles = { s: "#" + this.splitterId };
|
splitterOptions.handles = { s: "#" + this.splitterId };
|
||||||
}
|
}
|
||||||
|
|
||||||
$(this.leftSide).resizable(splitterOptions);
|
$(this.leftSide).resizable(splitterOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onResizeStart: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
private onResizeStart: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
||||||
if (this.direction === SplitterDirection.Vertical) {
|
if (this.direction === SplitterDirection.Vertical) {
|
||||||
$(".ui-resizable-helper").height("100%");
|
$(".ui-resizable-helper").height("100%");
|
||||||
} else {
|
} else {
|
||||||
$(".ui-resizable-helper").width("100%");
|
$(".ui-resizable-helper").width("100%");
|
||||||
}
|
}
|
||||||
$("iframe").css("pointer-events", "none");
|
$("iframe").css("pointer-events", "none");
|
||||||
};
|
};
|
||||||
|
|
||||||
private onResizeStop: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
private onResizeStop: JQueryUI.ResizableEvent = (e: Event, ui: JQueryUI.ResizableUIParams) => {
|
||||||
$("iframe").css("pointer-events", "auto");
|
$("iframe").css("pointer-events", "auto");
|
||||||
};
|
};
|
||||||
|
|
||||||
public collapseLeft() {
|
public collapseLeft() {
|
||||||
this.lastX = $(this.splitter).position().left;
|
this.lastX = $(this.splitter).position().left;
|
||||||
this.lastWidth = $(this.leftSide).width();
|
this.lastWidth = $(this.leftSide).width();
|
||||||
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
|
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
|
||||||
$(this.leftSide).css("width", "");
|
$(this.leftSide).css("width", "");
|
||||||
$(this.leftSide)
|
$(this.leftSide).resizable("option", "disabled", true).removeClass("ui-resizable-disabled"); // remove class so splitter is visible
|
||||||
.resizable("option", "disabled", true)
|
$(this.splitter).removeClass("ui-resizable-e");
|
||||||
.removeClass("ui-resizable-disabled"); // remove class so splitter is visible
|
this.isCollapsed(true);
|
||||||
$(this.splitter).removeClass("ui-resizable-e");
|
}
|
||||||
this.isCollapsed(true);
|
|
||||||
}
|
public expandLeft() {
|
||||||
|
$(this.splitter).addClass("ui-resizable-e");
|
||||||
public expandLeft() {
|
$(this.leftSide).css("width", this.lastWidth);
|
||||||
$(this.splitter).addClass("ui-resizable-e");
|
$(this.splitter).css("left", this.lastX);
|
||||||
$(this.leftSide).css("width", this.lastWidth);
|
$(this.splitter).css("left", ""); // this ensures the splitter's position is not fixed and enables movement during resizing
|
||||||
$(this.splitter).css("left", this.lastX);
|
$(this.leftSide).resizable("enable");
|
||||||
$(this.splitter).css("left", ""); // this ensures the splitter's position is not fixed and enables movement during resizing
|
this.isCollapsed(false);
|
||||||
$(this.leftSide).resizable("enable");
|
}
|
||||||
this.isCollapsed(false);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ export default class UrlUtility {
|
|||||||
type: type,
|
type: type,
|
||||||
objectBody: {
|
objectBody: {
|
||||||
id: id,
|
id: id,
|
||||||
self: resourcePath
|
self: resourcePath,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -1,250 +0,0 @@
|
|||||||
import * as DataModels from "../../Contracts/DataModels";
|
|
||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { DatabaseResponse } from "@azure/cosmos";
|
|
||||||
import { DatabaseRequest } from "@azure/cosmos/dist-esm/client/Database/DatabaseRequest";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
|
||||||
import {
|
|
||||||
SqlDatabaseCreateUpdateParameters,
|
|
||||||
CreateUpdateOptions
|
|
||||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
|
||||||
import {
|
|
||||||
createUpdateCassandraKeyspace,
|
|
||||||
getCassandraKeyspace
|
|
||||||
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
|
||||||
import {
|
|
||||||
createUpdateMongoDBDatabase,
|
|
||||||
getMongoDBDatabase
|
|
||||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
|
||||||
import {
|
|
||||||
createUpdateGremlinDatabase,
|
|
||||||
getGremlinDatabase
|
|
||||||
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
|
||||||
import { logConsoleProgress, logConsoleError, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { logError } from "../Logger";
|
|
||||||
import { refreshCachedOffers, refreshCachedResources } from "../DataAccessUtilityBase";
|
|
||||||
import { sendNotificationForError } from "./sendNotificationForError";
|
|
||||||
import { userContext } from "../../UserContext";
|
|
||||||
|
|
||||||
export async function createDatabase(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
|
||||||
let database: DataModels.Database;
|
|
||||||
const clearMessage = logConsoleProgress(`Creating a new database ${params.databaseId}`);
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
window.authType === AuthType.AAD &&
|
|
||||||
!userContext.useSDKOperations &&
|
|
||||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
|
||||||
) {
|
|
||||||
database = await createDatabaseWithARM(params);
|
|
||||||
} else {
|
|
||||||
database = await createDatabaseWithSDK(params);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logConsoleError(`Error while creating database ${params.databaseId}:\n ${error.message}`);
|
|
||||||
logError(JSON.stringify(error), "CreateDatabase", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
clearMessage();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
logConsoleInfo(`Successfully created database ${params.databaseId}`);
|
|
||||||
await refreshCachedResources();
|
|
||||||
await refreshCachedOffers();
|
|
||||||
clearMessage();
|
|
||||||
return database;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createDatabaseWithARM(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
|
||||||
const defaultExperience = userContext.defaultExperience;
|
|
||||||
switch (defaultExperience) {
|
|
||||||
case DefaultAccountExperienceType.DocumentDB:
|
|
||||||
return createSqlDatabase(params);
|
|
||||||
case DefaultAccountExperienceType.MongoDB:
|
|
||||||
return createMongoDatabase(params);
|
|
||||||
case DefaultAccountExperienceType.Cassandra:
|
|
||||||
return createCassandraKeyspace(params);
|
|
||||||
case DefaultAccountExperienceType.Graph:
|
|
||||||
return createGremlineDatabase(params);
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createSqlDatabase(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
|
||||||
try {
|
|
||||||
const getResponse = await getSqlDatabase(
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
params.databaseId
|
|
||||||
);
|
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
|
||||||
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (error.code !== "NotFound") {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const options: CreateUpdateOptions = constructRpOptions(params);
|
|
||||||
const rpPayload: SqlDatabaseCreateUpdateParameters = {
|
|
||||||
properties: {
|
|
||||||
resource: {
|
|
||||||
id: params.databaseId
|
|
||||||
},
|
|
||||||
options
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const createResponse = await createUpdateSqlDatabase(
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
params.databaseId,
|
|
||||||
rpPayload
|
|
||||||
);
|
|
||||||
return createResponse && (createResponse.properties.resource as DataModels.Database);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createMongoDatabase(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
|
||||||
try {
|
|
||||||
const getResponse = await getMongoDBDatabase(
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
params.databaseId
|
|
||||||
);
|
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
|
||||||
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (error.code !== "NotFound") {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const options: CreateUpdateOptions = constructRpOptions(params);
|
|
||||||
const rpPayload: SqlDatabaseCreateUpdateParameters = {
|
|
||||||
properties: {
|
|
||||||
resource: {
|
|
||||||
id: params.databaseId
|
|
||||||
},
|
|
||||||
options
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const createResponse = await createUpdateMongoDBDatabase(
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
params.databaseId,
|
|
||||||
rpPayload
|
|
||||||
);
|
|
||||||
return createResponse && (createResponse.properties.resource as DataModels.Database);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createCassandraKeyspace(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
|
||||||
try {
|
|
||||||
const getResponse = await getCassandraKeyspace(
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
params.databaseId
|
|
||||||
);
|
|
||||||
if (getResponse?.properties?.resource) {
|
|
||||||
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (error.code !== "NotFound") {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const options: CreateUpdateOptions = constructRpOptions(params);
|
|
||||||
const rpPayload: SqlDatabaseCreateUpdateParameters = {
|
|
||||||
properties: {
|
|
||||||
resource: {
|
|
||||||
id: params.databaseId
|
|
||||||
},
|
|
||||||
options
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const createResponse = await createUpdateCassandraKeyspace(
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
params.databaseId,
|
|
||||||
rpPayload
|
|
||||||
);
|
|
||||||
return createResponse && (createResponse.properties.resource as DataModels.Database);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createGremlineDatabase(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
|
||||||
try {
|
|
||||||
const getResponse = await getGremlinDatabase(
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
params.databaseId
|
|
||||||
);
|
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
|
||||||
throw new Error(`Create database failed: database with id ${params.databaseId} already exists`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (error.code !== "NotFound") {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const options: CreateUpdateOptions = constructRpOptions(params);
|
|
||||||
const rpPayload: SqlDatabaseCreateUpdateParameters = {
|
|
||||||
properties: {
|
|
||||||
resource: {
|
|
||||||
id: params.databaseId
|
|
||||||
},
|
|
||||||
options
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const createResponse = await createUpdateGremlinDatabase(
|
|
||||||
userContext.subscriptionId,
|
|
||||||
userContext.resourceGroup,
|
|
||||||
userContext.databaseAccount.name,
|
|
||||||
params.databaseId,
|
|
||||||
rpPayload
|
|
||||||
);
|
|
||||||
return createResponse && (createResponse.properties.resource as DataModels.Database);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createDatabaseWithSDK(params: DataModels.CreateDatabaseParams): Promise<DataModels.Database> {
|
|
||||||
const createBody: DatabaseRequest = { id: params.databaseId };
|
|
||||||
const databaseOptions: RequestOptions = {};
|
|
||||||
// TODO: replace when SDK support autopilot
|
|
||||||
if (params.databaseLevelThroughput) {
|
|
||||||
if (params.autoPilotMaxThroughput) {
|
|
||||||
createBody.maxThroughput = params.autoPilotMaxThroughput;
|
|
||||||
} else {
|
|
||||||
createBody.throughput = params.offerThroughput;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const response: DatabaseResponse = await client().databases.create(createBody, databaseOptions);
|
|
||||||
return response.resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
function constructRpOptions(params: DataModels.CreateDatabaseParams): CreateUpdateOptions {
|
|
||||||
if (!params.databaseLevelThroughput) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.autoPilotMaxThroughput) {
|
|
||||||
return {
|
|
||||||
autoscaleSettings: {
|
|
||||||
maxThroughput: params.autoPilotMaxThroughput
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
throughput: params.offerThroughput
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
jest.mock("../../Utils/arm/request");
|
|
||||||
jest.mock("../MessageHandler");
|
|
||||||
jest.mock("../CosmosClient");
|
|
||||||
import { deleteCollection } from "./deleteCollection";
|
|
||||||
import { armRequest } from "../../Utils/arm/request";
|
|
||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { updateUserContext } from "../../UserContext";
|
|
||||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
|
||||||
import { sendCachedDataMessage } from "../MessageHandler";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
|
|
||||||
describe("deleteCollection", () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
updateUserContext({
|
|
||||||
databaseAccount: {
|
|
||||||
name: "test"
|
|
||||||
} as DatabaseAccount,
|
|
||||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
|
||||||
});
|
|
||||||
(sendCachedDataMessage as jest.Mock).mockResolvedValue(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call ARM if logged in with AAD", async () => {
|
|
||||||
window.authType = AuthType.AAD;
|
|
||||||
await deleteCollection("database", "collection");
|
|
||||||
expect(armRequest).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call SDK if not logged in with non-AAD method", async () => {
|
|
||||||
window.authType = AuthType.MasterKey;
|
|
||||||
(client as jest.Mock).mockReturnValue({
|
|
||||||
database: () => {
|
|
||||||
return {
|
|
||||||
container: () => {
|
|
||||||
return {
|
|
||||||
delete: (): unknown => undefined
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await deleteCollection("database", "collection");
|
|
||||||
expect(client).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
import { deleteSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
|
||||||
import { deleteCassandraTable } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
|
||||||
import { deleteMongoDBCollection } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
|
||||||
import { deleteGremlinGraph } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
|
||||||
import { deleteTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
|
||||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { logError } from "../Logger";
|
|
||||||
import { sendNotificationForError } from "./sendNotificationForError";
|
|
||||||
import { userContext } from "../../UserContext";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { refreshCachedResources } from "../DataAccessUtilityBase";
|
|
||||||
|
|
||||||
export async function deleteCollection(databaseId: string, collectionId: string): Promise<void> {
|
|
||||||
const clearMessage = logConsoleProgress(`Deleting container ${collectionId}`);
|
|
||||||
try {
|
|
||||||
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
|
|
||||||
await deleteCollectionWithARM(databaseId, collectionId);
|
|
||||||
} else {
|
|
||||||
await client()
|
|
||||||
.database(databaseId)
|
|
||||||
.container(collectionId)
|
|
||||||
.delete();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logConsoleError(`Error while deleting container ${collectionId}:\n ${JSON.stringify(error)}`);
|
|
||||||
logError(JSON.stringify(error), "DeleteCollection", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
logConsoleInfo(`Successfully deleted container ${collectionId}`);
|
|
||||||
clearMessage();
|
|
||||||
await refreshCachedResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteCollectionWithARM(databaseId: string, collectionId: string): Promise<void> {
|
|
||||||
const subscriptionId = userContext.subscriptionId;
|
|
||||||
const resourceGroup = userContext.resourceGroup;
|
|
||||||
const accountName = userContext.databaseAccount.name;
|
|
||||||
const defaultExperience = userContext.defaultExperience;
|
|
||||||
|
|
||||||
switch (defaultExperience) {
|
|
||||||
case DefaultAccountExperienceType.DocumentDB:
|
|
||||||
return deleteSqlContainer(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
|
||||||
case DefaultAccountExperienceType.MongoDB:
|
|
||||||
return deleteMongoDBCollection(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
|
||||||
case DefaultAccountExperienceType.Cassandra:
|
|
||||||
return deleteCassandraTable(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
|
||||||
case DefaultAccountExperienceType.Graph:
|
|
||||||
return deleteGremlinGraph(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
|
||||||
case DefaultAccountExperienceType.Table:
|
|
||||||
return deleteTable(subscriptionId, resourceGroup, accountName, collectionId);
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
jest.mock("../../Utils/arm/request");
|
|
||||||
jest.mock("../MessageHandler");
|
|
||||||
jest.mock("../CosmosClient");
|
|
||||||
import { deleteDatabase } from "./deleteDatabase";
|
|
||||||
import { armRequest } from "../../Utils/arm/request";
|
|
||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { updateUserContext } from "../../UserContext";
|
|
||||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
|
||||||
import { sendCachedDataMessage } from "../MessageHandler";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
|
|
||||||
describe("deleteDatabase", () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
updateUserContext({
|
|
||||||
databaseAccount: {
|
|
||||||
name: "test"
|
|
||||||
} as DatabaseAccount,
|
|
||||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
|
||||||
});
|
|
||||||
(sendCachedDataMessage as jest.Mock).mockResolvedValue(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call ARM if logged in with AAD", async () => {
|
|
||||||
window.authType = AuthType.AAD;
|
|
||||||
await deleteDatabase("database");
|
|
||||||
expect(armRequest).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call SDK if not logged in with non-AAD method", async () => {
|
|
||||||
window.authType = AuthType.MasterKey;
|
|
||||||
(client as jest.Mock).mockReturnValue({
|
|
||||||
database: () => {
|
|
||||||
return {
|
|
||||||
delete: (): unknown => undefined
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await deleteDatabase("database");
|
|
||||||
expect(client).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
import { deleteSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
|
||||||
import { deleteCassandraKeyspace } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
|
||||||
import { deleteMongoDBDatabase } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
|
||||||
import { deleteGremlinDatabase } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
|
||||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { userContext } from "../../UserContext";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { refreshCachedResources } from "../DataAccessUtilityBase";
|
|
||||||
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 &&
|
|
||||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table &&
|
|
||||||
!userContext.useSDKOperations
|
|
||||||
) {
|
|
||||||
await deleteDatabaseWithARM(databaseId);
|
|
||||||
} else {
|
|
||||||
await 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteDatabaseWithARM(databaseId: string): Promise<void> {
|
|
||||||
const subscriptionId = userContext.subscriptionId;
|
|
||||||
const resourceGroup = userContext.resourceGroup;
|
|
||||||
const accountName = userContext.databaseAccount.name;
|
|
||||||
const defaultExperience = userContext.defaultExperience;
|
|
||||||
|
|
||||||
switch (defaultExperience) {
|
|
||||||
case DefaultAccountExperienceType.DocumentDB:
|
|
||||||
return deleteSqlDatabase(subscriptionId, resourceGroup, accountName, databaseId);
|
|
||||||
case DefaultAccountExperienceType.MongoDB:
|
|
||||||
return deleteMongoDBDatabase(subscriptionId, resourceGroup, accountName, databaseId);
|
|
||||||
case DefaultAccountExperienceType.Cassandra:
|
|
||||||
return deleteCassandraKeyspace(subscriptionId, resourceGroup, accountName, databaseId);
|
|
||||||
case DefaultAccountExperienceType.Graph:
|
|
||||||
return deleteGremlinDatabase(subscriptionId, resourceGroup, accountName, databaseId);
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
jest.mock("../CosmosClient");
|
|
||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { readCollection } from "./readCollection";
|
|
||||||
import { updateUserContext } from "../../UserContext";
|
|
||||||
|
|
||||||
describe("readCollection", () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
updateUserContext({
|
|
||||||
databaseAccount: {
|
|
||||||
name: "test"
|
|
||||||
} as DatabaseAccount,
|
|
||||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call SDK if logged in with resource token", async () => {
|
|
||||||
window.authType = AuthType.ResourceToken;
|
|
||||||
(client as jest.Mock).mockReturnValue({
|
|
||||||
database: () => {
|
|
||||||
return {
|
|
||||||
container: () => {
|
|
||||||
return {
|
|
||||||
read: (): unknown => ({})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await readCollection("database", "collection");
|
|
||||||
expect(client).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import * as DataModels from "../../Contracts/DataModels";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { logConsoleProgress, logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { logError } from "../Logger";
|
|
||||||
import { sendNotificationForError } from "./sendNotificationForError";
|
|
||||||
|
|
||||||
export async function readCollection(databaseId: string, collectionId: string): Promise<DataModels.Collection> {
|
|
||||||
let collection: DataModels.Collection;
|
|
||||||
const clearMessage = logConsoleProgress(`Querying container ${collectionId}`);
|
|
||||||
try {
|
|
||||||
const response = await client()
|
|
||||||
.database(databaseId)
|
|
||||||
.container(collectionId)
|
|
||||||
.read();
|
|
||||||
collection = response.resource as DataModels.Collection;
|
|
||||||
} catch (error) {
|
|
||||||
logConsoleError(`Error while querying container ${collectionId}:\n ${JSON.stringify(error)}`);
|
|
||||||
logError(JSON.stringify(error), "ReadCollection", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
clearMessage();
|
|
||||||
return collection;
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
jest.mock("../../Utils/arm/request");
|
|
||||||
jest.mock("../CosmosClient");
|
|
||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
import { armRequest } from "../../Utils/arm/request";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { readCollections } from "./readCollections";
|
|
||||||
import { updateUserContext } from "../../UserContext";
|
|
||||||
|
|
||||||
describe("readCollections", () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
updateUserContext({
|
|
||||||
databaseAccount: {
|
|
||||||
name: "test"
|
|
||||||
} as DatabaseAccount,
|
|
||||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call ARM if logged in with AAD", async () => {
|
|
||||||
window.authType = AuthType.AAD;
|
|
||||||
await readCollections("database");
|
|
||||||
expect(armRequest).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call SDK if not logged in with non-AAD method", async () => {
|
|
||||||
window.authType = AuthType.MasterKey;
|
|
||||||
(client as jest.Mock).mockReturnValue({
|
|
||||||
database: () => {
|
|
||||||
return {
|
|
||||||
containers: {
|
|
||||||
readAll: () => {
|
|
||||||
return {
|
|
||||||
fetchAll: (): unknown => []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await readCollections("database");
|
|
||||||
expect(client).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import * as DataModels from "../../Contracts/DataModels";
|
|
||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { listSqlContainers } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
|
||||||
import { listCassandraTables } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
|
||||||
import { listMongoDBCollections } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
|
||||||
import { listGremlinGraphs } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
|
||||||
import { listTables } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
|
||||||
import { logConsoleProgress, logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { logError } from "../Logger";
|
|
||||||
import { sendNotificationForError } from "./sendNotificationForError";
|
|
||||||
import { userContext } from "../../UserContext";
|
|
||||||
|
|
||||||
export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> {
|
|
||||||
let collections: DataModels.Collection[];
|
|
||||||
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
window.authType === AuthType.AAD &&
|
|
||||||
!userContext.useSDKOperations &&
|
|
||||||
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
|
|
||||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
|
||||||
) {
|
|
||||||
collections = await readCollectionsWithARM(databaseId);
|
|
||||||
} else {
|
|
||||||
const sdkResponse = await client()
|
|
||||||
.database(databaseId)
|
|
||||||
.containers.readAll()
|
|
||||||
.fetchAll();
|
|
||||||
collections = sdkResponse.resources as DataModels.Collection[];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logConsoleError(`Error while querying containers for database ${databaseId}:\n ${JSON.stringify(error)}`);
|
|
||||||
logError(JSON.stringify(error), "ReadCollections", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
clearMessage();
|
|
||||||
return collections;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function readCollectionsWithARM(databaseId: string): Promise<DataModels.Collection[]> {
|
|
||||||
let rpResponse;
|
|
||||||
const subscriptionId = userContext.subscriptionId;
|
|
||||||
const resourceGroup = userContext.resourceGroup;
|
|
||||||
const accountName = userContext.databaseAccount.name;
|
|
||||||
const defaultExperience = userContext.defaultExperience;
|
|
||||||
|
|
||||||
switch (defaultExperience) {
|
|
||||||
case DefaultAccountExperienceType.DocumentDB:
|
|
||||||
rpResponse = await listSqlContainers(subscriptionId, resourceGroup, accountName, databaseId);
|
|
||||||
break;
|
|
||||||
case DefaultAccountExperienceType.MongoDB:
|
|
||||||
rpResponse = await listMongoDBCollections(subscriptionId, resourceGroup, accountName, databaseId);
|
|
||||||
break;
|
|
||||||
case DefaultAccountExperienceType.Cassandra:
|
|
||||||
rpResponse = await listCassandraTables(subscriptionId, resourceGroup, accountName, databaseId);
|
|
||||||
break;
|
|
||||||
case DefaultAccountExperienceType.Graph:
|
|
||||||
rpResponse = await listGremlinGraphs(subscriptionId, resourceGroup, accountName, databaseId);
|
|
||||||
break;
|
|
||||||
case DefaultAccountExperienceType.Table:
|
|
||||||
rpResponse = await listTables(subscriptionId, resourceGroup, accountName);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rpResponse?.value?.map(collection => collection.properties?.resource as DataModels.Collection);
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
jest.mock("../../Utils/arm/request");
|
|
||||||
jest.mock("../CosmosClient");
|
|
||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { DatabaseAccount } from "../../Contracts/DataModels";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
import { armRequest } from "../../Utils/arm/request";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { readDatabases } from "./readDatabases";
|
|
||||||
import { updateUserContext } from "../../UserContext";
|
|
||||||
|
|
||||||
describe("readDatabases", () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
updateUserContext({
|
|
||||||
databaseAccount: {
|
|
||||||
name: "test"
|
|
||||||
} as DatabaseAccount,
|
|
||||||
defaultExperience: DefaultAccountExperienceType.DocumentDB
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call ARM if logged in with AAD", async () => {
|
|
||||||
window.authType = AuthType.AAD;
|
|
||||||
await readDatabases();
|
|
||||||
expect(armRequest).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call SDK if not logged in with non-AAD method", async () => {
|
|
||||||
window.authType = AuthType.MasterKey;
|
|
||||||
(client as jest.Mock).mockReturnValue({
|
|
||||||
databases: {
|
|
||||||
readAll: () => {
|
|
||||||
return {
|
|
||||||
fetchAll: (): unknown => []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await readDatabases();
|
|
||||||
expect(client).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import * as DataModels from "../../Contracts/DataModels";
|
|
||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { listSqlDatabases } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
|
||||||
import { listCassandraKeyspaces } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
|
||||||
import { listMongoDBDatabases } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
|
||||||
import { listGremlinDatabases } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
|
||||||
import { logConsoleProgress, logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { logError } from "../Logger";
|
|
||||||
import { sendNotificationForError } from "./sendNotificationForError";
|
|
||||||
import { userContext } from "../../UserContext";
|
|
||||||
|
|
||||||
export async function readDatabases(): Promise<DataModels.Database[]> {
|
|
||||||
let databases: DataModels.Database[];
|
|
||||||
const clearMessage = logConsoleProgress(`Querying databases`);
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
window.authType === AuthType.AAD &&
|
|
||||||
!userContext.useSDKOperations &&
|
|
||||||
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
|
|
||||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table &&
|
|
||||||
userContext.defaultExperience !== DefaultAccountExperienceType.Cassandra
|
|
||||||
) {
|
|
||||||
databases = await readDatabasesWithARM();
|
|
||||||
} else {
|
|
||||||
const sdkResponse = await client()
|
|
||||||
.databases.readAll()
|
|
||||||
.fetchAll();
|
|
||||||
databases = sdkResponse.resources as DataModels.Database[];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logConsoleError(`Error while querying databases:\n ${JSON.stringify(error)}`);
|
|
||||||
logError(JSON.stringify(error), "ReadDatabases", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
clearMessage();
|
|
||||||
return databases;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function readDatabasesWithARM(): Promise<DataModels.Database[]> {
|
|
||||||
let rpResponse;
|
|
||||||
const subscriptionId = userContext.subscriptionId;
|
|
||||||
const resourceGroup = userContext.resourceGroup;
|
|
||||||
const accountName = userContext.databaseAccount.name;
|
|
||||||
const defaultExperience = userContext.defaultExperience;
|
|
||||||
|
|
||||||
switch (defaultExperience) {
|
|
||||||
case DefaultAccountExperienceType.DocumentDB:
|
|
||||||
rpResponse = await listSqlDatabases(subscriptionId, resourceGroup, accountName);
|
|
||||||
break;
|
|
||||||
case DefaultAccountExperienceType.MongoDB:
|
|
||||||
rpResponse = await listMongoDBDatabases(subscriptionId, resourceGroup, accountName);
|
|
||||||
break;
|
|
||||||
case DefaultAccountExperienceType.Cassandra:
|
|
||||||
rpResponse = await listCassandraKeyspaces(subscriptionId, resourceGroup, accountName);
|
|
||||||
break;
|
|
||||||
case DefaultAccountExperienceType.Graph:
|
|
||||||
rpResponse = await listGremlinDatabases(subscriptionId, resourceGroup, accountName);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rpResponse?.value?.map(database => database.properties?.resource as DataModels.Database);
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
import { AuthType } from "../../AuthType";
|
|
||||||
import { Collection } from "../../Contracts/DataModels";
|
|
||||||
import { ContainerDefinition } from "@azure/cosmos";
|
|
||||||
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
|
|
||||||
import {
|
|
||||||
ExtendedResourceProperties,
|
|
||||||
SqlContainerCreateUpdateParameters,
|
|
||||||
SqlContainerResource
|
|
||||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
|
||||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
|
||||||
import {
|
|
||||||
createUpdateCassandraTable,
|
|
||||||
getCassandraTable
|
|
||||||
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
|
|
||||||
import {
|
|
||||||
createUpdateMongoDBCollection,
|
|
||||||
getMongoDBCollection
|
|
||||||
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
|
||||||
import {
|
|
||||||
createUpdateGremlinGraph,
|
|
||||||
getGremlinGraph
|
|
||||||
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
|
|
||||||
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
|
|
||||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { logError } from "../Logger";
|
|
||||||
import { refreshCachedResources } from "../DataAccessUtilityBase";
|
|
||||||
import { sendNotificationForError } from "./sendNotificationForError";
|
|
||||||
import { userContext } from "../../UserContext";
|
|
||||||
|
|
||||||
export async function updateCollection(
|
|
||||||
databaseId: string,
|
|
||||||
collectionId: string,
|
|
||||||
newCollection: Collection,
|
|
||||||
options: RequestOptions = {}
|
|
||||||
): Promise<Collection> {
|
|
||||||
let collection: Collection;
|
|
||||||
const clearMessage = logConsoleProgress(`Updating container ${collectionId}`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
window.authType === AuthType.AAD &&
|
|
||||||
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
|
|
||||||
userContext.defaultExperience !== DefaultAccountExperienceType.Table
|
|
||||||
) {
|
|
||||||
collection = await updateCollectionWithARM(databaseId, collectionId, newCollection);
|
|
||||||
} else {
|
|
||||||
const sdkResponse = await client()
|
|
||||||
.database(databaseId)
|
|
||||||
.container(collectionId)
|
|
||||||
.replace(newCollection as ContainerDefinition, options);
|
|
||||||
collection = sdkResponse.resource as Collection;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logConsoleError(`Failed to update container ${collectionId}: ${JSON.stringify(error)}`);
|
|
||||||
logError(JSON.stringify(error), "UpdateCollection", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
logConsoleInfo(`Successfully updated container ${collectionId}`);
|
|
||||||
clearMessage();
|
|
||||||
await refreshCachedResources();
|
|
||||||
return collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateCollectionWithARM(
|
|
||||||
databaseId: string,
|
|
||||||
collectionId: string,
|
|
||||||
newCollection: Collection
|
|
||||||
): Promise<Collection> {
|
|
||||||
const subscriptionId = userContext.subscriptionId;
|
|
||||||
const resourceGroup = userContext.resourceGroup;
|
|
||||||
const accountName = userContext.databaseAccount.name;
|
|
||||||
const defaultExperience = userContext.defaultExperience;
|
|
||||||
|
|
||||||
switch (defaultExperience) {
|
|
||||||
case DefaultAccountExperienceType.DocumentDB:
|
|
||||||
return updateSqlContainer(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection);
|
|
||||||
case DefaultAccountExperienceType.MongoDB:
|
|
||||||
return updateMongoDBCollection(
|
|
||||||
databaseId,
|
|
||||||
collectionId,
|
|
||||||
subscriptionId,
|
|
||||||
resourceGroup,
|
|
||||||
accountName,
|
|
||||||
newCollection
|
|
||||||
);
|
|
||||||
case DefaultAccountExperienceType.Cassandra:
|
|
||||||
return updateCassandraTable(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection);
|
|
||||||
case DefaultAccountExperienceType.Graph:
|
|
||||||
return updateGremlinGraph(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection);
|
|
||||||
case DefaultAccountExperienceType.Table:
|
|
||||||
return updateTable(collectionId, subscriptionId, resourceGroup, accountName, newCollection);
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateSqlContainer(
|
|
||||||
databaseId: string,
|
|
||||||
collectionId: string,
|
|
||||||
subscriptionId: string,
|
|
||||||
resourceGroup: string,
|
|
||||||
accountName: string,
|
|
||||||
newCollection: Collection
|
|
||||||
): Promise<Collection> {
|
|
||||||
const getResponse = await getSqlContainer(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
|
||||||
getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties;
|
|
||||||
const updateResponse = await createUpdateSqlContainer(
|
|
||||||
subscriptionId,
|
|
||||||
resourceGroup,
|
|
||||||
accountName,
|
|
||||||
databaseId,
|
|
||||||
collectionId,
|
|
||||||
getResponse as SqlContainerCreateUpdateParameters
|
|
||||||
);
|
|
||||||
return updateResponse && (updateResponse.properties.resource as Collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Sql container to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateMongoDBCollection(
|
|
||||||
databaseId: string,
|
|
||||||
collectionId: string,
|
|
||||||
subscriptionId: string,
|
|
||||||
resourceGroup: string,
|
|
||||||
accountName: string,
|
|
||||||
newCollection: Collection
|
|
||||||
): Promise<Collection> {
|
|
||||||
const getResponse = await getMongoDBCollection(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
|
||||||
getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties;
|
|
||||||
const updateResponse = await createUpdateMongoDBCollection(
|
|
||||||
subscriptionId,
|
|
||||||
resourceGroup,
|
|
||||||
accountName,
|
|
||||||
databaseId,
|
|
||||||
collectionId,
|
|
||||||
getResponse as SqlContainerCreateUpdateParameters
|
|
||||||
);
|
|
||||||
return updateResponse && (updateResponse.properties.resource as Collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`MongoDB collection to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateCassandraTable(
|
|
||||||
databaseId: string,
|
|
||||||
collectionId: string,
|
|
||||||
subscriptionId: string,
|
|
||||||
resourceGroup: string,
|
|
||||||
accountName: string,
|
|
||||||
newCollection: Collection
|
|
||||||
): Promise<Collection> {
|
|
||||||
const getResponse = await getCassandraTable(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
|
||||||
getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties;
|
|
||||||
const updateResponse = await createUpdateCassandraTable(
|
|
||||||
subscriptionId,
|
|
||||||
resourceGroup,
|
|
||||||
accountName,
|
|
||||||
databaseId,
|
|
||||||
collectionId,
|
|
||||||
getResponse as SqlContainerCreateUpdateParameters
|
|
||||||
);
|
|
||||||
return updateResponse && (updateResponse.properties.resource as Collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`Cassandra table to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateGremlinGraph(
|
|
||||||
databaseId: string,
|
|
||||||
collectionId: string,
|
|
||||||
subscriptionId: string,
|
|
||||||
resourceGroup: string,
|
|
||||||
accountName: string,
|
|
||||||
newCollection: Collection
|
|
||||||
): Promise<Collection> {
|
|
||||||
const getResponse = await getGremlinGraph(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
|
||||||
getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties;
|
|
||||||
const updateResponse = await createUpdateGremlinGraph(
|
|
||||||
subscriptionId,
|
|
||||||
resourceGroup,
|
|
||||||
accountName,
|
|
||||||
databaseId,
|
|
||||||
collectionId,
|
|
||||||
getResponse as SqlContainerCreateUpdateParameters
|
|
||||||
);
|
|
||||||
return updateResponse && (updateResponse.properties.resource as Collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Gremlin graph to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateTable(
|
|
||||||
collectionId: string,
|
|
||||||
subscriptionId: string,
|
|
||||||
resourceGroup: string,
|
|
||||||
accountName: string,
|
|
||||||
newCollection: Collection
|
|
||||||
): Promise<Collection> {
|
|
||||||
const getResponse = await getTable(subscriptionId, resourceGroup, accountName, collectionId);
|
|
||||||
if (getResponse && getResponse.properties && getResponse.properties.resource) {
|
|
||||||
getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties;
|
|
||||||
const updateResponse = await createUpdateTable(
|
|
||||||
subscriptionId,
|
|
||||||
resourceGroup,
|
|
||||||
accountName,
|
|
||||||
collectionId,
|
|
||||||
getResponse as SqlContainerCreateUpdateParameters
|
|
||||||
);
|
|
||||||
return updateResponse && (updateResponse.properties.resource as Collection);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Table to update does not exist. Table id: ${collectionId}`);
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { updateOfferThroughputBeyondLimit } from "./updateOfferThroughputBeyondLimit";
|
|
||||||
|
|
||||||
describe("updateOfferThroughputBeyondLimit", () => {
|
|
||||||
it("should call fetch", async () => {
|
|
||||||
window.fetch = jest.fn(() => {
|
|
||||||
return {
|
|
||||||
ok: true
|
|
||||||
};
|
|
||||||
});
|
|
||||||
window.dataExplorer = {
|
|
||||||
logConsoleData: jest.fn(),
|
|
||||||
deleteInProgressConsoleDataWithId: jest.fn(),
|
|
||||||
extensionEndpoint: jest.fn()
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
} as any;
|
|
||||||
await updateOfferThroughputBeyondLimit({
|
|
||||||
subscriptionId: "foo",
|
|
||||||
resourceGroup: "foo",
|
|
||||||
databaseAccountName: "foo",
|
|
||||||
databaseName: "foo",
|
|
||||||
throughput: 1000000000,
|
|
||||||
offerIsRUPerMinuteThroughputEnabled: false
|
|
||||||
});
|
|
||||||
expect(window.fetch).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import { Platform, configContext } from "../../ConfigContext";
|
|
||||||
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
|
|
||||||
import { AutoPilotOfferSettings } from "../../Contracts/DataModels";
|
|
||||||
import { logConsoleProgress, logConsoleInfo, logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { HttpHeaders } from "../Constants";
|
|
||||||
|
|
||||||
interface UpdateOfferThroughputRequest {
|
|
||||||
subscriptionId: string;
|
|
||||||
resourceGroup: string;
|
|
||||||
databaseAccountName: string;
|
|
||||||
databaseName: string;
|
|
||||||
collectionName?: string;
|
|
||||||
throughput: number;
|
|
||||||
offerIsRUPerMinuteThroughputEnabled: boolean;
|
|
||||||
offerAutopilotSettings?: AutoPilotOfferSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function updateOfferThroughputBeyondLimit(request: UpdateOfferThroughputRequest): Promise<void> {
|
|
||||||
if (configContext.platform !== Platform.Portal) {
|
|
||||||
throw new Error("Updating throughput beyond specified limit is not supported on this platform");
|
|
||||||
}
|
|
||||||
|
|
||||||
const resourceDescriptionInfo = request.collectionName
|
|
||||||
? `database ${request.databaseName} and container ${request.collectionName}`
|
|
||||||
: `database ${request.databaseName}`;
|
|
||||||
|
|
||||||
const clearMessage = logConsoleProgress(
|
|
||||||
`Requesting increase in throughput to ${request.throughput} for ${resourceDescriptionInfo}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const explorer = window.dataExplorer;
|
|
||||||
const url = `${explorer.extensionEndpoint()}/api/offerthroughputrequest/updatebeyondspecifiedlimit`;
|
|
||||||
const authorizationHeader = getAuthorizationHeader();
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(request),
|
|
||||||
headers: { [authorizationHeader.header]: authorizationHeader.token, [HttpHeaders.contentType]: "application/json" }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
logConsoleInfo(
|
|
||||||
`Successfully requested an increase in throughput to ${request.throughput} for ${resourceDescriptionInfo}`
|
|
||||||
);
|
|
||||||
clearMessage();
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const error = await response.json();
|
|
||||||
logConsoleError(`Failed to request an increase in throughput for ${request.throughput}: ${error.message}`);
|
|
||||||
clearMessage();
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
export enum Platform {
|
export enum Platform {
|
||||||
Portal = "Portal",
|
Portal = "Portal",
|
||||||
Hosted = "Hosted",
|
Hosted = "Hosted",
|
||||||
Emulator = "Emulator"
|
Emulator = "Emulator",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConfigContext {
|
interface Config {
|
||||||
platform: Platform;
|
platform: Platform;
|
||||||
allowedParentFrameOrigins: RegExp;
|
allowedParentFrameOrigins: RegExp;
|
||||||
gitSha?: string;
|
gitSha?: string;
|
||||||
@@ -16,6 +16,7 @@ interface ConfigContext {
|
|||||||
ARM_API_VERSION: string;
|
ARM_API_VERSION: string;
|
||||||
GRAPH_ENDPOINT: string;
|
GRAPH_ENDPOINT: string;
|
||||||
GRAPH_API_VERSION: string;
|
GRAPH_API_VERSION: string;
|
||||||
|
AZURESAMPLESCOSMOSDBPAT: string;
|
||||||
ARCADIA_ENDPOINT: string;
|
ARCADIA_ENDPOINT: string;
|
||||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
|
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
|
||||||
BACKEND_ENDPOINT?: string;
|
BACKEND_ENDPOINT?: string;
|
||||||
@@ -28,7 +29,7 @@ interface ConfigContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Default configuration
|
// Default configuration
|
||||||
let configContext: Readonly<ConfigContext> = {
|
let config: Config = {
|
||||||
platform: Platform.Portal,
|
platform: Platform.Portal,
|
||||||
allowedParentFrameOrigins: /^https:\/\/portal\.azure\.com$|^https:\/\/portal\.azure\.us$|^https:\/\/portal\.azure\.cn$|^https:\/\/portal\.microsoftazure\.de$|^https:\/\/.+\.portal\.azure\.com$|^https:\/\/.+\.portal\.azure\.us$|^https:\/\/.+\.portal\.azure\.cn$|^https:\/\/.+\.portal\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.com$|^https:\/\/main\.documentdb\.ext\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.cn$|^https:\/\/main\.documentdb\.ext\.azure\.us$/,
|
allowedParentFrameOrigins: /^https:\/\/portal\.azure\.com$|^https:\/\/portal\.azure\.us$|^https:\/\/portal\.azure\.cn$|^https:\/\/portal\.microsoftazure\.de$|^https:\/\/.+\.portal\.azure\.com$|^https:\/\/.+\.portal\.azure\.us$|^https:\/\/.+\.portal\.azure\.cn$|^https:\/\/.+\.portal\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.com$|^https:\/\/main\.documentdb\.ext\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.cn$|^https:\/\/main\.documentdb\.ext\.azure\.us$/,
|
||||||
// Webpack injects this at build time
|
// Webpack injects this at build time
|
||||||
@@ -43,61 +44,40 @@ let configContext: Readonly<ConfigContext> = {
|
|||||||
ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net",
|
ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net",
|
||||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
||||||
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
|
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
|
||||||
JUNO_ENDPOINT: "https://tools.cosmos.azure.com"
|
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
|
||||||
|
AZURESAMPLESCOSMOSDBPAT: "99e38770e29b4a61d7c49f188780504efd35cc86", //[SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification=" // this PAT is a "no scopes" PAT with zero access to any projects, this is just used to get around the dev.github.com rate limit when accessing public samples repo.")]
|
||||||
};
|
};
|
||||||
|
|
||||||
export function resetConfigContext(): void {
|
|
||||||
if (process.env.NODE_ENV !== "test") {
|
|
||||||
throw new Error("resetConfigContext can only becalled in a test environment");
|
|
||||||
}
|
|
||||||
configContext = {} as ConfigContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateConfigContext(newContext: Partial<ConfigContext>): void {
|
|
||||||
Object.assign(configContext, newContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Injected for local develpment. These will be removed in the production bundle by webpack
|
// Injected for local develpment. These will be removed in the production bundle by webpack
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
const port: string = process.env.PORT || "1234";
|
const port: string = process.env.PORT || "1234";
|
||||||
updateConfigContext({
|
config.BACKEND_ENDPOINT = "https://localhost:" + port;
|
||||||
BACKEND_ENDPOINT: "https://localhost:" + port,
|
config.MONGO_BACKEND_ENDPOINT = "https://localhost:" + port;
|
||||||
MONGO_BACKEND_ENDPOINT: "https://localhost:" + port,
|
config.PROXY_PATH = "/proxy";
|
||||||
PROXY_PATH: "/proxy",
|
config.EMULATOR_ENDPOINT = "https://localhost:8081";
|
||||||
EMULATOR_ENDPOINT: "https://localhost:8081"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initializeConfiguration(): Promise<ConfigContext> {
|
export async function initializeConfiguration(): Promise<Config> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("./config.json");
|
const response = await fetch("./config.json");
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
try {
|
try {
|
||||||
const externalConfig = await response.json();
|
const externalConfig = await response.json();
|
||||||
Object.assign(configContext, externalConfig);
|
config = Object.assign({}, config, externalConfig);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Unable to parse json in config file");
|
console.error("Unable to parse json in config file");
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Allow override of platform value with URL query parameter
|
// Allow override of any config value with URL query parameters
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
if (params.has("platform")) {
|
params.forEach((value, key) => {
|
||||||
const platform = params.get("platform");
|
(config as any)[key] = value;
|
||||||
switch (platform) {
|
});
|
||||||
default:
|
|
||||||
console.log("Invalid platform query parameter given, ignoring");
|
|
||||||
break;
|
|
||||||
case Platform.Portal:
|
|
||||||
case Platform.Hosted:
|
|
||||||
case Platform.Emulator:
|
|
||||||
updateConfigContext({ platform });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("No configuration file found using defaults");
|
console.log("No configuration file found using defaults");
|
||||||
}
|
}
|
||||||
return configContext;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { configContext };
|
export { config };
|
||||||
@@ -7,7 +7,7 @@ export enum TabKind {
|
|||||||
TableEntities,
|
TableEntities,
|
||||||
Graph,
|
Graph,
|
||||||
SQLQuery,
|
SQLQuery,
|
||||||
ScaleSettings
|
ScaleSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +20,7 @@ export enum PaneKind {
|
|||||||
DeleteDatabase,
|
DeleteDatabase,
|
||||||
GlobalSettings,
|
GlobalSettings,
|
||||||
AdHocAccess,
|
AdHocAccess,
|
||||||
SwitchDirectory
|
SwitchDirectory,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,5 +79,5 @@ export enum ActionType {
|
|||||||
OpenCollectionTab,
|
OpenCollectionTab,
|
||||||
OpenPane,
|
OpenPane,
|
||||||
TransmitCachedData,
|
TransmitCachedData,
|
||||||
OpenSampleNotebook
|
OpenSampleNotebook,
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ export enum LogEntryLevel {
|
|||||||
/**
|
/**
|
||||||
* Error level.
|
* Error level.
|
||||||
*/
|
*/
|
||||||
Error = 2
|
Error = 2,
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Schema of a log entry.
|
* Schema of a log entry.
|
||||||
@@ -46,7 +46,7 @@ export interface LogEntry {
|
|||||||
/**
|
/**
|
||||||
* The message code.
|
* The message code.
|
||||||
*/
|
*/
|
||||||
code?: number;
|
code: number;
|
||||||
/**
|
/**
|
||||||
* Any additional data to be logged.
|
* Any additional data to be logged.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
import * as Versions from "./Versions";
|
import * as Versions from "./Versions";
|
||||||
import * as ActionContracts from "./ActionContracts";
|
import * as ActionContracts from "./ActionContracts";
|
||||||
import * as Diagnostics from "./Diagnostics";
|
import * as Diagnostics from "./Diagnostics";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Messaging types used with Data Explorer <-> Portal communication
|
* Messaging types used with Data Explorer <-> Portal communication
|
||||||
* and Hosted <-> Explorer communication
|
* and Hosted <-> Explorer communication
|
||||||
*/
|
*/
|
||||||
export enum MessageTypes {
|
export enum MessageTypes {
|
||||||
TelemetryInfo,
|
TelemetryInfo,
|
||||||
LogInfo,
|
LogInfo,
|
||||||
RefreshResources,
|
RefreshResources,
|
||||||
AllDatabases,
|
AllDatabases,
|
||||||
CollectionsForDatabase,
|
CollectionsForDatabase,
|
||||||
RefreshOffers,
|
RefreshOffers,
|
||||||
AllOffers,
|
AllOffers,
|
||||||
UpdateLocationHash,
|
UpdateLocationHash,
|
||||||
SingleOffer,
|
SingleOffer,
|
||||||
RefreshOffer,
|
RefreshOffer,
|
||||||
UpdateAccountName,
|
UpdateAccountName,
|
||||||
ForbiddenError,
|
ForbiddenError,
|
||||||
AadSignIn,
|
AadSignIn,
|
||||||
GetAccessAadRequest,
|
GetAccessAadRequest,
|
||||||
GetAccessAadResponse,
|
GetAccessAadResponse,
|
||||||
UpdateAccountSwitch,
|
UpdateAccountSwitch,
|
||||||
UpdateDirectoryControl,
|
UpdateDirectoryControl,
|
||||||
SwitchAccount,
|
SwitchAccount,
|
||||||
SendNotification,
|
SendNotification,
|
||||||
ClearNotification,
|
ClearNotification,
|
||||||
ExplorerClickEvent,
|
ExplorerClickEvent,
|
||||||
LoadingStatus,
|
LoadingStatus,
|
||||||
GetArcadiaToken,
|
GetArcadiaToken,
|
||||||
CreateWorkspace,
|
CreateWorkspace,
|
||||||
CreateSparkPool,
|
CreateSparkPool,
|
||||||
RefreshDatabaseAccount
|
RefreshDatabaseAccount,
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Versions, ActionContracts, Diagnostics };
|
export { Versions, ActionContracts, Diagnostics };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/**
|
||||||
* Data Explorer version {major.minor.patch}
|
* Data Explorer version {major.minor.patch}
|
||||||
*/
|
*/
|
||||||
export const DataExplorer: string = "1.0.1";
|
export const DataExplorer: string = "1.0.1";
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user