mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-02-20 03:05:39 +00:00
Update to Prettier 2.0
This commit is contained in:
parent
9a95c7d069
commit
1636f20978
22
.eslintrc.js
22
.eslintrc.js
@ -1,44 +1,44 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es6: true
|
es6: true,
|
||||||
},
|
},
|
||||||
plugins: ["@typescript-eslint", "no-null"],
|
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",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
@ -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"],
|
||||||
};
|
};
|
||||||
|
@ -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}/`);
|
||||||
|
@ -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"],
|
||||||
};
|
};
|
||||||
|
@ -42,8 +42,8 @@ module.exports = {
|
|||||||
branches: 18,
|
branches: 18,
|
||||||
functions: 22,
|
functions: 22,
|
||||||
lines: 28,
|
lines: 28,
|
||||||
statements: 27
|
statements: 27,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Make calling deprecated APIs throw helpful error messages
|
// Make calling deprecated APIs throw helpful error messages
|
||||||
@ -76,7 +76,7 @@ module.exports = {
|
|||||||
"office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes
|
"office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes
|
||||||
"^dnd-core$": "dnd-core/dist/cjs",
|
"^dnd-core$": "dnd-core/dist/cjs",
|
||||||
"^react-dnd$": "react-dnd/dist/cjs",
|
"^react-dnd$": "react-dnd/dist/cjs",
|
||||||
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs"
|
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs",
|
||||||
},
|
},
|
||||||
|
|
||||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||||
@ -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,
|
||||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -20463,9 +20463,9 @@
|
|||||||
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
|
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"version": "1.19.1",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz",
|
||||||
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
|
"integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"pretty-error": {
|
"pretty-error": {
|
||||||
|
@ -146,7 +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",
|
||||||
"prettier": "1.19.1",
|
"prettier": "2.0.5",
|
||||||
"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",
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import * as ViewModels from "../Contracts/ViewModels";
|
import * as ViewModels from "../Contracts/ViewModels";
|
||||||
|
|
||||||
export class DefaultApi implements ViewModels.CosmosDbApi {
|
export class DefaultApi implements ViewModels.CosmosDbApi {
|
||||||
public isSystemDatabasePredicate = (database: ViewModels.Database): boolean => {
|
public isSystemDatabasePredicate = (database: ViewModels.Database): boolean => {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CassandraApi implements ViewModels.CosmosDbApi {
|
export class CassandraApi implements ViewModels.CosmosDbApi {
|
||||||
public isSystemDatabasePredicate = (database: ViewModels.Database): boolean => {
|
public isSystemDatabasePredicate = (database: ViewModels.Database): boolean => {
|
||||||
return database.id() === "system";
|
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,26 +1,26 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as ReactBindingHandler from "./ReactBindingHandler";
|
import * as ReactBindingHandler from "./ReactBindingHandler";
|
||||||
|
|
||||||
interface RestorePoint {
|
interface RestorePoint {
|
||||||
readonly element: JQuery;
|
readonly element: JQuery;
|
||||||
readonly width: number;
|
readonly width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BindingHandlersRegisterer {
|
export class BindingHandlersRegisterer {
|
||||||
public static registerBindingHandlers() {
|
public static registerBindingHandlers() {
|
||||||
ko.bindingHandlers.setTemplateReady = {
|
ko.bindingHandlers.setTemplateReady = {
|
||||||
init(
|
init(
|
||||||
element: any,
|
element: any,
|
||||||
wrappedValueAccessor: () => any,
|
wrappedValueAccessor: () => any,
|
||||||
allBindings?: ko.AllBindings,
|
allBindings?: ko.AllBindings,
|
||||||
viewModel?: any,
|
viewModel?: any,
|
||||||
bindingContext?: ko.BindingContext
|
bindingContext?: ko.BindingContext
|
||||||
) {
|
) {
|
||||||
const value = ko.unwrap(wrappedValueAccessor());
|
const value = ko.unwrap(wrappedValueAccessor());
|
||||||
bindingContext.$data.isTemplateReady(value);
|
bindingContext.$data.isTemplateReady(value);
|
||||||
}
|
},
|
||||||
} as ko.BindingHandler;
|
} as ko.BindingHandler;
|
||||||
|
|
||||||
ReactBindingHandler.Registerer.register();
|
ReactBindingHandler.Registerer.register();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ export class Registerer {
|
|||||||
|
|
||||||
// Initial rendering at mount point
|
// Initial rendering at mount point
|
||||||
ReactDOM.render(adapter.renderComponent(), element);
|
ReactDOM.render(adapter.renderComponent(), element);
|
||||||
}
|
},
|
||||||
} as ko.BindingHandler;
|
} as ko.BindingHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ export class ArrayHashMap<T> {
|
|||||||
public forEach(key: string, iteratorFct: (value: T) => void) {
|
public forEach(key: string, iteratorFct: (value: T) => void) {
|
||||||
const values = this.store.get(key);
|
const values = this.store.get(key);
|
||||||
if (values) {
|
if (values) {
|
||||||
values.forEach(value => iteratorFct(value));
|
values.forEach((value) => iteratorFct(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@ describe("tokenProvider", () => {
|
|||||||
resourceId: "",
|
resourceId: "",
|
||||||
resourceType: "dbs" as ResourceType,
|
resourceType: "dbs" as ResourceType,
|
||||||
headers: {},
|
headers: {},
|
||||||
getAuthorizationTokenUsingMasterKey: () => ""
|
getAuthorizationTokenUsingMasterKey: () => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -17,7 +17,7 @@ describe("tokenProvider", () => {
|
|||||||
window.fetch = jest.fn().mockImplementation(() => {
|
window.fetch = jest.fn().mockImplementation(() => {
|
||||||
return {
|
return {
|
||||||
json: () => "{}",
|
json: () => "{}",
|
||||||
headers: new Map()
|
headers: new Map(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -45,7 +45,7 @@ describe("getTokenFromAuthService", () => {
|
|||||||
window.fetch = jest.fn().mockImplementation(() => {
|
window.fetch = jest.fn().mockImplementation(() => {
|
||||||
return {
|
return {
|
||||||
json: () => "{}",
|
json: () => "{}",
|
||||||
headers: new Map()
|
headers: new Map(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -86,8 +86,8 @@ describe("endpoint", () => {
|
|||||||
documentEndpoint: "bar",
|
documentEndpoint: "bar",
|
||||||
gremlinEndpoint: "foo",
|
gremlinEndpoint: "foo",
|
||||||
tableEndpoint: "foo",
|
tableEndpoint: "foo",
|
||||||
cassandraEndpoint: "foo"
|
cassandraEndpoint: "foo",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
expect(endpoint()).toEqual("bar");
|
expect(endpoint()).toEqual("bar");
|
||||||
});
|
});
|
||||||
|
@ -63,13 +63,13 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
"x-ms-encrypted-auth-token": _accessToken
|
"x-ms-encrypted-auth-token": _accessToken,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
verb,
|
verb,
|
||||||
resourceType,
|
resourceType,
|
||||||
resourceId
|
resourceId,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
//TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json()
|
//TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json()
|
||||||
const result = JSON.parse(await response.json());
|
const result = JSON.parse(await response.json());
|
||||||
@ -93,9 +93,9 @@ export const CosmosClient = {
|
|||||||
key: _masterKey,
|
key: _masterKey,
|
||||||
tokenProvider,
|
tokenProvider,
|
||||||
connectionPolicy: {
|
connectionPolicy: {
|
||||||
enableEndpointDiscovery: false
|
enableEndpointDiscovery: false,
|
||||||
},
|
},
|
||||||
userAgentSuffix: "Azure Portal"
|
userAgentSuffix: "Azure Portal",
|
||||||
};
|
};
|
||||||
|
|
||||||
// In development we proxy requests to the backend via webpack. This is removed in production bundles.
|
// In development we proxy requests to the backend via webpack. This is removed in production bundles.
|
||||||
@ -176,5 +176,5 @@ export const CosmosClient = {
|
|||||||
_client = null;
|
_client = null;
|
||||||
_resourceToken = value;
|
_resourceToken = value;
|
||||||
return value;
|
return value;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
@ -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,48 +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";
|
||||||
|
|
||||||
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: ViewModels.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";
|
||||||
|
@ -26,7 +26,7 @@ function _parse(err: any): DataModels.ErrorDataModel[] {
|
|||||||
normalizedErrors.push(err);
|
normalizedErrors.push(err);
|
||||||
} else {
|
} else {
|
||||||
const innerErrors: any[] = _getInnerErrors(err.message);
|
const innerErrors: any[] = _getInnerErrors(err.message);
|
||||||
normalizedErrors = innerErrors.map(innerError =>
|
normalizedErrors = innerErrors.map((innerError) =>
|
||||||
typeof innerError === "string" ? { message: innerError } : innerError
|
typeof innerError === "string" ? { message: innerError } : innerError
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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,46 +1,46 @@
|
|||||||
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 { MessageHandler } from "./MessageHandler";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
|
|
||||||
describe("Logger", () => {
|
describe("Logger", () => {
|
||||||
let sendMessageSpy: jasmine.Spy;
|
let sendMessageSpy: jasmine.Spy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sendMessageSpy = spyOn(MessageHandler, "sendMessage");
|
sendMessageSpy = spyOn(MessageHandler, "sendMessage");
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
sendMessageSpy = null;
|
sendMessageSpy = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should log info messages", () => {
|
it("should log info messages", () => {
|
||||||
Logger.logInfo("Test info", "DocDB");
|
Logger.logInfo("Test info", "DocDB");
|
||||||
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
||||||
|
|
||||||
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
||||||
expect(spyArgs.data).toContain(LogEntryLevel.Verbose);
|
expect(spyArgs.data).toContain(LogEntryLevel.Verbose);
|
||||||
expect(spyArgs.data).toContain("DocDB");
|
expect(spyArgs.data).toContain("DocDB");
|
||||||
expect(spyArgs.data).toContain("Test info");
|
expect(spyArgs.data).toContain("Test info");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should log error messages", () => {
|
it("should log error messages", () => {
|
||||||
Logger.logError("Test error", "DocDB");
|
Logger.logError("Test error", "DocDB");
|
||||||
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
||||||
|
|
||||||
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
||||||
expect(spyArgs.data).toContain(LogEntryLevel.Error);
|
expect(spyArgs.data).toContain(LogEntryLevel.Error);
|
||||||
expect(spyArgs.data).toContain("DocDB");
|
expect(spyArgs.data).toContain("DocDB");
|
||||||
expect(spyArgs.data).toContain("Test error");
|
expect(spyArgs.data).toContain("Test error");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should log warnings", () => {
|
it("should log warnings", () => {
|
||||||
Logger.logWarning("Test warning", "DocDB");
|
Logger.logWarning("Test warning", "DocDB");
|
||||||
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
const spyArgs = sendMessageSpy.calls.mostRecent().args[0];
|
||||||
|
|
||||||
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
expect(spyArgs.type).toBe(MessageTypes.LogInfo);
|
||||||
expect(spyArgs.data).toContain(LogEntryLevel.Warning);
|
expect(spyArgs.data).toContain(LogEntryLevel.Warning);
|
||||||
expect(spyArgs.data).toContain("DocDB");
|
expect(spyArgs.data).toContain("DocDB");
|
||||||
expect(spyArgs.data).toContain("Test warning");
|
expect(spyArgs.data).toContain("Test warning");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -35,7 +35,7 @@ export function logError(message: string | Error, area: string, code?: number):
|
|||||||
function _logEntry(entry: Diagnostics.LogEntry): void {
|
function _logEntry(entry: Diagnostics.LogEntry): void {
|
||||||
MessageHandler.sendMessage({
|
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 => {
|
||||||
@ -66,6 +66,6 @@ function _generateLogEntry(
|
|||||||
level: level,
|
level: level,
|
||||||
message: message,
|
message: message,
|
||||||
area: area,
|
area: area,
|
||||||
code: code
|
code: code,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,65 +1,65 @@
|
|||||||
import Q from "q";
|
import Q from "q";
|
||||||
import { CachedDataPromise, MessageHandler } from "./MessageHandler";
|
import { CachedDataPromise, MessageHandler } from "./MessageHandler";
|
||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
|
|
||||||
class MockMessageHandler extends MessageHandler {
|
class MockMessageHandler extends MessageHandler {
|
||||||
public static addToMap(key: string, value: CachedDataPromise<any>): void {
|
public static addToMap(key: string, value: CachedDataPromise<any>): void {
|
||||||
MessageHandler.RequestMap[key] = value;
|
MessageHandler.RequestMap[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static mapContainsKey(key: string): boolean {
|
public static mapContainsKey(key: string): boolean {
|
||||||
return MessageHandler.RequestMap[key] != null;
|
return MessageHandler.RequestMap[key] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static clearAllEntries(): void {
|
public static clearAllEntries(): void {
|
||||||
MessageHandler.RequestMap = {};
|
MessageHandler.RequestMap = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static runGarbageCollector(): void {
|
public static runGarbageCollector(): void {
|
||||||
MessageHandler.runGarbageCollector();
|
MessageHandler.runGarbageCollector();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("Message Handler", () => {
|
describe("Message Handler", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
MockMessageHandler.clearAllEntries();
|
MockMessageHandler.clearAllEntries();
|
||||||
});
|
});
|
||||||
|
|
||||||
xit("should send cached data message", (done: any) => {
|
xit("should send cached data message", (done: any) => {
|
||||||
const testValidationCallback = (e: MessageEvent) => {
|
const testValidationCallback = (e: MessageEvent) => {
|
||||||
expect(e.data.data).toEqual(
|
expect(e.data.data).toEqual(
|
||||||
jasmine.objectContaining({ type: MessageTypes.AllDatabases, params: ["some param"] })
|
jasmine.objectContaining({ type: MessageTypes.AllDatabases, params: ["some param"] })
|
||||||
);
|
);
|
||||||
e.currentTarget.removeEventListener(e.type, testValidationCallback);
|
e.currentTarget.removeEventListener(e.type, testValidationCallback);
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
window.parent.addEventListener("message", testValidationCallback);
|
window.parent.addEventListener("message", testValidationCallback);
|
||||||
MockMessageHandler.sendCachedDataMessage(MessageTypes.AllDatabases, ["some param"]);
|
MockMessageHandler.sendCachedDataMessage(MessageTypes.AllDatabases, ["some param"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle cached message", () => {
|
it("should handle cached message", () => {
|
||||||
let mockPromise: CachedDataPromise<any> = {
|
let mockPromise: CachedDataPromise<any> = {
|
||||||
id: "123",
|
id: "123",
|
||||||
startTime: new Date(),
|
startTime: new Date(),
|
||||||
deferred: Q.defer<any>()
|
deferred: Q.defer<any>(),
|
||||||
};
|
};
|
||||||
let mockMessage = { message: { id: "123", data: "{}" } };
|
let mockMessage = { message: { id: "123", data: "{}" } };
|
||||||
|
|
||||||
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
||||||
MockMessageHandler.handleCachedDataMessage(mockMessage);
|
MockMessageHandler.handleCachedDataMessage(mockMessage);
|
||||||
expect(mockPromise.deferred.promise.isFulfilled()).toBe(true);
|
expect(mockPromise.deferred.promise.isFulfilled()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should delete fulfilled promises on running the garbage collector", () => {
|
it("should delete fulfilled promises on running the garbage collector", () => {
|
||||||
let mockPromise: CachedDataPromise<any> = {
|
let mockPromise: CachedDataPromise<any> = {
|
||||||
id: "123",
|
id: "123",
|
||||||
startTime: new Date(),
|
startTime: new Date(),
|
||||||
deferred: Q.defer<any>()
|
deferred: Q.defer<any>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
MockMessageHandler.addToMap(mockPromise.id, mockPromise);
|
||||||
mockPromise.deferred.reject("some error");
|
mockPromise.deferred.reject("some error");
|
||||||
MockMessageHandler.runGarbageCollector();
|
MockMessageHandler.runGarbageCollector();
|
||||||
expect(MockMessageHandler.mapContainsKey(mockPromise.id)).toBe(false);
|
expect(MockMessageHandler.mapContainsKey(mockPromise.id)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,85 +1,85 @@
|
|||||||
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import * as _ from "underscore";
|
import * as _ from "underscore";
|
||||||
import * as Constants from "./Constants";
|
import * as Constants from "./Constants";
|
||||||
|
|
||||||
export interface CachedDataPromise<T> {
|
export interface CachedDataPromise<T> {
|
||||||
deferred: Q.Deferred<T>;
|
deferred: Q.Deferred<T>;
|
||||||
startTime: Date;
|
startTime: Date;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
* 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.
|
* so we define our own custom implementation of the ES6 Map to work around it.
|
||||||
*/
|
*/
|
||||||
type Map = { [key: string]: CachedDataPromise<any> };
|
type Map = { [key: string]: CachedDataPromise<any> };
|
||||||
|
|
||||||
export class MessageHandler {
|
export class MessageHandler {
|
||||||
protected static RequestMap: Map = {};
|
protected static RequestMap: Map = {};
|
||||||
|
|
||||||
public static handleCachedDataMessage(message: any): void {
|
public static handleCachedDataMessage(message: any): void {
|
||||||
const messageContent = message && message.message;
|
const messageContent = message && message.message;
|
||||||
if (
|
if (
|
||||||
message == null ||
|
message == null ||
|
||||||
messageContent == null ||
|
messageContent == null ||
|
||||||
messageContent.id == null ||
|
messageContent.id == null ||
|
||||||
!MessageHandler.RequestMap[messageContent.id]
|
!MessageHandler.RequestMap[messageContent.id]
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cachedDataPromise = MessageHandler.RequestMap[messageContent.id];
|
const cachedDataPromise = MessageHandler.RequestMap[messageContent.id];
|
||||||
if (messageContent.error != null) {
|
if (messageContent.error != null) {
|
||||||
cachedDataPromise.deferred.reject(messageContent.error);
|
cachedDataPromise.deferred.reject(messageContent.error);
|
||||||
} else {
|
} else {
|
||||||
cachedDataPromise.deferred.resolve(JSON.parse(messageContent.data));
|
cachedDataPromise.deferred.resolve(JSON.parse(messageContent.data));
|
||||||
}
|
}
|
||||||
MessageHandler.runGarbageCollector();
|
MessageHandler.runGarbageCollector();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static sendCachedDataMessage<TResponseDataModel>(
|
public static sendCachedDataMessage<TResponseDataModel>(
|
||||||
messageType: MessageTypes,
|
messageType: MessageTypes,
|
||||||
params: Object[],
|
params: Object[],
|
||||||
timeoutInMs?: number
|
timeoutInMs?: number
|
||||||
): Q.Promise<TResponseDataModel> {
|
): Q.Promise<TResponseDataModel> {
|
||||||
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
|
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
|
||||||
deferred: Q.defer<TResponseDataModel>(),
|
deferred: Q.defer<TResponseDataModel>(),
|
||||||
startTime: new Date(),
|
startTime: new Date(),
|
||||||
id: _.uniqueId()
|
id: _.uniqueId(),
|
||||||
};
|
};
|
||||||
MessageHandler.RequestMap[cachedDataPromise.id] = cachedDataPromise;
|
MessageHandler.RequestMap[cachedDataPromise.id] = cachedDataPromise;
|
||||||
MessageHandler.sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
|
MessageHandler.sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
|
||||||
|
|
||||||
//TODO: Use telemetry to measure optimal time to resolve/reject promises
|
//TODO: Use telemetry to measure optimal time to resolve/reject promises
|
||||||
return cachedDataPromise.deferred.promise.timeout(
|
return cachedDataPromise.deferred.promise.timeout(
|
||||||
timeoutInMs || Constants.ClientDefaults.requestTimeoutMs,
|
timeoutInMs || Constants.ClientDefaults.requestTimeoutMs,
|
||||||
"Timed out while waiting for response from portal"
|
"Timed out while waiting for response from portal"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static sendMessage(data: any): void {
|
public static sendMessage(data: any): void {
|
||||||
if (MessageHandler.canSendMessage()) {
|
if (MessageHandler.canSendMessage()) {
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
signature: "pcIframe",
|
signature: "pcIframe",
|
||||||
data: data
|
data: data,
|
||||||
},
|
},
|
||||||
window.document.referrer
|
window.document.referrer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static canSendMessage(): boolean {
|
public static canSendMessage(): boolean {
|
||||||
return window.parent !== window;
|
return window.parent !== window;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static runGarbageCollector() {
|
protected static runGarbageCollector() {
|
||||||
Object.keys(MessageHandler.RequestMap).forEach((key: string) => {
|
Object.keys(MessageHandler.RequestMap).forEach((key: string) => {
|
||||||
const promise: Q.Promise<any> = MessageHandler.RequestMap[key].deferred.promise;
|
const promise: Q.Promise<any> = MessageHandler.RequestMap[key].deferred.promise;
|
||||||
if (promise.isFulfilled() || promise.isRejected()) {
|
if (promise.isFulfilled() || promise.isRejected()) {
|
||||||
delete MessageHandler.RequestMap[key];
|
delete MessageHandler.RequestMap[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
getEndpoint,
|
getEndpoint,
|
||||||
queryDocuments,
|
queryDocuments,
|
||||||
readDocument,
|
readDocument,
|
||||||
updateDocument
|
updateDocument,
|
||||||
} from "./MongoProxyClient";
|
} from "./MongoProxyClient";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { Collection, DatabaseAccount, DocumentId } from "../Contracts/ViewModels";
|
import { Collection, DatabaseAccount, DocumentId } from "../Contracts/ViewModels";
|
||||||
@ -20,7 +20,7 @@ const fetchMock = () => {
|
|||||||
ok: true,
|
ok: true,
|
||||||
text: () => "{}",
|
text: () => "{}",
|
||||||
json: () => "{}",
|
json: () => "{}",
|
||||||
headers: new Map()
|
headers: new Map(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,8 +33,8 @@ const collection = {
|
|||||||
partitionKey: {
|
partitionKey: {
|
||||||
paths: ["/pk"],
|
paths: ["/pk"],
|
||||||
kind: "Hash",
|
kind: "Hash",
|
||||||
version: 1
|
version: 1,
|
||||||
}
|
},
|
||||||
} as Collection;
|
} as Collection;
|
||||||
|
|
||||||
const documentId = ({
|
const documentId = ({
|
||||||
@ -44,8 +44,8 @@ const documentId = ({
|
|||||||
partitionKey: {
|
partitionKey: {
|
||||||
paths: ["/pk"],
|
paths: ["/pk"],
|
||||||
kind: "Hash",
|
kind: "Hash",
|
||||||
version: 1
|
version: 1,
|
||||||
}
|
},
|
||||||
} as unknown) as DocumentId;
|
} as unknown) as DocumentId;
|
||||||
|
|
||||||
const databaseAccount = {
|
const databaseAccount = {
|
||||||
@ -58,8 +58,8 @@ const databaseAccount = {
|
|||||||
documentEndpoint: "bar",
|
documentEndpoint: "bar",
|
||||||
gremlinEndpoint: "foo",
|
gremlinEndpoint: "foo",
|
||||||
tableEndpoint: "foo",
|
tableEndpoint: "foo",
|
||||||
cassandraEndpoint: "foo"
|
cassandraEndpoint: "foo",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("MongoProxyClient", () => {
|
describe("MongoProxyClient", () => {
|
||||||
@ -69,7 +69,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@ -100,7 +100,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@ -131,7 +131,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@ -162,7 +162,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@ -193,7 +193,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
window.fetch = jest.fn().mockImplementation(fetchMock);
|
window.fetch = jest.fn().mockImplementation(fetchMock);
|
||||||
});
|
});
|
||||||
@ -225,7 +225,7 @@ describe("MongoProxyClient", () => {
|
|||||||
CosmosClient.databaseAccount(databaseAccount as any);
|
CosmosClient.databaseAccount(databaseAccount as any);
|
||||||
window.dataExplorer = {
|
window.dataExplorer = {
|
||||||
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
|
||||||
serverId: () => ""
|
serverId: () => "",
|
||||||
} as any;
|
} as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -259,7 +259,7 @@ describe("MongoProxyClient", () => {
|
|||||||
sid: "a2",
|
sid: "a2",
|
||||||
rg: "c1",
|
rg: "c1",
|
||||||
dba: "main",
|
dba: "main",
|
||||||
is: false
|
is: false,
|
||||||
};
|
};
|
||||||
_createMongoCollectionWithARM("management.azure.com", properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
|
_createMongoCollectionWithARM("management.azure.com", properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
|
||||||
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
||||||
@ -268,8 +268,8 @@ describe("MongoProxyClient", () => {
|
|||||||
{
|
{
|
||||||
properties: {
|
properties: {
|
||||||
options: { "x-ms-cosmos-offer-autopilot-tier": "1" },
|
options: { "x-ms-cosmos-offer-autopilot-tier": "1" },
|
||||||
resource: { id: "abc-collection" }
|
resource: { id: "abc-collection" },
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -285,7 +285,7 @@ describe("MongoProxyClient", () => {
|
|||||||
rg: "c1",
|
rg: "c1",
|
||||||
dba: "main",
|
dba: "main",
|
||||||
is: false,
|
is: false,
|
||||||
offerThroughput: 400
|
offerThroughput: 400,
|
||||||
};
|
};
|
||||||
_createMongoCollectionWithARM("management.azure.com", properties, undefined);
|
_createMongoCollectionWithARM("management.azure.com", properties, undefined);
|
||||||
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
|
||||||
@ -294,8 +294,8 @@ describe("MongoProxyClient", () => {
|
|||||||
{
|
{
|
||||||
properties: {
|
properties: {
|
||||||
options: { throughput: "400" },
|
options: { throughput: "400" },
|
||||||
resource: { id: "abc-collection" }
|
resource: { id: "abc-collection" },
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -20,7 +20,7 @@ import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClie
|
|||||||
const defaultHeaders = {
|
const defaultHeaders = {
|
||||||
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
|
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
|
||||||
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
|
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
|
||||||
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15"
|
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15",
|
||||||
};
|
};
|
||||||
|
|
||||||
function authHeaders(): any {
|
function authHeaders(): any {
|
||||||
@ -35,7 +35,7 @@ export function queryIterator(databaseId: string, collection: Collection, query:
|
|||||||
let continuationToken: string;
|
let continuationToken: string;
|
||||||
return {
|
return {
|
||||||
fetchNext: () => {
|
fetchNext: () => {
|
||||||
return queryDocuments(databaseId, collection, false, query).then(response => {
|
return queryDocuments(databaseId, collection, false, query).then((response) => {
|
||||||
continuationToken = response.continuationToken;
|
continuationToken = response.continuationToken;
|
||||||
const headers = {} as any;
|
const headers = {} as any;
|
||||||
response.headers.forEach((value: any, key: any) => {
|
response.headers.forEach((value: any, key: any) => {
|
||||||
@ -46,10 +46,10 @@ export function queryIterator(databaseId: string, collection: Collection, query:
|
|||||||
headers,
|
headers,
|
||||||
requestCharge: headers[CosmosSDKConstants.HttpHeaders.RequestCharge],
|
requestCharge: headers[CosmosSDKConstants.HttpHeaders.RequestCharge],
|
||||||
activityId: headers[CosmosSDKConstants.HttpHeaders.ActivityId],
|
activityId: headers[CosmosSDKConstants.HttpHeaders.ActivityId],
|
||||||
hasMoreResults: !!continuationToken
|
hasMoreResults: !!continuationToken,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +78,9 @@ export function queryDocuments(
|
|||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
collection && collection.partitionKey && !collection.partitionKey.systemKey ? collection.partitionKeyProperty : ""
|
collection && collection.partitionKey && !collection.partitionKey.systemKey
|
||||||
|
? collection.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount) || "";
|
const endpoint = getEndpoint(databaseAccount) || "";
|
||||||
@ -91,7 +93,7 @@ export function queryDocuments(
|
|||||||
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
|
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
|
||||||
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
|
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
|
||||||
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
|
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
|
||||||
[HttpHeaders.contentType]: "application/query+json"
|
[HttpHeaders.contentType]: "application/query+json",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (continuationToken) {
|
if (continuationToken) {
|
||||||
@ -104,14 +106,14 @@ 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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return errorHandling(response, "querying documents", params);
|
return errorHandling(response, "querying documents", params);
|
||||||
@ -138,7 +140,9 @@ export function readDocument(
|
|||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
|
? documentId.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
@ -150,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();
|
||||||
}
|
}
|
||||||
@ -178,7 +182,7 @@ export function createDocument(
|
|||||||
sid: CosmosClient.subscriptionId(),
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : ""
|
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
@ -189,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();
|
||||||
}
|
}
|
||||||
@ -221,7 +225,9 @@ export function updateDocument(
|
|||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
|
? documentId.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
|
|
||||||
@ -233,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();
|
||||||
}
|
}
|
||||||
@ -264,7 +270,9 @@ export function deleteDocument(
|
|||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
pk:
|
pk:
|
||||||
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
|
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
|
||||||
|
? documentId.partitionKeyProperty
|
||||||
|
: "",
|
||||||
};
|
};
|
||||||
const endpoint = getEndpoint(databaseAccount);
|
const endpoint = getEndpoint(databaseAccount);
|
||||||
|
|
||||||
@ -275,10 +283,10 @@ export function deleteDocument(
|
|||||||
...defaultHeaders,
|
...defaultHeaders,
|
||||||
...authHeaders(),
|
...authHeaders(),
|
||||||
[HttpHeaders.contentType]: "application/json",
|
[HttpHeaders.contentType]: "application/json",
|
||||||
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
|
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -311,7 +319,7 @@ export function createMongoCollectionWithProxy(
|
|||||||
sid: CosmosClient.subscriptionId(),
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
isAutoPilot: false
|
isAutoPilot: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (autopilotOptions) {
|
if (autopilotOptions) {
|
||||||
@ -329,11 +337,11 @@ 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 undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -368,7 +376,7 @@ export function createMongoCollectionWithARM(
|
|||||||
sid: CosmosClient.subscriptionId(),
|
sid: CosmosClient.subscriptionId(),
|
||||||
rg: CosmosClient.resourceGroup(),
|
rg: CosmosClient.resourceGroup(),
|
||||||
dba: databaseAccount.name,
|
dba: databaseAccount.name,
|
||||||
analyticalStorageTtl
|
analyticalStorageTtl,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (createDatabase) {
|
if (createDatabase) {
|
||||||
@ -424,10 +432,10 @@ export async function _createMongoCollectionWithARM(
|
|||||||
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
|
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
|
||||||
properties: {
|
properties: {
|
||||||
resource: {
|
resource: {
|
||||||
id: params.coll
|
id: params.coll,
|
||||||
},
|
},
|
||||||
options: {}
|
options: {},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (params.is) {
|
if (params.is) {
|
||||||
|
@ -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,47 +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 { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||||
import { CosmosClient } from "./CosmosClient";
|
import { CosmosClient } from "./CosmosClient";
|
||||||
|
|
||||||
export class NotificationsClientBase implements ViewModels.NotificationsClient {
|
export class NotificationsClientBase implements ViewModels.NotificationsClient {
|
||||||
private _extensionEndpoint: string;
|
private _extensionEndpoint: string;
|
||||||
private _notificationsApiSuffix: string;
|
private _notificationsApiSuffix: string;
|
||||||
|
|
||||||
protected constructor(notificationsApiSuffix: string) {
|
protected constructor(notificationsApiSuffix: string) {
|
||||||
this._notificationsApiSuffix = notificationsApiSuffix;
|
this._notificationsApiSuffix = notificationsApiSuffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchNotifications(): Q.Promise<DataModels.Notification[]> {
|
public fetchNotifications(): Q.Promise<DataModels.Notification[]> {
|
||||||
const deferred: Q.Deferred<DataModels.Notification[]> = Q.defer<DataModels.Notification[]>();
|
const deferred: Q.Deferred<DataModels.Notification[]> = Q.defer<DataModels.Notification[]>();
|
||||||
const databaseAccount: ViewModels.DatabaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount: ViewModels.DatabaseAccount = CosmosClient.databaseAccount();
|
||||||
const subscriptionId: string = CosmosClient.subscriptionId();
|
const subscriptionId: string = CosmosClient.subscriptionId();
|
||||||
const resourceGroup: string = CosmosClient.resourceGroup();
|
const resourceGroup: string = CosmosClient.resourceGroup();
|
||||||
const url: string = `${this._extensionEndpoint}${this._notificationsApiSuffix}?accountName=${databaseAccount.name}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
|
const url: string = `${this._extensionEndpoint}${this._notificationsApiSuffix}?accountName=${databaseAccount.name}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
|
||||||
const authorizationHeader: ViewModels.AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
|
const authorizationHeader: ViewModels.AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
|
||||||
const headers: any = {};
|
const headers: any = {};
|
||||||
headers[authorizationHeader.header] = authorizationHeader.token;
|
headers[authorizationHeader.header] = authorizationHeader.token;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
type: "GET",
|
type: "GET",
|
||||||
headers: headers,
|
headers: headers,
|
||||||
cache: false
|
cache: false,
|
||||||
}).then(
|
}).then(
|
||||||
(notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => {
|
(notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => {
|
||||||
deferred.resolve(notifications);
|
deferred.resolve(notifications);
|
||||||
},
|
},
|
||||||
(xhr: JQueryXHR<any>, textStatus: string, error: any) => {
|
(xhr: JQueryXHR<any>, textStatus: string, error: any) => {
|
||||||
deferred.reject(xhr.responseText);
|
deferred.reject(xhr.responseText);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setExtensionEndpoint(extensionEndpoint: string): void {
|
public setExtensionEndpoint(extensionEndpoint: string): void {
|
||||||
this._extensionEndpoint = extensionEndpoint;
|
this._extensionEndpoint = extensionEndpoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,286 +1,286 @@
|
|||||||
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 DocumentId from "../Explorer/Tree/DocumentId";
|
||||||
import * as ErrorParserUtility from "./ErrorParserUtility";
|
import * as ErrorParserUtility from "./ErrorParserUtility";
|
||||||
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
||||||
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { CosmosClient } from "./CosmosClient";
|
import { CosmosClient } from "./CosmosClient";
|
||||||
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
import * as Logger from "./Logger";
|
import * as Logger from "./Logger";
|
||||||
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
import { NotificationConsoleUtils } from "../Utils/NotificationConsoleUtils";
|
||||||
import { QueryUtils } from "../Utils/QueryUtils";
|
import { QueryUtils } from "../Utils/QueryUtils";
|
||||||
|
|
||||||
export class QueriesClient implements ViewModels.QueriesClient {
|
export class QueriesClient implements ViewModels.QueriesClient {
|
||||||
private static readonly PartitionKey: DataModels.PartitionKey = {
|
private static readonly PartitionKey: DataModels.PartitionKey = {
|
||||||
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
paths: [`/${SavedQueries.PartitionKeyProperty}`],
|
||||||
kind: BackendDefaults.partitionKeyKind,
|
kind: BackendDefaults.partitionKeyKind,
|
||||||
version: BackendDefaults.partitionKeyVersion
|
version: BackendDefaults.partitionKeyVersion,
|
||||||
};
|
};
|
||||||
private static readonly FetchQuery: string = "SELECT * FROM c";
|
private static readonly FetchQuery: string = "SELECT * FROM c";
|
||||||
private static readonly FetchMongoQuery: string = "{}";
|
private static readonly FetchMongoQuery: string = "{}";
|
||||||
|
|
||||||
public constructor(private container: ViewModels.Explorer) {}
|
public constructor(private container: ViewModels.Explorer) {}
|
||||||
|
|
||||||
public async setupQueriesCollection(): Promise<DataModels.Collection> {
|
public async setupQueriesCollection(): Promise<DataModels.Collection> {
|
||||||
const queriesCollection: ViewModels.Collection = this.findQueriesCollection();
|
const queriesCollection: ViewModels.Collection = this.findQueriesCollection();
|
||||||
if (queriesCollection) {
|
if (queriesCollection) {
|
||||||
return Promise.resolve(queriesCollection.rawDataModel);
|
return Promise.resolve(queriesCollection.rawDataModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const id = NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.InProgress,
|
ConsoleDataType.InProgress,
|
||||||
"Setting up account for saving queries"
|
"Setting up account for saving queries"
|
||||||
);
|
);
|
||||||
return this.container.documentClientUtility
|
return this.container.documentClientUtility
|
||||||
.getOrCreateDatabaseAndCollection({
|
.getOrCreateDatabaseAndCollection({
|
||||||
collectionId: SavedQueries.CollectionName,
|
collectionId: SavedQueries.CollectionName,
|
||||||
databaseId: SavedQueries.DatabaseName,
|
databaseId: SavedQueries.DatabaseName,
|
||||||
partitionKey: QueriesClient.PartitionKey,
|
partitionKey: QueriesClient.PartitionKey,
|
||||||
offerThroughput: SavedQueries.OfferThroughput,
|
offerThroughput: SavedQueries.OfferThroughput,
|
||||||
databaseLevelThroughput: undefined
|
databaseLevelThroughput: undefined,
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(collection: DataModels.Collection) => {
|
(collection: DataModels.Collection) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Info,
|
ConsoleDataType.Info,
|
||||||
"Successfully set up account for saving queries"
|
"Successfully set up account for saving queries"
|
||||||
);
|
);
|
||||||
return Promise.resolve(collection);
|
return Promise.resolve(collection);
|
||||||
},
|
},
|
||||||
(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 set up account for saving queries: ${stringifiedError}`
|
`Failed to set up account for saving queries: ${stringifiedError}`
|
||||||
);
|
);
|
||||||
Logger.logError(stringifiedError, "setupQueriesCollection");
|
Logger.logError(stringifiedError, "setupQueriesCollection");
|
||||||
return Promise.reject(stringifiedError);
|
return Promise.reject(stringifiedError);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async saveQuery(query: DataModels.Query): Promise<void> {
|
public async saveQuery(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 save query ${query.queryName}: ${errorMessage}`
|
`Failed to save query ${query.queryName}: ${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 save query ${query.queryName}: ${errorMessage}`
|
`Failed to save query ${query.queryName}: ${errorMessage}`
|
||||||
);
|
);
|
||||||
return Promise.reject(errorMessage);
|
return Promise.reject(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(
|
const id = NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.InProgress,
|
ConsoleDataType.InProgress,
|
||||||
`Saving query ${query.queryName}`
|
`Saving query ${query.queryName}`
|
||||||
);
|
);
|
||||||
query.id = query.queryName;
|
query.id = query.queryName;
|
||||||
return this.container.documentClientUtility
|
return this.container.documentClientUtility
|
||||||
.createDocument(queriesCollection, query)
|
.createDocument(queriesCollection, query)
|
||||||
.then(
|
.then(
|
||||||
(savedQuery: DataModels.Query) => {
|
(savedQuery: DataModels.Query) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Info,
|
ConsoleDataType.Info,
|
||||||
`Successfully saved query ${query.queryName}`
|
`Successfully saved query ${query.queryName}`
|
||||||
);
|
);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
let errorMessage: string;
|
let errorMessage: string;
|
||||||
const parsedError: DataModels.ErrorDataModel = ErrorParserUtility.parse(error)[0];
|
const parsedError: DataModels.ErrorDataModel = ErrorParserUtility.parse(error)[0];
|
||||||
if (parsedError.code === HttpStatusCodes.Conflict.toString()) {
|
if (parsedError.code === HttpStatusCodes.Conflict.toString()) {
|
||||||
errorMessage = `Query ${query.queryName} already exists`;
|
errorMessage = `Query ${query.queryName} already exists`;
|
||||||
} else {
|
} else {
|
||||||
errorMessage = parsedError.message;
|
errorMessage = parsedError.message;
|
||||||
}
|
}
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
`Failed to save query ${query.queryName}: ${errorMessage}`
|
`Failed to save query ${query.queryName}: ${errorMessage}`
|
||||||
);
|
);
|
||||||
Logger.logError(JSON.stringify(parsedError), "saveQuery");
|
Logger.logError(JSON.stringify(parsedError), "saveQuery");
|
||||||
return Promise.reject(errorMessage);
|
return Promise.reject(errorMessage);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getQueries(): Promise<DataModels.Query[]> {
|
public async getQueries(): Promise<DataModels.Query[]> {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
const options: any = { enableCrossPartitionQuery: true };
|
const options: any = { enableCrossPartitionQuery: true };
|
||||||
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
|
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
|
||||||
return this.container.documentClientUtility
|
return this.container.documentClientUtility
|
||||||
.queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
|
.queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
|
||||||
.then(
|
.then(
|
||||||
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
|
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
|
||||||
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
|
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
|
||||||
this.container.documentClientUtility.queryDocumentsPage(
|
this.container.documentClientUtility.queryDocumentsPage(
|
||||||
queriesCollection.id(),
|
queriesCollection.id(),
|
||||||
queryIterator,
|
queryIterator,
|
||||||
firstItemIndex,
|
firstItemIndex,
|
||||||
options
|
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: ViewModels.DocumentId = new DocumentId(
|
||||||
{
|
{
|
||||||
partitionKey: QueriesClient.PartitionKey,
|
partitionKey: QueriesClient.PartitionKey,
|
||||||
partitionKeyProperty: "id"
|
partitionKeyProperty: "id",
|
||||||
} as ViewModels.DocumentsTab,
|
} as ViewModels.DocumentsTab,
|
||||||
query,
|
query,
|
||||||
query.queryName
|
query.queryName
|
||||||
); // 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 this.container.documentClientUtility
|
||||||
.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: ViewModels.DatabaseAccount = CosmosClient.databaseAccount();
|
const databaseAccount: ViewModels.DatabaseAccount = CosmosClient.databaseAccount();
|
||||||
const databaseAccountName: string = (databaseAccount && databaseAccount.name) || "";
|
const databaseAccountName: string = (databaseAccount && databaseAccount.name) || "";
|
||||||
const subscriptionId: string = CosmosClient.subscriptionId() || "";
|
const subscriptionId: string = CosmosClient.subscriptionId() || "";
|
||||||
const resourceGroup: string = CosmosClient.resourceGroup() || "";
|
const resourceGroup: string = CosmosClient.resourceGroup() || "";
|
||||||
|
|
||||||
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,7 +1,7 @@
|
|||||||
export enum Platform {
|
export enum Platform {
|
||||||
Portal = "Portal",
|
Portal = "Portal",
|
||||||
Hosted = "Hosted",
|
Hosted = "Hosted",
|
||||||
Emulator = "Emulator"
|
Emulator = "Emulator",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
@ -45,7 +45,7 @@ let config: Config = {
|
|||||||
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
|
||||||
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
|
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
|
||||||
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
|
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
|
||||||
AZURESAMPLESCOSMOSDBPAT: "99e38770e29b4a61d7c49f188780504efd35cc86" //[SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification=" // this PAT is a "no scopes" PAT with zero access to any projects, this is just used to get around the dev.github.com rate limit when accessing public samples repo.")]
|
AZURESAMPLESCOSMOSDBPAT: "99e38770e29b4a61d7c49f188780504efd35cc86", //[SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification=" // this PAT is a "no scopes" PAT with zero access to any projects, this is just used to get around the dev.github.com rate limit when accessing public samples repo.")]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Injected for local develpment. These will be removed in the production bundle by webpack
|
// Injected for local develpment. These will be removed in the production bundle by webpack
|
||||||
|
@ -7,7 +7,7 @@ export enum TabKind {
|
|||||||
TableEntities,
|
TableEntities,
|
||||||
Graph,
|
Graph,
|
||||||
SQLQuery,
|
SQLQuery,
|
||||||
ScaleSettings
|
ScaleSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,7 +20,7 @@ export enum PaneKind {
|
|||||||
DeleteDatabase,
|
DeleteDatabase,
|
||||||
GlobalSettings,
|
GlobalSettings,
|
||||||
AdHocAccess,
|
AdHocAccess,
|
||||||
SwitchDirectory
|
SwitchDirectory,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,5 +79,5 @@ export enum ActionType {
|
|||||||
OpenCollectionTab,
|
OpenCollectionTab,
|
||||||
OpenPane,
|
OpenPane,
|
||||||
TransmitCachedData,
|
TransmitCachedData,
|
||||||
OpenSampleNotebook
|
OpenSampleNotebook,
|
||||||
}
|
}
|
||||||
|
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.
|
||||||
|
@ -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
@ -6,19 +6,19 @@ describe("The Heatmap Control", () => {
|
|||||||
const dataPoints = {
|
const dataPoints = {
|
||||||
"1": {
|
"1": {
|
||||||
"2019-06-19T00:59:10Z": {
|
"2019-06-19T00:59:10Z": {
|
||||||
"Normalized Throughput": 0.35
|
"Normalized Throughput": 0.35,
|
||||||
},
|
},
|
||||||
"2019-06-19T00:48:10Z": {
|
"2019-06-19T00:48:10Z": {
|
||||||
"Normalized Throughput": 0.25
|
"Normalized Throughput": 0.25,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const chartCaptions = {
|
const chartCaptions = {
|
||||||
chartTitle: "chart title",
|
chartTitle: "chart title",
|
||||||
yAxisTitle: "YAxisTitle",
|
yAxisTitle: "YAxisTitle",
|
||||||
tooltipText: "Tooltip text",
|
tooltipText: "Tooltip text",
|
||||||
timeWindow: 123456789
|
timeWindow: 123456789,
|
||||||
};
|
};
|
||||||
|
|
||||||
let heatmap: Heatmap;
|
let heatmap: Heatmap;
|
||||||
@ -75,12 +75,12 @@ describe("The Heatmap Control", () => {
|
|||||||
if (dayjs().utcOffset()) {
|
if (dayjs().utcOffset()) {
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
|
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
|
||||||
"2019-06-19T00:48:10Z",
|
"2019-06-19T00:48:10Z",
|
||||||
"2019-06-19T00:59:10Z"
|
"2019-06-19T00:59:10Z",
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
|
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
|
||||||
"2019-06-19T00:48:10Z",
|
"2019-06-19T00:48:10Z",
|
||||||
"2019-06-19T00:59:10Z"
|
"2019-06-19T00:59:10Z",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -106,9 +106,9 @@ describe("iframe rendering when there is no data", () => {
|
|||||||
data: {
|
data: {
|
||||||
chartData: {},
|
chartData: {},
|
||||||
chartSettings: {},
|
chartSettings: {},
|
||||||
theme: 4
|
theme: 4,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
||||||
@ -126,9 +126,9 @@ describe("iframe rendering when there is no data", () => {
|
|||||||
data: {
|
data: {
|
||||||
chartData: {},
|
chartData: {},
|
||||||
chartSettings: {},
|
chartSettings: {},
|
||||||
theme: 2
|
theme: 2,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
HeatmapData,
|
HeatmapData,
|
||||||
LayoutSettings,
|
LayoutSettings,
|
||||||
PartitionTimeStampToData,
|
PartitionTimeStampToData,
|
||||||
PortalTheme
|
PortalTheme,
|
||||||
} from "./HeatmapDatatypes";
|
} from "./HeatmapDatatypes";
|
||||||
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
||||||
import { MessageHandler } from "../../Common/MessageHandler";
|
import { MessageHandler } from "../../Common/MessageHandler";
|
||||||
@ -43,7 +43,7 @@ export class Heatmap {
|
|||||||
return {
|
return {
|
||||||
family: StyleConstants.DataExplorerFont,
|
family: StyleConstants.DataExplorerFont,
|
||||||
size,
|
size,
|
||||||
color
|
color,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ export class Heatmap {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
// go thru all rows and create 2d matrix for heatmap...
|
// go thru all rows and create 2d matrix for heatmap...
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
@ -115,7 +115,7 @@ export class Heatmap {
|
|||||||
[0.7, "#E46612"],
|
[0.7, "#E46612"],
|
||||||
[0.8, "#E64914"],
|
[0.8, "#E64914"],
|
||||||
[0.9, "#B80016"],
|
[0.9, "#B80016"],
|
||||||
[1.0, "#B80016"]
|
[1.0, "#B80016"],
|
||||||
],
|
],
|
||||||
name: "",
|
name: "",
|
||||||
hovertemplate: this._heatmapCaptions.tooltipText,
|
hovertemplate: this._heatmapCaptions.tooltipText,
|
||||||
@ -123,11 +123,11 @@ export class Heatmap {
|
|||||||
thickness: 15,
|
thickness: 15,
|
||||||
outlinewidth: 0,
|
outlinewidth: 0,
|
||||||
tickcolor: StyleConstants.BaseDark,
|
tickcolor: StyleConstants.BaseDark,
|
||||||
tickfont: this._getFontStyles(10, this._defaultFontColor)
|
tickfont: this._getFontStyles(10, this._defaultFontColor),
|
||||||
},
|
},
|
||||||
y: this._chartData.yAxisPoints,
|
y: this._chartData.yAxisPoints,
|
||||||
x: this._chartData.xAxisPoints
|
x: this._chartData.xAxisPoints,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ export class Heatmap {
|
|||||||
r: 10,
|
r: 10,
|
||||||
b: 35,
|
b: 35,
|
||||||
t: 30,
|
t: 30,
|
||||||
pad: 0
|
pad: 0,
|
||||||
},
|
},
|
||||||
paper_bgcolor: "transparent",
|
paper_bgcolor: "transparent",
|
||||||
plot_bgcolor: "transparent",
|
plot_bgcolor: "transparent",
|
||||||
@ -154,7 +154,7 @@ export class Heatmap {
|
|||||||
autotick: true,
|
autotick: true,
|
||||||
fixedrange: true,
|
fixedrange: true,
|
||||||
ticks: "",
|
ticks: "",
|
||||||
showticklabels: false
|
showticklabels: false,
|
||||||
},
|
},
|
||||||
xaxis: {
|
xaxis: {
|
||||||
fixedrange: true,
|
fixedrange: true,
|
||||||
@ -167,13 +167,13 @@ export class Heatmap {
|
|||||||
autotick: true,
|
autotick: true,
|
||||||
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
|
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
|
||||||
showticklabels: true,
|
showticklabels: true,
|
||||||
tickfont: this._getFontStyles(10)
|
tickfont: this._getFontStyles(10),
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: this._heatmapCaptions.chartTitle,
|
text: this._heatmapCaptions.chartTitle,
|
||||||
x: 0.01,
|
x: 0.01,
|
||||||
font: this._getFontStyles(13, this._defaultFontColor)
|
font: this._getFontStyles(13, this._defaultFontColor),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ export class Heatmap {
|
|||||||
return {
|
return {
|
||||||
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
|
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
|
||||||
responsive: true,*/
|
responsive: true,*/
|
||||||
displayModeBar: false
|
displayModeBar: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ export enum PortalTheme {
|
|||||||
blue = 1,
|
blue = 1,
|
||||||
azure,
|
azure,
|
||||||
light,
|
light,
|
||||||
dark
|
dark,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HeatmapData {
|
export interface HeatmapData {
|
||||||
|
3908
src/Definitions/datatables.d.ts
vendored
3908
src/Definitions/datatables.d.ts
vendored
File diff suppressed because it is too large
Load Diff
68
src/Definitions/jquery-typescript.d.ts
vendored
68
src/Definitions/jquery-typescript.d.ts
vendored
@ -1,34 +1,34 @@
|
|||||||
/* Type definitions for code-runner's jquery-typeahead v2.8.0
|
/* Type definitions for code-runner's jquery-typeahead v2.8.0
|
||||||
* https://github.com/running-coder/jquery-typeahead
|
* https://github.com/running-coder/jquery-typeahead
|
||||||
*
|
*
|
||||||
* There is no DefinitelyTyped support for this library, yet, so we only define here what we use.
|
* There is no DefinitelyTyped support for this library, yet, so we only define here what we use.
|
||||||
* https://github.com/running-coder/jquery-typeahead/issues/156
|
* https://github.com/running-coder/jquery-typeahead/issues/156
|
||||||
* TODO: Replace this minimum definition by the official one when it comes out.
|
* TODO: Replace this minimum definition by the official one when it comes out.
|
||||||
*/
|
*/
|
||||||
/// <reference path="jquery.d.ts" />
|
/// <reference path="jquery.d.ts" />
|
||||||
|
|
||||||
interface JQueryTypeaheadParam {
|
interface JQueryTypeaheadParam {
|
||||||
input: string;
|
input: string;
|
||||||
order?: string;
|
order?: string;
|
||||||
source: any;
|
source: any;
|
||||||
callback?: any;
|
callback?: any;
|
||||||
minLength?: number;
|
minLength?: number;
|
||||||
searchOnFocus?: boolean;
|
searchOnFocus?: boolean;
|
||||||
template?: string | { (query: string, item: any): string };
|
template?: string | { (query: string, item: any): string };
|
||||||
dynamic?: boolean;
|
dynamic?: boolean;
|
||||||
mustSelectItem?: boolean;
|
mustSelectItem?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For use with: $.typeahead()
|
* For use with: $.typeahead()
|
||||||
*/
|
*/
|
||||||
interface JQueryStatic {
|
interface JQueryStatic {
|
||||||
typeahead(arg: JQueryTypeaheadParam): void;
|
typeahead(arg: JQueryTypeaheadParam): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For use with $('').typehead()
|
* For use with $('').typehead()
|
||||||
*/
|
*/
|
||||||
// interface JQuery {
|
// interface JQuery {
|
||||||
// typeahead(arg: JQueryTypeaheadParam): void;
|
// typeahead(arg: JQueryTypeaheadParam): void;
|
||||||
// }
|
// }
|
||||||
|
3542
src/Definitions/jquery-ui.d.ts
vendored
3542
src/Definitions/jquery-ui.d.ts
vendored
File diff suppressed because it is too large
Load Diff
84
src/Definitions/jquery.contextmenu.d.ts
vendored
84
src/Definitions/jquery.contextmenu.d.ts
vendored
@ -1,42 +1,42 @@
|
|||||||
// Type definitions for jQuery contextMenu 1.7.0
|
// Type definitions for jQuery contextMenu 1.7.0
|
||||||
// Project: http://medialize.github.com/jQuery-contextMenu/
|
// Project: http://medialize.github.com/jQuery-contextMenu/
|
||||||
// Definitions by: Natan Vivo <https://github.com/nvivo/>
|
// Definitions by: Natan Vivo <https://github.com/nvivo/>
|
||||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||||
|
|
||||||
/// <reference path="jquery.d.ts" />
|
/// <reference path="jquery.d.ts" />
|
||||||
|
|
||||||
interface JQueryContextMenuOptions {
|
interface JQueryContextMenuOptions {
|
||||||
selector: string;
|
selector: string;
|
||||||
appendTo?: string;
|
appendTo?: string;
|
||||||
trigger?: string;
|
trigger?: string;
|
||||||
autoHide?: boolean;
|
autoHide?: boolean;
|
||||||
delay?: number;
|
delay?: number;
|
||||||
determinePosition?: (menu: JQuery) => void;
|
determinePosition?: (menu: JQuery) => void;
|
||||||
position?: (opt: JQuery, x: number, y: number) => void;
|
position?: (opt: JQuery, x: number, y: number) => void;
|
||||||
positionSubmenu?: (menu: JQuery) => void;
|
positionSubmenu?: (menu: JQuery) => void;
|
||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
animation?: {
|
animation?: {
|
||||||
duration?: number;
|
duration?: number;
|
||||||
show?: string;
|
show?: string;
|
||||||
hide?: string;
|
hide?: string;
|
||||||
};
|
};
|
||||||
events?: {
|
events?: {
|
||||||
show?: () => void;
|
show?: () => void;
|
||||||
hide?: () => void;
|
hide?: () => void;
|
||||||
};
|
};
|
||||||
callback?: (key: any, options: any) => any;
|
callback?: (key: any, options: any) => any;
|
||||||
items?: any;
|
items?: any;
|
||||||
build?: (triggerElement: JQuery, e: Event) => any;
|
build?: (triggerElement: JQuery, e: Event) => any;
|
||||||
reposition?: boolean;
|
reposition?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
itemClickEvent?: string;
|
itemClickEvent?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JQueryStatic {
|
interface JQueryStatic {
|
||||||
contextMenu(options?: JQueryContextMenuOptions): JQuery;
|
contextMenu(options?: JQueryContextMenuOptions): JQuery;
|
||||||
contextMenu(type: string, selector?: any): JQuery;
|
contextMenu(type: string, selector?: any): JQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface JQuery {
|
interface JQuery {
|
||||||
contextMenu(options?: any): JQuery;
|
contextMenu(options?: any): JQuery;
|
||||||
}
|
}
|
||||||
|
3780
src/Definitions/jquery.d.ts
vendored
3780
src/Definitions/jquery.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@ -1,142 +1,142 @@
|
|||||||
jest.mock("monaco-editor");
|
jest.mock("monaco-editor");
|
||||||
|
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import "./ComponentRegisterer";
|
import "./ComponentRegisterer";
|
||||||
|
|
||||||
describe("Component Registerer", () => {
|
describe("Component Registerer", () => {
|
||||||
it("should register input-typeahead component", () => {
|
it("should register input-typeahead component", () => {
|
||||||
expect(ko.components.isRegistered("input-typeahead")).toBe(true);
|
expect(ko.components.isRegistered("input-typeahead")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register new-vertex-form component", () => {
|
it("should register new-vertex-form component", () => {
|
||||||
expect(ko.components.isRegistered("new-vertex-form")).toBe(true);
|
expect(ko.components.isRegistered("new-vertex-form")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register error-display component", () => {
|
it("should register error-display component", () => {
|
||||||
expect(ko.components.isRegistered("error-display")).toBe(true);
|
expect(ko.components.isRegistered("error-display")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register graph-style component", () => {
|
it("should register graph-style component", () => {
|
||||||
expect(ko.components.isRegistered("graph-style")).toBe(true);
|
expect(ko.components.isRegistered("graph-style")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register collapsible-panel component", () => {
|
it("should register collapsible-panel component", () => {
|
||||||
expect(ko.components.isRegistered("collapsible-panel")).toBe(true);
|
expect(ko.components.isRegistered("collapsible-panel")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register json-editor component", () => {
|
it("should register json-editor component", () => {
|
||||||
expect(ko.components.isRegistered("json-editor")).toBe(true);
|
expect(ko.components.isRegistered("json-editor")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register documents-tab component", () => {
|
it("should register documents-tab component", () => {
|
||||||
expect(ko.components.isRegistered("documents-tab")).toBe(true);
|
expect(ko.components.isRegistered("documents-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register stored-procedure-tab component", () => {
|
it("should register stored-procedure-tab component", () => {
|
||||||
expect(ko.components.isRegistered("stored-procedure-tab")).toBe(true);
|
expect(ko.components.isRegistered("stored-procedure-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register trigger-tab component", () => {
|
it("should register trigger-tab component", () => {
|
||||||
expect(ko.components.isRegistered("trigger-tab")).toBe(true);
|
expect(ko.components.isRegistered("trigger-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register user-defined-function-tab component", () => {
|
it("should register user-defined-function-tab component", () => {
|
||||||
expect(ko.components.isRegistered("user-defined-function-tab")).toBe(true);
|
expect(ko.components.isRegistered("user-defined-function-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register settings-tab component", () => {
|
it("should register settings-tab component", () => {
|
||||||
expect(ko.components.isRegistered("settings-tab")).toBe(true);
|
expect(ko.components.isRegistered("settings-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register query-tab component", () => {
|
it("should register query-tab component", () => {
|
||||||
expect(ko.components.isRegistered("query-tab")).toBe(true);
|
expect(ko.components.isRegistered("query-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register tables-query-tab component", () => {
|
it("should register tables-query-tab component", () => {
|
||||||
expect(ko.components.isRegistered("tables-query-tab")).toBe(true);
|
expect(ko.components.isRegistered("tables-query-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register graph-tab component", () => {
|
it("should register graph-tab component", () => {
|
||||||
expect(ko.components.isRegistered("graph-tab")).toBe(true);
|
expect(ko.components.isRegistered("graph-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register notebookv2-tab component", () => {
|
it("should register notebookv2-tab component", () => {
|
||||||
expect(ko.components.isRegistered("notebookv2-tab")).toBe(true);
|
expect(ko.components.isRegistered("notebookv2-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register terminal-tab component", () => {
|
it("should register terminal-tab component", () => {
|
||||||
expect(ko.components.isRegistered("terminal-tab")).toBe(true);
|
expect(ko.components.isRegistered("terminal-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register spark-master-tab component", () => {
|
it("should register spark-master-tab component", () => {
|
||||||
expect(ko.components.isRegistered("spark-master-tab")).toBe(true);
|
expect(ko.components.isRegistered("spark-master-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register mongo-shell-tab component", () => {
|
it("should register mongo-shell-tab component", () => {
|
||||||
expect(ko.components.isRegistered("mongo-shell-tab")).toBe(true);
|
expect(ko.components.isRegistered("mongo-shell-tab")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should registeradd-collection-pane component", () => {
|
it("should registeradd-collection-pane component", () => {
|
||||||
expect(ko.components.isRegistered("add-collection-pane")).toBe(true);
|
expect(ko.components.isRegistered("add-collection-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register delete-collection-confirmation-pane component", () => {
|
it("should register delete-collection-confirmation-pane component", () => {
|
||||||
expect(ko.components.isRegistered("delete-collection-confirmation-pane")).toBe(true);
|
expect(ko.components.isRegistered("delete-collection-confirmation-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register delete-database-confirmation-pane component", () => {
|
it("should register delete-database-confirmation-pane component", () => {
|
||||||
expect(ko.components.isRegistered("delete-database-confirmation-pane")).toBe(true);
|
expect(ko.components.isRegistered("delete-database-confirmation-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register save-query-pane component", () => {
|
it("should register save-query-pane component", () => {
|
||||||
expect(ko.components.isRegistered("save-query-pane")).toBe(true);
|
expect(ko.components.isRegistered("save-query-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register browse-queries-pane component", () => {
|
it("should register browse-queries-pane component", () => {
|
||||||
expect(ko.components.isRegistered("browse-queries-pane")).toBe(true);
|
expect(ko.components.isRegistered("browse-queries-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register graph-new-vertex-pane component", () => {
|
it("should register graph-new-vertex-pane component", () => {
|
||||||
expect(ko.components.isRegistered("graph-new-vertex-pane")).toBe(true);
|
expect(ko.components.isRegistered("graph-new-vertex-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register graph-styling-pane component", () => {
|
it("should register graph-styling-pane component", () => {
|
||||||
expect(ko.components.isRegistered("graph-styling-pane")).toBe(true);
|
expect(ko.components.isRegistered("graph-styling-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register upload-file-pane component", () => {
|
it("should register upload-file-pane component", () => {
|
||||||
expect(ko.components.isRegistered("upload-file-pane")).toBe(true);
|
expect(ko.components.isRegistered("upload-file-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register string-input-pane component", () => {
|
it("should register string-input-pane component", () => {
|
||||||
expect(ko.components.isRegistered("string-input-pane")).toBe(true);
|
expect(ko.components.isRegistered("string-input-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register setup-notebooks-pane component", () => {
|
it("should register setup-notebooks-pane component", () => {
|
||||||
expect(ko.components.isRegistered("setup-notebooks-pane")).toBe(true);
|
expect(ko.components.isRegistered("setup-notebooks-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register setup-spark-cluster-pane component", () => {
|
it("should register setup-spark-cluster-pane component", () => {
|
||||||
expect(ko.components.isRegistered("setup-spark-cluster-pane")).toBe(true);
|
expect(ko.components.isRegistered("setup-spark-cluster-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register manage-spark-cluster-pane component", () => {
|
it("should register manage-spark-cluster-pane component", () => {
|
||||||
expect(ko.components.isRegistered("manage-spark-cluster-pane")).toBe(true);
|
expect(ko.components.isRegistered("manage-spark-cluster-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register dynamic-list component", () => {
|
it("should register dynamic-list component", () => {
|
||||||
expect(ko.components.isRegistered("dynamic-list")).toBe(true);
|
expect(ko.components.isRegistered("dynamic-list")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register throughput-input component", () => {
|
it("should register throughput-input component", () => {
|
||||||
expect(ko.components.isRegistered("throughput-input")).toBe(true);
|
expect(ko.components.isRegistered("throughput-input")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register library-manage-pane component", () => {
|
it("should register library-manage-pane component", () => {
|
||||||
expect(ko.components.isRegistered("library-manage-pane")).toBe(true);
|
expect(ko.components.isRegistered("library-manage-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register cluster-library-pane component", () => {
|
it("should register cluster-library-pane component", () => {
|
||||||
expect(ko.components.isRegistered("cluster-library-pane")).toBe(true);
|
expect(ko.components.isRegistered("cluster-library-pane")).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,83 +1,83 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as PaneComponents from "./Panes/PaneComponents";
|
import * as PaneComponents from "./Panes/PaneComponents";
|
||||||
import * as TabComponents from "./Tabs/TabComponents";
|
import * as TabComponents from "./Tabs/TabComponents";
|
||||||
import { CollapsiblePanelComponent } from "./Controls/CollapsiblePanel/CollapsiblePanelComponent";
|
import { CollapsiblePanelComponent } from "./Controls/CollapsiblePanel/CollapsiblePanelComponent";
|
||||||
import { DiffEditorComponent } from "./Controls/DiffEditor/DiffEditorComponent";
|
import { DiffEditorComponent } from "./Controls/DiffEditor/DiffEditorComponent";
|
||||||
import { DynamicListComponent } from "./Controls/DynamicList/DynamicListComponent";
|
import { DynamicListComponent } from "./Controls/DynamicList/DynamicListComponent";
|
||||||
import { EditorComponent } from "./Controls/Editor/EditorComponent";
|
import { EditorComponent } from "./Controls/Editor/EditorComponent";
|
||||||
import { ErrorDisplayComponent } from "./Controls/ErrorDisplayComponent/ErrorDisplayComponent";
|
import { ErrorDisplayComponent } from "./Controls/ErrorDisplayComponent/ErrorDisplayComponent";
|
||||||
import { GraphStyleComponent } from "./Graph/GraphStyleComponent/GraphStyleComponent";
|
import { GraphStyleComponent } from "./Graph/GraphStyleComponent/GraphStyleComponent";
|
||||||
import { InputTypeaheadComponent } from "./Controls/InputTypeahead/InputTypeahead";
|
import { InputTypeaheadComponent } from "./Controls/InputTypeahead/InputTypeahead";
|
||||||
import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
|
import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
|
||||||
import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponent";
|
import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponent";
|
||||||
import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputInputComponent";
|
import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputInputComponent";
|
||||||
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
||||||
import { ToolbarComponent } from "./Controls/Toolbar/Toolbar";
|
import { ToolbarComponent } from "./Controls/Toolbar/Toolbar";
|
||||||
|
|
||||||
ko.components.register("toolbar", new ToolbarComponent());
|
ko.components.register("toolbar", new ToolbarComponent());
|
||||||
ko.components.register("input-typeahead", new InputTypeaheadComponent());
|
ko.components.register("input-typeahead", new InputTypeaheadComponent());
|
||||||
ko.components.register("new-vertex-form", NewVertexComponent);
|
ko.components.register("new-vertex-form", NewVertexComponent);
|
||||||
ko.components.register("error-display", new ErrorDisplayComponent());
|
ko.components.register("error-display", new ErrorDisplayComponent());
|
||||||
ko.components.register("graph-style", GraphStyleComponent);
|
ko.components.register("graph-style", GraphStyleComponent);
|
||||||
ko.components.register("collapsible-panel", new CollapsiblePanelComponent());
|
ko.components.register("collapsible-panel", new CollapsiblePanelComponent());
|
||||||
ko.components.register("editor", new EditorComponent());
|
ko.components.register("editor", new EditorComponent());
|
||||||
ko.components.register("json-editor", new JsonEditorComponent());
|
ko.components.register("json-editor", new JsonEditorComponent());
|
||||||
ko.components.register("diff-editor", new DiffEditorComponent());
|
ko.components.register("diff-editor", new DiffEditorComponent());
|
||||||
ko.components.register("dynamic-list", DynamicListComponent);
|
ko.components.register("dynamic-list", DynamicListComponent);
|
||||||
ko.components.register("throughput-input", ThroughputInputComponent);
|
ko.components.register("throughput-input", ThroughputInputComponent);
|
||||||
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
|
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
|
||||||
|
|
||||||
// Collection Tabs
|
// Collection Tabs
|
||||||
ko.components.register("documents-tab", new TabComponents.DocumentsTab());
|
ko.components.register("documents-tab", new TabComponents.DocumentsTab());
|
||||||
ko.components.register("mongo-documents-tab", new TabComponents.MongoDocumentsTab());
|
ko.components.register("mongo-documents-tab", new TabComponents.MongoDocumentsTab());
|
||||||
ko.components.register("stored-procedure-tab", new TabComponents.StoredProcedureTab());
|
ko.components.register("stored-procedure-tab", new TabComponents.StoredProcedureTab());
|
||||||
ko.components.register("trigger-tab", new TabComponents.TriggerTab());
|
ko.components.register("trigger-tab", new TabComponents.TriggerTab());
|
||||||
ko.components.register("user-defined-function-tab", new TabComponents.UserDefinedFunctionTab());
|
ko.components.register("user-defined-function-tab", new TabComponents.UserDefinedFunctionTab());
|
||||||
ko.components.register("settings-tab", new TabComponents.SettingsTab());
|
ko.components.register("settings-tab", new TabComponents.SettingsTab());
|
||||||
ko.components.register("query-tab", new TabComponents.QueryTab());
|
ko.components.register("query-tab", new TabComponents.QueryTab());
|
||||||
ko.components.register("tables-query-tab", new TabComponents.QueryTablesTab());
|
ko.components.register("tables-query-tab", new TabComponents.QueryTablesTab());
|
||||||
ko.components.register("graph-tab", new TabComponents.GraphTab());
|
ko.components.register("graph-tab", new TabComponents.GraphTab());
|
||||||
ko.components.register("mongo-shell-tab", new TabComponents.MongoShellTab());
|
ko.components.register("mongo-shell-tab", new TabComponents.MongoShellTab());
|
||||||
ko.components.register("conflicts-tab", new TabComponents.ConflictsTab());
|
ko.components.register("conflicts-tab", new TabComponents.ConflictsTab());
|
||||||
ko.components.register("notebookv2-tab", new TabComponents.NotebookV2Tab());
|
ko.components.register("notebookv2-tab", new TabComponents.NotebookV2Tab());
|
||||||
ko.components.register("terminal-tab", new TabComponents.TerminalTab());
|
ko.components.register("terminal-tab", new TabComponents.TerminalTab());
|
||||||
ko.components.register("spark-master-tab", new TabComponents.SparkMasterTab());
|
ko.components.register("spark-master-tab", new TabComponents.SparkMasterTab());
|
||||||
ko.components.register("gallery-tab", new TabComponents.GalleryTab());
|
ko.components.register("gallery-tab", new TabComponents.GalleryTab());
|
||||||
ko.components.register("notebook-viewer-tab", new TabComponents.NotebookViewerTab());
|
ko.components.register("notebook-viewer-tab", new TabComponents.NotebookViewerTab());
|
||||||
|
|
||||||
// Database Tabs
|
// Database Tabs
|
||||||
ko.components.register("database-settings-tab", new TabComponents.DatabaseSettingsTab());
|
ko.components.register("database-settings-tab", new TabComponents.DatabaseSettingsTab());
|
||||||
|
|
||||||
// Panes
|
// Panes
|
||||||
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
||||||
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
||||||
ko.components.register(
|
ko.components.register(
|
||||||
"delete-collection-confirmation-pane",
|
"delete-collection-confirmation-pane",
|
||||||
new PaneComponents.DeleteCollectionConfirmationPaneComponent()
|
new PaneComponents.DeleteCollectionConfirmationPaneComponent()
|
||||||
);
|
);
|
||||||
ko.components.register(
|
ko.components.register(
|
||||||
"delete-database-confirmation-pane",
|
"delete-database-confirmation-pane",
|
||||||
new PaneComponents.DeleteDatabaseConfirmationPaneComponent()
|
new PaneComponents.DeleteDatabaseConfirmationPaneComponent()
|
||||||
);
|
);
|
||||||
ko.components.register("graph-new-vertex-pane", new PaneComponents.GraphNewVertexPaneComponent());
|
ko.components.register("graph-new-vertex-pane", new PaneComponents.GraphNewVertexPaneComponent());
|
||||||
ko.components.register("graph-styling-pane", new PaneComponents.GraphStylingPaneComponent());
|
ko.components.register("graph-styling-pane", new PaneComponents.GraphStylingPaneComponent());
|
||||||
ko.components.register("table-add-entity-pane", new PaneComponents.TableAddEntityPaneComponent());
|
ko.components.register("table-add-entity-pane", new PaneComponents.TableAddEntityPaneComponent());
|
||||||
ko.components.register("table-edit-entity-pane", new PaneComponents.TableEditEntityPaneComponent());
|
ko.components.register("table-edit-entity-pane", new PaneComponents.TableEditEntityPaneComponent());
|
||||||
ko.components.register("table-column-options-pane", new PaneComponents.TableColumnOptionsPaneComponent());
|
ko.components.register("table-column-options-pane", new PaneComponents.TableColumnOptionsPaneComponent());
|
||||||
ko.components.register("table-query-select-pane", new PaneComponents.TableQuerySelectPaneComponent());
|
ko.components.register("table-query-select-pane", new PaneComponents.TableQuerySelectPaneComponent());
|
||||||
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
|
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
|
||||||
ko.components.register("settings-pane", new PaneComponents.SettingsPaneComponent());
|
ko.components.register("settings-pane", new PaneComponents.SettingsPaneComponent());
|
||||||
ko.components.register("execute-sproc-params-pane", new PaneComponents.ExecuteSprocParamsComponent());
|
ko.components.register("execute-sproc-params-pane", new PaneComponents.ExecuteSprocParamsComponent());
|
||||||
ko.components.register("renew-adhoc-access-pane", new PaneComponents.RenewAdHocAccessPane());
|
ko.components.register("renew-adhoc-access-pane", new PaneComponents.RenewAdHocAccessPane());
|
||||||
ko.components.register("upload-items-pane", new PaneComponents.UploadItemsPaneComponent());
|
ko.components.register("upload-items-pane", new PaneComponents.UploadItemsPaneComponent());
|
||||||
ko.components.register("load-query-pane", new PaneComponents.LoadQueryPaneComponent());
|
ko.components.register("load-query-pane", new PaneComponents.LoadQueryPaneComponent());
|
||||||
ko.components.register("save-query-pane", new PaneComponents.SaveQueryPaneComponent());
|
ko.components.register("save-query-pane", new PaneComponents.SaveQueryPaneComponent());
|
||||||
ko.components.register("browse-queries-pane", new PaneComponents.BrowseQueriesPaneComponent());
|
ko.components.register("browse-queries-pane", new PaneComponents.BrowseQueriesPaneComponent());
|
||||||
ko.components.register("upload-file-pane", new PaneComponents.UploadFilePaneComponent());
|
ko.components.register("upload-file-pane", new PaneComponents.UploadFilePaneComponent());
|
||||||
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
||||||
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
|
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
|
||||||
ko.components.register("setup-spark-cluster-pane", new PaneComponents.SetupSparkClusterPaneComponent());
|
ko.components.register("setup-spark-cluster-pane", new PaneComponents.SetupSparkClusterPaneComponent());
|
||||||
ko.components.register("manage-spark-cluster-pane", new PaneComponents.ManageSparkClusterPaneComponent());
|
ko.components.register("manage-spark-cluster-pane", new PaneComponents.ManageSparkClusterPaneComponent());
|
||||||
ko.components.register("library-manage-pane", new PaneComponents.LibraryManagePaneComponent());
|
ko.components.register("library-manage-pane", new PaneComponents.LibraryManagePaneComponent());
|
||||||
ko.components.register("cluster-library-pane", new PaneComponents.ClusterLibraryPaneComponent());
|
ko.components.register("cluster-library-pane", new PaneComponents.ClusterLibraryPaneComponent());
|
||||||
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
|
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
|
||||||
|
@ -32,13 +32,13 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const newCollectionMenuItem: TreeNodeMenuItem = {
|
const newCollectionMenuItem: TreeNodeMenuItem = {
|
||||||
iconSrc: AddCollectionIcon,
|
iconSrc: AddCollectionIcon,
|
||||||
onClick: () => container.onNewCollectionClicked(),
|
onClick: () => container.onNewCollectionClicked(),
|
||||||
label: container.addCollectionText()
|
label: container.addCollectionText(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteDatabaseMenuItem = {
|
const deleteDatabaseMenuItem = {
|
||||||
iconSrc: DeleteDatabaseIcon,
|
iconSrc: DeleteDatabaseIcon,
|
||||||
onClick: () => container.deleteDatabaseConfirmationPane.open(),
|
onClick: () => container.deleteDatabaseConfirmationPane.open(),
|
||||||
label: container.deleteDatabaseText()
|
label: container.deleteDatabaseText(),
|
||||||
};
|
};
|
||||||
return [newCollectionMenuItem, deleteDatabaseMenuItem];
|
return [newCollectionMenuItem, deleteDatabaseMenuItem];
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
items.push({
|
items.push({
|
||||||
iconSrc: AddSqlQueryIcon,
|
iconSrc: AddSqlQueryIcon,
|
||||||
onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null),
|
onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null),
|
||||||
label: "New SQL Query"
|
label: "New SQL Query",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
items.push({
|
items.push({
|
||||||
iconSrc: AddSqlQueryIcon,
|
iconSrc: AddSqlQueryIcon,
|
||||||
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null),
|
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null),
|
||||||
label: "New Query"
|
label: "New Query",
|
||||||
});
|
});
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
@ -69,7 +69,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewMongoShellClick();
|
selectedCollection && selectedCollection.onNewMongoShellClick();
|
||||||
},
|
},
|
||||||
label: "New Shell"
|
label: "New Shell",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
|
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
|
||||||
},
|
},
|
||||||
label: "New Stored Procedure"
|
label: "New Stored Procedure",
|
||||||
});
|
});
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
@ -89,7 +89,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null);
|
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null);
|
||||||
},
|
},
|
||||||
label: "New UDF"
|
label: "New UDF",
|
||||||
});
|
});
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
@ -98,7 +98,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null);
|
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null);
|
||||||
},
|
},
|
||||||
label: "New Trigger"
|
label: "New Trigger",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
|
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
|
||||||
},
|
},
|
||||||
label: container.deleteCollectionText()
|
label: container.deleteCollectionText(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
@ -126,8 +126,8 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
{
|
{
|
||||||
iconSrc: DeleteSprocIcon,
|
iconSrc: DeleteSprocIcon,
|
||||||
onClick: () => storedProcedure.delete(),
|
onClick: () => storedProcedure.delete(),
|
||||||
label: "Delete Store Procedure"
|
label: "Delete Store Procedure",
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,8 +143,8 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
{
|
{
|
||||||
iconSrc: DeleteTriggerIcon,
|
iconSrc: DeleteTriggerIcon,
|
||||||
onClick: () => trigger.delete(),
|
onClick: () => trigger.delete(),
|
||||||
label: "Delete Trigger"
|
label: "Delete Trigger",
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,8 +160,8 @@ export class ResourceTreeContextMenuButtonFactory {
|
|||||||
{
|
{
|
||||||
iconSrc: DeleteUDFIcon,
|
iconSrc: DeleteUDFIcon,
|
||||||
onClick: () => userDefinedFunction.delete(),
|
onClick: () => userDefinedFunction.delete(),
|
||||||
label: "Delete User Defined Function"
|
label: "Delete User Defined Function",
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ export class AccessibleElement extends React.Component<AccessibleElementProps> {
|
|||||||
...elementProps,
|
...elementProps,
|
||||||
onKeyPress: this.onKeyPress,
|
onKeyPress: this.onKeyPress,
|
||||||
onClick: this.props.onActivated,
|
onClick: this.props.onActivated,
|
||||||
tabIndex
|
tabIndex,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
|
|||||||
super(props);
|
super(props);
|
||||||
this.isExpanded = props.isExpanded;
|
this.isExpanded = props.isExpanded;
|
||||||
this.state = {
|
this.state = {
|
||||||
isExpanded: true
|
isExpanded: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
|
|||||||
if (this.props.isExpanded !== this.isExpanded) {
|
if (this.props.isExpanded !== this.isExpanded) {
|
||||||
this.isExpanded = this.props.isExpanded;
|
this.isExpanded = this.props.isExpanded;
|
||||||
this.setState({
|
this.setState({
|
||||||
isExpanded: this.props.isExpanded
|
isExpanded: this.props.isExpanded,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ const createBlankProps = (): AccountSwitchComponentProps => {
|
|||||||
subscriptions: [],
|
subscriptions: [],
|
||||||
selectedSubscriptionId: null,
|
selectedSubscriptionId: null,
|
||||||
isLoadingSubscriptions: false,
|
isLoadingSubscriptions: false,
|
||||||
onSubscriptionChange: jest.fn()
|
onSubscriptionChange: jest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ const createBlankAccount = (): DatabaseAccount => {
|
|||||||
properties: null,
|
properties: null,
|
||||||
location: "",
|
location: "",
|
||||||
tags: null,
|
tags: null,
|
||||||
type: ""
|
type: "",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ const createBlankSubscription = (): Subscription => {
|
|||||||
state: "",
|
state: "",
|
||||||
subscriptionPolicies: null,
|
subscriptionPolicies: null,
|
||||||
tenantId: "",
|
tenantId: "",
|
||||||
uniqueDisplayName: ""
|
uniqueDisplayName: "",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,13 +34,13 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: "switchSubscription",
|
key: "switchSubscription",
|
||||||
onRender: this._renderSubscriptionDropdown.bind(this)
|
onRender: this._renderSubscriptionDropdown.bind(this),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "switchAccount",
|
key: "switchAccount",
|
||||||
onRender: this._renderAccountDropDown.bind(this)
|
onRender: this._renderAccountDropDown.bind(this),
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonStyles: IButtonStyles = {
|
const buttonStyles: IButtonStyles = {
|
||||||
@ -51,27 +51,27 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
paddingLeft: 10,
|
paddingLeft: 10,
|
||||||
marginRight: 5,
|
marginRight: 5,
|
||||||
backgroundColor: StyleConstants.BaseDark,
|
backgroundColor: StyleConstants.BaseDark,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
rootHovered: {
|
rootHovered: {
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
rootFocused: {
|
rootFocused: {
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
rootPressed: {
|
rootPressed: {
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
rootExpanded: {
|
rootExpanded: {
|
||||||
backgroundColor: StyleConstants.BaseHigh,
|
backgroundColor: StyleConstants.BaseHigh,
|
||||||
color: StyleConstants.BaseLight
|
color: StyleConstants.BaseLight,
|
||||||
},
|
},
|
||||||
textContainer: {
|
textContainer: {
|
||||||
flexGrow: "initial"
|
flexGrow: "initial",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonProps: IButtonProps = {
|
const buttonProps: IButtonProps = {
|
||||||
@ -79,7 +79,7 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
menuProps: menuProps,
|
menuProps: menuProps,
|
||||||
styles: buttonStyles,
|
styles: buttonStyles,
|
||||||
className: "accountSwitchButton",
|
className: "accountSwitchButton",
|
||||||
id: "accountSwitchButton"
|
id: "accountSwitchButton",
|
||||||
};
|
};
|
||||||
|
|
||||||
return <DefaultButton {...buttonProps} />;
|
return <DefaultButton {...buttonProps} />;
|
||||||
@ -87,11 +87,11 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
|
|
||||||
private _renderSubscriptionDropdown(): JSX.Element {
|
private _renderSubscriptionDropdown(): JSX.Element {
|
||||||
const { subscriptions, selectedSubscriptionId, isLoadingSubscriptions } = this.props;
|
const { subscriptions, selectedSubscriptionId, isLoadingSubscriptions } = this.props;
|
||||||
const options: IDropdownOption[] = subscriptions.map(sub => {
|
const options: IDropdownOption[] = subscriptions.map((sub) => {
|
||||||
return {
|
return {
|
||||||
key: sub.subscriptionId,
|
key: sub.subscriptionId,
|
||||||
text: sub.displayName,
|
text: sub.displayName,
|
||||||
data: sub
|
data: sub,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
defaultSelectedKey: selectedSubscriptionId,
|
defaultSelectedKey: selectedSubscriptionId,
|
||||||
placeholder: placeHolderText,
|
placeholder: placeHolderText,
|
||||||
styles: {
|
styles: {
|
||||||
callout: "accountSwitchSubscriptionDropdownMenu"
|
callout: "accountSwitchSubscriptionDropdownMenu",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Dropdown {...dropdownProps} />;
|
return <Dropdown {...dropdownProps} />;
|
||||||
@ -126,11 +126,11 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
|
|
||||||
private _renderAccountDropDown(): JSX.Element {
|
private _renderAccountDropDown(): JSX.Element {
|
||||||
const { accounts, selectedAccountName, isLoadingAccounts } = this.props;
|
const { accounts, selectedAccountName, isLoadingAccounts } = this.props;
|
||||||
const options: IDropdownOption[] = accounts.map(account => {
|
const options: IDropdownOption[] = accounts.map((account) => {
|
||||||
return {
|
return {
|
||||||
key: account.name,
|
key: account.name,
|
||||||
text: account.name,
|
text: account.name,
|
||||||
data: account
|
data: account,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
// Fabric UI will also try to select the first non-disabled option from dropdown.
|
// Fabric UI will also try to select the first non-disabled option from dropdown.
|
||||||
@ -138,7 +138,7 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
options.unshift({
|
options.unshift({
|
||||||
key: "select from list",
|
key: "select from list",
|
||||||
text: "Select Cosmos DB account from list",
|
text: "Select Cosmos DB account from list",
|
||||||
data: undefined
|
data: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const placeHolderText = isLoadingAccounts
|
const placeHolderText = isLoadingAccounts
|
||||||
@ -155,8 +155,8 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
|
|||||||
defaultSelectedKey: selectedAccountName,
|
defaultSelectedKey: selectedAccountName,
|
||||||
placeholder: placeHolderText,
|
placeholder: placeHolderText,
|
||||||
styles: {
|
styles: {
|
||||||
callout: "accountSwitchAccountDropdownMenu"
|
callout: "accountSwitchAccountDropdownMenu",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Dropdown {...dropdownProps} />;
|
return <Dropdown {...dropdownProps} />;
|
||||||
|
@ -4,7 +4,7 @@ import { DefaultButton, IButtonStyles } from "office-ui-fabric-react/lib/Button"
|
|||||||
import {
|
import {
|
||||||
IContextualMenuItem,
|
IContextualMenuItem,
|
||||||
IContextualMenuProps,
|
IContextualMenuProps,
|
||||||
ContextualMenuItemType
|
ContextualMenuItemType,
|
||||||
} from "office-ui-fabric-react/lib/ContextualMenu";
|
} from "office-ui-fabric-react/lib/ContextualMenu";
|
||||||
import * as Logger from "../../../Common/Logger";
|
import * as Logger from "../../../Common/Logger";
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
constructor(props: ArcadiaMenuPickerProps) {
|
constructor(props: ArcadiaMenuPickerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedSparkPool: props.selectedSparkPool
|
selectedSparkPool: props.selectedSparkPool,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
try {
|
try {
|
||||||
this.props.onSparkPoolSelect(e, item);
|
this.props.onSparkPoolSelect(e, item);
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedSparkPool: item.text
|
selectedSparkPool: item.text,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "ArcadiaMenuPicker/_onSparkPoolClicked");
|
Logger.logError(error, "ArcadiaMenuPicker/_onSparkPoolClicked");
|
||||||
@ -68,28 +68,28 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { workspaces } = this.props;
|
const { workspaces } = this.props;
|
||||||
let workspaceMenuItems: IContextualMenuItem[] = workspaces.map(workspace => {
|
let workspaceMenuItems: IContextualMenuItem[] = workspaces.map((workspace) => {
|
||||||
let sparkPoolsMenuProps: IContextualMenuProps = {
|
let sparkPoolsMenuProps: IContextualMenuProps = {
|
||||||
items: workspace.sparkPools.map(
|
items: workspace.sparkPools.map(
|
||||||
(sparkpool): IContextualMenuItem => ({
|
(sparkpool): IContextualMenuItem => ({
|
||||||
key: sparkpool.id,
|
key: sparkpool.id,
|
||||||
text: sparkpool.name,
|
text: sparkpool.name,
|
||||||
onClick: this._onSparkPoolClicked
|
onClick: this._onSparkPoolClicked,
|
||||||
})
|
})
|
||||||
)
|
),
|
||||||
};
|
};
|
||||||
if (!sparkPoolsMenuProps.items.length) {
|
if (!sparkPoolsMenuProps.items.length) {
|
||||||
sparkPoolsMenuProps.items.push({
|
sparkPoolsMenuProps.items.push({
|
||||||
key: workspace.id,
|
key: workspace.id,
|
||||||
text: "Create new spark pool",
|
text: "Create new spark pool",
|
||||||
onClick: this._onCreateNewSparkPoolClicked
|
onClick: this._onCreateNewSparkPoolClicked,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: workspace.id,
|
key: workspace.id,
|
||||||
text: workspace.name,
|
text: workspace.name,
|
||||||
subMenuProps: this.props.disableSubmenu ? undefined : sparkPoolsMenuProps
|
subMenuProps: this.props.disableSubmenu ? undefined : sparkPoolsMenuProps,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
workspaceMenuItems.push({
|
workspaceMenuItems.push({
|
||||||
key: "create_workspace",
|
key: "create_workspace",
|
||||||
text: "Create new workspace",
|
text: "Create new workspace",
|
||||||
onClick: this._onCreateNewWorkspaceClicked
|
onClick: this._onCreateNewWorkspaceClicked,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,29 +106,29 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
backgroundColor: "transparent",
|
backgroundColor: "transparent",
|
||||||
margin: "auto 5px",
|
margin: "auto 5px",
|
||||||
padding: "0",
|
padding: "0",
|
||||||
border: "0"
|
border: "0",
|
||||||
},
|
},
|
||||||
rootHovered: {
|
rootHovered: {
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent",
|
||||||
},
|
},
|
||||||
rootChecked: {
|
rootChecked: {
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent",
|
||||||
},
|
},
|
||||||
rootFocused: {
|
rootFocused: {
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent",
|
||||||
},
|
},
|
||||||
rootExpanded: {
|
rootExpanded: {
|
||||||
backgroundColor: "transparent"
|
backgroundColor: "transparent",
|
||||||
},
|
},
|
||||||
flexContainer: {
|
flexContainer: {
|
||||||
height: "30px",
|
height: "30px",
|
||||||
border: "1px solid #a6a6a6",
|
border: "1px solid #a6a6a6",
|
||||||
padding: "0 8px"
|
padding: "0 8px",
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
fontWeight: "400",
|
fontWeight: "400",
|
||||||
fontSize: "12px"
|
fontSize: "12px",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -137,7 +137,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
persistMenu={true}
|
persistMenu={true}
|
||||||
className="arcadia-menu-picker"
|
className="arcadia-menu-picker"
|
||||||
menuProps={{
|
menuProps={{
|
||||||
items: workspaceMenuItems
|
items: workspaceMenuItems,
|
||||||
}}
|
}}
|
||||||
styles={dropdownStyle}
|
styles={dropdownStyle}
|
||||||
/>
|
/>
|
||||||
|
@ -1,56 +1,56 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import template from "./collapsible-panel-component.html";
|
import template from "./collapsible-panel-component.html";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for ko component registration
|
* Helper class for ko component registration
|
||||||
*/
|
*/
|
||||||
export class CollapsiblePanelComponent {
|
export class CollapsiblePanelComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
viewModel: CollapsiblePanelViewModel,
|
viewModel: CollapsiblePanelViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameters for this component
|
* Parameters for this component
|
||||||
*/
|
*/
|
||||||
interface CollapsiblePanelParams {
|
interface CollapsiblePanelParams {
|
||||||
collapsedTitle: ko.Observable<string>;
|
collapsedTitle: ko.Observable<string>;
|
||||||
expandedTitle: ko.Observable<string>;
|
expandedTitle: ko.Observable<string>;
|
||||||
isCollapsed?: ko.Observable<boolean>;
|
isCollapsed?: ko.Observable<boolean>;
|
||||||
collapseToLeft?: boolean;
|
collapseToLeft?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collapsible panel:
|
* Collapsible panel:
|
||||||
* Contains a header with [>] button to collapse and an title ("expandedTitle").
|
* Contains a header with [>] button to collapse and an title ("expandedTitle").
|
||||||
* Collapsing the panel:
|
* Collapsing the panel:
|
||||||
* - shrinks width to narrow amount
|
* - shrinks width to narrow amount
|
||||||
* - hides children
|
* - hides children
|
||||||
* - shows [<]
|
* - shows [<]
|
||||||
* - shows vertical title ("collapsedTitle")
|
* - shows vertical title ("collapsedTitle")
|
||||||
* - the default behavior is to collapse to the right (ie, place this component on the right or use "collapseToLeft" parameter)
|
* - the default behavior is to collapse to the right (ie, place this component on the right or use "collapseToLeft" parameter)
|
||||||
*
|
*
|
||||||
* How to use in your markup:
|
* How to use in your markup:
|
||||||
* <collapsible-panel params="{ collapsedTitle:'Properties', expandedTitle:'Expanded properties' }">
|
* <collapsible-panel params="{ collapsedTitle:'Properties', expandedTitle:'Expanded properties' }">
|
||||||
* <!-- add your markup here: the ko context is the same as outside of collapsible-panel (ie $data) -->
|
* <!-- add your markup here: the ko context is the same as outside of collapsible-panel (ie $data) -->
|
||||||
* </collapsible-panel>
|
* </collapsible-panel>
|
||||||
*
|
*
|
||||||
* Use the optional "isCollapsed" parameter to programmatically collapse/expand the pane from outside the component.
|
* Use the optional "isCollapsed" parameter to programmatically collapse/expand the pane from outside the component.
|
||||||
* Use the optional "collapseToLeft" parameter to collapse to the left.
|
* Use the optional "collapseToLeft" parameter to collapse to the left.
|
||||||
*/
|
*/
|
||||||
class CollapsiblePanelViewModel {
|
class CollapsiblePanelViewModel {
|
||||||
private params: CollapsiblePanelParams;
|
private params: CollapsiblePanelParams;
|
||||||
private isCollapsed: ko.Observable<boolean>;
|
private isCollapsed: ko.Observable<boolean>;
|
||||||
|
|
||||||
public constructor(params: CollapsiblePanelParams) {
|
public constructor(params: CollapsiblePanelParams) {
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.isCollapsed = params.isCollapsed || ko.observable(false);
|
this.isCollapsed = params.isCollapsed || ko.observable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleCollapse(): void {
|
private toggleCollapse(): void {
|
||||||
this.isCollapsed(!this.isCollapsed());
|
this.isCollapsed(!this.isCollapsed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,44 @@
|
|||||||
<div class="collapsiblePanel" data-bind="css: { paneCollapsed:isCollapsed() }">
|
<div class="collapsiblePanel" data-bind="css: { paneCollapsed:isCollapsed() }">
|
||||||
<div class="panelHeader" data-bind="visible: !isCollapsed()">
|
<div class="panelHeader" data-bind="visible: !isCollapsed()">
|
||||||
<span
|
<span
|
||||||
class="collapsedIconContainer collapseExpandButton"
|
class="collapsedIconContainer collapseExpandButton"
|
||||||
data-bind="click:toggleCollapse, css: { 'pull-right':params.collapseToLeft }"
|
data-bind="click:toggleCollapse, css: { 'pull-right':params.collapseToLeft }"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="collapsedIcon imgVerticalAlignment"
|
class="collapsedIcon imgVerticalAlignment"
|
||||||
src="/imgarrowlefticon.svg"
|
src="/imgarrowlefticon.svg"
|
||||||
alt="Collapse"
|
alt="Collapse"
|
||||||
data-bind="css: { expanded:!isCollapsed(), iconMirror:params.collapseToLeft }"
|
data-bind="css: { expanded:!isCollapsed(), iconMirror:params.collapseToLeft }"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="expandedTitle"
|
class="expandedTitle"
|
||||||
data-bind="text: params.expandedTitle, css:{ iconSpacer:!params.collapseToLeft }"
|
data-bind="text: params.expandedTitle, css:{ iconSpacer:!params.collapseToLeft }"
|
||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="collapsibleNav nav" data-bind="visible:isCollapsed">
|
<div class="collapsibleNav nav" data-bind="visible:isCollapsed">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="collapsedBtn collapseExpandButton">
|
<li class="collapsedBtn collapseExpandButton">
|
||||||
<span class="collapsedIconContainer" data-bind="click: toggleCollapse">
|
<span class="collapsedIconContainer" data-bind="click: toggleCollapse">
|
||||||
<img
|
<img
|
||||||
class="collapsedIcon"
|
class="collapsedIcon"
|
||||||
src="/imgarrowlefticon.svg"
|
src="/imgarrowlefticon.svg"
|
||||||
data-bind="css: { expanded:!isCollapsed(), iconMirror:params.collapseToLeft }"
|
data-bind="css: { expanded:!isCollapsed(), iconMirror:params.collapseToLeft }"
|
||||||
alt="Expand"
|
alt="Expand"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="rotatedInner" data-bind="click: toggleCollapse">
|
<span class="rotatedInner" data-bind="click: toggleCollapse">
|
||||||
<span data-bind="text: params.collapsedTitle"></span>
|
<span data-bind="text: params.collapsedTitle"></span>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panelContent" data-bind="visible:!isCollapsed()">
|
<div class="panelContent" data-bind="visible:!isCollapsed()">
|
||||||
<!-- ko with:$parent -->
|
<!-- ko with:$parent -->
|
||||||
<!-- ko template: { nodes: $componentTemplateNodes } -->
|
<!-- ko template: { nodes: $componentTemplateNodes } -->
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -149,9 +149,7 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
|
|||||||
private onLauncherKeyDown(event: React.KeyboardEvent<HTMLDivElement>): boolean {
|
private onLauncherKeyDown(event: React.KeyboardEvent<HTMLDivElement>): boolean {
|
||||||
if (event.keyCode === KeyCodes.DownArrow) {
|
if (event.keyCode === KeyCodes.DownArrow) {
|
||||||
$(this.dropdownElt).hide();
|
$(this.dropdownElt).hide();
|
||||||
$(this.dropdownElt)
|
$(this.dropdownElt).show().focus();
|
||||||
.show()
|
|
||||||
.focus();
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -187,7 +185,7 @@ export class CommandButtonComponent extends React.Component<CommandButtonCompone
|
|||||||
}
|
}
|
||||||
this.props.onCommandClick(e);
|
this.props.onCommandClick(e);
|
||||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||||
commandButtonClicked: this.props.commandButtonLabel
|
commandButtonClicked: this.props.commandButtonLabel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,94 +1,94 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Dialog, DialogType, DialogFooter, IDialogProps } from "office-ui-fabric-react/lib/Dialog";
|
import { Dialog, DialogType, DialogFooter, IDialogProps } from "office-ui-fabric-react/lib/Dialog";
|
||||||
import { IButtonProps, PrimaryButton, DefaultButton } from "office-ui-fabric-react/lib/Button";
|
import { IButtonProps, PrimaryButton, DefaultButton } from "office-ui-fabric-react/lib/Button";
|
||||||
import { ITextFieldProps, TextField } from "office-ui-fabric-react/lib/TextField";
|
import { ITextFieldProps, TextField } from "office-ui-fabric-react/lib/TextField";
|
||||||
import { Link } from "office-ui-fabric-react/lib/Link";
|
import { Link } from "office-ui-fabric-react/lib/Link";
|
||||||
|
|
||||||
export interface TextFieldProps extends ITextFieldProps {
|
export interface TextFieldProps extends ITextFieldProps {
|
||||||
label: string;
|
label: string;
|
||||||
multiline: boolean;
|
multiline: boolean;
|
||||||
autoAdjustHeight: boolean;
|
autoAdjustHeight: boolean;
|
||||||
rows: number;
|
rows: number;
|
||||||
onChange: (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => void;
|
onChange: (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => void;
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LinkProps {
|
export interface LinkProps {
|
||||||
linkText: string;
|
linkText: string;
|
||||||
linkUrl: string;
|
linkUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DialogProps {
|
export interface DialogProps {
|
||||||
title: string;
|
title: string;
|
||||||
subText: string;
|
subText: string;
|
||||||
isModal: boolean;
|
isModal: boolean;
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
textFieldProps?: TextFieldProps;
|
textFieldProps?: TextFieldProps;
|
||||||
linkProps?: LinkProps;
|
linkProps?: LinkProps;
|
||||||
primaryButtonText: string;
|
primaryButtonText: string;
|
||||||
secondaryButtonText: string;
|
secondaryButtonText: string;
|
||||||
onPrimaryButtonClick: () => void;
|
onPrimaryButtonClick: () => void;
|
||||||
onSecondaryButtonClick: () => void;
|
onSecondaryButtonClick: () => void;
|
||||||
primaryButtonDisabled?: boolean;
|
primaryButtonDisabled?: boolean;
|
||||||
type?: DialogType;
|
type?: DialogType;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DIALOG_MIN_WIDTH = "400px";
|
const DIALOG_MIN_WIDTH = "400px";
|
||||||
const DIALOG_MAX_WIDTH = "600px";
|
const DIALOG_MAX_WIDTH = "600px";
|
||||||
const DIALOG_TITLE_FONT_SIZE = "17px";
|
const DIALOG_TITLE_FONT_SIZE = "17px";
|
||||||
const DIALOG_TITLE_FONT_WEIGHT = 400;
|
const DIALOG_TITLE_FONT_WEIGHT = 400;
|
||||||
const DIALOG_SUBTEXT_FONT_SIZE = "15px";
|
const DIALOG_SUBTEXT_FONT_SIZE = "15px";
|
||||||
|
|
||||||
export class DialogComponent extends React.Component<DialogProps, {}> {
|
export class DialogComponent extends React.Component<DialogProps, {}> {
|
||||||
constructor(props: DialogProps) {
|
constructor(props: DialogProps) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const dialogProps: IDialogProps = {
|
const dialogProps: IDialogProps = {
|
||||||
hidden: !this.props.visible,
|
hidden: !this.props.visible,
|
||||||
dialogContentProps: {
|
dialogContentProps: {
|
||||||
type: this.props.type || DialogType.normal,
|
type: this.props.type || DialogType.normal,
|
||||||
title: this.props.title,
|
title: this.props.title,
|
||||||
subText: this.props.subText,
|
subText: this.props.subText,
|
||||||
styles: {
|
styles: {
|
||||||
title: { fontSize: DIALOG_TITLE_FONT_SIZE, fontWeight: DIALOG_TITLE_FONT_WEIGHT },
|
title: { fontSize: DIALOG_TITLE_FONT_SIZE, fontWeight: DIALOG_TITLE_FONT_WEIGHT },
|
||||||
subText: { fontSize: DIALOG_SUBTEXT_FONT_SIZE }
|
subText: { fontSize: DIALOG_SUBTEXT_FONT_SIZE },
|
||||||
},
|
},
|
||||||
showCloseButton: false
|
showCloseButton: false,
|
||||||
},
|
},
|
||||||
modalProps: { isBlocking: this.props.isModal },
|
modalProps: { isBlocking: this.props.isModal },
|
||||||
minWidth: DIALOG_MIN_WIDTH,
|
minWidth: DIALOG_MIN_WIDTH,
|
||||||
maxWidth: DIALOG_MAX_WIDTH
|
maxWidth: DIALOG_MAX_WIDTH,
|
||||||
};
|
};
|
||||||
const textFieldProps: ITextFieldProps = this.props.textFieldProps;
|
const textFieldProps: ITextFieldProps = this.props.textFieldProps;
|
||||||
const linkProps: LinkProps = this.props.linkProps;
|
const linkProps: LinkProps = this.props.linkProps;
|
||||||
const primaryButtonProps: IButtonProps = {
|
const primaryButtonProps: IButtonProps = {
|
||||||
text: this.props.primaryButtonText,
|
text: this.props.primaryButtonText,
|
||||||
disabled: this.props.primaryButtonDisabled || false,
|
disabled: this.props.primaryButtonDisabled || false,
|
||||||
onClick: this.props.onPrimaryButtonClick
|
onClick: this.props.onPrimaryButtonClick,
|
||||||
};
|
};
|
||||||
const secondaryButtonProps: IButtonProps =
|
const secondaryButtonProps: IButtonProps =
|
||||||
this.props.secondaryButtonText && this.props.onSecondaryButtonClick
|
this.props.secondaryButtonText && this.props.onSecondaryButtonClick
|
||||||
? {
|
? {
|
||||||
text: this.props.secondaryButtonText,
|
text: this.props.secondaryButtonText,
|
||||||
onClick: this.props.onSecondaryButtonClick
|
onClick: this.props.onSecondaryButtonClick,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog {...dialogProps}>
|
<Dialog {...dialogProps}>
|
||||||
{textFieldProps && <TextField {...textFieldProps} />}
|
{textFieldProps && <TextField {...textFieldProps} />}
|
||||||
{linkProps && (
|
{linkProps && (
|
||||||
<Link href={linkProps.linkUrl} target="_blank">
|
<Link href={linkProps.linkUrl} target="_blank">
|
||||||
{linkProps.linkText}
|
{linkProps.linkText}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<PrimaryButton {...primaryButtonProps} />
|
<PrimaryButton {...primaryButtonProps} />
|
||||||
{secondaryButtonProps && <DefaultButton {...secondaryButtonProps} />}
|
{secondaryButtonProps && <DefaultButton {...secondaryButtonProps} />}
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
/**
|
/**
|
||||||
* This adapter is responsible to render the Dialog React component
|
* This adapter is responsible to render the Dialog React component
|
||||||
* If the component signals a change through the callback passed in the properties, it must render the React component when appropriate
|
* If the component signals a change through the callback passed in the properties, it must render the React component when appropriate
|
||||||
* and update any knockout observables passed from the parent.
|
* and update any knockout observables passed from the parent.
|
||||||
*/
|
*/
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { DialogComponent, DialogProps } from "./DialogComponent";
|
import { DialogComponent, DialogProps } from "./DialogComponent";
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||||
|
|
||||||
export class DialogComponentAdapter implements ReactAdapter {
|
export class DialogComponentAdapter implements ReactAdapter {
|
||||||
public parameters: ko.Observable<DialogProps>;
|
public parameters: ko.Observable<DialogProps>;
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
public renderComponent(): JSX.Element {
|
||||||
return <DialogComponent {...this.parameters()} />;
|
return <DialogComponent {...this.parameters()} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ export class DiffEditorComponent {
|
|||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
viewModel: DiffEditorViewModel,
|
viewModel: DiffEditorViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ export class DiffEditorViewModel {
|
|||||||
lineNumbers: this.params.lineNumbers || "off",
|
lineNumbers: this.params.lineNumbers || "off",
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
ariaLabel: this.params.ariaLabel,
|
ariaLabel: this.params.ariaLabel,
|
||||||
theme: this.params.theme
|
theme: this.params.theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.params.renderSideBySide !== undefined) {
|
if (this.params.renderSideBySide !== undefined) {
|
||||||
@ -120,7 +120,7 @@ export class DiffEditorViewModel {
|
|||||||
);
|
);
|
||||||
diffEditor.setModel({
|
diffEditor.setModel({
|
||||||
original: originalModel,
|
original: originalModel,
|
||||||
modified: modifiedModel
|
modified: modifiedModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
createCallback(diffEditor);
|
createCallback(diffEditor);
|
||||||
@ -147,7 +147,7 @@ export class DiffEditorViewModel {
|
|||||||
this.observer.observe(document.body, {
|
this.observer.observe(document.body, {
|
||||||
attributes: true,
|
attributes: true,
|
||||||
subtree: true,
|
subtree: true,
|
||||||
childList: true
|
childList: true,
|
||||||
});
|
});
|
||||||
this.editor.focus();
|
this.editor.focus();
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ const createBlankProps = (): DefaultDirectoryDropdownProps => {
|
|||||||
return {
|
return {
|
||||||
defaultDirectoryId: "",
|
defaultDirectoryId: "",
|
||||||
directories: [],
|
directories: [],
|
||||||
onDefaultDirectoryChange: jest.fn()
|
onDefaultDirectoryChange: jest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ const createBlankDirectory = (): Tenant => {
|
|||||||
displayName: "",
|
displayName: "",
|
||||||
domains: [],
|
domains: [],
|
||||||
id: "",
|
id: "",
|
||||||
tenantId: ""
|
tenantId: "",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,27 +90,15 @@ describe("test function", () => {
|
|||||||
|
|
||||||
const wrapper = mount(<DefaultDirectoryDropdownComponent {...props} />);
|
const wrapper = mount(<DefaultDirectoryDropdownComponent {...props} />);
|
||||||
|
|
||||||
wrapper
|
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
|
||||||
.find("div.defaultDirectoryDropdown")
|
|
||||||
.find("div.ms-Dropdown")
|
|
||||||
.simulate("click");
|
|
||||||
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
||||||
wrapper
|
wrapper.find("button.ms-Dropdown-item").at(1).simulate("click");
|
||||||
.find("button.ms-Dropdown-item")
|
|
||||||
.at(1)
|
|
||||||
.simulate("click");
|
|
||||||
expect(props.onDefaultDirectoryChange).toBeCalled();
|
expect(props.onDefaultDirectoryChange).toBeCalled();
|
||||||
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
||||||
|
|
||||||
wrapper
|
wrapper.find("div.defaultDirectoryDropdown").find("div.ms-Dropdown").simulate("click");
|
||||||
.find("div.defaultDirectoryDropdown")
|
|
||||||
.find("div.ms-Dropdown")
|
|
||||||
.simulate("click");
|
|
||||||
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
expect(wrapper.exists("div.ms-Callout-main")).toBe(true);
|
||||||
wrapper
|
wrapper.find("button.ms-Dropdown-item").at(0).simulate("click");
|
||||||
.find("button.ms-Dropdown-item")
|
|
||||||
.at(0)
|
|
||||||
.simulate("click");
|
|
||||||
expect(props.onDefaultDirectoryChange).toBeCalled();
|
expect(props.onDefaultDirectoryChange).toBeCalled();
|
||||||
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
expect(props.onDefaultDirectoryChange).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -19,13 +19,13 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
|
|||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const lastVisitedOption: IDropdownOption = {
|
const lastVisitedOption: IDropdownOption = {
|
||||||
key: DefaultDirectoryDropdownComponent.lastVisitedKey,
|
key: DefaultDirectoryDropdownComponent.lastVisitedKey,
|
||||||
text: "Sign in to your last visited directory"
|
text: "Sign in to your last visited directory",
|
||||||
};
|
};
|
||||||
const directoryOptions: Array<IDropdownOption> = this.props.directories.map(
|
const directoryOptions: Array<IDropdownOption> = this.props.directories.map(
|
||||||
(dirc): IDropdownOption => {
|
(dirc): IDropdownOption => {
|
||||||
return {
|
return {
|
||||||
key: dirc.tenantId,
|
key: dirc.tenantId,
|
||||||
text: `${dirc.displayName}(${dirc.tenantId})`
|
text: `${dirc.displayName}(${dirc.tenantId})`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -35,7 +35,7 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
|
|||||||
options: dropDownOptions,
|
options: dropDownOptions,
|
||||||
defaultSelectedKey: this.props.defaultDirectoryId ? this.props.defaultDirectoryId : lastVisitedOption.key,
|
defaultSelectedKey: this.props.defaultDirectoryId ? this.props.defaultDirectoryId : lastVisitedOption.key,
|
||||||
onChange: this._onDropdownChange,
|
onChange: this._onDropdownChange,
|
||||||
className: "defaultDirectoryDropdown"
|
className: "defaultDirectoryDropdown",
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Dropdown {...dropDownProps} />;
|
return <Dropdown {...dropDownProps} />;
|
||||||
@ -56,12 +56,12 @@ export class DefaultDirectoryDropdownComponent extends React.Component<DefaultDi
|
|||||||
countryCode: undefined,
|
countryCode: undefined,
|
||||||
displayName: undefined,
|
displayName: undefined,
|
||||||
domains: [],
|
domains: [],
|
||||||
id: undefined
|
id: undefined,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedDirectory = _.find(this.props.directories, d => d.tenantId === option.key);
|
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === option.key);
|
||||||
if (!selectedDirectory) {
|
if (!selectedDirectory) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ const createBlankProps = (): DirectoryListProps => {
|
|||||||
return {
|
return {
|
||||||
selectedDirectoryId: undefined,
|
selectedDirectoryId: undefined,
|
||||||
directories: [],
|
directories: [],
|
||||||
onNewDirectorySelected: jest.fn()
|
onNewDirectorySelected: jest.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ const createBlankDirectory = (): Tenant => {
|
|||||||
displayName: undefined,
|
displayName: undefined,
|
||||||
domains: [],
|
domains: [],
|
||||||
id: undefined,
|
id: undefined,
|
||||||
tenantId: undefined
|
tenantId: undefined,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
filterText: ""
|
filterText: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,12 +38,12 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
const filteredItems =
|
const filteredItems =
|
||||||
originalItems && originalItems.length && filterText
|
originalItems && originalItems.length && filterText
|
||||||
? originalItems.filter(
|
? originalItems.filter(
|
||||||
directory =>
|
(directory) =>
|
||||||
directory.displayName &&
|
directory.displayName &&
|
||||||
directory.displayName.toLowerCase().indexOf(filterText && filterText.toLowerCase()) >= 0
|
directory.displayName.toLowerCase().indexOf(filterText && filterText.toLowerCase()) >= 0
|
||||||
)
|
)
|
||||||
: originalItems;
|
: originalItems;
|
||||||
const filteredItemsSelected = filteredItems.map(t => {
|
const filteredItemsSelected = filteredItems.map((t) => {
|
||||||
let tenant: ListTenant = t;
|
let tenant: ListTenant = t;
|
||||||
tenant.selected = t.tenantId === selectedDirectoryId;
|
tenant.selected = t.tenantId === selectedDirectoryId;
|
||||||
return tenant;
|
return tenant;
|
||||||
@ -53,7 +53,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
className: "directoryListFilterTextBox",
|
className: "directoryListFilterTextBox",
|
||||||
placeholder: "Filter by directory name",
|
placeholder: "Filter by directory name",
|
||||||
onChange: this._onFilterChanged,
|
onChange: this._onFilterChanged,
|
||||||
ariaLabel: "Directory filter text box"
|
ariaLabel: "Directory filter text box",
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: add magnify glass to search bar with onRenderSuffix
|
// TODO: add magnify glass to search bar with onRenderSuffix
|
||||||
@ -69,7 +69,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
|
|
||||||
private _onFilterChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text?: string): void => {
|
private _onFilterChanged = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, text?: string): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
filterText: text
|
filterText: text,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -84,19 +84,19 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
height: "auto",
|
height: "auto",
|
||||||
borderBottom: "1px solid #ccc",
|
borderBottom: "1px solid #ccc",
|
||||||
padding: "1px 0",
|
padding: "1px 0",
|
||||||
width: "100%"
|
width: "100%",
|
||||||
},
|
},
|
||||||
rootDisabled: {
|
rootDisabled: {
|
||||||
backgroundColor: "#f1f1f8"
|
backgroundColor: "#f1f1f8",
|
||||||
},
|
},
|
||||||
rootHovered: {
|
rootHovered: {
|
||||||
backgroundColor: "rgba(85,179,255,.1)"
|
backgroundColor: "rgba(85,179,255,.1)",
|
||||||
},
|
},
|
||||||
flexContainer: {
|
flexContainer: {
|
||||||
height: "auto",
|
height: "auto",
|
||||||
justifyContent: "flex-start"
|
justifyContent: "flex-start",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -115,7 +115,7 @@ export class DirectoryListComponent extends React.Component<DirectoryListProps,
|
|||||||
}
|
}
|
||||||
const buttonElement = e.currentTarget;
|
const buttonElement = e.currentTarget;
|
||||||
const selectedDirectoryId = buttonElement.getElementsByClassName("directoryListItemId")[0].textContent;
|
const selectedDirectoryId = buttonElement.getElementsByClassName("directoryListItemId")[0].textContent;
|
||||||
const selectedDirectory = _.find(this.props.directories, d => d.tenantId === selectedDirectoryId);
|
const selectedDirectory = _.find(this.props.directories, (d) => d.tenantId === selectedDirectoryId);
|
||||||
|
|
||||||
this.props.onNewDirectorySelected(selectedDirectory);
|
this.props.onNewDirectorySelected(selectedDirectory);
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,7 @@ describe("Dynamic List Component", () => {
|
|||||||
placeholder: placeholder,
|
placeholder: placeholder,
|
||||||
listItems: items,
|
listItems: items,
|
||||||
buttonText: mockButton,
|
buttonText: mockButton,
|
||||||
ariaLabel: mockAriaLabel
|
ariaLabel: mockAriaLabel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,5 +112,5 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
|
|||||||
*/
|
*/
|
||||||
export const DynamicListComponent = {
|
export const DynamicListComponent = {
|
||||||
viewModel: DynamicListViewModel,
|
viewModel: DynamicListViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
|
@ -1,63 +1,63 @@
|
|||||||
import { JsonEditorParams, JsonEditorViewModel } from "../JsonEditor/JsonEditorComponent";
|
import { JsonEditorParams, JsonEditorViewModel } from "../JsonEditor/JsonEditorComponent";
|
||||||
import template from "./editor-component.html";
|
import template from "./editor-component.html";
|
||||||
import * as monaco from "monaco-editor";
|
import * as monaco from "monaco-editor";
|
||||||
import { SqlCompletionItemProvider, ErrorMarkProvider } from "@azure/cosmos-language-service";
|
import { SqlCompletionItemProvider, ErrorMarkProvider } from "@azure/cosmos-language-service";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for ko component registration
|
* Helper class for ko component registration
|
||||||
*/
|
*/
|
||||||
export class EditorComponent {
|
export class EditorComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
viewModel: EditorViewModel,
|
viewModel: EditorViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameters for this component
|
* Parameters for this component
|
||||||
*/
|
*/
|
||||||
export interface EditorParams extends JsonEditorParams {
|
export interface EditorParams extends JsonEditorParams {
|
||||||
contentType: string;
|
contentType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a generic editor component that builds on top of the pre-existing JsonEditorComponent.
|
* This is a generic editor component that builds on top of the pre-existing JsonEditorComponent.
|
||||||
*/
|
*/
|
||||||
// TODO: Ideally, JsonEditorViewModel should extend EditorViewModel and not the other way around
|
// TODO: Ideally, JsonEditorViewModel should extend EditorViewModel and not the other way around
|
||||||
class EditorViewModel extends JsonEditorViewModel {
|
class EditorViewModel extends JsonEditorViewModel {
|
||||||
public params: EditorParams;
|
public params: EditorParams;
|
||||||
private static providerRegistered: string[] = [];
|
private static providerRegistered: string[] = [];
|
||||||
|
|
||||||
public constructor(params: EditorParams) {
|
public constructor(params: EditorParams) {
|
||||||
super(params);
|
super(params);
|
||||||
this.params = params;
|
this.params = params;
|
||||||
super.createEditor.bind(this);
|
super.createEditor.bind(this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setTimeout is needed as creating the edtior manipulates the dom directly and expects
|
* setTimeout is needed as creating the edtior manipulates the dom directly and expects
|
||||||
* Knockout to have completed all of the initial bindings for the component
|
* Knockout to have completed all of the initial bindings for the component
|
||||||
*/
|
*/
|
||||||
this.params.content() != null &&
|
this.params.content() != null &&
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.createEditor(this.params.content(), this.configureEditor.bind(this));
|
this.createEditor(this.params.content(), this.configureEditor.bind(this));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getEditorLanguage(): string {
|
protected getEditorLanguage(): string {
|
||||||
return this.params.contentType;
|
return this.params.contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected registerCompletionItemProvider() {
|
protected registerCompletionItemProvider() {
|
||||||
let sqlCompletionItemProvider = new SqlCompletionItemProvider();
|
let sqlCompletionItemProvider = new SqlCompletionItemProvider();
|
||||||
if (EditorViewModel.providerRegistered.indexOf("sql") < 0) {
|
if (EditorViewModel.providerRegistered.indexOf("sql") < 0) {
|
||||||
monaco.languages.registerCompletionItemProvider("sql", sqlCompletionItemProvider);
|
monaco.languages.registerCompletionItemProvider("sql", sqlCompletionItemProvider);
|
||||||
EditorViewModel.providerRegistered.push("sql");
|
EditorViewModel.providerRegistered.push("sql");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getErrorMarkers(input: string): Q.Promise<monaco.editor.IMarkerData[]> {
|
protected getErrorMarkers(input: string): Q.Promise<monaco.editor.IMarkerData[]> {
|
||||||
return ErrorMarkProvider.getErrorMark(input);
|
return ErrorMarkProvider.getErrorMark(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ export class EditorReact extends React.Component<EditorReactProps> {
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
ariaLabel: this.props.ariaLabel,
|
ariaLabel: this.props.ariaLabel,
|
||||||
theme: this.props.theme,
|
theme: this.props.theme,
|
||||||
automaticLayout: true
|
automaticLayout: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.rootNode.innerHTML = "";
|
this.rootNode.innerHTML = "";
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
import template from "./error-display-component.html";
|
import template from "./error-display-component.html";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for ko component registration
|
* Helper class for ko component registration
|
||||||
* This component displays an error as designed in:
|
* This component displays an error as designed in:
|
||||||
* https://microsoft.sharepoint.com/teams/DPX/Modern/DocDB/_layouts/15/WopiFrame.aspx?sourcedoc={66864d4a-f925-4cbe-9eb4-79f8d191a115}&action=edit&wd=target%28DocumentDB%20emulator%2Eone%7CE617D0A7-F77C-4968-B75A-1451049F4FEA%2FError%20notification%7CAA1E4BC9-4D72-472C-B40C-2437FA217226%2F%29
|
* https://microsoft.sharepoint.com/teams/DPX/Modern/DocDB/_layouts/15/WopiFrame.aspx?sourcedoc={66864d4a-f925-4cbe-9eb4-79f8d191a115}&action=edit&wd=target%28DocumentDB%20emulator%2Eone%7CE617D0A7-F77C-4968-B75A-1451049F4FEA%2FError%20notification%7CAA1E4BC9-4D72-472C-B40C-2437FA217226%2F%29
|
||||||
* TODO: support "More details"
|
* TODO: support "More details"
|
||||||
*/
|
*/
|
||||||
export class ErrorDisplayComponent {
|
export class ErrorDisplayComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
viewModel: ErrorDisplayViewModel,
|
viewModel: ErrorDisplayViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameters for this component
|
* Parameters for this component
|
||||||
*/
|
*/
|
||||||
interface ErrorDisplayParams {
|
interface ErrorDisplayParams {
|
||||||
errorMsg: ko.Observable<string>; // Primary message
|
errorMsg: ko.Observable<string>; // Primary message
|
||||||
}
|
}
|
||||||
|
|
||||||
class ErrorDisplayViewModel {
|
class ErrorDisplayViewModel {
|
||||||
private params: ErrorDisplayParams;
|
private params: ErrorDisplayParams;
|
||||||
public constructor(params: ErrorDisplayParams) {
|
public constructor(params: ErrorDisplayParams) {
|
||||||
this.params = params;
|
this.params = params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="warningErrorContainer" data-bind="visible: !!params.errorMsg()">
|
<div class="warningErrorContainer" data-bind="visible: !!params.errorMsg()">
|
||||||
<div class="warningErrorContent">
|
<div class="warningErrorContent">
|
||||||
<span><img src="/error_red.svg" alt="Error"/></span>
|
<span><img src="/error_red.svg" alt="Error" /></span>
|
||||||
<span class="settingErrorMsg warningErrorDetailsLinkContainer" data-bind="text: params.errorMsg()"></span>
|
<span class="settingErrorMsg warningErrorDetailsLinkContainer" data-bind="text: params.errorMsg()"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,23 +15,23 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
const baseUrlOptions = [
|
const baseUrlOptions = [
|
||||||
{ key: "https://localhost:1234/explorer.html", text: "localhost:1234" },
|
{ key: "https://localhost:1234/explorer.html", text: "localhost:1234" },
|
||||||
{ key: "https://cosmos.azure.com/explorer.html", text: "cosmos.azure.com" },
|
{ key: "https://cosmos.azure.com/explorer.html", text: "cosmos.azure.com" },
|
||||||
{ key: "https://portal.azure.com", text: "portal" }
|
{ key: "https://portal.azure.com", text: "portal" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const platformOptions = [
|
const platformOptions = [
|
||||||
{ key: "Hosted", text: "Hosted" },
|
{ key: "Hosted", text: "Hosted" },
|
||||||
{ key: "Portal", text: "Portal" },
|
{ key: "Portal", text: "Portal" },
|
||||||
{ key: "Emulator", text: "Emulator" },
|
{ key: "Emulator", text: "Emulator" },
|
||||||
{ key: "", text: "None" }
|
{ key: "", text: "None" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// React hooks to keep state
|
// React hooks to keep state
|
||||||
const [baseUrl, setBaseUrl] = React.useState<IDropdownOption>(
|
const [baseUrl, setBaseUrl] = React.useState<IDropdownOption>(
|
||||||
baseUrlOptions.find(o => o.key === window.location.origin + window.location.pathname) || baseUrlOptions[0]
|
baseUrlOptions.find((o) => o.key === window.location.origin + window.location.pathname) || baseUrlOptions[0]
|
||||||
);
|
);
|
||||||
const [platform, setPlatform] = React.useState<IDropdownOption>(
|
const [platform, setPlatform] = React.useState<IDropdownOption>(
|
||||||
urlParams.has("platform")
|
urlParams.has("platform")
|
||||||
? platformOptions.find(o => o.key === urlParams.get("platform")) || platformOptions[0]
|
? platformOptions.find((o) => o.key === urlParams.get("platform")) || platformOptions[0]
|
||||||
: platformOptions[0]
|
: platformOptions[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
{
|
{
|
||||||
key: "feature.enablefixedcollectionwithsharedthroughput",
|
key: "feature.enablefixedcollectionwithsharedthroughput",
|
||||||
label: "Enable fixed collection with shared throughput",
|
label: "Enable fixed collection with shared throughput",
|
||||||
value: "true"
|
value: "true",
|
||||||
},
|
},
|
||||||
{ key: "feature.ttl90days", label: "TTL 90 days", value: "true" },
|
{ key: "feature.ttl90days", label: "TTL 90 days", value: "true" },
|
||||||
{ key: "feature.enablenotebooks", label: "Enable notebooks", value: "true" },
|
{ key: "feature.enablenotebooks", label: "Enable notebooks", value: "true" },
|
||||||
@ -62,10 +62,10 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
key: "feature.customportal",
|
key: "feature.customportal",
|
||||||
label: "Force Production portal (portal only)",
|
label: "Force Production portal (portal only)",
|
||||||
value: "false",
|
value: "false",
|
||||||
disabled: (): boolean => baseUrl.key !== "https://portal.azure.com"
|
disabled: (): boolean => baseUrl.key !== "https://portal.azure.com",
|
||||||
},
|
},
|
||||||
{ key: "feature.enablespark", label: "Enable Synapse", value: "true" },
|
{ key: "feature.enablespark", label: "Enable Synapse", value: "true" },
|
||||||
{ key: "feature.enableautopilotv2", label: "Enable Auto-pilot V2", value: "true" }
|
{ key: "feature.enableautopilotv2", label: "Enable Auto-pilot V2", value: "true" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const stringFeatures: {
|
const stringFeatures: {
|
||||||
@ -84,23 +84,23 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
key: "dataExplorerSource",
|
key: "dataExplorerSource",
|
||||||
label: "Data Explorer Source (portal only)",
|
label: "Data Explorer Source (portal only)",
|
||||||
placeholder: "https://localhost:1234/explorer.html",
|
placeholder: "https://localhost:1234/explorer.html",
|
||||||
disabled: (): boolean => baseUrl.key !== "https://portal.azure.com"
|
disabled: (): boolean => baseUrl.key !== "https://portal.azure.com",
|
||||||
},
|
},
|
||||||
{ key: "feature.livyendpoint", label: "Livy endpoint", placeholder: "" }
|
{ key: "feature.livyendpoint", label: "Livy endpoint", placeholder: "" },
|
||||||
];
|
];
|
||||||
|
|
||||||
booleanFeatures.forEach(
|
booleanFeatures.forEach(
|
||||||
f => (f.reactState = React.useState<boolean>(urlParams.has(f.key) ? urlParams.get(f.key) === "true" : false))
|
(f) => (f.reactState = React.useState<boolean>(urlParams.has(f.key) ? urlParams.get(f.key) === "true" : false))
|
||||||
);
|
);
|
||||||
stringFeatures.forEach(
|
stringFeatures.forEach(
|
||||||
f => (f.reactState = React.useState<string>(urlParams.has(f.key) ? urlParams.get(f.key) : undefined))
|
(f) => (f.reactState = React.useState<string>(urlParams.has(f.key) ? urlParams.get(f.key) : undefined))
|
||||||
);
|
);
|
||||||
|
|
||||||
const buildUrl = (): string => {
|
const buildUrl = (): string => {
|
||||||
const fragments = (platform.key === "" ? [] : [`platform=${platform.key}`])
|
const fragments = (platform.key === "" ? [] : [`platform=${platform.key}`])
|
||||||
.concat(booleanFeatures.map(f => (f.reactState[0] ? `${f.key}=${f.value}` : "")))
|
.concat(booleanFeatures.map((f) => (f.reactState[0] ? `${f.key}=${f.value}` : "")))
|
||||||
.concat(stringFeatures.map(f => (f.reactState[0] ? `${f.key}=${encodeURIComponent(f.reactState[0])}` : "")))
|
.concat(stringFeatures.map((f) => (f.reactState[0] ? `${f.key}=${encodeURIComponent(f.reactState[0])}` : "")))
|
||||||
.filter(v => v && v.length > 0);
|
.filter((v) => v && v.length > 0);
|
||||||
|
|
||||||
const paramString = fragments.length < 1 ? "" : `?${fragments.join("&")}`;
|
const paramString = fragments.length < 1 ? "" : `?${fragments.join("&")}`;
|
||||||
return `${baseUrl.key}${paramString}`;
|
return `${baseUrl.key}${paramString}`;
|
||||||
@ -115,38 +115,38 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
booleanFeatures.forEach(
|
booleanFeatures.forEach(
|
||||||
f =>
|
(f) =>
|
||||||
(f.onChange = (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean): void => {
|
(f.onChange = (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean): void => {
|
||||||
f.reactState[1](checked);
|
f.reactState[1](checked);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
stringFeatures.forEach(
|
stringFeatures.forEach(
|
||||||
f =>
|
(f) =>
|
||||||
(f.onChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void => {
|
(f.onChange = (event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void => {
|
||||||
f.reactState[1](newValue);
|
f.reactState[1](newValue);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const onNotebookShortcut = (): void => {
|
const onNotebookShortcut = (): void => {
|
||||||
booleanFeatures.find(f => f.key === "feature.enablenotebooks").reactState[1](true);
|
booleanFeatures.find((f) => f.key === "feature.enablenotebooks").reactState[1](true);
|
||||||
stringFeatures
|
stringFeatures
|
||||||
.find(f => f.key === "feature.notebookserverurl")
|
.find((f) => f.key === "feature.notebookserverurl")
|
||||||
.reactState[1]("https://localhost:10001/12345/notebook/");
|
.reactState[1]("https://localhost:10001/12345/notebook/");
|
||||||
stringFeatures.find(f => f.key === "feature.notebookservertoken").reactState[1]("token");
|
stringFeatures.find((f) => f.key === "feature.notebookservertoken").reactState[1]("token");
|
||||||
stringFeatures.find(f => f.key === "feature.notebookbasepath").reactState[1]("./notebooks");
|
stringFeatures.find((f) => f.key === "feature.notebookbasepath").reactState[1]("./notebooks");
|
||||||
setPlatform(platformOptions.find(o => o.key === "Hosted"));
|
setPlatform(platformOptions.find((o) => o.key === "Hosted"));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPortalLocalDEShortcut = (): void => {
|
const onPortalLocalDEShortcut = (): void => {
|
||||||
setBaseUrl(baseUrlOptions.find(o => o.key === "https://portal.azure.com"));
|
setBaseUrl(baseUrlOptions.find((o) => o.key === "https://portal.azure.com"));
|
||||||
setPlatform(platformOptions.find(o => o.key === "Portal"));
|
setPlatform(platformOptions.find((o) => o.key === "Portal"));
|
||||||
stringFeatures.find(f => f.key === "dataExplorerSource").reactState[1]("https://localhost:1234/explorer.html");
|
stringFeatures.find((f) => f.key === "dataExplorerSource").reactState[1]("https://localhost:1234/explorer.html");
|
||||||
};
|
};
|
||||||
|
|
||||||
const onReset = (): void => {
|
const onReset = (): void => {
|
||||||
booleanFeatures.forEach(f => f.reactState[1](false));
|
booleanFeatures.forEach((f) => f.reactState[1](false));
|
||||||
stringFeatures.forEach(f => f.reactState[1](""));
|
stringFeatures.forEach((f) => f.reactState[1](""));
|
||||||
};
|
};
|
||||||
|
|
||||||
const stackTokens = { childrenGap: 10 };
|
const stackTokens = { childrenGap: 10 };
|
||||||
@ -165,7 +165,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
const anchorOptions = {
|
const anchorOptions = {
|
||||||
href: buildUrl(),
|
href: buildUrl(),
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
rel: "noopener"
|
rel: "noopener",
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -197,7 +197,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Stack className="checkboxRow" horizontalAlign="space-between">
|
<Stack className="checkboxRow" horizontalAlign="space-between">
|
||||||
{leftBooleanFeatures.map(f => (
|
{leftBooleanFeatures.map((f) => (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
key={f.key}
|
key={f.key}
|
||||||
label={f.label}
|
label={f.label}
|
||||||
@ -208,7 +208,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack className="checkboxRow" horizontalAlign="space-between">
|
<Stack className="checkboxRow" horizontalAlign="space-between">
|
||||||
{rightBooleanFeatures.map(f => (
|
{rightBooleanFeatures.map((f) => (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
key={f.key}
|
key={f.key}
|
||||||
label={f.label}
|
label={f.label}
|
||||||
@ -221,7 +221,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
<Stack horizontal tokens={stackTokens}>
|
<Stack horizontal tokens={stackTokens}>
|
||||||
<Stack horizontalAlign="space-between">
|
<Stack horizontalAlign="space-between">
|
||||||
{leftStringFeatures.map(f => (
|
{leftStringFeatures.map((f) => (
|
||||||
<TextField
|
<TextField
|
||||||
key={f.key}
|
key={f.key}
|
||||||
value={f.reactState[0]}
|
value={f.reactState[0]}
|
||||||
@ -234,7 +234,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack horizontalAlign="space-between">
|
<Stack horizontalAlign="space-between">
|
||||||
{rightStringFeatures.map(f => (
|
{rightStringFeatures.map((f) => (
|
||||||
<TextField
|
<TextField
|
||||||
key={f.key}
|
key={f.key}
|
||||||
value={f.reactState[0]}
|
value={f.reactState[0]}
|
||||||
|
@ -20,7 +20,7 @@ export const FeaturePanelLauncher: React.FunctionComponent = (): JSX.Element =>
|
|||||||
container: {
|
container: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexFlow: "column nowrap",
|
flexFlow: "column nowrap",
|
||||||
alignItems: "stretch"
|
alignItems: "stretch",
|
||||||
},
|
},
|
||||||
header: [
|
header: [
|
||||||
// tslint:disable-next-line:deprecation
|
// tslint:disable-next-line:deprecation
|
||||||
@ -32,16 +32,16 @@ export const FeaturePanelLauncher: React.FunctionComponent = (): JSX.Element =>
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
fontWeight: FontWeights.semibold,
|
fontWeight: FontWeights.semibold,
|
||||||
padding: "12px 12px 14px 24px"
|
padding: "12px 12px 14px 24px",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
body: {
|
body: {
|
||||||
flex: "4 4 auto",
|
flex: "4 4 auto",
|
||||||
overflowY: "hidden",
|
overflowY: "hidden",
|
||||||
marginBottom: 40,
|
marginBottom: 40,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
display: "flex"
|
display: "flex",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const iconButtonStyles = {
|
const iconButtonStyles = {
|
||||||
@ -49,11 +49,11 @@ export const FeaturePanelLauncher: React.FunctionComponent = (): JSX.Element =>
|
|||||||
color: theme.palette.neutralPrimary,
|
color: theme.palette.neutralPrimary,
|
||||||
marginLeft: "auto",
|
marginLeft: "auto",
|
||||||
marginTop: "4px",
|
marginTop: "4px",
|
||||||
marginRight: "2px"
|
marginRight: "2px",
|
||||||
},
|
},
|
||||||
rootHovered: {
|
rootHovered: {
|
||||||
color: theme.palette.neutralDark
|
color: theme.palette.neutralDark,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
const cancelIcon: IIconProps = { iconName: "Cancel" };
|
const cancelIcon: IIconProps = { iconName: "Cancel" };
|
||||||
const hideModal = (): void => showModal(false);
|
const hideModal = (): void => showModal(false);
|
||||||
|
@ -1,132 +1,132 @@
|
|||||||
import { DefaultButton, IButtonProps, ITextFieldProps, TextField } from "office-ui-fabric-react";
|
import { DefaultButton, IButtonProps, ITextFieldProps, TextField } from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
import { RepoListItem } from "./GitHubReposComponent";
|
import { RepoListItem } from "./GitHubReposComponent";
|
||||||
import { ChildrenMargin } from "./GitHubStyleConstants";
|
import { ChildrenMargin } from "./GitHubStyleConstants";
|
||||||
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
||||||
import { IGitHubRepo } from "../../../GitHub/GitHubClient";
|
import { IGitHubRepo } from "../../../GitHub/GitHubClient";
|
||||||
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import UrlUtility from "../../../Common/UrlUtility";
|
import UrlUtility from "../../../Common/UrlUtility";
|
||||||
|
|
||||||
export interface AddRepoComponentProps {
|
export interface AddRepoComponentProps {
|
||||||
container: ViewModels.Explorer;
|
container: ViewModels.Explorer;
|
||||||
getRepo: (owner: string, repo: string) => Promise<IGitHubRepo>;
|
getRepo: (owner: string, repo: string) => Promise<IGitHubRepo>;
|
||||||
pinRepo: (item: RepoListItem) => void;
|
pinRepo: (item: RepoListItem) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddRepoComponentState {
|
interface AddRepoComponentState {
|
||||||
textFieldValue: string;
|
textFieldValue: string;
|
||||||
textFieldErrorMessage: string;
|
textFieldErrorMessage: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AddRepoComponent extends React.Component<AddRepoComponentProps, AddRepoComponentState> {
|
export class AddRepoComponent extends React.Component<AddRepoComponentProps, AddRepoComponentState> {
|
||||||
private static readonly DescriptionText =
|
private static readonly DescriptionText =
|
||||||
"Don't see what you're looking for? Add your repo/branch, or any public repo (read-access only) by entering the URL: ";
|
"Don't see what you're looking for? Add your repo/branch, or any public repo (read-access only) by entering the URL: ";
|
||||||
private static readonly ButtonText = "Add";
|
private static readonly ButtonText = "Add";
|
||||||
private static readonly TextFieldPlaceholder = "https://github.com/owner/repo/tree/branch";
|
private static readonly TextFieldPlaceholder = "https://github.com/owner/repo/tree/branch";
|
||||||
private static readonly TextFieldErrorMessage = "Invalid url";
|
private static readonly TextFieldErrorMessage = "Invalid url";
|
||||||
private static readonly DefaultBranchName = "master";
|
private static readonly DefaultBranchName = "master";
|
||||||
|
|
||||||
constructor(props: AddRepoComponentProps) {
|
constructor(props: AddRepoComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
textFieldValue: "",
|
textFieldValue: "",
|
||||||
textFieldErrorMessage: undefined
|
textFieldErrorMessage: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const textFieldProps: ITextFieldProps = {
|
const textFieldProps: ITextFieldProps = {
|
||||||
placeholder: AddRepoComponent.TextFieldPlaceholder,
|
placeholder: AddRepoComponent.TextFieldPlaceholder,
|
||||||
autoFocus: true,
|
autoFocus: true,
|
||||||
value: this.state.textFieldValue,
|
value: this.state.textFieldValue,
|
||||||
errorMessage: this.state.textFieldErrorMessage,
|
errorMessage: this.state.textFieldErrorMessage,
|
||||||
onChange: this.onTextFieldChange
|
onChange: this.onTextFieldChange,
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonProps: IButtonProps = {
|
const buttonProps: IButtonProps = {
|
||||||
text: AddRepoComponent.ButtonText,
|
text: AddRepoComponent.ButtonText,
|
||||||
ariaLabel: AddRepoComponent.ButtonText,
|
ariaLabel: AddRepoComponent.ButtonText,
|
||||||
onClick: this.onAddRepoButtonClick
|
onClick: this.onAddRepoButtonClick,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p style={{ marginBottom: ChildrenMargin }}>{AddRepoComponent.DescriptionText}</p>
|
<p style={{ marginBottom: ChildrenMargin }}>{AddRepoComponent.DescriptionText}</p>
|
||||||
<TextField {...textFieldProps} />
|
<TextField {...textFieldProps} />
|
||||||
<DefaultButton style={{ marginTop: ChildrenMargin }} {...buttonProps} />
|
<DefaultButton style={{ marginTop: ChildrenMargin }} {...buttonProps} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onTextFieldChange = (
|
private onTextFieldChange = (
|
||||||
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
newValue?: string
|
newValue?: string
|
||||||
): void => {
|
): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
textFieldValue: newValue || "",
|
textFieldValue: newValue || "",
|
||||||
textFieldErrorMessage: undefined
|
textFieldErrorMessage: undefined,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAddRepoButtonClick = async (): Promise<void> => {
|
private onAddRepoButtonClick = async (): Promise<void> => {
|
||||||
const startKey: number = TelemetryProcessor.traceStart(Action.NotebooksGitHubManualRepoAdd, {
|
const startKey: number = TelemetryProcessor.traceStart(Action.NotebooksGitHubManualRepoAdd, {
|
||||||
databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name,
|
databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name,
|
||||||
defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(),
|
defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.Notebook
|
dataExplorerArea: Constants.Areas.Notebook,
|
||||||
});
|
});
|
||||||
let enteredUrl = this.state.textFieldValue;
|
let enteredUrl = this.state.textFieldValue;
|
||||||
if (enteredUrl.indexOf("/tree/") === -1) {
|
if (enteredUrl.indexOf("/tree/") === -1) {
|
||||||
enteredUrl = UrlUtility.createUri(enteredUrl, `tree/${AddRepoComponent.DefaultBranchName}`);
|
enteredUrl = UrlUtility.createUri(enteredUrl, `tree/${AddRepoComponent.DefaultBranchName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const repoInfo = GitHubUtils.fromRepoUri(enteredUrl);
|
const repoInfo = GitHubUtils.fromRepoUri(enteredUrl);
|
||||||
if (repoInfo) {
|
if (repoInfo) {
|
||||||
this.setState({
|
this.setState({
|
||||||
textFieldValue: "",
|
textFieldValue: "",
|
||||||
textFieldErrorMessage: undefined
|
textFieldErrorMessage: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const repo = await this.props.getRepo(repoInfo.owner, repoInfo.repo);
|
const repo = await this.props.getRepo(repoInfo.owner, repoInfo.repo);
|
||||||
if (repo) {
|
if (repo) {
|
||||||
const item: RepoListItem = {
|
const item: RepoListItem = {
|
||||||
key: GitHubUtils.toRepoFullName(repo.owner, repo.name),
|
key: GitHubUtils.toRepoFullName(repo.owner, repo.name),
|
||||||
repo,
|
repo,
|
||||||
branches: [
|
branches: [
|
||||||
{
|
{
|
||||||
name: repoInfo.branch
|
name: repoInfo.branch,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
Action.NotebooksGitHubManualRepoAdd,
|
Action.NotebooksGitHubManualRepoAdd,
|
||||||
{
|
{
|
||||||
databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name,
|
databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name,
|
||||||
defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(),
|
defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.Notebook
|
dataExplorerArea: Constants.Areas.Notebook,
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
return this.props.pinRepo(item);
|
return this.props.pinRepo(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
textFieldErrorMessage: AddRepoComponent.TextFieldErrorMessage
|
textFieldErrorMessage: AddRepoComponent.TextFieldErrorMessage,
|
||||||
});
|
});
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.NotebooksGitHubManualRepoAdd,
|
Action.NotebooksGitHubManualRepoAdd,
|
||||||
{
|
{
|
||||||
databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name,
|
databaseAccountName: this.props.container.databaseAccount() && this.props.container.databaseAccount().name,
|
||||||
defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(),
|
defaultExperience: this.props.container.defaultExperience && this.props.container.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.Notebook,
|
dataExplorerArea: Constants.Areas.Notebook,
|
||||||
error: AddRepoComponent.TextFieldErrorMessage
|
error: AddRepoComponent.TextFieldErrorMessage,
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,91 +1,91 @@
|
|||||||
import {
|
import {
|
||||||
ChoiceGroup,
|
ChoiceGroup,
|
||||||
IButtonProps,
|
IButtonProps,
|
||||||
IChoiceGroupProps,
|
IChoiceGroupProps,
|
||||||
PrimaryButton,
|
PrimaryButton,
|
||||||
IChoiceGroupOption
|
IChoiceGroupOption,
|
||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ChildrenMargin } from "./GitHubStyleConstants";
|
import { ChildrenMargin } from "./GitHubStyleConstants";
|
||||||
|
|
||||||
export interface AuthorizeAccessComponentProps {
|
export interface AuthorizeAccessComponentProps {
|
||||||
scope: string;
|
scope: string;
|
||||||
authorizeAccess: (scope: string) => void;
|
authorizeAccess: (scope: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthorizeAccessComponentState {
|
export interface AuthorizeAccessComponentState {
|
||||||
scope: string;
|
scope: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AuthorizeAccessComponent extends React.Component<
|
export class AuthorizeAccessComponent extends React.Component<
|
||||||
AuthorizeAccessComponentProps,
|
AuthorizeAccessComponentProps,
|
||||||
AuthorizeAccessComponentState
|
AuthorizeAccessComponentState
|
||||||
> {
|
> {
|
||||||
// Scopes supported by GitHub OAuth. We're only interested in ones which allow us access to repos.
|
// Scopes supported by GitHub OAuth. We're only interested in ones which allow us access to repos.
|
||||||
// https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/
|
// https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/
|
||||||
public static readonly Scopes = {
|
public static readonly Scopes = {
|
||||||
Public: {
|
Public: {
|
||||||
key: "public_repo",
|
key: "public_repo",
|
||||||
text: "Public repos only"
|
text: "Public repos only",
|
||||||
},
|
},
|
||||||
PublicAndPrivate: {
|
PublicAndPrivate: {
|
||||||
key: "repo",
|
key: "repo",
|
||||||
text: "Public and private repos"
|
text: "Public and private repos",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly DescriptionPara1 =
|
private static readonly DescriptionPara1 =
|
||||||
"Connect your notebooks workspace to GitHub. You'll be able to view, edit, and run notebooks stored in your GitHub repositories in Data Explorer.";
|
"Connect your notebooks workspace to GitHub. You'll be able to view, edit, and run notebooks stored in your GitHub repositories in Data Explorer.";
|
||||||
private static readonly DescriptionPara2 =
|
private static readonly DescriptionPara2 =
|
||||||
"Complete setup by authorizing Azure Cosmos DB to access the repositories in your GitHub account: ";
|
"Complete setup by authorizing Azure Cosmos DB to access the repositories in your GitHub account: ";
|
||||||
private static readonly AuthorizeButtonText = "Authorize access";
|
private static readonly AuthorizeButtonText = "Authorize access";
|
||||||
|
|
||||||
private onChoiceGroupChange = (event: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void =>
|
private onChoiceGroupChange = (event: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void =>
|
||||||
this.setState({
|
this.setState({
|
||||||
scope: option.key
|
scope: option.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
private onButtonClick = (): void => this.props.authorizeAccess(this.state.scope);
|
private onButtonClick = (): void => this.props.authorizeAccess(this.state.scope);
|
||||||
|
|
||||||
constructor(props: AuthorizeAccessComponentProps) {
|
constructor(props: AuthorizeAccessComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
scope: this.props.scope
|
scope: this.props.scope,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const choiceGroupProps: IChoiceGroupProps = {
|
const choiceGroupProps: IChoiceGroupProps = {
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
key: AuthorizeAccessComponent.Scopes.Public.key,
|
key: AuthorizeAccessComponent.Scopes.Public.key,
|
||||||
text: AuthorizeAccessComponent.Scopes.Public.text,
|
text: AuthorizeAccessComponent.Scopes.Public.text,
|
||||||
ariaLabel: AuthorizeAccessComponent.Scopes.Public.text
|
ariaLabel: AuthorizeAccessComponent.Scopes.Public.text,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: AuthorizeAccessComponent.Scopes.PublicAndPrivate.key,
|
key: AuthorizeAccessComponent.Scopes.PublicAndPrivate.key,
|
||||||
text: AuthorizeAccessComponent.Scopes.PublicAndPrivate.text,
|
text: AuthorizeAccessComponent.Scopes.PublicAndPrivate.text,
|
||||||
ariaLabel: AuthorizeAccessComponent.Scopes.PublicAndPrivate.text
|
ariaLabel: AuthorizeAccessComponent.Scopes.PublicAndPrivate.text,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
selectedKey: this.state.scope,
|
selectedKey: this.state.scope,
|
||||||
onChange: this.onChoiceGroupChange
|
onChange: this.onChoiceGroupChange,
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonProps: IButtonProps = {
|
const buttonProps: IButtonProps = {
|
||||||
text: AuthorizeAccessComponent.AuthorizeButtonText,
|
text: AuthorizeAccessComponent.AuthorizeButtonText,
|
||||||
ariaLabel: AuthorizeAccessComponent.AuthorizeButtonText,
|
ariaLabel: AuthorizeAccessComponent.AuthorizeButtonText,
|
||||||
onClick: this.onButtonClick
|
onClick: this.onButtonClick,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>{AuthorizeAccessComponent.DescriptionPara1}</p>
|
<p>{AuthorizeAccessComponent.DescriptionPara1}</p>
|
||||||
<p style={{ marginTop: ChildrenMargin }}>{AuthorizeAccessComponent.DescriptionPara2}</p>
|
<p style={{ marginTop: ChildrenMargin }}>{AuthorizeAccessComponent.DescriptionPara2}</p>
|
||||||
<ChoiceGroup style={{ marginTop: ChildrenMargin }} {...choiceGroupProps} />
|
<ChoiceGroup style={{ marginTop: ChildrenMargin }} {...choiceGroupProps} />
|
||||||
<PrimaryButton style={{ marginTop: ChildrenMargin }} {...buttonProps} />
|
<PrimaryButton style={{ marginTop: ChildrenMargin }} {...buttonProps} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,85 +1,85 @@
|
|||||||
import { DefaultButton, IButtonProps, Link, PrimaryButton } from "office-ui-fabric-react";
|
import { DefaultButton, IButtonProps, Link, PrimaryButton } from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { IGitHubBranch, IGitHubRepo } from "../../../GitHub/GitHubClient";
|
import { IGitHubBranch, IGitHubRepo } from "../../../GitHub/GitHubClient";
|
||||||
import { AddRepoComponent, AddRepoComponentProps } from "./AddRepoComponent";
|
import { AddRepoComponent, AddRepoComponentProps } from "./AddRepoComponent";
|
||||||
import { AuthorizeAccessComponent, AuthorizeAccessComponentProps } from "./AuthorizeAccessComponent";
|
import { AuthorizeAccessComponent, AuthorizeAccessComponentProps } from "./AuthorizeAccessComponent";
|
||||||
import { ButtonsFooterStyle, ChildrenMargin, ContentFooterStyle } from "./GitHubStyleConstants";
|
import { ButtonsFooterStyle, ChildrenMargin, ContentFooterStyle } from "./GitHubStyleConstants";
|
||||||
import { ReposListComponent, ReposListComponentProps } from "./ReposListComponent";
|
import { ReposListComponent, ReposListComponentProps } from "./ReposListComponent";
|
||||||
|
|
||||||
export interface GitHubReposComponentProps {
|
export interface GitHubReposComponentProps {
|
||||||
showAuthorizeAccess: boolean;
|
showAuthorizeAccess: boolean;
|
||||||
authorizeAccessProps: AuthorizeAccessComponentProps;
|
authorizeAccessProps: AuthorizeAccessComponentProps;
|
||||||
reposListProps: ReposListComponentProps;
|
reposListProps: ReposListComponentProps;
|
||||||
addRepoProps: AddRepoComponentProps;
|
addRepoProps: AddRepoComponentProps;
|
||||||
resetConnection: () => void;
|
resetConnection: () => void;
|
||||||
onOkClick: () => void;
|
onOkClick: () => void;
|
||||||
onCancelClick: () => void;
|
onCancelClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RepoListItem {
|
export interface RepoListItem {
|
||||||
key: string;
|
key: string;
|
||||||
repo: IGitHubRepo;
|
repo: IGitHubRepo;
|
||||||
branches: IGitHubBranch[];
|
branches: IGitHubBranch[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GitHubReposComponent extends React.Component<GitHubReposComponentProps> {
|
export class GitHubReposComponent extends React.Component<GitHubReposComponentProps> {
|
||||||
public static readonly ConnectToGitHubTitle = "Connect to GitHub";
|
public static readonly ConnectToGitHubTitle = "Connect to GitHub";
|
||||||
public static readonly ManageGitHubRepoTitle = "Manage GitHub settings";
|
public static readonly ManageGitHubRepoTitle = "Manage GitHub settings";
|
||||||
private static readonly ManageGitHubRepoDescription =
|
private static readonly ManageGitHubRepoDescription =
|
||||||
"Select your GitHub repos and branch(es) to pin to your notebooks workspace.";
|
"Select your GitHub repos and branch(es) to pin to your notebooks workspace.";
|
||||||
private static readonly ManageGitHubRepoResetConnection = "View or change your GitHub authorization settings.";
|
private static readonly ManageGitHubRepoResetConnection = "View or change your GitHub authorization settings.";
|
||||||
private static readonly OKButtonText = "OK";
|
private static readonly OKButtonText = "OK";
|
||||||
private static readonly CancelButtonText = "Cancel";
|
private static readonly CancelButtonText = "Cancel";
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const header: JSX.Element = (
|
const header: JSX.Element = (
|
||||||
<p>
|
<p>
|
||||||
{this.props.showAuthorizeAccess
|
{this.props.showAuthorizeAccess
|
||||||
? GitHubReposComponent.ConnectToGitHubTitle
|
? GitHubReposComponent.ConnectToGitHubTitle
|
||||||
: GitHubReposComponent.ManageGitHubRepoTitle}
|
: GitHubReposComponent.ManageGitHubRepoTitle}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
|
|
||||||
const content: JSX.Element = this.props.showAuthorizeAccess ? (
|
const content: JSX.Element = this.props.showAuthorizeAccess ? (
|
||||||
<AuthorizeAccessComponent {...this.props.authorizeAccessProps} />
|
<AuthorizeAccessComponent {...this.props.authorizeAccessProps} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<p>{GitHubReposComponent.ManageGitHubRepoDescription}</p>
|
<p>{GitHubReposComponent.ManageGitHubRepoDescription}</p>
|
||||||
<Link style={{ marginTop: ChildrenMargin }} onClick={this.props.resetConnection}>
|
<Link style={{ marginTop: ChildrenMargin }} onClick={this.props.resetConnection}>
|
||||||
{GitHubReposComponent.ManageGitHubRepoResetConnection}
|
{GitHubReposComponent.ManageGitHubRepoResetConnection}
|
||||||
</Link>
|
</Link>
|
||||||
<ReposListComponent {...this.props.reposListProps} />
|
<ReposListComponent {...this.props.reposListProps} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const okProps: IButtonProps = {
|
const okProps: IButtonProps = {
|
||||||
text: GitHubReposComponent.OKButtonText,
|
text: GitHubReposComponent.OKButtonText,
|
||||||
ariaLabel: GitHubReposComponent.OKButtonText,
|
ariaLabel: GitHubReposComponent.OKButtonText,
|
||||||
onClick: this.props.onOkClick
|
onClick: this.props.onOkClick,
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelProps: IButtonProps = {
|
const cancelProps: IButtonProps = {
|
||||||
text: GitHubReposComponent.CancelButtonText,
|
text: GitHubReposComponent.CancelButtonText,
|
||||||
ariaLabel: GitHubReposComponent.CancelButtonText,
|
ariaLabel: GitHubReposComponent.CancelButtonText,
|
||||||
onClick: this.props.onCancelClick
|
onClick: this.props.onCancelClick,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={"firstdivbg headerline"}>{header}</div>
|
<div className={"firstdivbg headerline"}>{header}</div>
|
||||||
<div className={"paneMainContent"}>{content}</div>
|
<div className={"paneMainContent"}>{content}</div>
|
||||||
{!this.props.showAuthorizeAccess && (
|
{!this.props.showAuthorizeAccess && (
|
||||||
<>
|
<>
|
||||||
<div className={"paneFooter"} style={ContentFooterStyle}>
|
<div className={"paneFooter"} style={ContentFooterStyle}>
|
||||||
<AddRepoComponent {...this.props.addRepoProps} />
|
<AddRepoComponent {...this.props.addRepoProps} />
|
||||||
</div>
|
</div>
|
||||||
<div className={"paneFooter"} style={ButtonsFooterStyle}>
|
<div className={"paneFooter"} style={ButtonsFooterStyle}>
|
||||||
<PrimaryButton {...okProps} />
|
<PrimaryButton {...okProps} />
|
||||||
<DefaultButton style={{ marginLeft: ChildrenMargin }} {...cancelProps} />
|
<DefaultButton style={{ marginLeft: ChildrenMargin }} {...cancelProps} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||||
import { GitHubReposComponent, GitHubReposComponentProps } from "./GitHubReposComponent";
|
import { GitHubReposComponent, GitHubReposComponentProps } from "./GitHubReposComponent";
|
||||||
|
|
||||||
export class GitHubReposComponentAdapter implements ReactAdapter {
|
export class GitHubReposComponentAdapter implements ReactAdapter {
|
||||||
public parameters: ko.Observable<number>;
|
public parameters: ko.Observable<number>;
|
||||||
|
|
||||||
constructor(private props: GitHubReposComponentProps) {
|
constructor(private props: GitHubReposComponentProps) {
|
||||||
this.parameters = ko.observable<number>(Date.now());
|
this.parameters = ko.observable<number>(Date.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
public renderComponent(): JSX.Element {
|
||||||
return <GitHubReposComponent {...this.props} />;
|
return <GitHubReposComponent {...this.props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
public triggerRender(): void {
|
public triggerRender(): void {
|
||||||
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,58 +1,58 @@
|
|||||||
import {
|
import {
|
||||||
IStyleFunctionOrObject,
|
IStyleFunctionOrObject,
|
||||||
ICheckboxStyleProps,
|
ICheckboxStyleProps,
|
||||||
ICheckboxStyles,
|
ICheckboxStyles,
|
||||||
IDropdownStyles,
|
IDropdownStyles,
|
||||||
IDropdownStyleProps
|
IDropdownStyleProps,
|
||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
|
|
||||||
export const ButtonsFooterStyle: React.CSSProperties = {
|
export const ButtonsFooterStyle: React.CSSProperties = {
|
||||||
padding: 14,
|
padding: 14,
|
||||||
height: "auto"
|
height: "auto",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ContentFooterStyle: React.CSSProperties = {
|
export const ContentFooterStyle: React.CSSProperties = {
|
||||||
padding: "10px 24px 10px 24px",
|
padding: "10px 24px 10px 24px",
|
||||||
height: "auto"
|
height: "auto",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ChildrenMargin = 10;
|
export const ChildrenMargin = 10;
|
||||||
export const FontSize = 12;
|
export const FontSize = 12;
|
||||||
|
|
||||||
export const ReposListCheckboxStyles: IStyleFunctionOrObject<ICheckboxStyleProps, ICheckboxStyles> = {
|
export const ReposListCheckboxStyles: IStyleFunctionOrObject<ICheckboxStyleProps, ICheckboxStyles> = {
|
||||||
label: {
|
label: {
|
||||||
margin: 0,
|
margin: 0,
|
||||||
padding: "2 0 2 0"
|
padding: "2 0 2 0",
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: FontSize
|
fontSize: FontSize,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BranchesDropdownCheckboxStyles: IStyleFunctionOrObject<ICheckboxStyleProps, ICheckboxStyles> = {
|
export const BranchesDropdownCheckboxStyles: IStyleFunctionOrObject<ICheckboxStyleProps, ICheckboxStyles> = {
|
||||||
label: {
|
label: {
|
||||||
margin: 0,
|
margin: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
fontSize: FontSize
|
fontSize: FontSize,
|
||||||
},
|
},
|
||||||
root: {
|
root: {
|
||||||
padding: 0
|
padding: 0,
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: FontSize
|
fontSize: FontSize,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BranchesDropdownStyles: IStyleFunctionOrObject<IDropdownStyleProps, IDropdownStyles> = {
|
export const BranchesDropdownStyles: IStyleFunctionOrObject<IDropdownStyleProps, IDropdownStyles> = {
|
||||||
title: {
|
title: {
|
||||||
fontSize: FontSize
|
fontSize: FontSize,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BranchesDropdownOptionContainerStyle: React.CSSProperties = {
|
export const BranchesDropdownOptionContainerStyle: React.CSSProperties = {
|
||||||
padding: 8
|
padding: 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ReposListRepoColumnMinWidth = 192;
|
export const ReposListRepoColumnMinWidth = 192;
|
||||||
export const ReposListBranchesColumnWidth = 116;
|
export const ReposListBranchesColumnWidth = 116;
|
||||||
export const BranchesDropdownWidth = 200;
|
export const BranchesDropdownWidth = 200;
|
||||||
|
@ -1,304 +1,304 @@
|
|||||||
import {
|
import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
DetailsList,
|
DetailsList,
|
||||||
DetailsRow,
|
DetailsRow,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
ICheckboxProps,
|
ICheckboxProps,
|
||||||
IDetailsFooterProps,
|
IDetailsFooterProps,
|
||||||
IDetailsListProps,
|
IDetailsListProps,
|
||||||
IDetailsRowBaseProps,
|
IDetailsRowBaseProps,
|
||||||
IDropdown,
|
IDropdown,
|
||||||
IDropdownOption,
|
IDropdownOption,
|
||||||
IDropdownProps,
|
IDropdownProps,
|
||||||
ILinkProps,
|
ILinkProps,
|
||||||
ISelectableDroppableTextProps,
|
ISelectableDroppableTextProps,
|
||||||
Link,
|
Link,
|
||||||
ResponsiveMode,
|
ResponsiveMode,
|
||||||
SelectionMode,
|
SelectionMode,
|
||||||
Text
|
Text,
|
||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { IGitHubBranch, IGitHubPageInfo } from "../../../GitHub/GitHubClient";
|
import { IGitHubBranch, IGitHubPageInfo } from "../../../GitHub/GitHubClient";
|
||||||
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
||||||
import { RepoListItem } from "./GitHubReposComponent";
|
import { RepoListItem } from "./GitHubReposComponent";
|
||||||
import {
|
import {
|
||||||
BranchesDropdownCheckboxStyles,
|
BranchesDropdownCheckboxStyles,
|
||||||
BranchesDropdownOptionContainerStyle,
|
BranchesDropdownOptionContainerStyle,
|
||||||
ReposListCheckboxStyles,
|
ReposListCheckboxStyles,
|
||||||
ReposListRepoColumnMinWidth,
|
ReposListRepoColumnMinWidth,
|
||||||
ReposListBranchesColumnWidth,
|
ReposListBranchesColumnWidth,
|
||||||
BranchesDropdownWidth,
|
BranchesDropdownWidth,
|
||||||
BranchesDropdownStyles
|
BranchesDropdownStyles,
|
||||||
} from "./GitHubStyleConstants";
|
} from "./GitHubStyleConstants";
|
||||||
|
|
||||||
export interface ReposListComponentProps {
|
export interface ReposListComponentProps {
|
||||||
branchesProps: Record<string, BranchesProps>; // key'd by repo key
|
branchesProps: Record<string, BranchesProps>; // key'd by repo key
|
||||||
pinnedReposProps: PinnedReposProps;
|
pinnedReposProps: PinnedReposProps;
|
||||||
unpinnedReposProps: UnpinnedReposProps;
|
unpinnedReposProps: UnpinnedReposProps;
|
||||||
pinRepo: (repo: RepoListItem) => void;
|
pinRepo: (repo: RepoListItem) => void;
|
||||||
unpinRepo: (repo: RepoListItem) => void;
|
unpinRepo: (repo: RepoListItem) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BranchesProps {
|
export interface BranchesProps {
|
||||||
branches: IGitHubBranch[];
|
branches: IGitHubBranch[];
|
||||||
lastPageInfo?: IGitHubPageInfo;
|
lastPageInfo?: IGitHubPageInfo;
|
||||||
hasMore: boolean;
|
hasMore: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
loadMore: () => void;
|
loadMore: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PinnedReposProps {
|
export interface PinnedReposProps {
|
||||||
repos: RepoListItem[];
|
repos: RepoListItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UnpinnedReposProps {
|
export interface UnpinnedReposProps {
|
||||||
repos: RepoListItem[];
|
repos: RepoListItem[];
|
||||||
hasMore: boolean;
|
hasMore: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
loadMore: () => void;
|
loadMore: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ReposListComponent extends React.Component<ReposListComponentProps> {
|
export class ReposListComponent extends React.Component<ReposListComponentProps> {
|
||||||
private static readonly PinnedReposColumnName = "Pinned repos";
|
private static readonly PinnedReposColumnName = "Pinned repos";
|
||||||
private static readonly UnpinnedReposColumnName = "Unpinned repos";
|
private static readonly UnpinnedReposColumnName = "Unpinned repos";
|
||||||
private static readonly BranchesColumnName = "Branches";
|
private static readonly BranchesColumnName = "Branches";
|
||||||
private static readonly LoadingText = "Loading...";
|
private static readonly LoadingText = "Loading...";
|
||||||
private static readonly LoadMoreText = "Load more";
|
private static readonly LoadMoreText = "Load more";
|
||||||
private static readonly DefaultBranchName = "master";
|
private static readonly DefaultBranchName = "master";
|
||||||
private static readonly FooterIndex = -1;
|
private static readonly FooterIndex = -1;
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const pinnedReposListProps: IDetailsListProps = {
|
const pinnedReposListProps: IDetailsListProps = {
|
||||||
styles: {
|
styles: {
|
||||||
contentWrapper: {
|
contentWrapper: {
|
||||||
height: this.props.pinnedReposProps.repos.length ? undefined : 0
|
height: this.props.pinnedReposProps.repos.length ? undefined : 0,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
items: this.props.pinnedReposProps.repos,
|
items: this.props.pinnedReposProps.repos,
|
||||||
getKey: ReposListComponent.getKey,
|
getKey: ReposListComponent.getKey,
|
||||||
selectionMode: SelectionMode.none,
|
selectionMode: SelectionMode.none,
|
||||||
compact: true,
|
compact: true,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
key: ReposListComponent.PinnedReposColumnName,
|
key: ReposListComponent.PinnedReposColumnName,
|
||||||
name: ReposListComponent.PinnedReposColumnName,
|
name: ReposListComponent.PinnedReposColumnName,
|
||||||
ariaLabel: ReposListComponent.PinnedReposColumnName,
|
ariaLabel: ReposListComponent.PinnedReposColumnName,
|
||||||
minWidth: ReposListRepoColumnMinWidth,
|
minWidth: ReposListRepoColumnMinWidth,
|
||||||
onRender: this.onRenderPinnedReposColumnItem
|
onRender: this.onRenderPinnedReposColumnItem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: ReposListComponent.BranchesColumnName,
|
key: ReposListComponent.BranchesColumnName,
|
||||||
name: ReposListComponent.BranchesColumnName,
|
name: ReposListComponent.BranchesColumnName,
|
||||||
ariaLabel: ReposListComponent.BranchesColumnName,
|
ariaLabel: ReposListComponent.BranchesColumnName,
|
||||||
minWidth: ReposListBranchesColumnWidth,
|
minWidth: ReposListBranchesColumnWidth,
|
||||||
maxWidth: ReposListBranchesColumnWidth,
|
maxWidth: ReposListBranchesColumnWidth,
|
||||||
onRender: this.onRenderPinnedReposBranchesColumnItem
|
onRender: this.onRenderPinnedReposBranchesColumnItem,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
onRenderDetailsFooter: this.props.pinnedReposProps.repos.length ? undefined : this.onRenderReposFooter
|
onRenderDetailsFooter: this.props.pinnedReposProps.repos.length ? undefined : this.onRenderReposFooter,
|
||||||
};
|
};
|
||||||
|
|
||||||
const unpinnedReposListProps: IDetailsListProps = {
|
const unpinnedReposListProps: IDetailsListProps = {
|
||||||
items: this.props.unpinnedReposProps.repos,
|
items: this.props.unpinnedReposProps.repos,
|
||||||
getKey: ReposListComponent.getKey,
|
getKey: ReposListComponent.getKey,
|
||||||
selectionMode: SelectionMode.none,
|
selectionMode: SelectionMode.none,
|
||||||
compact: true,
|
compact: true,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
key: ReposListComponent.UnpinnedReposColumnName,
|
key: ReposListComponent.UnpinnedReposColumnName,
|
||||||
name: ReposListComponent.UnpinnedReposColumnName,
|
name: ReposListComponent.UnpinnedReposColumnName,
|
||||||
ariaLabel: ReposListComponent.UnpinnedReposColumnName,
|
ariaLabel: ReposListComponent.UnpinnedReposColumnName,
|
||||||
minWidth: ReposListRepoColumnMinWidth,
|
minWidth: ReposListRepoColumnMinWidth,
|
||||||
onRender: this.onRenderUnpinnedReposColumnItem
|
onRender: this.onRenderUnpinnedReposColumnItem,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: ReposListComponent.BranchesColumnName,
|
key: ReposListComponent.BranchesColumnName,
|
||||||
name: ReposListComponent.BranchesColumnName,
|
name: ReposListComponent.BranchesColumnName,
|
||||||
ariaLabel: ReposListComponent.BranchesColumnName,
|
ariaLabel: ReposListComponent.BranchesColumnName,
|
||||||
minWidth: ReposListBranchesColumnWidth,
|
minWidth: ReposListBranchesColumnWidth,
|
||||||
maxWidth: ReposListBranchesColumnWidth,
|
maxWidth: ReposListBranchesColumnWidth,
|
||||||
onRender: this.onRenderUnpinnedReposBranchesColumnItem
|
onRender: this.onRenderUnpinnedReposBranchesColumnItem,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
onRenderDetailsFooter:
|
onRenderDetailsFooter:
|
||||||
this.props.unpinnedReposProps.isLoading || this.props.unpinnedReposProps.hasMore
|
this.props.unpinnedReposProps.isLoading || this.props.unpinnedReposProps.hasMore
|
||||||
? this.onRenderReposFooter
|
? this.onRenderReposFooter
|
||||||
: undefined
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DetailsList {...pinnedReposListProps} />
|
<DetailsList {...pinnedReposListProps} />
|
||||||
<DetailsList {...unpinnedReposListProps} />
|
<DetailsList {...unpinnedReposListProps} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRenderPinnedReposColumnItem = (item: RepoListItem, index: number): JSX.Element => {
|
private onRenderPinnedReposColumnItem = (item: RepoListItem, index: number): JSX.Element => {
|
||||||
if (index === ReposListComponent.FooterIndex) {
|
if (index === ReposListComponent.FooterIndex) {
|
||||||
return <Text>None</Text>;
|
return <Text>None</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkboxProps: ICheckboxProps = {
|
const checkboxProps: ICheckboxProps = {
|
||||||
...ReposListComponent.getCheckboxPropsForLabel(GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)),
|
...ReposListComponent.getCheckboxPropsForLabel(GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)),
|
||||||
styles: ReposListCheckboxStyles,
|
styles: ReposListCheckboxStyles,
|
||||||
defaultChecked: true,
|
defaultChecked: true,
|
||||||
onChange: () => this.props.unpinRepo(item)
|
onChange: () => this.props.unpinRepo(item),
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Checkbox {...checkboxProps} />;
|
return <Checkbox {...checkboxProps} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRenderPinnedReposBranchesColumnItem = (item: RepoListItem, index: number): JSX.Element => {
|
private onRenderPinnedReposBranchesColumnItem = (item: RepoListItem, index: number): JSX.Element => {
|
||||||
if (index === ReposListComponent.FooterIndex) {
|
if (index === ReposListComponent.FooterIndex) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const branchesProps = this.props.branchesProps[GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)];
|
const branchesProps = this.props.branchesProps[GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)];
|
||||||
const options: IDropdownOption[] = branchesProps.branches.map(branch => ({
|
const options: IDropdownOption[] = branchesProps.branches.map((branch) => ({
|
||||||
key: branch.name,
|
key: branch.name,
|
||||||
text: branch.name,
|
text: branch.name,
|
||||||
data: item,
|
data: item,
|
||||||
disabled: item.branches.length === 1 && branch.name === item.branches[0].name,
|
disabled: item.branches.length === 1 && branch.name === item.branches[0].name,
|
||||||
selected: item.branches.findIndex(element => element.name === branch.name) !== -1
|
selected: item.branches.findIndex((element) => element.name === branch.name) !== -1,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (branchesProps.hasMore || branchesProps.isLoading) {
|
if (branchesProps.hasMore || branchesProps.isLoading) {
|
||||||
const text = branchesProps.isLoading ? ReposListComponent.LoadingText : ReposListComponent.LoadMoreText;
|
const text = branchesProps.isLoading ? ReposListComponent.LoadingText : ReposListComponent.LoadMoreText;
|
||||||
options.push({
|
options.push({
|
||||||
key: text,
|
key: text,
|
||||||
text,
|
text,
|
||||||
data: item,
|
data: item,
|
||||||
index: ReposListComponent.FooterIndex
|
index: ReposListComponent.FooterIndex,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const dropdownProps: IDropdownProps = {
|
const dropdownProps: IDropdownProps = {
|
||||||
styles: BranchesDropdownStyles,
|
styles: BranchesDropdownStyles,
|
||||||
dropdownWidth: BranchesDropdownWidth,
|
dropdownWidth: BranchesDropdownWidth,
|
||||||
responsiveMode: ResponsiveMode.large,
|
responsiveMode: ResponsiveMode.large,
|
||||||
options,
|
options,
|
||||||
onRenderList: this.onRenderBranchesDropdownList
|
onRenderList: this.onRenderBranchesDropdownList,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (item.branches.length === 1) {
|
if (item.branches.length === 1) {
|
||||||
dropdownProps.placeholder = item.branches[0].name;
|
dropdownProps.placeholder = item.branches[0].name;
|
||||||
} else if (item.branches.length > 1) {
|
} else if (item.branches.length > 1) {
|
||||||
dropdownProps.placeholder = `${item.branches.length} branches`;
|
dropdownProps.placeholder = `${item.branches.length} branches`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Dropdown {...dropdownProps} />;
|
return <Dropdown {...dropdownProps} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRenderUnpinnedReposBranchesColumnItem = (item: RepoListItem, index: number): JSX.Element => {
|
private onRenderUnpinnedReposBranchesColumnItem = (item: RepoListItem, index: number): JSX.Element => {
|
||||||
if (index === ReposListComponent.FooterIndex) {
|
if (index === ReposListComponent.FooterIndex) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dropdownProps: IDropdownProps = {
|
const dropdownProps: IDropdownProps = {
|
||||||
styles: BranchesDropdownStyles,
|
styles: BranchesDropdownStyles,
|
||||||
options: [],
|
options: [],
|
||||||
placeholder: ReposListComponent.DefaultBranchName,
|
placeholder: ReposListComponent.DefaultBranchName,
|
||||||
disabled: true
|
disabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Dropdown {...dropdownProps} />;
|
return <Dropdown {...dropdownProps} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRenderBranchesDropdownList = (
|
private onRenderBranchesDropdownList = (
|
||||||
props: ISelectableDroppableTextProps<IDropdown, HTMLDivElement>
|
props: ISelectableDroppableTextProps<IDropdown, HTMLDivElement>
|
||||||
): JSX.Element => {
|
): JSX.Element => {
|
||||||
const renderedList: JSX.Element[] = [];
|
const renderedList: JSX.Element[] = [];
|
||||||
props.options.forEach((option: IDropdownOption) => {
|
props.options.forEach((option: IDropdownOption) => {
|
||||||
const item = (
|
const item = (
|
||||||
<div key={option.key} style={BranchesDropdownOptionContainerStyle}>
|
<div key={option.key} style={BranchesDropdownOptionContainerStyle}>
|
||||||
{this.onRenderPinnedReposBranchesDropdownOption(option)}
|
{this.onRenderPinnedReposBranchesDropdownOption(option)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
renderedList.push(item);
|
renderedList.push(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
return <>{renderedList}</>;
|
return <>{renderedList}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRenderPinnedReposBranchesDropdownOption(option: IDropdownOption): JSX.Element {
|
private onRenderPinnedReposBranchesDropdownOption(option: IDropdownOption): JSX.Element {
|
||||||
const item: RepoListItem = option.data;
|
const item: RepoListItem = option.data;
|
||||||
const branchesProps = this.props.branchesProps[GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)];
|
const branchesProps = this.props.branchesProps[GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)];
|
||||||
|
|
||||||
if (option.index === ReposListComponent.FooterIndex) {
|
if (option.index === ReposListComponent.FooterIndex) {
|
||||||
const linkProps: ILinkProps = {
|
const linkProps: ILinkProps = {
|
||||||
disabled: branchesProps.isLoading,
|
disabled: branchesProps.isLoading,
|
||||||
onClick: branchesProps.loadMore
|
onClick: branchesProps.loadMore,
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Link {...linkProps}>{option.text}</Link>;
|
return <Link {...linkProps}>{option.text}</Link>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkboxProps: ICheckboxProps = {
|
const checkboxProps: ICheckboxProps = {
|
||||||
...ReposListComponent.getCheckboxPropsForLabel(option.text),
|
...ReposListComponent.getCheckboxPropsForLabel(option.text),
|
||||||
styles: BranchesDropdownCheckboxStyles,
|
styles: BranchesDropdownCheckboxStyles,
|
||||||
defaultChecked: option.selected,
|
defaultChecked: option.selected,
|
||||||
disabled: option.disabled,
|
disabled: option.disabled,
|
||||||
onChange: (event, checked) => {
|
onChange: (event, checked) => {
|
||||||
const repoListItem = { ...item };
|
const repoListItem = { ...item };
|
||||||
const branch: IGitHubBranch = { name: option.text };
|
const branch: IGitHubBranch = { name: option.text };
|
||||||
repoListItem.branches = repoListItem.branches.filter(element => element.name !== branch.name);
|
repoListItem.branches = repoListItem.branches.filter((element) => element.name !== branch.name);
|
||||||
if (checked) {
|
if (checked) {
|
||||||
repoListItem.branches.push(branch);
|
repoListItem.branches.push(branch);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.pinRepo(repoListItem);
|
this.props.pinRepo(repoListItem);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Checkbox {...checkboxProps} />;
|
return <Checkbox {...checkboxProps} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRenderUnpinnedReposColumnItem = (item: RepoListItem, index: number): JSX.Element => {
|
private onRenderUnpinnedReposColumnItem = (item: RepoListItem, index: number): JSX.Element => {
|
||||||
if (index === ReposListComponent.FooterIndex) {
|
if (index === ReposListComponent.FooterIndex) {
|
||||||
const linkProps: ILinkProps = {
|
const linkProps: ILinkProps = {
|
||||||
disabled: this.props.unpinnedReposProps.isLoading,
|
disabled: this.props.unpinnedReposProps.isLoading,
|
||||||
onClick: this.props.unpinnedReposProps.loadMore
|
onClick: this.props.unpinnedReposProps.loadMore,
|
||||||
};
|
};
|
||||||
|
|
||||||
const linkText = this.props.unpinnedReposProps.isLoading
|
const linkText = this.props.unpinnedReposProps.isLoading
|
||||||
? ReposListComponent.LoadingText
|
? ReposListComponent.LoadingText
|
||||||
: ReposListComponent.LoadMoreText;
|
: ReposListComponent.LoadMoreText;
|
||||||
return <Link {...linkProps}>{linkText}</Link>;
|
return <Link {...linkProps}>{linkText}</Link>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkboxProps: ICheckboxProps = {
|
const checkboxProps: ICheckboxProps = {
|
||||||
...ReposListComponent.getCheckboxPropsForLabel(GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)),
|
...ReposListComponent.getCheckboxPropsForLabel(GitHubUtils.toRepoFullName(item.repo.owner, item.repo.name)),
|
||||||
styles: ReposListCheckboxStyles,
|
styles: ReposListCheckboxStyles,
|
||||||
onChange: () => {
|
onChange: () => {
|
||||||
const repoListItem = { ...item };
|
const repoListItem = { ...item };
|
||||||
repoListItem.branches = [{ name: ReposListComponent.DefaultBranchName }];
|
repoListItem.branches = [{ name: ReposListComponent.DefaultBranchName }];
|
||||||
this.props.pinRepo(repoListItem);
|
this.props.pinRepo(repoListItem);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Checkbox {...checkboxProps} />;
|
return <Checkbox {...checkboxProps} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRenderReposFooter = (detailsFooterProps: IDetailsFooterProps): JSX.Element => {
|
private onRenderReposFooter = (detailsFooterProps: IDetailsFooterProps): JSX.Element => {
|
||||||
const props: IDetailsRowBaseProps = {
|
const props: IDetailsRowBaseProps = {
|
||||||
...detailsFooterProps,
|
...detailsFooterProps,
|
||||||
item: {},
|
item: {},
|
||||||
itemIndex: ReposListComponent.FooterIndex
|
itemIndex: ReposListComponent.FooterIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
return <DetailsRow {...props} />;
|
return <DetailsRow {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
private static getCheckboxPropsForLabel(label: string): ICheckboxProps {
|
private static getCheckboxPropsForLabel(label: string): ICheckboxProps {
|
||||||
return {
|
return {
|
||||||
label,
|
label,
|
||||||
title: label,
|
title: label,
|
||||||
ariaLabel: label
|
ariaLabel: label,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getKey(item: RepoListItem): string {
|
private static getKey(item: RepoListItem): string {
|
||||||
return item.key;
|
return item.key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,189 +1,189 @@
|
|||||||
/**
|
/**
|
||||||
* How to use this component:
|
* How to use this component:
|
||||||
*
|
*
|
||||||
* In your html markup, use:
|
* In your html markup, use:
|
||||||
* <input-typeahead params="{
|
* <input-typeahead params="{
|
||||||
choices:choices,
|
choices:choices,
|
||||||
selection:selection,
|
selection:selection,
|
||||||
inputValue:inputValue,
|
inputValue:inputValue,
|
||||||
placeholder:'Enter source',
|
placeholder:'Enter source',
|
||||||
typeaheadOverrideOptions:typeaheadOverrideOptions
|
typeaheadOverrideOptions:typeaheadOverrideOptions
|
||||||
}"></input-typeahead>
|
}"></input-typeahead>
|
||||||
* The parameters are documented below.
|
* The parameters are documented below.
|
||||||
*
|
*
|
||||||
* Notes:
|
* Notes:
|
||||||
* - dynamic:true by default, this allows choices to change after initialization.
|
* - dynamic:true by default, this allows choices to change after initialization.
|
||||||
* To turn it off, use:
|
* To turn it off, use:
|
||||||
* typeaheadOverrideOptions: { dynamic:false }
|
* typeaheadOverrideOptions: { dynamic:false }
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "jquery-typeahead";
|
import "jquery-typeahead";
|
||||||
import template from "./input-typeahead.html";
|
import template from "./input-typeahead.html";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for ko component registration
|
* Helper class for ko component registration
|
||||||
*/
|
*/
|
||||||
export class InputTypeaheadComponent {
|
export class InputTypeaheadComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
viewModel: InputTypeaheadViewModel,
|
viewModel: InputTypeaheadViewModel,
|
||||||
template
|
template,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Item {
|
export interface Item {
|
||||||
caption: string;
|
caption: string;
|
||||||
value: any;
|
value: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameters for this component
|
* Parameters for this component
|
||||||
*/
|
*/
|
||||||
interface InputTypeaheadParams {
|
interface InputTypeaheadParams {
|
||||||
/**
|
/**
|
||||||
* List of choices available in the dropdown.
|
* List of choices available in the dropdown.
|
||||||
*/
|
*/
|
||||||
choices: ko.ObservableArray<Item>;
|
choices: ko.ObservableArray<Item>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets updated when user clicks on the choice in the dropdown
|
* Gets updated when user clicks on the choice in the dropdown
|
||||||
*/
|
*/
|
||||||
selection?: ko.Observable<Item>;
|
selection?: ko.Observable<Item>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current string value of <input>
|
* The current string value of <input>
|
||||||
*/
|
*/
|
||||||
inputValue?: ko.Observable<string>;
|
inputValue?: ko.Observable<string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define what text you want as the input placeholder
|
* Define what text you want as the input placeholder
|
||||||
*/
|
*/
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override default jquery-typeahead options
|
* Override default jquery-typeahead options
|
||||||
* WARNING: do not override input, source or callback to avoid breaking the components behavior.
|
* WARNING: do not override input, source or callback to avoid breaking the components behavior.
|
||||||
*/
|
*/
|
||||||
typeaheadOverrideOptions?: any;
|
typeaheadOverrideOptions?: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function gets called when pressing ENTER on the input box
|
* This function gets called when pressing ENTER on the input box
|
||||||
*/
|
*/
|
||||||
submitFct?: (inputValue: string, selection: Item) => void;
|
submitFct?: (inputValue: string, selection: Item) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Typehead comes with a Search button that we normally remove.
|
* Typehead comes with a Search button that we normally remove.
|
||||||
* If you want to use it, turn this on
|
* If you want to use it, turn this on
|
||||||
*/
|
*/
|
||||||
showSearchButton?: boolean;
|
showSearchButton?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnClickItem {
|
interface OnClickItem {
|
||||||
matchedKey: string;
|
matchedKey: string;
|
||||||
value: any;
|
value: any;
|
||||||
caption: string;
|
caption: string;
|
||||||
group: string;
|
group: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Cache {
|
interface Cache {
|
||||||
inputValue: string;
|
inputValue: string;
|
||||||
selection: Item;
|
selection: Item;
|
||||||
}
|
}
|
||||||
|
|
||||||
class InputTypeaheadViewModel {
|
class InputTypeaheadViewModel {
|
||||||
private static instanceCount = 0; // Generate unique id for each component's typeahead instance
|
private static instanceCount = 0; // Generate unique id for each component's typeahead instance
|
||||||
private instanceNumber: number;
|
private instanceNumber: number;
|
||||||
private params: InputTypeaheadParams;
|
private params: InputTypeaheadParams;
|
||||||
|
|
||||||
private cache: Cache;
|
private cache: Cache;
|
||||||
private inputValue: string;
|
private inputValue: string;
|
||||||
private selection: Item;
|
private selection: Item;
|
||||||
|
|
||||||
public constructor(params: InputTypeaheadParams) {
|
public constructor(params: InputTypeaheadParams) {
|
||||||
this.instanceNumber = InputTypeaheadViewModel.instanceCount++;
|
this.instanceNumber = InputTypeaheadViewModel.instanceCount++;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
|
|
||||||
this.params.choices.subscribe(this.initializeTypeahead.bind(this));
|
this.params.choices.subscribe(this.initializeTypeahead.bind(this));
|
||||||
|
|
||||||
this.cache = {
|
this.cache = {
|
||||||
inputValue: null,
|
inputValue: null,
|
||||||
selection: null
|
selection: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Must execute once ko is rendered, so that it can find the input element by id
|
* Must execute once ko is rendered, so that it can find the input element by id
|
||||||
*/
|
*/
|
||||||
private initializeTypeahead() {
|
private initializeTypeahead() {
|
||||||
let params = this.params;
|
let params = this.params;
|
||||||
let cache = this.cache;
|
let cache = this.cache;
|
||||||
let options: any = {
|
let options: any = {
|
||||||
input: `#${this.getComponentId()}`, //'.input-typeahead',
|
input: `#${this.getComponentId()}`, //'.input-typeahead',
|
||||||
order: "asc",
|
order: "asc",
|
||||||
minLength: 0,
|
minLength: 0,
|
||||||
searchOnFocus: true,
|
searchOnFocus: true,
|
||||||
source: {
|
source: {
|
||||||
display: "caption",
|
display: "caption",
|
||||||
data: () => {
|
data: () => {
|
||||||
return this.params.choices();
|
return this.params.choices();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
callback: {
|
callback: {
|
||||||
onClick: (node: any, a: any, item: OnClickItem, event: any) => {
|
onClick: (node: any, a: any, item: OnClickItem, event: any) => {
|
||||||
cache.selection = item;
|
cache.selection = item;
|
||||||
|
|
||||||
if (params.selection) {
|
if (params.selection) {
|
||||||
params.selection(item);
|
params.selection(item);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onResult(node: any, query: any, result: any, resultCount: any, resultCountPerGroup: any) {
|
onResult(node: any, query: any, result: any, resultCount: any, resultCountPerGroup: any) {
|
||||||
cache.inputValue = query;
|
cache.inputValue = query;
|
||||||
if (params.inputValue) {
|
if (params.inputValue) {
|
||||||
params.inputValue(query);
|
params.inputValue(query);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
template: (query: string, item: any) => {
|
template: (query: string, item: any) => {
|
||||||
// Don't display id if caption *IS* the id
|
// Don't display id if caption *IS* the id
|
||||||
return item.caption === item.value
|
return item.caption === item.value
|
||||||
? "<span>{{caption}}</span>"
|
? "<span>{{caption}}</span>"
|
||||||
: "<span><div>{{caption}}</div><div><small>{{value}}</small></div></span>";
|
: "<span><div>{{caption}}</div><div><small>{{value}}</small></div></span>";
|
||||||
},
|
},
|
||||||
dynamic: true
|
dynamic: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Override options
|
// Override options
|
||||||
if (params.typeaheadOverrideOptions) {
|
if (params.typeaheadOverrideOptions) {
|
||||||
for (let p in params.typeaheadOverrideOptions) {
|
for (let p in params.typeaheadOverrideOptions) {
|
||||||
options[p] = params.typeaheadOverrideOptions[p];
|
options[p] = params.typeaheadOverrideOptions[p];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$.typeahead(options);
|
$.typeahead(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this component id
|
* Get this component id
|
||||||
* @return unique id per instance
|
* @return unique id per instance
|
||||||
*/
|
*/
|
||||||
private getComponentId(): string {
|
private getComponentId(): string {
|
||||||
return `input-typeahead${this.instanceNumber}`;
|
return `input-typeahead${this.instanceNumber}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executed once ko is done rendering bindings
|
* Executed once ko is done rendering bindings
|
||||||
* Use ko's "template: afterRender" callback to do that without actually using any template.
|
* Use ko's "template: afterRender" callback to do that without actually using any template.
|
||||||
* Another way is to call it within setTimeout() in constructor.
|
* Another way is to call it within setTimeout() in constructor.
|
||||||
*/
|
*/
|
||||||
private afterRender(): void {
|
private afterRender(): void {
|
||||||
this.initializeTypeahead();
|
this.initializeTypeahead();
|
||||||
}
|
}
|
||||||
|
|
||||||
private submit(): void {
|
private submit(): void {
|
||||||
if (this.params.submitFct) {
|
if (this.params.submitFct) {
|
||||||
this.params.submitFct(this.cache.inputValue, this.cache.selection);
|
this.params.submitFct(this.cache.inputValue, this.cache.selection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ describe("inputTypeahead", () => {
|
|||||||
const props: InputTypeaheadComponentProps = {
|
const props: InputTypeaheadComponentProps = {
|
||||||
choices: [
|
choices: [
|
||||||
{ caption: "item1", value: "value1" },
|
{ caption: "item1", value: "value1" },
|
||||||
{ caption: "item2", value: "value2" }
|
{ caption: "item2", value: "value2" },
|
||||||
],
|
],
|
||||||
placeholder: "placeholder",
|
placeholder: "placeholder",
|
||||||
useTextarea: false
|
useTextarea: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallow(<InputTypeaheadComponent {...props} />);
|
const wrapper = shallow(<InputTypeaheadComponent {...props} />);
|
||||||
@ -22,10 +22,10 @@ describe("inputTypeahead", () => {
|
|||||||
const props: InputTypeaheadComponentProps = {
|
const props: InputTypeaheadComponentProps = {
|
||||||
choices: [
|
choices: [
|
||||||
{ caption: "item1", value: "value1" },
|
{ caption: "item1", value: "value1" },
|
||||||
{ caption: "item2", value: "value2" }
|
{ caption: "item2", value: "value2" },
|
||||||
],
|
],
|
||||||
placeholder: "placeholder",
|
placeholder: "placeholder",
|
||||||
useTextarea: true
|
useTextarea: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = shallow(<InputTypeaheadComponent {...props} />);
|
const wrapper = shallow(<InputTypeaheadComponent {...props} />);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user