Test Explorer Improvements (#541)
This commit is contained in:
parent
f86883de6c
commit
254c551999
|
@ -15,10 +15,10 @@ jobs:
|
|||
if: github.ref == 'refs/heads/master'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.x
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: node utils/codeMetrics.js
|
||||
env:
|
||||
|
@ -28,10 +28,10 @@ jobs:
|
|||
name: "Compile TypeScript"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.x
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run compile
|
||||
- run: npm run compile:strict
|
||||
|
@ -40,10 +40,10 @@ jobs:
|
|||
name: "Check Format"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.x
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run format:check
|
||||
lint:
|
||||
|
@ -51,10 +51,10 @@ jobs:
|
|||
name: "Lint"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.x
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
unittest:
|
||||
|
@ -62,10 +62,10 @@ jobs:
|
|||
name: "Unit Tests"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.x
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run test
|
||||
build:
|
||||
|
@ -74,10 +74,10 @@ jobs:
|
|||
name: "Build"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.x
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run build:contracts
|
||||
- name: Restore Build Cache
|
||||
|
@ -98,10 +98,10 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.x
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- uses: southpolesteve/cosmos-emulator-github-action@v1
|
||||
- name: End to End Tests
|
||||
run: |
|
||||
|
@ -125,10 +125,10 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.x
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 14.x
|
||||
- name: Accessibility Check
|
||||
run: |
|
||||
# Ubuntu gets mad when webpack runs too many files watchers
|
||||
|
@ -163,6 +163,7 @@ jobs:
|
|||
TABLES_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_TABLE }}
|
||||
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/hostedExplorer.html"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test-file:
|
||||
- ./test/cassandra/container.spec.ts
|
||||
|
|
|
@ -949,14 +949,20 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-class-properties": {
|
||||
"version": "7.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz",
|
||||
"integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==",
|
||||
"version": "7.12.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
|
||||
"integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.10.4"
|
||||
"@babel/helper-plugin-utils": "^7.12.13"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-class-properties/node_modules/@babel/helper-plugin-utils": {
|
||||
"version": "7.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz",
|
||||
"integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-decorators": {
|
||||
"version": "7.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz",
|
||||
|
@ -1951,9 +1957,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@istanbuljs/schema": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
|
||||
"integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==",
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
|
||||
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
@ -2127,9 +2133,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@jest/globals/node_modules/@types/yargs": {
|
||||
"version": "15.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz",
|
||||
"integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==",
|
||||
"version": "15.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz",
|
||||
"integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
|
@ -4908,9 +4914,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/graceful-fs": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz",
|
||||
"integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==",
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
|
||||
"integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
|
@ -15439,9 +15445,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jest/node_modules/@types/yargs": {
|
||||
"version": "15.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz",
|
||||
"integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==",
|
||||
"version": "15.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz",
|
||||
"integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
|
@ -15756,9 +15762,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jest/node_modules/fsevents": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz",
|
||||
"integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -16751,9 +16757,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jest/node_modules/string-width": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
|
||||
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
|
@ -22760,11 +22766,6 @@
|
|||
"safer-buffer": "^2.0.2",
|
||||
"tweetnacl": "~0.14.0"
|
||||
},
|
||||
"bin": {
|
||||
"sshpk-conv": "bin/sshpk-conv",
|
||||
"sshpk-sign": "bin/sshpk-sign",
|
||||
"sshpk-verify": "bin/sshpk-verify"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
@ -25098,8 +25099,7 @@
|
|||
"node_modules/webcrypto-liner/node_modules/core-js": {
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.3.tgz",
|
||||
"integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==",
|
||||
"hasInstallScript": true
|
||||
"integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q=="
|
||||
},
|
||||
"node_modules/webfontloader": {
|
||||
"version": "1.6.28",
|
||||
|
@ -27019,12 +27019,20 @@
|
|||
}
|
||||
},
|
||||
"@babel/plugin-syntax-class-properties": {
|
||||
"version": "7.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz",
|
||||
"integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==",
|
||||
"version": "7.12.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
|
||||
"integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.10.4"
|
||||
"@babel/helper-plugin-utils": "^7.12.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": {
|
||||
"version": "7.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz",
|
||||
"integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/plugin-syntax-decorators": {
|
||||
|
@ -27993,9 +28001,9 @@
|
|||
}
|
||||
},
|
||||
"@istanbuljs/schema": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
|
||||
"integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==",
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
|
||||
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
|
||||
"dev": true
|
||||
},
|
||||
"@jest/console": {
|
||||
|
@ -28135,9 +28143,9 @@
|
|||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "15.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz",
|
||||
"integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==",
|
||||
"version": "15.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz",
|
||||
"integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
|
@ -30752,9 +30760,9 @@
|
|||
}
|
||||
},
|
||||
"@types/graceful-fs": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz",
|
||||
"integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==",
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
|
||||
"integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
|
@ -39218,9 +39226,9 @@
|
|||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "15.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz",
|
||||
"integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==",
|
||||
"version": "15.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz",
|
||||
"integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
|
@ -39473,9 +39481,9 @@
|
|||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz",
|
||||
"integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
|
@ -40261,9 +40269,9 @@
|
|||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
|
||||
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import * as Cosmos from "@azure/cosmos";
|
||||
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
|
||||
import { configContext, Platform } from "../ConfigContext";
|
||||
import { getErrorMessage } from "./ErrorHandlingUtils";
|
||||
import { userContext } from "../UserContext";
|
||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getErrorMessage } from "./ErrorHandlingUtils";
|
||||
|
||||
const _global = typeof self === "undefined" ? window : self;
|
||||
|
||||
|
|
|
@ -775,14 +775,8 @@ export default class Explorer {
|
|||
$(document.body).click(() => $(".commandDropdownContainer").hide());
|
||||
});
|
||||
|
||||
// TODO move this to API customization class
|
||||
this.defaultExperience.subscribe((defaultExperience) => {
|
||||
const defaultExperienceNormalizedString = (
|
||||
defaultExperience || Constants.DefaultAccountExperience.Default
|
||||
).toLowerCase();
|
||||
|
||||
switch (defaultExperienceNormalizedString) {
|
||||
case Constants.DefaultAccountExperience.DocumentDB.toLowerCase():
|
||||
switch (userContext.apiType) {
|
||||
case "SQL":
|
||||
this.addCollectionText("New Container");
|
||||
this.addDatabaseText("New Database");
|
||||
this.collectionTitle("SQL API");
|
||||
|
@ -798,8 +792,7 @@ export default class Explorer {
|
|||
this.deleteCollectionConfirmationPane.collectionIdConfirmationText("Confirm by typing the container id");
|
||||
this.refreshTreeTitle("Refresh containers");
|
||||
break;
|
||||
case Constants.DefaultAccountExperience.MongoDB.toLowerCase():
|
||||
case Constants.DefaultAccountExperience.ApiForMongoDB.toLowerCase():
|
||||
case "Mongo":
|
||||
this.addCollectionText("New Collection");
|
||||
this.addDatabaseText("New Database");
|
||||
this.collectionTitle("Collections");
|
||||
|
@ -813,7 +806,7 @@ export default class Explorer {
|
|||
);
|
||||
this.refreshTreeTitle("Refresh collections");
|
||||
break;
|
||||
case Constants.DefaultAccountExperience.Graph.toLowerCase():
|
||||
case "Gremlin":
|
||||
this.addCollectionText("New Graph");
|
||||
this.addDatabaseText("New Database");
|
||||
this.deleteCollectionText("Delete Graph");
|
||||
|
@ -827,7 +820,7 @@ export default class Explorer {
|
|||
this.deleteCollectionConfirmationPane.collectionIdConfirmationText("Confirm by typing the graph id");
|
||||
this.refreshTreeTitle("Refresh graphs");
|
||||
break;
|
||||
case Constants.DefaultAccountExperience.Table.toLowerCase():
|
||||
case "Tables":
|
||||
this.addCollectionText("New Table");
|
||||
this.addDatabaseText("New Database");
|
||||
this.deleteCollectionText("Delete Table");
|
||||
|
@ -844,7 +837,7 @@ export default class Explorer {
|
|||
this.deleteCollectionConfirmationPane.collectionIdConfirmationText("Confirm by typing the table id");
|
||||
this.tableDataClient = new TablesAPIDataClient();
|
||||
break;
|
||||
case Constants.DefaultAccountExperience.Cassandra.toLowerCase():
|
||||
case "Cassandra":
|
||||
this.addCollectionText("New Table");
|
||||
this.addDatabaseText("New Keyspace");
|
||||
this.deleteCollectionText("Delete Table");
|
||||
|
@ -864,7 +857,6 @@ export default class Explorer {
|
|||
this.tableDataClient = new CassandraAPIDataClient();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
this.commandBarComponentAdapter = new CommandBarComponentAdapter(this);
|
||||
|
||||
|
|
149
src/Main.tsx
149
src/Main.tsx
|
@ -1,74 +1,70 @@
|
|||
// CSS Dependencies
|
||||
import "abort-controller/polyfill";
|
||||
import "babel-polyfill";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import "../less/documentDB.less";
|
||||
import "../less/tree.less";
|
||||
import "../less/forms.less";
|
||||
import "../less/menus.less";
|
||||
import "../less/infobox.less";
|
||||
import "../less/messagebox.less";
|
||||
import "./Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.less";
|
||||
import "./Explorer/Menus/NotificationConsole/NotificationConsole.less";
|
||||
import "./Explorer/Menus/CommandBar/CommandBarComponent.less";
|
||||
import "./Explorer/Menus/CommandBar/MemoryTrackerComponent.less";
|
||||
import "./Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.less";
|
||||
import "./Explorer/Controls/DynamicList/DynamicListComponent.less";
|
||||
import "./Explorer/Controls/JsonEditor/JsonEditorComponent.less";
|
||||
import "./Explorer/Graph/GraphExplorerComponent/graphExplorer.less";
|
||||
import "./Explorer/Panes/PanelComponent.less";
|
||||
import "../less/TableStyles/queryBuilder.less";
|
||||
import "../externals/jquery.dataTables.min.css";
|
||||
import "../less/TableStyles/fulldatatables.less";
|
||||
import "../less/TableStyles/EntityEditor.less";
|
||||
import "../less/TableStyles/CustomizeColumns.less";
|
||||
import "../less/resourceTree.less";
|
||||
import "../externals/jquery.typeahead.min.css";
|
||||
import "es6-object-assign/auto";
|
||||
import "es6-symbol/implement";
|
||||
import "object.entries/auto";
|
||||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||
import "promise-polyfill/src/polyfill";
|
||||
import "promise.prototype.finally/auto";
|
||||
import React, { useState } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "url-polyfill/url-polyfill.min";
|
||||
import "webcrypto-liner/build/webcrypto-liner.shim.min";
|
||||
import "whatwg-fetch";
|
||||
import "../externals/jquery-ui.min.css";
|
||||
import "../externals/jquery-ui.min.js";
|
||||
import "../externals/jquery-ui.structure.min.css";
|
||||
import "../externals/jquery-ui.theme.min.css";
|
||||
import "./Explorer/Graph/NewVertexComponent/newVertexComponent.less";
|
||||
import "./Explorer/Panes/GraphNewVertexPane.less";
|
||||
import "./Explorer/Tabs/QueryTab.less";
|
||||
import "./Explorer/Controls/TreeComponent/treeComponent.less";
|
||||
import "./Explorer/Controls/Accordion/AccordionComponent.less";
|
||||
import "./Explorer/SplashScreen/SplashScreen.less";
|
||||
import "./Explorer/Controls/Notebook/NotebookTerminalComponent.less";
|
||||
|
||||
import "../externals/jquery.dataTables.min.css";
|
||||
import "../externals/jquery.typeahead.min.css";
|
||||
import "../externals/jquery.typeahead.min.js";
|
||||
// Image Dependencies
|
||||
import "../images/CosmosDB_rgb_ui_lighttheme.ico";
|
||||
import "../images/favicon.ico";
|
||||
|
||||
import "./Shared/appInsights";
|
||||
import "babel-polyfill";
|
||||
import "es6-symbol/implement";
|
||||
import "webcrypto-liner/build/webcrypto-liner.shim.min";
|
||||
import "./Libs/jquery";
|
||||
import "bootstrap/dist/js/npm";
|
||||
import "../externals/jquery.typeahead.min.js";
|
||||
import "../externals/jquery-ui.min.js";
|
||||
import "promise-polyfill/src/polyfill";
|
||||
import "abort-controller/polyfill";
|
||||
import "whatwg-fetch";
|
||||
import "es6-object-assign/auto";
|
||||
import "promise.prototype.finally/auto";
|
||||
import "object.entries/auto";
|
||||
import "./Libs/is-integer-polyfill";
|
||||
import "url-polyfill/url-polyfill.min";
|
||||
|
||||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||
import { ExplorerParams } from "./Explorer/Explorer";
|
||||
import React, { useState } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import hdeConnectImage from "../images/HdeConnectCosmosDB.svg";
|
||||
import refreshImg from "../images/refresh-cosmos.svg";
|
||||
import arrowLeftImg from "../images/imgarrowlefticon.svg";
|
||||
import { KOCommentEnd, KOCommentIfStart } from "./koComment";
|
||||
import refreshImg from "../images/refresh-cosmos.svg";
|
||||
import "../less/documentDB.less";
|
||||
import "../less/forms.less";
|
||||
import "../less/infobox.less";
|
||||
import "../less/menus.less";
|
||||
import "../less/messagebox.less";
|
||||
import "../less/resourceTree.less";
|
||||
import "../less/TableStyles/CustomizeColumns.less";
|
||||
import "../less/TableStyles/EntityEditor.less";
|
||||
import "../less/TableStyles/fulldatatables.less";
|
||||
import "../less/TableStyles/queryBuilder.less";
|
||||
import "../less/tree.less";
|
||||
import "./Explorer/Controls/Accordion/AccordionComponent.less";
|
||||
import "./Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.less";
|
||||
import { Dialog, DialogProps } from "./Explorer/Controls/Dialog";
|
||||
import "./Explorer/Controls/DynamicList/DynamicListComponent.less";
|
||||
import "./Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.less";
|
||||
import "./Explorer/Controls/JsonEditor/JsonEditorComponent.less";
|
||||
import "./Explorer/Controls/Notebook/NotebookTerminalComponent.less";
|
||||
import "./Explorer/Controls/TreeComponent/treeComponent.less";
|
||||
import { ExplorerParams } from "./Explorer/Explorer";
|
||||
import "./Explorer/Graph/GraphExplorerComponent/graphExplorer.less";
|
||||
import "./Explorer/Graph/NewVertexComponent/newVertexComponent.less";
|
||||
import "./Explorer/Menus/CommandBar/CommandBarComponent.less";
|
||||
import "./Explorer/Menus/CommandBar/MemoryTrackerComponent.less";
|
||||
import "./Explorer/Menus/NotificationConsole/NotificationConsole.less";
|
||||
import { NotificationConsoleComponent } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import "./Explorer/Panes/GraphNewVertexPane.less";
|
||||
import "./Explorer/Panes/PanelComponent.less";
|
||||
import { PanelContainerComponent } from "./Explorer/Panes/PanelContainerComponent";
|
||||
import { SplashScreen } from "./Explorer/SplashScreen/SplashScreen";
|
||||
import "./Explorer/SplashScreen/SplashScreen.less";
|
||||
import "./Explorer/Tabs/QueryTab.less";
|
||||
import { useConfig } from "./hooks/useConfig";
|
||||
import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
|
||||
import { useSidePanel } from "./hooks/useSidePanel";
|
||||
import { NotificationConsoleComponent } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import { PanelContainerComponent } from "./Explorer/Panes/PanelContainerComponent";
|
||||
import { SplashScreen } from "./Explorer/SplashScreen/SplashScreen";
|
||||
import { Dialog, DialogProps } from "./Explorer/Controls/Dialog";
|
||||
import { KOCommentEnd, KOCommentIfStart } from "./koComment";
|
||||
import "./Libs/is-integer-polyfill";
|
||||
import "./Libs/jquery";
|
||||
import "./Shared/appInsights";
|
||||
|
||||
initializeIcons();
|
||||
|
||||
|
@ -103,6 +99,10 @@ const App: React.FunctionComponent = () => {
|
|||
const config = useConfig();
|
||||
const explorer = useKnockoutExplorer(config?.platform, explorerParams);
|
||||
|
||||
if (!explorer) {
|
||||
return <LoadingExplorer />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flexContainer">
|
||||
<div id="divExplorer" className="flexContainer hideOverflows" style={{ display: "none" }}>
|
||||
|
@ -236,21 +236,6 @@ const App: React.FunctionComponent = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* Global loader - Start */}
|
||||
<div className="splashLoaderContainer" data-bind="visible: isRefreshingExplorer">
|
||||
<div className="splashLoaderContentContainer">
|
||||
<p className="connectExplorerContent">
|
||||
<img src={hdeConnectImage} alt="Azure Cosmos DB" />
|
||||
</p>
|
||||
<p className="splashLoaderTitle" id="explorerLoadingStatusTitle">
|
||||
Welcome to Azure Cosmos DB
|
||||
</p>
|
||||
<p className="splashLoaderText" id="explorerLoadingStatusText" role="alert">
|
||||
Connecting...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* Global loader - End */}
|
||||
<PanelContainerComponent
|
||||
isOpen={isPanelOpen}
|
||||
panelContent={panelContent}
|
||||
|
@ -294,3 +279,21 @@ const App: React.FunctionComponent = () => {
|
|||
};
|
||||
|
||||
ReactDOM.render(<App />, document.body);
|
||||
|
||||
function LoadingExplorer(): JSX.Element {
|
||||
return (
|
||||
<div className="splashLoaderContainer">
|
||||
<div className="splashLoaderContentContainer">
|
||||
<p className="connectExplorerContent">
|
||||
<img src={hdeConnectImage} alt="Azure Cosmos DB" />
|
||||
</p>
|
||||
<p className="splashLoaderTitle" id="explorerLoadingStatusTitle">
|
||||
Welcome to Azure Cosmos DB
|
||||
</p>
|
||||
<p className="splashLoaderText" id="explorerLoadingStatusText" role="alert">
|
||||
Connecting...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { applyExplorerBindings } from "../applyExplorerBindings";
|
||||
import { AuthType } from "../AuthType";
|
||||
import { AccountKind, DefaultAccountExperience } from "../Common/Constants";
|
||||
|
@ -32,53 +32,65 @@ import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
|
|||
// This hook will create a new instance of Explorer.ts and bind it to the DOM
|
||||
// This hook has a LOT of magic, but ideally we can delete it once we have removed KO and switched entirely to React
|
||||
// Pleas tread carefully :)
|
||||
let explorer: Explorer;
|
||||
|
||||
export function useKnockoutExplorer(platform: Platform, explorerParams: ExplorerParams): Explorer {
|
||||
explorer = explorer || new Explorer(explorerParams);
|
||||
const [explorer, setExplorer] = useState<Explorer>();
|
||||
|
||||
useEffect(() => {
|
||||
const effect = async () => {
|
||||
if (platform) {
|
||||
if (platform === Platform.Hosted) {
|
||||
await configureHosted();
|
||||
applyExplorerBindings(explorer);
|
||||
const explorer = await configureHosted(explorerParams);
|
||||
setExplorer(explorer);
|
||||
} else if (platform === Platform.Emulator) {
|
||||
configureEmulator();
|
||||
applyExplorerBindings(explorer);
|
||||
const explorer = configureEmulator(explorerParams);
|
||||
setExplorer(explorer);
|
||||
} else if (platform === Platform.Portal) {
|
||||
configurePortal();
|
||||
const explorer = await configurePortal(explorerParams);
|
||||
setExplorer(explorer);
|
||||
}
|
||||
}
|
||||
};
|
||||
effect();
|
||||
}, [platform]);
|
||||
|
||||
useEffect(() => {
|
||||
if (explorer) {
|
||||
applyExplorerBindings(explorer);
|
||||
}
|
||||
}, [explorer]);
|
||||
|
||||
return explorer;
|
||||
}
|
||||
|
||||
async function configureHosted() {
|
||||
async function configureHosted(explorerParams: ExplorerParams): Promise<Explorer> {
|
||||
const win = (window as unknown) as HostedExplorerChildFrame;
|
||||
if (win.hostedConfig.authType === AuthType.EncryptedToken) {
|
||||
configureHostedWithEncryptedToken(win.hostedConfig);
|
||||
return configureHostedWithEncryptedToken(win.hostedConfig, explorerParams);
|
||||
} else if (win.hostedConfig.authType === AuthType.ResourceToken) {
|
||||
configureHostedWithResourceToken(win.hostedConfig);
|
||||
return configureHostedWithResourceToken(win.hostedConfig, explorerParams);
|
||||
} else if (win.hostedConfig.authType === AuthType.ConnectionString) {
|
||||
configureHostedWithConnectionString(win.hostedConfig);
|
||||
return configureHostedWithConnectionString(win.hostedConfig, explorerParams);
|
||||
} else if (win.hostedConfig.authType === AuthType.AAD) {
|
||||
await configureHostedWithAAD(win.hostedConfig);
|
||||
return configureHostedWithAAD(win.hostedConfig, explorerParams);
|
||||
}
|
||||
throw new Error(`Unknown hosted config: ${win.hostedConfig}`);
|
||||
}
|
||||
|
||||
async function configureHostedWithAAD(config: AAD) {
|
||||
async function configureHostedWithAAD(config: AAD, explorerParams: ExplorerParams): Promise<Explorer> {
|
||||
const account = config.databaseAccount;
|
||||
const accountResourceId = account.id;
|
||||
const subscriptionId = accountResourceId && accountResourceId.split("subscriptions/")[1].split("/")[0];
|
||||
const resourceGroup = accountResourceId && accountResourceId.split("resourceGroups/")[1].split("/")[0];
|
||||
updateUserContext({
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
authType: AuthType.AAD,
|
||||
authorizationToken: `Bearer ${config.authorizationToken}`,
|
||||
databaseAccount: config.databaseAccount,
|
||||
});
|
||||
const keys = await listKeys(subscriptionId, resourceGroup, account.name);
|
||||
const explorer = new Explorer(explorerParams);
|
||||
explorer.configure({
|
||||
databaseAccount: account,
|
||||
subscriptionId,
|
||||
|
@ -87,9 +99,10 @@ async function configureHostedWithAAD(config: AAD) {
|
|||
authorizationToken: `Bearer ${config.authorizationToken}`,
|
||||
features: extractFeatures(),
|
||||
});
|
||||
return explorer;
|
||||
}
|
||||
|
||||
function configureHostedWithConnectionString(config: ConnectionString) {
|
||||
function configureHostedWithConnectionString(config: ConnectionString, explorerParams: ExplorerParams): Explorer {
|
||||
const apiExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(config.encryptedTokenMetadata.apiKind);
|
||||
const databaseAccount = {
|
||||
id: "",
|
||||
|
@ -106,14 +119,16 @@ function configureHostedWithConnectionString(config: ConnectionString) {
|
|||
accessToken: encodeURIComponent(config.encryptedToken),
|
||||
databaseAccount,
|
||||
});
|
||||
const explorer = new Explorer(explorerParams);
|
||||
explorer.configure({
|
||||
databaseAccount,
|
||||
masterKey: config.masterKey,
|
||||
features: extractFeatures(),
|
||||
});
|
||||
return explorer;
|
||||
}
|
||||
|
||||
function configureHostedWithResourceToken(config: ResourceToken) {
|
||||
function configureHostedWithResourceToken(config: ResourceToken, explorerParams: ExplorerParams): Explorer {
|
||||
const parsedResourceToken = parseResourceTokenConnectionString(config.resourceToken);
|
||||
const databaseAccount = {
|
||||
id: "",
|
||||
|
@ -131,6 +146,7 @@ function configureHostedWithResourceToken(config: ResourceToken) {
|
|||
resourceToken: parsedResourceToken.resourceToken,
|
||||
endpoint: parsedResourceToken.accountEndpoint,
|
||||
});
|
||||
const explorer = new Explorer(explorerParams);
|
||||
explorer.resourceTokenDatabaseId(parsedResourceToken.databaseId);
|
||||
explorer.resourceTokenCollectionId(parsedResourceToken.collectionId);
|
||||
if (parsedResourceToken.partitionKey) {
|
||||
|
@ -142,9 +158,10 @@ function configureHostedWithResourceToken(config: ResourceToken) {
|
|||
isAuthWithresourceToken: true,
|
||||
});
|
||||
explorer.isRefreshingExplorer(false);
|
||||
return explorer;
|
||||
}
|
||||
|
||||
function configureHostedWithEncryptedToken(config: EncryptedToken) {
|
||||
function configureHostedWithEncryptedToken(config: EncryptedToken, explorerParams: ExplorerParams): Explorer {
|
||||
updateUserContext({
|
||||
authType: AuthType.EncryptedToken,
|
||||
accessToken: encodeURIComponent(config.encryptedToken),
|
||||
|
@ -152,6 +169,7 @@ function configureHostedWithEncryptedToken(config: EncryptedToken) {
|
|||
const apiExperience: string = DefaultExperienceUtility.getDefaultExperienceFromApiKind(
|
||||
config.encryptedTokenMetadata.apiKind
|
||||
);
|
||||
const explorer = new Explorer(explorerParams);
|
||||
explorer.configure({
|
||||
databaseAccount: {
|
||||
id: "",
|
||||
|
@ -162,21 +180,25 @@ function configureHostedWithEncryptedToken(config: EncryptedToken) {
|
|||
},
|
||||
features: extractFeatures(),
|
||||
});
|
||||
return explorer;
|
||||
}
|
||||
|
||||
function configureEmulator() {
|
||||
function configureEmulator(explorerParams: ExplorerParams): Explorer {
|
||||
updateUserContext({
|
||||
databaseAccount: emulatorAccount,
|
||||
authType: AuthType.MasterKey,
|
||||
});
|
||||
const explorer = new Explorer(explorerParams);
|
||||
explorer.databaseAccount(emulatorAccount);
|
||||
explorer.isAccountReady(true);
|
||||
return explorer;
|
||||
}
|
||||
|
||||
function configurePortal() {
|
||||
async function configurePortal(explorerParams: ExplorerParams): Promise<Explorer> {
|
||||
updateUserContext({
|
||||
authType: AuthType.AAD,
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
// In development mode, try to load the iframe message from session storage.
|
||||
// This allows webpack hot reload to function properly in the portal
|
||||
if (process.env.NODE_ENV === "development" && !window.location.search.includes("disablePortalInitCache")) {
|
||||
|
@ -187,8 +209,9 @@ function configurePortal() {
|
|||
"Loaded cached portal iframe message from session storage. Do a full page refresh to get a new message"
|
||||
);
|
||||
console.dir(message);
|
||||
const explorer = new Explorer(explorerParams);
|
||||
explorer.configure(message);
|
||||
applyExplorerBindings(explorer);
|
||||
resolve(explorer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,8 +259,9 @@ function configurePortal() {
|
|||
quotaId: inputs.quotaId,
|
||||
});
|
||||
|
||||
const explorer = new Explorer(explorerParams);
|
||||
explorer.configure(inputs);
|
||||
applyExplorerBindings(explorer);
|
||||
resolve(explorer);
|
||||
if (openAction) {
|
||||
handleOpenAction(openAction, explorer.nonSystemDatabases(), explorer);
|
||||
}
|
||||
|
@ -247,6 +271,7 @@ function configurePortal() {
|
|||
);
|
||||
|
||||
sendMessage("ready");
|
||||
});
|
||||
}
|
||||
|
||||
function shouldProcessMessage(event: MessageEvent): boolean {
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import "expect-puppeteer";
|
||||
import { getTestExplorerFrame } from "../testExplorer/TestExplorerUtils";
|
||||
import { createDatabase, onClickSaveButton } from "../utils/shared";
|
||||
import { generateUniqueName } from "../utils/shared";
|
||||
import { ApiKind } from "../../src/Contracts/DataModels";
|
||||
import { createDatabase, generateUniqueName, onClickSaveButton } from "../utils/shared";
|
||||
|
||||
const LOADING_STATE_DELAY = 5000;
|
||||
jest.setTimeout(300000);
|
||||
|
@ -12,7 +9,9 @@ describe("MongoDB Index policy tests", () => {
|
|||
try {
|
||||
const singleFieldId = generateUniqueName("key");
|
||||
const wildCardId = generateUniqueName("key") + "$**";
|
||||
const frame = await getTestExplorerFrame(ApiKind.MongoDB);
|
||||
await page.goto("https://localhost:1234/testExplorer.html?accountName=portal-mongo-runner");
|
||||
const handle = await page.waitForSelector("iframe");
|
||||
const frame = await handle.contentFrame();
|
||||
const dropDown = "Index Type ";
|
||||
let index = 0;
|
||||
|
||||
|
@ -20,24 +19,18 @@ describe("MongoDB Index policy tests", () => {
|
|||
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
||||
await frame.waitFor(LOADING_STATE_DELAY);
|
||||
await frame.waitForSelector('div[class="splashScreen"] > div[class="title"]', { visible: true });
|
||||
const dbId = await createDatabase(frame);
|
||||
const { databaseId, collectionId } = await createDatabase(frame);
|
||||
await frame.waitFor(25000);
|
||||
// click on database
|
||||
await frame.waitForSelector(`div[data-test="${dbId}"]`);
|
||||
await frame.waitForSelector(`div[data-test="${databaseId}"]`);
|
||||
await frame.waitFor(LOADING_STATE_DELAY);
|
||||
await frame.click(`div[data-test="${dbId}"]`);
|
||||
await frame.click(`div[data-test="${databaseId}"]`);
|
||||
await frame.waitFor(LOADING_STATE_DELAY);
|
||||
|
||||
// click on scale & setting
|
||||
const containers = await frame.$$(
|
||||
`div[class="nodeChildren"] > div[class="collectionHeader main2 nodeItem "]> div[class="treeNodeHeader "]`
|
||||
);
|
||||
const selectedContainer = (await frame.evaluate((element) => element.innerText, containers[0]))
|
||||
.replace(/[\u{0080}-\u{FFFF}]/gu, "")
|
||||
.trim();
|
||||
await frame.waitFor(`div[data-test="${selectedContainer}"]`), { visible: true };
|
||||
await frame.waitFor(`div[data-test="${collectionId}"]`), { visible: true };
|
||||
await frame.waitFor(LOADING_STATE_DELAY);
|
||||
await frame.click(`div[data-test="${selectedContainer}"]`);
|
||||
await frame.click(`div[data-test="${collectionId}"]`);
|
||||
|
||||
await frame.waitFor(`div[data-test="Scale & Settings"]`), { visible: true };
|
||||
await frame.waitFor(LOADING_STATE_DELAY);
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
import { uploadNotebookIfNotExist } from "./notebookTestUtils";
|
||||
import { ElementHandle, Frame } from "puppeteer";
|
||||
import { getTestExplorerFrame } from "../testExplorer/TestExplorerUtils";
|
||||
|
||||
jest.setTimeout(300000);
|
||||
|
||||
const notebookName = "GettingStarted.ipynb";
|
||||
let frame: Frame;
|
||||
let uploadedNotebookNode: ElementHandle<Element>;
|
||||
|
||||
describe("Notebook UI tests", () => {
|
||||
it("Upload, Open and Delete Notebook", async () => {
|
||||
try {
|
||||
frame = await getTestExplorerFrame();
|
||||
await page.goto("https://localhost:1234/testExplorer.html");
|
||||
const handle = await page.waitForSelector("iframe");
|
||||
const frame = await handle.contentFrame();
|
||||
await frame.waitForSelector(".galleryHeader");
|
||||
uploadedNotebookNode = await uploadNotebookIfNotExist(frame, notebookName);
|
||||
const uploadedNotebookNode = await uploadNotebookIfNotExist(frame, notebookName);
|
||||
await uploadedNotebookNode.click();
|
||||
await frame.waitForSelector(".tabNavText");
|
||||
const tabTitle = await frame.$eval(".tabNavText", (element) => element.textContent);
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
import { Frame } from "puppeteer";
|
||||
import { TestExplorerParams } from "../testExplorer/TestExplorerParams";
|
||||
import { getTestExplorerFrame } from "../testExplorer/TestExplorerUtils";
|
||||
import { SelfServeType } from "../../src/SelfServe/SelfServeUtils";
|
||||
import { ApiKind } from "../../src/Contracts/DataModels";
|
||||
|
||||
jest.setTimeout(300000);
|
||||
|
||||
let frame: Frame;
|
||||
describe("Self Serve", () => {
|
||||
it("Launch Self Serve Example", async () => {
|
||||
try {
|
||||
frame = await getTestExplorerFrame(
|
||||
ApiKind.SQL,
|
||||
new Map<string, string>([[TestExplorerParams.selfServeType, SelfServeType.example]])
|
||||
);
|
||||
await page.goto("https://localhost:1234/testExplorer.html?iframeSrc=selfServe.html");
|
||||
const handle = await page.waitForSelector("iframe");
|
||||
const frame = await handle.contentFrame();
|
||||
|
||||
// wait for refresh RP call to end
|
||||
await frame.waitFor(10000);
|
||||
|
|
|
@ -1,82 +1,50 @@
|
|||
/* eslint-disable no-console */
|
||||
import { ClientSecretCredential } from "@azure/identity";
|
||||
import "../../less/hostedexplorer.less";
|
||||
import { TestExplorerParams } from "./TestExplorerParams";
|
||||
import { CosmosDBManagementClient } from "@azure/arm-cosmosdb";
|
||||
import * as msRest from "@azure/ms-rest-js";
|
||||
import * as ViewModels from "../../src/Contracts/ViewModels";
|
||||
import { Capability, DatabaseAccount } from "../../src/Contracts/DataModels";
|
||||
import { DataExplorerInputsFrame } from "../../src/Contracts/ViewModels";
|
||||
import { updateUserContext } from "../../src/UserContext";
|
||||
import { get, listKeys } from "../../src/Utils/arm/generatedClients/2020-04-01/databaseAccounts";
|
||||
|
||||
class CustomSigner implements msRest.ServiceClientCredentials {
|
||||
private token: string;
|
||||
constructor(token: string) {
|
||||
this.token = token;
|
||||
}
|
||||
const resourceGroup = process.env.RESOURCE_GROUP || "";
|
||||
const subscriptionId = process.env.SUBSCRIPTION_ID || "";
|
||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||
const accountName = urlSearchParams.get("accountName") || "portal-sql-runner";
|
||||
const selfServeType = urlSearchParams.get("selfServeType") || "example";
|
||||
const iframeSrc = urlSearchParams.get("iframeSrc") || "explorer.html?platform=Portal&disablePortalInitCache";
|
||||
|
||||
async signRequest(webResource: msRest.WebResourceLike): Promise<msRest.WebResourceLike> {
|
||||
webResource.headers.set("authorization", `bearer ${this.token}`);
|
||||
return webResource;
|
||||
}
|
||||
if (!process.env.AZURE_CLIENT_SECRET) {
|
||||
throw new Error(
|
||||
"process.env.AZURE_CLIENT_SECRET was not set! Set it in your .env file and restart webpack dev server"
|
||||
);
|
||||
}
|
||||
|
||||
const getDatabaseAccount = async (
|
||||
token: string,
|
||||
notebooksAccountSubscriptonId: string,
|
||||
notebooksAccountResourceGroup: string,
|
||||
notebooksAccountName: string
|
||||
): Promise<DatabaseAccount> => {
|
||||
const client = new CosmosDBManagementClient(new CustomSigner(token), notebooksAccountSubscriptonId);
|
||||
const databaseAccountGetResponse = await client.databaseAccounts.get(
|
||||
notebooksAccountResourceGroup,
|
||||
notebooksAccountName
|
||||
);
|
||||
// Azure SDK clients accept the credential as a parameter
|
||||
const credentials = new ClientSecretCredential(
|
||||
process.env.AZURE_TENANT_ID,
|
||||
process.env.AZURE_CLIENT_ID,
|
||||
process.env.AZURE_CLIENT_SECRET,
|
||||
{
|
||||
authorityHost: "https://localhost:1234",
|
||||
}
|
||||
);
|
||||
|
||||
const databaseAccount: DatabaseAccount = {
|
||||
id: databaseAccountGetResponse.id,
|
||||
name: databaseAccountGetResponse.name,
|
||||
location: databaseAccountGetResponse.location,
|
||||
type: databaseAccountGetResponse.type,
|
||||
kind: databaseAccountGetResponse.kind,
|
||||
tags: databaseAccountGetResponse.tags,
|
||||
properties: {
|
||||
documentEndpoint: databaseAccountGetResponse.documentEndpoint,
|
||||
tableEndpoint: undefined,
|
||||
gremlinEndpoint: undefined,
|
||||
cassandraEndpoint: undefined,
|
||||
capabilities: databaseAccountGetResponse.capabilities.map((capability) => {
|
||||
return { name: capability.name } as Capability;
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
return databaseAccount;
|
||||
};
|
||||
console.log("Resource Group:", resourceGroup);
|
||||
console.log("Subcription: ", subscriptionId);
|
||||
console.log("Account Name: ", accountName);
|
||||
|
||||
const initTestExplorer = async (): Promise<void> => {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||
const portalRunnerDatabaseAccount = decodeURIComponent(
|
||||
urlSearchParams.get(TestExplorerParams.portalRunnerDatabaseAccount)
|
||||
);
|
||||
const portalRunnerDatabaseAccountKey = decodeURIComponent(
|
||||
urlSearchParams.get(TestExplorerParams.portalRunnerDatabaseAccountKey)
|
||||
);
|
||||
const portalRunnerSubscripton = decodeURIComponent(urlSearchParams.get(TestExplorerParams.portalRunnerSubscripton));
|
||||
const portalRunnerResourceGroup = decodeURIComponent(
|
||||
urlSearchParams.get(TestExplorerParams.portalRunnerResourceGroup)
|
||||
);
|
||||
const selfServeType = urlSearchParams.get(TestExplorerParams.selfServeType);
|
||||
|
||||
const token = decodeURIComponent(urlSearchParams.get(TestExplorerParams.token));
|
||||
const databaseAccount = await getDatabaseAccount(
|
||||
token,
|
||||
portalRunnerSubscripton,
|
||||
portalRunnerResourceGroup,
|
||||
portalRunnerDatabaseAccount
|
||||
);
|
||||
const { token } = await credentials.getToken("https://management.core.windows.net/.default");
|
||||
updateUserContext({
|
||||
authorizationToken: `bearer ${token}`,
|
||||
});
|
||||
const databaseAccount = await get(subscriptionId, resourceGroup, accountName);
|
||||
const keys = await listKeys(subscriptionId, resourceGroup, accountName);
|
||||
|
||||
const initTestExplorerContent = {
|
||||
inputs: {
|
||||
databaseAccount: databaseAccount,
|
||||
subscriptionId: portalRunnerSubscripton,
|
||||
resourceGroup: portalRunnerResourceGroup,
|
||||
subscriptionId,
|
||||
resourceGroup,
|
||||
authorizationToken: `Bearer ${token}`,
|
||||
features: {},
|
||||
hasWriteAccess: true,
|
||||
|
@ -88,7 +56,7 @@ const initTestExplorer = async (): Promise<void> => {
|
|||
quotaId: "Internal_2014-09-01",
|
||||
addCollectionDefaultFlight: "2",
|
||||
isTryCosmosDBSubscription: false,
|
||||
masterKey: portalRunnerDatabaseAccountKey,
|
||||
masterKey: keys.primaryMasterKey,
|
||||
loadDatabaseAccountTimestamp: 1604663109836,
|
||||
dataExplorerVersion: "1.0.1",
|
||||
sharedThroughputMinimum: 400,
|
||||
|
@ -101,7 +69,7 @@ const initTestExplorer = async (): Promise<void> => {
|
|||
// add UI test only when feature is not dependent on flights anymore
|
||||
flights: [],
|
||||
selfServeType,
|
||||
} as ViewModels.DataExplorerInputsFrame,
|
||||
} as DataExplorerInputsFrame,
|
||||
};
|
||||
|
||||
const iframe = document.createElement("iframe");
|
||||
|
@ -127,16 +95,8 @@ const initTestExplorer = async (): Promise<void> => {
|
|||
iframe.name = "explorer";
|
||||
iframe.classList.add("iframe");
|
||||
iframe.title = "explorer";
|
||||
iframe.src = getIframeSrc(selfServeType);
|
||||
iframe.src = iframeSrc;
|
||||
document.body.appendChild(iframe);
|
||||
};
|
||||
|
||||
const getIframeSrc = (selfServeType: string): string => {
|
||||
let iframeSrc = "explorer.html?platform=Portal&disablePortalInitCache";
|
||||
if (selfServeType) {
|
||||
iframeSrc = `selfServe.html?selfServeType=${selfServeType}`;
|
||||
}
|
||||
return iframeSrc;
|
||||
};
|
||||
|
||||
initTestExplorer();
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
export enum TestExplorerParams {
|
||||
portalRunnerDatabaseAccount = "portalRunnerDatabaseAccount",
|
||||
portalRunnerDatabaseAccountKey = "portalRunnerDatabaseAccountKey",
|
||||
portalRunnerSubscripton = "portalRunnerSubscripton",
|
||||
portalRunnerResourceGroup = "portalRunnerResourceGroup",
|
||||
selfServeType = "selfServeType",
|
||||
token = "token",
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
import { Frame } from "puppeteer";
|
||||
import { TestExplorerParams } from "./TestExplorerParams";
|
||||
import { ClientSecretCredential } from "@azure/identity";
|
||||
import { ApiKind } from "../../src/Contracts/DataModels";
|
||||
|
||||
let testExplorerFrame: Frame;
|
||||
export const getTestExplorerFrame = async (apiKind?: ApiKind, params?: Map<string, string>): Promise<Frame> => {
|
||||
if (testExplorerFrame) {
|
||||
return testExplorerFrame;
|
||||
}
|
||||
|
||||
let portalRunnerDatabaseAccount: string;
|
||||
let portalRunnerDatabaseAccountKey: string;
|
||||
|
||||
switch (apiKind) {
|
||||
case ApiKind.MongoDB:
|
||||
portalRunnerDatabaseAccount = process.env.PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT;
|
||||
portalRunnerDatabaseAccountKey = process.env.PORTAL_RUNNER_MONGO_DATABASE_ACCOUNT_KEY;
|
||||
break;
|
||||
default:
|
||||
portalRunnerDatabaseAccount = process.env.PORTAL_RUNNER_DATABASE_ACCOUNT;
|
||||
portalRunnerDatabaseAccountKey = process.env.PORTAL_RUNNER_DATABASE_ACCOUNT_KEY;
|
||||
}
|
||||
|
||||
const notebooksTestRunnerTenantId = process.env.NOTEBOOKS_TEST_RUNNER_TENANT_ID;
|
||||
const notebooksTestRunnerClientId = process.env.NOTEBOOKS_TEST_RUNNER_CLIENT_ID;
|
||||
const notebooksTestRunnerClientSecret = process.env.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET;
|
||||
const portalRunnerSubscripton = process.env.PORTAL_RUNNER_SUBSCRIPTION;
|
||||
const portalRunnerResourceGroup = process.env.PORTAL_RUNNER_RESOURCE_GROUP;
|
||||
|
||||
const credentials = new ClientSecretCredential(
|
||||
notebooksTestRunnerTenantId,
|
||||
notebooksTestRunnerClientId,
|
||||
notebooksTestRunnerClientSecret
|
||||
);
|
||||
|
||||
const { token } = await credentials.getToken("https://management.core.windows.net/.default");
|
||||
|
||||
const testExplorerUrl = new URL("testExplorer.html", "https://localhost:1234");
|
||||
testExplorerUrl.searchParams.append(
|
||||
TestExplorerParams.portalRunnerDatabaseAccount,
|
||||
encodeURI(portalRunnerDatabaseAccount)
|
||||
);
|
||||
testExplorerUrl.searchParams.append(
|
||||
TestExplorerParams.portalRunnerDatabaseAccountKey,
|
||||
encodeURI(portalRunnerDatabaseAccountKey)
|
||||
);
|
||||
testExplorerUrl.searchParams.append(TestExplorerParams.portalRunnerSubscripton, encodeURI(portalRunnerSubscripton));
|
||||
testExplorerUrl.searchParams.append(
|
||||
TestExplorerParams.portalRunnerResourceGroup,
|
||||
encodeURI(portalRunnerResourceGroup)
|
||||
);
|
||||
testExplorerUrl.searchParams.append(TestExplorerParams.token, encodeURI(token));
|
||||
|
||||
if (params) {
|
||||
for (const key of params.keys()) {
|
||||
testExplorerUrl.searchParams.append(key, encodeURI(params.get(key)));
|
||||
}
|
||||
}
|
||||
|
||||
await page.goto(testExplorerUrl.toString());
|
||||
const handle = await page.waitForSelector("iframe");
|
||||
return await handle.contentFrame();
|
||||
};
|
|
@ -30,8 +30,8 @@ export function generateDatabaseName(baseName = "db", length = 1): string {
|
|||
return `${baseName}${crypto.randomBytes(length).toString("hex")}-${Date.now()}`;
|
||||
}
|
||||
|
||||
export async function createDatabase(frame: Frame): Promise<string> {
|
||||
const dbId = generateDatabaseName();
|
||||
export async function createDatabase(frame: Frame): Promise<{ databaseId: string; collectionId: string }> {
|
||||
const databaseId = generateDatabaseName();
|
||||
const collectionId = generateUniqueName("col");
|
||||
const shardKey = "partitionKey";
|
||||
// create new collection
|
||||
|
@ -50,7 +50,7 @@ export async function createDatabase(frame: Frame): Promise<string> {
|
|||
await frame.waitFor('input[data-test="addCollection-newDatabaseId"]');
|
||||
const dbInput = await frame.$('input[data-test="addCollection-newDatabaseId"]');
|
||||
await dbInput.press("Backspace");
|
||||
await dbInput.type(dbId);
|
||||
await dbInput.type(databaseId);
|
||||
|
||||
// type collection id
|
||||
await frame.waitFor('input[data-test="addCollection-collectionId"]');
|
||||
|
@ -67,7 +67,7 @@ export async function createDatabase(frame: Frame): Promise<string> {
|
|||
// click submit
|
||||
await frame.waitFor("#submitBtnAddCollection");
|
||||
await frame.click("#submitBtnAddCollection");
|
||||
return dbId;
|
||||
return { databaseId, collectionId };
|
||||
}
|
||||
|
||||
export async function onClickSaveButton(frame: Frame): Promise<void> {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const msRestNodeAuth = require("@azure/ms-rest-nodeauth");
|
||||
const { CosmosDBManagementClient } = require("@azure/arm-cosmosdb");
|
||||
const ms = require("ms");
|
||||
const { time } = require("console");
|
||||
|
||||
const clientId = process.env["NOTEBOOKS_TEST_RUNNER_CLIENT_ID"];
|
||||
const secret = process.env["NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET"];
|
||||
|
@ -9,7 +8,7 @@ const tenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47";
|
|||
const subscriptionId = "69e02f2d-f059-4409-9eac-97e8a276ae2c";
|
||||
const resourceGroupName = "runners";
|
||||
|
||||
const sixtyMinutesAgo = new Date(Date.now() - 1000 * 60 * 60).getTime();
|
||||
const thirtyMinutesAgo = new Date(Date.now() - 1000 * 60 * 30).getTime();
|
||||
|
||||
function friendlyTime(date) {
|
||||
try {
|
||||
|
@ -29,7 +28,7 @@ async function main() {
|
|||
const mongoDatabases = await client.mongoDBResources.listMongoDBDatabases(resourceGroupName, account.name);
|
||||
for (const database of mongoDatabases) {
|
||||
const timestamp = Number(database.name.split("-")[1]);
|
||||
if (timestamp && timestamp < sixtyMinutesAgo) {
|
||||
if (timestamp && timestamp < thirtyMinutesAgo) {
|
||||
await client.mongoDBResources.deleteMongoDBDatabase(resourceGroupName, account.name, database.name);
|
||||
console.log(`DELETED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
|
||||
} else {
|
||||
|
@ -40,7 +39,7 @@ async function main() {
|
|||
const sqlDatabases = await client.sqlResources.listSqlDatabases(resourceGroupName, account.name);
|
||||
for (const database of sqlDatabases) {
|
||||
const timestamp = Number(database.name.split("-")[1]);
|
||||
if (timestamp && timestamp < sixtyMinutesAgo) {
|
||||
if (timestamp && timestamp < thirtyMinutesAgo) {
|
||||
await client.sqlResources.deleteSqlDatabase(resourceGroupName, account.name, database.name);
|
||||
console.log(`DELETED: ${account.name} | ${database.name} | Age: ${friendlyTime(Date.now() - timestamp)}`);
|
||||
} else {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
require("dotenv/config");
|
||||
const path = require("path");
|
||||
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
|
@ -14,6 +15,16 @@ const isCI = require("is-ci");
|
|||
|
||||
const gitSha = childProcess.execSync("git rev-parse HEAD").toString("utf8");
|
||||
|
||||
const AZURE_CLIENT_ID = "fd8753b0-0707-4e32-84e9-2532af865fb4";
|
||||
const AZURE_TENANT_ID = "72f988bf-86f1-41af-91ab-2d7cd011db47";
|
||||
const SUBSCRIPTION_ID = "69e02f2d-f059-4409-9eac-97e8a276ae2c";
|
||||
const RESOURCE_GROUP = "runners";
|
||||
const AZURE_CLIENT_SECRET = process.env.AZURE_CLIENT_SECRET || process.env.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET; // TODO Remove. Exists for backwards compat with old .env files. Prefer AZURE_CLIENT_SECRET
|
||||
|
||||
if (!AZURE_CLIENT_SECRET) {
|
||||
console.warn("AZURE_CLIENT_SECRET is not set. testExplorer.html will not work.");
|
||||
}
|
||||
|
||||
const cssRule = {
|
||||
test: /\.css$/,
|
||||
use: [MiniCssExtractPlugin.loader, "css-loader"],
|
||||
|
@ -102,6 +113,11 @@ module.exports = function (env = {}, argv = {}) {
|
|||
|
||||
if (mode === "development") {
|
||||
envVars.NODE_ENV = "development";
|
||||
envVars.AZURE_CLIENT_ID = AZURE_CLIENT_ID;
|
||||
envVars.AZURE_TENANT_ID = AZURE_TENANT_ID;
|
||||
envVars.AZURE_CLIENT_SECRET = AZURE_CLIENT_SECRET;
|
||||
envVars.SUBSCRIPTION_ID = SUBSCRIPTION_ID;
|
||||
envVars.RESOURCE_GROUP = RESOURCE_GROUP;
|
||||
typescriptRule.use[0].options.compilerOptions = { target: "ES2018" };
|
||||
}
|
||||
|
||||
|
@ -282,6 +298,12 @@ module.exports = function (env = {}, argv = {}) {
|
|||
secure: false,
|
||||
logLevel: "debug",
|
||||
},
|
||||
[`/${AZURE_TENANT_ID}`]: {
|
||||
target: "https://login.microsoftonline.com/",
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
logLevel: "debug",
|
||||
},
|
||||
},
|
||||
},
|
||||
stats: "minimal",
|
||||
|
|
Loading…
Reference in New Issue