Compare commits

..

14 Commits

Author SHA1 Message Date
Laurent Nguyen
8d94849c7b Re-add package-lock.json 2021-04-13 08:52:13 +02:00
Laurent Nguyen
8285f1fc37 Merge branch 'master' into languy-upgrade-markdown-notebooks 2021-04-13 08:17:04 +02:00
Jordi Bunster
1e6ad113dd Add rel='noreferrer' (#651) 2021-04-12 17:24:11 -07:00
Hardikkumar Nai
05932e1d38 Resolve Lint errors in NotificationConsoleComponent.ts (#527) 2021-04-12 18:06:30 -05:00
Hardikkumar Nai
02e6d8442b Add GraphUtil to Eslint (#626) 2021-04-12 18:04:52 -05:00
Sunil Kumar Yadav
8cf09acc19 Migration/table query select pane to react (#615) 2021-04-12 17:53:56 -05:00
Jordi Bunster
5cd4e93c65 Fix preview URL branch display, include link in new PRs (#644)
* Make it easy to include preview links

* Fix display of currently previewed branch
2021-04-12 15:07:37 -07:00
Jordi Bunster
76e3b7e6f1 Warn on React hook misuse (#632) 2021-04-12 15:13:17 -05:00
Jordi Bunster
dc5679ffd3 Switch to accessibility insights's version of these tools (#603)
* Switch to accessibility insights's version of these tools

* auto-add files meeting strict checks
2021-04-12 15:12:19 -05:00
Jordi Bunster
88f5e7485a Pull request preview URLs (#625)
* Dynamic link to HEAD of a given PR

* Display pr name and link in notification console

* Pass along query string to Explorer

This means you can write a URL like:

https://cosmos-explorer-preview.azurewebsites.net/pull/123?feature.enableFoo=true

and when Explorer loads it'll have enableFoo set to true in the features
object.
2021-04-12 15:10:31 -05:00
Steve Faulkner
662c03580a Call readCollections for Mongo using ARM (#636) 2021-04-12 11:28:52 -05:00
Tanuj Mittal
14fd9054dd Sandbox all outputs in iFrame (#624) 2021-04-12 08:59:18 -07:00
Laurent Nguyen
ad1d5add96 Update test snapshot 2021-04-01 13:56:25 +02:00
Laurent Nguyen
c9be25b1e8 Upgrade @nteract/stateful-component and @nteract/markdown in order to include the fix that enables escape HTML on markdown 2021-04-01 13:38:17 +02:00
61 changed files with 9404 additions and 4130 deletions

View File

@@ -135,7 +135,6 @@ src/Explorer/Panes/SwitchDirectoryPane.ts
src/Explorer/Panes/Tables/AddTableEntityPane.ts src/Explorer/Panes/Tables/AddTableEntityPane.ts
src/Explorer/Panes/Tables/EditTableEntityPane.ts src/Explorer/Panes/Tables/EditTableEntityPane.ts
src/Explorer/Panes/Tables/EntityPropertyViewModel.ts src/Explorer/Panes/Tables/EntityPropertyViewModel.ts
src/Explorer/Panes/Tables/QuerySelectPane.ts
src/Explorer/Panes/Tables/TableEntityPane.ts src/Explorer/Panes/Tables/TableEntityPane.ts
src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts
src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts
@@ -299,8 +298,6 @@ src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.t
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx
src/Explorer/Menus/CommandBar/CommandBarUtil.tsx src/Explorer/Menus/CommandBar/CommandBarUtil.tsx
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.test.tsx src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.test.tsx
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponent.tsx
src/Explorer/Menus/NotificationConsole/NotificationConsoleComponentAdapter.tsx
src/Explorer/Notebook/NotebookComponent/NotebookComponent.tsx src/Explorer/Notebook/NotebookComponent/NotebookComponent.tsx
src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx
src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx

View File

@@ -3,7 +3,7 @@ module.exports = {
browser: true, browser: true,
es6: true, es6: true,
}, },
plugins: ["@typescript-eslint", "no-null", "prefer-arrow"], plugins: ["@typescript-eslint", "no-null", "prefer-arrow", "react-hooks"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
globals: { globals: {
Atomics: "readonly", Atomics: "readonly",
@@ -20,7 +20,7 @@ module.exports = {
overrides: [ overrides: [
{ {
files: ["**/*.tsx"], files: ["**/*.tsx"],
extends: ["plugin:react/recommended"], // TODO: Add react-hooks extends: ["plugin:react/recommended"],
plugins: ["react"], plugins: ["react"],
}, },
{ {
@@ -42,6 +42,8 @@ module.exports = {
"prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }], "prefer-arrow/prefer-arrow-functions": ["error", { allowStandaloneDeclarations: true }],
eqeqeq: "error", eqeqeq: "error",
"react/display-name": "off", "react/display-name": "off",
"react-hooks/rules-of-hooks": "warn", // TODO: error
"react-hooks/exhaustive-deps": "warn", // TODO: error
"no-restricted-syntax": [ "no-restricted-syntax": [
"error", "error",
{ {

1
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1 @@
[Preview this branch](https://cosmos-explorer-preview.azurewebsites.net/pull/EDIT_THIS_NUMBER_IN_THE_PR_DESCRIPTION?feature.someFeatureFlagYouMightNeed=true)

6201
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,12 +25,12 @@
"@nteract/iron-icons": "1.0.0", "@nteract/iron-icons": "1.0.0",
"@nteract/jupyter-widgets": "2.0.0", "@nteract/jupyter-widgets": "2.0.0",
"@nteract/logos": "1.0.0", "@nteract/logos": "1.0.0",
"@nteract/markdown": "4.4.0", "@nteract/markdown": "4.6.1",
"@nteract/monaco-editor": "3.2.2", "@nteract/monaco-editor": "3.2.2",
"@nteract/octicons": "2.0.0", "@nteract/octicons": "2.0.0",
"@nteract/outputs": "3.0.9", "@nteract/outputs": "3.0.9",
"@nteract/presentational-components": "3.0.7", "@nteract/presentational-components": "3.0.7",
"@nteract/stateful-components": "1.7.0", "@nteract/stateful-components": "1.7.7",
"@nteract/styles": "2.0.2", "@nteract/styles": "2.0.2",
"@nteract/transform-geojson": "5.1.8", "@nteract/transform-geojson": "5.1.8",
"@nteract/transform-model-debug": "5.0.1", "@nteract/transform-model-debug": "5.0.1",
@@ -200,8 +200,8 @@
"format:check": "prettier --check \"{src,test}/**/*.{ts,tsx,html}\" \"*.{js,html}\"", "format:check": "prettier --check \"{src,test}/**/*.{ts,tsx,html}\" \"*.{js,html}\"",
"lint": "tslint --project tsconfig.json && eslint \"**/*.{ts,tsx}\"", "lint": "tslint --project tsconfig.json && eslint \"**/*.{ts,tsx}\"",
"build:contracts": "npm run compile:contracts", "build:contracts": "npm run compile:contracts",
"strictEligibleFiles": "node ./strict-migration-tools/index.js", "strict:find": "node ./strict-null-checks/find.js",
"autoAddStrictEligibleFiles": "node ./strict-migration-tools/autoAdd.js", "strict:add": "node ./strict-null-checks/auto-add.js",
"compile:fullStrict": "tsc -p ./tsconfig.json --strictNullChecks", "compile:fullStrict": "tsc -p ./tsconfig.json --strictNullChecks",
"generateARMClients": "ts-node --compiler-options '{\"module\":\"commonjs\"}' utils/armClientGenerator/generator.ts" "generateARMClients": "ts-node --compiler-options '{\"module\":\"commonjs\"}' utils/armClientGenerator/generator.ts"
}, },

View File

@@ -1,6 +1,7 @@
const express = require("express"); const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware"); const { createProxyMiddleware } = require("http-proxy-middleware");
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;
const fetch = require("node-fetch");
const api = createProxyMiddleware("/api", { const api = createProxyMiddleware("/api", {
target: "https://main.documentdb.ext.azure.com", target: "https://main.documentdb.ext.azure.com",
@@ -39,6 +40,31 @@ const app = express();
app.use(api); app.use(api);
app.use(proxy); app.use(proxy);
app.use(commit); app.use(commit);
app.get("/pull/:pr(\\d+)", (req, res) => {
const pr = req.params.pr;
const [, query] = req.originalUrl.split("?");
const search = new URLSearchParams(query);
fetch("https://api.github.com/repos/Azure/cosmos-explorer/pulls/" + pr)
.then((response) => response.json())
.then(({ head: { ref, sha } }) => {
const prUrl = new URL("https://github.com/Azure/cosmos-explorer/pull/" + pr);
prUrl.hash = ref;
search.set("feature.pr", prUrl.href);
const explorer = new URL("https://cosmos-explorer-preview.azurewebsites.net/commit/" + sha + "/explorer.html");
explorer.search = search.toString();
const portal = new URL("https://ms.portal.azure.com/");
portal.searchParams.set("dataExplorerSource", explorer.href);
portal.hash =
"@microsoft.onmicrosoft.com/resource/subscriptions/b9c77f10-b438-4c32-9819-eef8a654e478/resourceGroups/stfaul/providers/Microsoft.DocumentDb/databaseAccounts/stfaul-sql/dataExplorer";
return res.redirect(portal.href);
})
.catch(() => res.sendStatus(500));
});
app.listen(port, () => { app.listen(port, () => {
console.log(`Example app listening on port: ${port}`); console.log(`Example app listening on port: ${port}`);
}); });

View File

@@ -1,8 +1,658 @@
{ {
"name": "preview", "name": "cosmos-explorer-preview",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 1, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": {
"": {
"name": "cosmos-explorer-preview",
"version": "1.0.0",
"dependencies": {
"express": "^4.17.1",
"http-proxy-middleware": "^1.1.0",
"node-fetch": "^2.6.1"
}
},
"node_modules/@types/http-proxy": {
"version": "1.17.5",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.5.tgz",
"integrity": "sha512-GNkDE7bTv6Sf8JbV2GksknKOsk7OznNYHSdrtvPJXO0qJ9odZig6IZKUi5RFGi6d1bf6dgIAe4uXi3DBc7069Q==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "14.14.37",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz",
"integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw=="
},
"node_modules/accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"dependencies": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"node_modules/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"dependencies": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/camelcase": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
"integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
"engines": {
"node": ">=10"
}
},
"node_modules/content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"dependencies": {
"safe-buffer": "5.1.2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"node_modules/express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"dependencies": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/follow-redirects": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz",
"integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==",
"engines": {
"node": ">=4.0"
}
},
"node_modules/forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dependencies": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/http-proxy": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"dependencies": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
"requires-port": "^1.0.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/http-proxy-middleware": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.1.0.tgz",
"integrity": "sha512-OnjU5vyVgcZVe2AjLJyMrk8YLNOC2lspCHirB5ldM+B/dwEfZ5bgVTrFyzE9R7xRWAP/i/FXtvIqKjTNEZBhBg==",
"dependencies": {
"@types/http-proxy": "^1.17.5",
"camelcase": "^6.2.0",
"http-proxy": "^1.18.1",
"is-glob": "^4.0.1",
"is-plain-obj": "^3.0.0",
"micromatch": "^4.0.2"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-plain-obj": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz",
"integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
"engines": {
"node": ">=10"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"dependencies": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
},
"engines": {
"node": ">=8"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.46.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz",
"integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.29",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz",
"integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==",
"dependencies": {
"mime-db": "1.46.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"node_modules/picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"engines": {
"node": ">=8.6"
}
},
"node_modules/proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"dependencies": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"dependencies": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"dependencies": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
},
"node_modules/serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
"engines": {
"node": ">= 0.8"
}
}
},
"dependencies": { "dependencies": {
"@types/http-proxy": { "@types/http-proxy": {
"version": "1.17.5", "version": "1.17.5",
@@ -334,6 +984,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
}, },
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"on-finished": { "on-finished": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",

View File

@@ -12,6 +12,7 @@
"author": "Microsoft Corporation", "author": "Microsoft Corporation",
"dependencies": { "dependencies": {
"express": "^4.17.1", "express": "^4.17.1",
"http-proxy-middleware": "^1.1.0" "http-proxy-middleware": "^1.1.0",
"node-fetch": "^2.6.1"
} }
} }

View File

@@ -1,15 +1,15 @@
import * as DataModels from "../../Contracts/DataModels";
import { AuthType } from "../../AuthType"; import { AuthType } from "../../AuthType";
import * as DataModels from "../../Contracts/DataModels";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType"; import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
import { client } from "../CosmosClient"; import { userContext } from "../../UserContext";
import { handleError } from "../ErrorHandlingUtils";
import { listSqlContainers } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { listCassandraTables } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; import { listCassandraTables } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { listMongoDBCollections } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { listGremlinGraphs } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; import { listGremlinGraphs } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { listMongoDBCollections } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { listSqlContainers } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { listTables } from "../../Utils/arm/generatedClients/2020-04-01/tableResources"; import { listTables } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { userContext } from "../../UserContext"; import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> { export async function readCollections(databaseId: string): Promise<DataModels.Collection[]> {
const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`); const clearMessage = logConsoleProgress(`Querying containers for database ${databaseId}`);
@@ -17,7 +17,6 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
if ( if (
userContext.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table userContext.defaultExperience !== DefaultAccountExperienceType.Table
) { ) {
return await readCollectionsWithARM(databaseId); return await readCollectionsWithARM(databaseId);

View File

@@ -1,39 +1,37 @@
import { ContainerDefinition } from "@azure/cosmos";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import { AuthType } from "../../AuthType"; import { AuthType } from "../../AuthType";
import { Collection } from "../../Contracts/DataModels"; import { Collection } from "../../Contracts/DataModels";
import { ContainerDefinition } from "@azure/cosmos";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType"; import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
import { import { userContext } from "../../UserContext";
CreateUpdateOptions,
ExtendedResourceProperties,
MongoDBCollectionCreateUpdateParameters,
MongoDBCollectionResource,
SqlContainerCreateUpdateParameters,
SqlContainerResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import { client } from "../CosmosClient";
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { import {
createUpdateCassandraTable, createUpdateCassandraTable,
getCassandraTable, getCassandraTable,
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
createUpdateMongoDBCollection,
getMongoDBCollection,
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { import {
createUpdateGremlinGraph, createUpdateGremlinGraph,
getGremlinGraph, getGremlinGraph,
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import {
createUpdateMongoDBCollection,
getMongoDBCollection,
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources"; import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { handleError } from "../ErrorHandlingUtils"; import {
ExtendedResourceProperties,
MongoDBCollectionCreateUpdateParameters,
SqlContainerCreateUpdateParameters,
SqlContainerResource,
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { userContext } from "../../UserContext"; import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
export async function updateCollection( export async function updateCollection(
databaseId: string, databaseId: string,
collectionId: string, collectionId: string,
newCollection: Collection, newCollection: Partial<Collection>,
options: RequestOptions = {} options: RequestOptions = {}
): Promise<Collection> { ): Promise<Collection> {
let collection: Collection; let collection: Collection;
@@ -43,7 +41,6 @@ export async function updateCollection(
if ( if (
userContext.authType === AuthType.AAD && userContext.authType === AuthType.AAD &&
!userContext.useSDKOperations && !userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table userContext.defaultExperience !== DefaultAccountExperienceType.Table
) { ) {
collection = await updateCollectionWithARM(databaseId, collectionId, newCollection); collection = await updateCollectionWithARM(databaseId, collectionId, newCollection);
@@ -69,7 +66,7 @@ export async function updateCollection(
async function updateCollectionWithARM( async function updateCollectionWithARM(
databaseId: string, databaseId: string,
collectionId: string, collectionId: string,
newCollection: Collection newCollection: Partial<Collection>
): Promise<Collection> { ): Promise<Collection> {
const subscriptionId = userContext.subscriptionId; const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup; const resourceGroup = userContext.resourceGroup;
@@ -85,6 +82,15 @@ async function updateCollectionWithARM(
return updateGremlinGraph(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection); return updateGremlinGraph(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection);
case DefaultAccountExperienceType.Table: case DefaultAccountExperienceType.Table:
return updateTable(collectionId, subscriptionId, resourceGroup, accountName, newCollection); return updateTable(collectionId, subscriptionId, resourceGroup, accountName, newCollection);
case DefaultAccountExperienceType.MongoDB:
return updateMongoDBCollection(
databaseId,
collectionId,
subscriptionId,
resourceGroup,
accountName,
newCollection
);
default: default:
throw new Error(`Unsupported default experience type: ${defaultExperience}`); throw new Error(`Unsupported default experience type: ${defaultExperience}`);
} }
@@ -96,7 +102,7 @@ async function updateSqlContainer(
subscriptionId: string, subscriptionId: string,
resourceGroup: string, resourceGroup: string,
accountName: string, accountName: string,
newCollection: Collection newCollection: Partial<Collection>
): Promise<Collection> { ): Promise<Collection> {
const getResponse = await getSqlContainer(subscriptionId, resourceGroup, accountName, databaseId, collectionId); const getResponse = await getSqlContainer(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
if (getResponse && getResponse.properties && getResponse.properties.resource) { if (getResponse && getResponse.properties && getResponse.properties.resource) {
@@ -115,35 +121,26 @@ async function updateSqlContainer(
throw new Error(`Sql container to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`); throw new Error(`Sql container to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`);
} }
export async function updateMongoDBCollectionThroughRP( export async function updateMongoDBCollection(
databaseId: string, databaseId: string,
collectionId: string, collectionId: string,
newCollection: MongoDBCollectionResource, subscriptionId: string,
updateOptions?: CreateUpdateOptions resourceGroup: string,
): Promise<MongoDBCollectionResource> { accountName: string,
const subscriptionId = userContext.subscriptionId; newCollection: Partial<Collection>
const resourceGroup = userContext.resourceGroup; ): Promise<Collection> {
const accountName = userContext.databaseAccount.name;
const getResponse = await getMongoDBCollection(subscriptionId, resourceGroup, accountName, databaseId, collectionId); const getResponse = await getMongoDBCollection(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
if (getResponse && getResponse.properties && getResponse.properties.resource) { if (getResponse && getResponse.properties && getResponse.properties.resource) {
const updateParams: MongoDBCollectionCreateUpdateParameters = { getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties;
properties: {
resource: newCollection,
options: updateOptions,
},
};
const updateResponse = await createUpdateMongoDBCollection( const updateResponse = await createUpdateMongoDBCollection(
subscriptionId, subscriptionId,
resourceGroup, resourceGroup,
accountName, accountName,
databaseId, databaseId,
collectionId, collectionId,
updateParams getResponse as MongoDBCollectionCreateUpdateParameters
); );
return updateResponse && (updateResponse.properties.resource as Collection);
return updateResponse && (updateResponse.properties.resource as MongoDBCollectionResource);
} }
throw new Error( throw new Error(
@@ -157,7 +154,7 @@ async function updateCassandraTable(
subscriptionId: string, subscriptionId: string,
resourceGroup: string, resourceGroup: string,
accountName: string, accountName: string,
newCollection: Collection newCollection: Partial<Collection>
): Promise<Collection> { ): Promise<Collection> {
const getResponse = await getCassandraTable(subscriptionId, resourceGroup, accountName, databaseId, collectionId); const getResponse = await getCassandraTable(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
if (getResponse && getResponse.properties && getResponse.properties.resource) { if (getResponse && getResponse.properties && getResponse.properties.resource) {
@@ -184,7 +181,7 @@ async function updateGremlinGraph(
subscriptionId: string, subscriptionId: string,
resourceGroup: string, resourceGroup: string,
accountName: string, accountName: string,
newCollection: Collection newCollection: Partial<Collection>
): Promise<Collection> { ): Promise<Collection> {
const getResponse = await getGremlinGraph(subscriptionId, resourceGroup, accountName, databaseId, collectionId); const getResponse = await getGremlinGraph(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
if (getResponse && getResponse.properties && getResponse.properties.resource) { if (getResponse && getResponse.properties && getResponse.properties.resource) {
@@ -208,7 +205,7 @@ async function updateTable(
subscriptionId: string, subscriptionId: string,
resourceGroup: string, resourceGroup: string,
accountName: string, accountName: string,
newCollection: Collection newCollection: Partial<Collection>
): Promise<Collection> { ): Promise<Collection> {
const getResponse = await getTable(subscriptionId, resourceGroup, accountName, collectionId); const getResponse = await getTable(subscriptionId, resourceGroup, accountName, collectionId);
if (getResponse && getResponse.properties && getResponse.properties.resource) { if (getResponse && getResponse.properties && getResponse.properties.resource) {

View File

@@ -67,7 +67,6 @@ ko.components.register("graph-new-vertex-pane", new PaneComponents.GraphNewVerte
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-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("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());

View File

@@ -1,11 +1,10 @@
import { shallow } from "enzyme"; import { shallow } from "enzyme";
import ko from "knockout"; import ko from "knockout";
import React from "react"; import React from "react";
import { updateCollection, updateMongoDBCollectionThroughRP } from "../../../Common/dataAccess/updateCollection"; import { updateCollection } from "../../../Common/dataAccess/updateCollection";
import { updateOffer } from "../../../Common/dataAccess/updateOffer"; import { updateOffer } from "../../../Common/dataAccess/updateOffer";
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 { MongoDBCollectionResource } from "../../../Utils/arm/generatedClients/2020-04-01/types";
import Explorer from "../../Explorer"; import Explorer from "../../Explorer";
import { CollectionSettingsTabV2 } from "../../Tabs/SettingsTabV2"; import { CollectionSettingsTabV2 } from "../../Tabs/SettingsTabV2";
import { SettingsComponent, SettingsComponentProps, SettingsComponentState } from "./SettingsComponent"; import { SettingsComponent, SettingsComponentProps, SettingsComponentState } from "./SettingsComponent";
@@ -23,13 +22,8 @@ jest.mock("../../../Common/dataAccess/updateCollection", () => ({
changeFeedPolicy: undefined, changeFeedPolicy: undefined,
analyticalStorageTtl: undefined, analyticalStorageTtl: undefined,
geospatialConfig: undefined, geospatialConfig: undefined,
} as DataModels.Collection),
updateMongoDBCollectionThroughRP: jest.fn().mockReturnValue({
id: undefined,
shardKey: undefined,
indexes: [], indexes: [],
analyticalStorageTtl: undefined, }),
} as MongoDBCollectionResource),
})); }));
jest.mock("../../../Common/dataAccess/updateOffer", () => ({ jest.mock("../../../Common/dataAccess/updateOffer", () => ({
updateOffer: jest.fn().mockReturnValue({} as DataModels.Offer), updateOffer: jest.fn().mockReturnValue({} as DataModels.Offer),
@@ -193,7 +187,6 @@ describe("SettingsComponent", () => {
}; };
await settingsComponentInstance.onSaveClick(); await settingsComponentInstance.onSaveClick();
expect(updateCollection).toBeCalled(); expect(updateCollection).toBeCalled();
expect(updateMongoDBCollectionThroughRP).toBeCalled();
expect(updateOffer).toBeCalled(); expect(updateOffer).toBeCalled();
}); });

View File

@@ -6,7 +6,7 @@ import { AuthType } from "../../../AuthType";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress"; import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";
import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection"; import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection";
import { updateCollection, updateMongoDBCollectionThroughRP } from "../../../Common/dataAccess/updateCollection"; import { updateCollection } from "../../../Common/dataAccess/updateCollection";
import { updateOffer } from "../../../Common/dataAccess/updateOffer"; import { updateOffer } from "../../../Common/dataAccess/updateOffer";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
import * as DataModels from "../../../Contracts/DataModels"; import * as DataModels from "../../../Contracts/DataModels";
@@ -782,12 +782,12 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
if (this.state.isMongoIndexingPolicySaveable && this.mongoDBCollectionResource) { if (this.state.isMongoIndexingPolicySaveable && this.mongoDBCollectionResource) {
try { try {
const newMongoIndexes = this.getMongoIndexesToSave(); const newMongoIndexes = this.getMongoIndexesToSave();
const newMongoCollection: MongoDBCollectionResource = { const newMongoCollection = {
...this.mongoDBCollectionResource, ...this.mongoDBCollectionResource,
indexes: newMongoIndexes, indexes: newMongoIndexes,
}; };
this.mongoDBCollectionResource = await updateMongoDBCollectionThroughRP( this.mongoDBCollectionResource = await updateCollection(
this.collection.databaseId, this.collection.databaseId,
this.collection.id(), this.collection.id(),
newMongoCollection newMongoCollection

View File

@@ -262,27 +262,6 @@ exports[`SettingsComponent renders 1`] = `
"title": [Function], "title": [Function],
"visible": [Function], "visible": [Function],
}, },
QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
NewVertexPane { NewVertexPane {
"buildString": [Function], "buildString": [Function],
"container": [Circular], "container": [Circular],
@@ -740,27 +719,6 @@ exports[`SettingsComponent renders 1`] = `
"queriesClient": QueriesClient { "queriesClient": QueriesClient {
"container": [Circular], "container": [Circular],
}, },
"querySelectPane": QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
"refreshDatabaseAccount": [Function], "refreshDatabaseAccount": [Function],
"refreshNotebookList": [Function], "refreshNotebookList": [Function],
"refreshTreeTitle": [Function], "refreshTreeTitle": [Function],
@@ -1099,27 +1057,6 @@ exports[`SettingsComponent renders 1`] = `
"title": [Function], "title": [Function],
"visible": [Function], "visible": [Function],
}, },
QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
NewVertexPane { NewVertexPane {
"buildString": [Function], "buildString": [Function],
"container": [Circular], "container": [Circular],
@@ -1577,27 +1514,6 @@ exports[`SettingsComponent renders 1`] = `
"queriesClient": QueriesClient { "queriesClient": QueriesClient {
"container": [Circular], "container": [Circular],
}, },
"querySelectPane": QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
"refreshDatabaseAccount": [Function], "refreshDatabaseAccount": [Function],
"refreshNotebookList": [Function], "refreshNotebookList": [Function],
"refreshTreeTitle": [Function], "refreshTreeTitle": [Function],
@@ -1949,27 +1865,6 @@ exports[`SettingsComponent renders 1`] = `
"title": [Function], "title": [Function],
"visible": [Function], "visible": [Function],
}, },
QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
NewVertexPane { NewVertexPane {
"buildString": [Function], "buildString": [Function],
"container": [Circular], "container": [Circular],
@@ -2427,27 +2322,6 @@ exports[`SettingsComponent renders 1`] = `
"queriesClient": QueriesClient { "queriesClient": QueriesClient {
"container": [Circular], "container": [Circular],
}, },
"querySelectPane": QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
"refreshDatabaseAccount": [Function], "refreshDatabaseAccount": [Function],
"refreshNotebookList": [Function], "refreshNotebookList": [Function],
"refreshTreeTitle": [Function], "refreshTreeTitle": [Function],
@@ -2786,27 +2660,6 @@ exports[`SettingsComponent renders 1`] = `
"title": [Function], "title": [Function],
"visible": [Function], "visible": [Function],
}, },
QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
NewVertexPane { NewVertexPane {
"buildString": [Function], "buildString": [Function],
"container": [Circular], "container": [Circular],
@@ -3264,27 +3117,6 @@ exports[`SettingsComponent renders 1`] = `
"queriesClient": QueriesClient { "queriesClient": QueriesClient {
"container": [Circular], "container": [Circular],
}, },
"querySelectPane": QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
"refreshDatabaseAccount": [Function], "refreshDatabaseAccount": [Function],
"refreshNotebookList": [Function], "refreshNotebookList": [Function],
"refreshTreeTitle": [Function], "refreshTreeTitle": [Function],

View File

@@ -65,9 +65,10 @@ import { SetupNotebooksPane } from "./Panes/SetupNotebooksPane";
import { StringInputPane } from "./Panes/StringInputPane"; import { StringInputPane } from "./Panes/StringInputPane";
import AddTableEntityPane from "./Panes/Tables/AddTableEntityPane"; import AddTableEntityPane from "./Panes/Tables/AddTableEntityPane";
import EditTableEntityPane from "./Panes/Tables/EditTableEntityPane"; import EditTableEntityPane from "./Panes/Tables/EditTableEntityPane";
import { QuerySelectPane } from "./Panes/Tables/QuerySelectPane"; import { TableQuerySelectPanel } from "./Panes/Tables/TableQuerySelectPanel";
import { UploadFilePane } from "./Panes/UploadFilePane"; import { UploadFilePane } from "./Panes/UploadFilePane";
import { UploadItemsPane } from "./Panes/UploadItemsPane"; import { UploadItemsPane } from "./Panes/UploadItemsPane";
import QueryViewModel from "./Tables/QueryBuilder/QueryViewModel";
import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient"; import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient";
import NotebookV2Tab, { NotebookTabOptions } from "./Tabs/NotebookV2Tab"; import NotebookV2Tab, { NotebookTabOptions } from "./Tabs/NotebookV2Tab";
import TabsBase from "./Tabs/TabsBase"; import TabsBase from "./Tabs/TabsBase";
@@ -195,7 +196,6 @@ export default class Explorer {
public graphStylingPane: GraphStylingPane; public graphStylingPane: GraphStylingPane;
public addTableEntityPane: AddTableEntityPane; public addTableEntityPane: AddTableEntityPane;
public editTableEntityPane: EditTableEntityPane; public editTableEntityPane: EditTableEntityPane;
public querySelectPane: QuerySelectPane;
public newVertexPane: NewVertexPane; public newVertexPane: NewVertexPane;
public cassandraAddCollectionPane: CassandraAddCollectionPane; public cassandraAddCollectionPane: CassandraAddCollectionPane;
public stringInputPane: StringInputPane; public stringInputPane: StringInputPane;
@@ -566,13 +566,6 @@ export default class Explorer {
container: this, container: this,
}); });
this.querySelectPane = new QuerySelectPane({
id: "queryselectpane",
visible: ko.observable<boolean>(false),
container: this,
});
this.newVertexPane = new NewVertexPane({ this.newVertexPane = new NewVertexPane({
id: "newvertexpane", id: "newvertexpane",
visible: ko.observable<boolean>(false), visible: ko.observable<boolean>(false),
@@ -610,7 +603,6 @@ export default class Explorer {
this.graphStylingPane, this.graphStylingPane,
this.addTableEntityPane, this.addTableEntityPane,
this.editTableEntityPane, this.editTableEntityPane,
this.querySelectPane,
this.newVertexPane, this.newVertexPane,
this.cassandraAddCollectionPane, this.cassandraAddCollectionPane,
this.stringInputPane, this.stringInputPane,
@@ -2361,4 +2353,11 @@ export default class Explorer {
/> />
); );
} }
public openTableSelectQueryPanel(queryViewModal: QueryViewModel): void {
this.openSidePanel(
"Select Column",
<TableQuerySelectPanel explorer={this} closePanel={this.closeSidePanel} queryViewModel={queryViewModal} />
);
}
} }

View File

@@ -1,6 +1,6 @@
import { NeighborVertexBasicInfo } from "./GraphExplorer";
import * as GraphData from "./GraphData";
import * as ViewModels from "../../../Contracts/ViewModels"; import * as ViewModels from "../../../Contracts/ViewModels";
import * as GraphData from "./GraphData";
import { NeighborVertexBasicInfo } from "./GraphExplorer";
interface JoinArrayMaxCharOutput { interface JoinArrayMaxCharOutput {
result: string; // string output result: string; // string output
@@ -13,9 +13,9 @@ interface EdgePropertyType {
inV?: string; inV?: string;
} }
export function getNeighborTitle(neighbor: NeighborVertexBasicInfo): string { export const getNeighborTitle = (neighbor: NeighborVertexBasicInfo): string => {
return `edge id: ${neighbor.edgeId}, vertex id: ${neighbor.id}`; return `edge id: ${neighbor.edgeId}, vertex id: ${neighbor.id}`;
} };
/** /**
* Collect all edges from this node * Collect all edges from this node
@@ -23,11 +23,11 @@ export function getNeighborTitle(neighbor: NeighborVertexBasicInfo): string {
* @param graphData * @param graphData
* @param newNodes (optional) object describing new nodes encountered * @param newNodes (optional) object describing new nodes encountered
*/ */
export function createEdgesfromNode( export const createEdgesfromNode = (
vertex: GraphData.GremlinVertex, vertex: GraphData.GremlinVertex,
graphData: GraphData.GraphData<GraphData.GremlinVertex, GraphData.GremlinEdge>, graphData: GraphData.GraphData<GraphData.GremlinVertex, GraphData.GremlinEdge>,
newNodes?: { [id: string]: boolean } newNodes?: { [id: string]: boolean }
): void { ): void => {
if (Object.prototype.hasOwnProperty.call(vertex, "outE")) { if (Object.prototype.hasOwnProperty.call(vertex, "outE")) {
const outE = vertex.outE; const outE = vertex.outE;
for (const label in outE) { for (const label in outE) {
@@ -66,7 +66,7 @@ export function createEdgesfromNode(
}); });
} }
} }
} };
/** /**
* From ['id1', 'id2', 'idn'] build the following string "'id1','id2','idn'". * From ['id1', 'id2', 'idn'] build the following string "'id1','id2','idn'".
@@ -75,7 +75,7 @@ export function createEdgesfromNode(
* @param maxSize * @param maxSize
* @return * @return
*/ */
export function getLimitedArrayString(array: string[], maxSize: number): JoinArrayMaxCharOutput { export const getLimitedArrayString = (array: string[], maxSize: number): JoinArrayMaxCharOutput => {
if (!array || array.length === 0 || array[0].length + 2 > maxSize) { if (!array || array.length === 0 || array[0].length + 2 > maxSize) {
return { result: "", consumedCount: 0 }; return { result: "", consumedCount: 0 };
} }
@@ -96,16 +96,16 @@ export function getLimitedArrayString(array: string[], maxSize: number): JoinArr
result: output, result: output,
consumedCount: i + 1, consumedCount: i + 1,
}; };
} };
export function createFetchEdgePairQuery( export const createFetchEdgePairQuery = (
outE: boolean, outE: boolean,
pkid: string, pkid: string,
excludedEdgeIds: string[], excludedEdgeIds: string[],
startIndex: number, startIndex: number,
pageSize: number, pageSize: number,
withoutStepArgMaxLenght: number withoutStepArgMaxLenght: number
): string { ): string => {
let gremlinQuery: string; let gremlinQuery: string;
if (excludedEdgeIds.length > 0) { if (excludedEdgeIds.length > 0) {
// build a string up to max char // build a string up to max char
@@ -128,15 +128,15 @@ export function createFetchEdgePairQuery(
}().as('v').select('e', 'v')`; }().as('v').select('e', 'v')`;
} }
return gremlinQuery; return gremlinQuery;
} };
/** /**
* Trim graph * Trim graph
*/ */
export function trimGraph( export const trimGraph = (
currentRoot: GraphData.GremlinVertex, currentRoot: GraphData.GremlinVertex,
graphData: GraphData.GraphData<GraphData.GremlinVertex, GraphData.GremlinEdge> graphData: GraphData.GraphData<GraphData.GremlinVertex, GraphData.GremlinEdge>
): void { ): void => {
const importantNodes = [currentRoot.id].concat(currentRoot._ancestorsId); const importantNodes = [currentRoot.id].concat(currentRoot._ancestorsId);
graphData.unloadAllVertices(importantNodes); graphData.unloadAllVertices(importantNodes);
@@ -144,32 +144,32 @@ export function trimGraph(
$.each(graphData.ids, (index: number, id: string) => { $.each(graphData.ids, (index: number, id: string) => {
graphData.getVertexById(id)._isFixedPosition = importantNodes.indexOf(id) !== -1; graphData.getVertexById(id)._isFixedPosition = importantNodes.indexOf(id) !== -1;
}); });
} };
export function addRootChildToGraph( export const addRootChildToGraph = (
root: GraphData.GremlinVertex, root: GraphData.GremlinVertex,
child: GraphData.GremlinVertex, child: GraphData.GremlinVertex,
graphData: GraphData.GraphData<GraphData.GremlinVertex, GraphData.GremlinEdge> graphData: GraphData.GraphData<GraphData.GremlinVertex, GraphData.GremlinEdge>
): void { ): void => {
child._ancestorsId = (root._ancestorsId || []).concat([root.id]); child._ancestorsId = (root._ancestorsId || []).concat([root.id]);
graphData.addVertex(child); graphData.addVertex(child);
createEdgesfromNode(child, graphData); createEdgesfromNode(child, graphData);
graphData.addNeighborInfo(child); graphData.addNeighborInfo(child);
} };
/** /**
* TODO Perform minimal substitution to prevent breaking gremlin query and allow \"" for now. * TODO Perform minimal substitution to prevent breaking gremlin query and allow \"" for now.
* @param value * @param value
*/ */
export function escapeDoubleQuotes(value: string): string { export const escapeDoubleQuotes = (value: string): string => {
return value === undefined ? value : value.replace(/"/g, '\\"'); return value === undefined ? value : value.replace(/"/g, '\\"');
} };
/** /**
* Surround with double-quotes if val is a string. * Surround with double-quotes if val is a string.
* @param val * @param val
*/ */
export function getQuotedPropValue(ip: ViewModels.InputPropertyValue): string { export const getQuotedPropValue = (ip: ViewModels.InputPropertyValue): string => {
switch (ip.type) { switch (ip.type) {
case "number": case "number":
case "boolean": case "boolean":
@@ -179,12 +179,12 @@ export function getQuotedPropValue(ip: ViewModels.InputPropertyValue): string {
default: default:
return `"${escapeDoubleQuotes(ip.value as string)}"`; return `"${escapeDoubleQuotes(ip.value as string)}"`;
} }
} };
/** /**
* TODO Perform minimal substitution to prevent breaking gremlin query and allow \' for now. * TODO Perform minimal substitution to prevent breaking gremlin query and allow \' for now.
* @param value * @param value
*/ */
export function escapeSingleQuotes(value: string): string { export const escapeSingleQuotes = (value: string): string => {
return value === undefined ? value : value.replace(/'/g, "\\'"); return value === undefined ? value : value.replace(/'/g, "\\'");
} };

View File

@@ -1,9 +1,9 @@
import React from "react";
import { shallow } from "enzyme"; import { shallow } from "enzyme";
import React from "react";
import { import {
NotificationConsoleComponentProps,
NotificationConsoleComponent,
ConsoleDataType, ConsoleDataType,
NotificationConsoleComponent,
NotificationConsoleComponentProps,
} from "./NotificationConsoleComponent"; } from "./NotificationConsoleComponent";
describe("NotificationConsoleComponent", () => { describe("NotificationConsoleComponent", () => {
@@ -12,7 +12,7 @@ describe("NotificationConsoleComponent", () => {
consoleData: undefined, consoleData: undefined,
isConsoleExpanded: false, isConsoleExpanded: false,
inProgressConsoleDataIdToBeDeleted: "", inProgressConsoleDataIdToBeDeleted: "",
setIsConsoleExpanded: (isExpanded: boolean): void => {}, setIsConsoleExpanded: (): void => undefined,
}; };
}; };
@@ -98,7 +98,7 @@ describe("NotificationConsoleComponent", () => {
wrapper.setProps(props); wrapper.setProps(props);
expect(wrapper.find(".notificationConsoleData .date").text()).toEqual(date); expect(wrapper.find(".notificationConsoleData .date").text()).toEqual(date);
expect(wrapper.find(".notificationConsoleData .message").text()).toEqual(message); expect(wrapper.find(".notificationConsoleData .message").text()).toEqual(message);
expect(wrapper.exists(`.notificationConsoleData .${iconClassName}`)); expect(wrapper.exists(`.notificationConsoleData .${iconClassName}`)).toBe(true);
}; };
it("renders progress notifications", () => { it("renders progress notifications", () => {
@@ -139,7 +139,7 @@ describe("NotificationConsoleComponent", () => {
wrapper.setProps(props); wrapper.setProps(props);
wrapper.find(".clearNotificationsButton").simulate("click"); wrapper.find(".clearNotificationsButton").simulate("click");
expect(!wrapper.exists(".notificationConsoleData")); expect(wrapper.exists(".notificationConsoleData")).toBe(true);
}); });
it("collapses and hide content", () => { it("collapses and hide content", () => {
@@ -155,7 +155,7 @@ describe("NotificationConsoleComponent", () => {
wrapper.setProps(props); wrapper.setProps(props);
wrapper.find(".notificationConsoleHeader").simulate("click"); wrapper.find(".notificationConsoleHeader").simulate("click");
expect(!wrapper.exists(".notificationConsoleContent")); expect(wrapper.exists(".notificationConsoleContent")).toBe(false);
}); });
it("display latest data in header", () => { it("display latest data in header", () => {

View File

@@ -2,19 +2,20 @@
* React component for control bar * React component for control bar
*/ */
import * as React from "react";
import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
import AnimateHeight from "react-animate-height";
import { Dropdown, IDropdownOption } from "office-ui-fabric-react"; import { Dropdown, IDropdownOption } from "office-ui-fabric-react";
import LoadingIcon from "../../../../images/loading.svg"; import * as React from "react";
import AnimateHeight from "react-animate-height";
import LoaderIcon from "../../../../images/circular_loader_black_16x16.gif";
import ClearIcon from "../../../../images/Clear.svg";
import ErrorBlackIcon from "../../../../images/error_black.svg"; import ErrorBlackIcon from "../../../../images/error_black.svg";
import ErrorRedIcon from "../../../../images/error_red.svg";
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg"; import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
import InfoIcon from "../../../../images/info_color.svg"; import InfoIcon from "../../../../images/info_color.svg";
import ErrorRedIcon from "../../../../images/error_red.svg"; import LoadingIcon from "../../../../images/loading.svg";
import ClearIcon from "../../../../images/Clear.svg";
import LoaderIcon from "../../../../images/circular_loader_black_16x16.gif";
import ChevronUpIcon from "../../../../images/QueryBuilder/CollapseChevronUp_16x.png";
import ChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png"; import ChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
import ChevronUpIcon from "../../../../images/QueryBuilder/CollapseChevronUp_16x.png";
import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
import { userContext } from "../../../UserContext";
/** /**
* Log levels * Log levels
@@ -76,7 +77,7 @@ export class NotificationConsoleComponent extends React.Component<
public componentDidUpdate( public componentDidUpdate(
prevProps: NotificationConsoleComponentProps, prevProps: NotificationConsoleComponentProps,
prevState: NotificationConsoleComponentState prevState: NotificationConsoleComponentState
) { ): void {
const currentHeaderStatus = NotificationConsoleComponent.extractHeaderStatus(this.props.consoleData); const currentHeaderStatus = NotificationConsoleComponent.extractHeaderStatus(this.props.consoleData);
if ( if (
@@ -97,7 +98,7 @@ export class NotificationConsoleComponent extends React.Component<
} }
} }
public setElememntRef = (element: HTMLElement) => { public setElememntRef = (element: HTMLElement): void => {
this.consoleHeaderElement = element; this.consoleHeaderElement = element;
}; };
@@ -116,7 +117,7 @@ export class NotificationConsoleComponent extends React.Component<
className="notificationConsoleHeader" className="notificationConsoleHeader"
id="notificationConsoleHeader" id="notificationConsoleHeader"
ref={this.setElememntRef} ref={this.setElememntRef}
onClick={(event: React.MouseEvent<HTMLDivElement>) => this.expandCollapseConsole()} onClick={() => this.expandCollapseConsole()}
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => this.onExpandCollapseKeyPress(event)} onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => this.onExpandCollapseKeyPress(event)}
tabIndex={0} tabIndex={0}
> >
@@ -135,6 +136,7 @@ export class NotificationConsoleComponent extends React.Component<
<span className="numInfoItems">{numInfoItems}</span> <span className="numInfoItems">{numInfoItems}</span>
</span> </span>
</span> </span>
{userContext.features.pr && <PrPreview pr={userContext.features.pr} />}
<span className="consoleSplitter" /> <span className="consoleSplitter" />
<span className="headerStatus"> <span className="headerStatus">
<span className="headerStatusEllipsis">{this.state.headerStatus}</span> <span className="headerStatusEllipsis">{this.state.headerStatus}</span>
@@ -304,3 +306,18 @@ export class NotificationConsoleComponent extends React.Component<
); );
}; };
} }
const PrPreview = (props: { pr: string }) => {
const url = new URL(props.pr);
const [, ref] = url.hash.split("#");
url.hash = "";
return (
<>
<span className="consoleSplitter" />
<a target="_blank" rel="noreferrer" href={url.href} style={{ marginRight: "1em", fontWeight: "bold" }}>
{ref}
</a>
</>
);
};

View File

@@ -1,15 +1,16 @@
// Manages all the redux logic for the notebook nteract code // Manages all the redux logic for the notebook nteract code
// TODO: Merge with NotebookClient? // TODO: Merge with NotebookClient?
import { NotebookWorkspaceConnectionInfo } from "../../Contracts/DataModels";
import * as Constants from "../../Common/Constants";
import { CdbAppState, makeCdbRecord } from "./NotebookComponent/types";
// Vendor modules // Vendor modules
import { import {
actions, actions,
AppState, AppState,
ContentRecord,
createHostRef, createHostRef,
createKernelspecsRef, createKernelspecsRef,
HostRecord,
HostRef,
IContentProvider,
KernelspecsRef,
makeAppRecord, makeAppRecord,
makeCommsRecord, makeCommsRecord,
makeContentsRecord, makeContentsRecord,
@@ -19,23 +20,21 @@ import {
makeJupyterHostRecord, makeJupyterHostRecord,
makeStateRecord, makeStateRecord,
makeTransformsRecord, makeTransformsRecord,
ContentRecord,
HostRecord,
HostRef,
KernelspecsRef,
IContentProvider,
} from "@nteract/core"; } from "@nteract/core";
import { configOption, createConfigCollection, defineConfigOption } from "@nteract/mythic-configuration";
import { Media } from "@nteract/outputs"; import { Media } from "@nteract/outputs";
import TransformVDOM from "@nteract/transform-vdom"; import TransformVDOM from "@nteract/transform-vdom";
import * as Immutable from "immutable"; import * as Immutable from "immutable";
import { Store, AnyAction, MiddlewareAPI, Middleware, Dispatch } from "redux";
import configureStore from "./NotebookComponent/store";
import { Notification } from "react-notification-system"; import { Notification } from "react-notification-system";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import { AnyAction, Dispatch, Middleware, MiddlewareAPI, Store } from "redux";
import * as Constants from "../../Common/Constants";
import { NotebookWorkspaceConnectionInfo } from "../../Contracts/DataModels";
import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import { configOption, createConfigCollection, defineConfigOption } from "@nteract/mythic-configuration"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
import configureStore from "./NotebookComponent/store";
import { CdbAppState, makeCdbRecord } from "./NotebookComponent/types";
import JavaScript from "./NotebookRenderer/outputs/javascript";
export type KernelSpecsDisplay = { name: string; displayName: string }; export type KernelSpecsDisplay = { name: string; displayName: string };
@@ -168,7 +167,7 @@ export class NotebookClientV2 {
"application/vnd.vega.v5+json": NullTransform, "application/vnd.vega.v5+json": NullTransform,
"application/vdom.v1+json": TransformVDOM, "application/vdom.v1+json": TransformVDOM,
"application/json": Media.Json, "application/json": Media.Json,
"application/javascript": Media.JavaScript, "application/javascript": userContext.features.sandboxNotebookOutputs ? JavaScript : Media.JavaScript,
"text/html": Media.HTML, "text/html": Media.HTML,
"text/markdown": Media.Markdown, "text/markdown": Media.Markdown,
"text/latex": Media.LaTeX, "text/latex": Media.LaTeX,

View File

@@ -1,18 +1,20 @@
import * as React from "react";
import "./base.css";
import "./default.css";
import { CodeCell, RawCell, Cells, MarkdownCell } from "@nteract/stateful-components";
import Prompt, { PassedPromptProps } from "@nteract/stateful-components/lib/inputs/prompt";
import { AzureTheme } from "./AzureTheme";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { actions, ContentRef } from "@nteract/core"; import { actions, ContentRef } from "@nteract/core";
import loadTransform from "../NotebookComponent/loadTransform"; import { KernelOutputError, StreamText } from "@nteract/outputs";
import { Cells, CodeCell, MarkdownCell, RawCell } from "@nteract/stateful-components";
import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor"; import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor";
import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor";
import Prompt, { PassedPromptProps } from "@nteract/stateful-components/lib/inputs/prompt";
import TransformMedia from "@nteract/stateful-components/lib/outputs/transform-media";
import * as React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { userContext } from "../../../UserContext";
import loadTransform from "../NotebookComponent/loadTransform";
import { AzureTheme } from "./AzureTheme";
import "./base.css";
import "./default.css";
import "./NotebookReadOnlyRenderer.less"; import "./NotebookReadOnlyRenderer.less";
import IFrameOutputs from "./outputs/IFrameOutputs";
export interface NotebookRendererProps { export interface NotebookRendererProps {
contentRef: any; contentRef: any;
@@ -60,6 +62,16 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
<CodeCell id={id} contentRef={contentRef}> <CodeCell id={id} contentRef={contentRef}>
{{ {{
prompt: (props: { id: string; contentRef: string }) => this.renderPrompt(props.id, props.contentRef), prompt: (props: { id: string; contentRef: string }) => this.renderPrompt(props.id, props.contentRef),
outputs: userContext.features.sandboxNotebookOutputs
? (props: any) => (
<IFrameOutputs id={id} contentRef={contentRef}>
<TransformMedia output_type={"display_data"} id={id} contentRef={contentRef} />
<TransformMedia output_type={"execute_result"} id={id} contentRef={contentRef} />
<KernelOutputError />
<StreamText />
</IFrameOutputs>
)
: undefined,
editor: { editor: {
monaco: (props: PassedEditorProps) => monaco: (props: PassedEditorProps) =>
this.props.hideInputs ? <></> : <MonacoEditor readOnly={true} {...props} editorType={"monaco"} />, this.props.hideInputs ? <></> : <MonacoEditor readOnly={true} {...props} editorType={"monaco"} />,

View File

@@ -1,37 +1,32 @@
import * as React from "react"; import { CellId } from "@nteract/commutable";
import "./base.css"; import { CellType } from "@nteract/commutable/src";
import "./default.css"; import { actions, ContentRef } from "@nteract/core";
import { KernelOutputError, StreamText } from "@nteract/outputs";
import { RawCell, Cells, CodeCell, MarkdownCell } from "@nteract/stateful-components"; import { Cells, CodeCell, MarkdownCell, RawCell } from "@nteract/stateful-components";
import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor"; import MonacoEditor from "@nteract/stateful-components/lib/inputs/connected-editors/monacoEditor";
import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor";
import TransformMedia from "@nteract/stateful-components/lib/outputs/transform-media";
import Prompt from "./Prompt"; import * as React from "react";
import { promptContent } from "./PromptContent";
import { AzureTheme } from "./AzureTheme";
import { DndProvider } from "react-dnd"; import { DndProvider } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend"; import HTML5Backend from "react-dnd-html5-backend";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
import { actions, ContentRef } from "@nteract/core"; import { userContext } from "../../../UserContext";
import { CellId } from "@nteract/commutable";
import loadTransform from "../NotebookComponent/loadTransform";
import DraggableCell from "./decorators/draggable";
import CellCreator from "./decorators/CellCreator";
import KeyboardShortcuts from "./decorators/kbd-shortcuts";
import CellToolbar from "./Toolbar";
import StatusBar from "./StatusBar";
import HijackScroll from "./decorators/hijack-scroll";
import { CellType } from "@nteract/commutable/src";
import "./NotebookRenderer.less";
import HoverableCell from "./decorators/HoverableCell";
import CellLabeler from "./decorators/CellLabeler";
import * as cdbActions from "../NotebookComponent/actions"; import * as cdbActions from "../NotebookComponent/actions";
import loadTransform from "../NotebookComponent/loadTransform";
import { AzureTheme } from "./AzureTheme";
import "./base.css";
import CellCreator from "./decorators/CellCreator";
import CellLabeler from "./decorators/CellLabeler";
import HoverableCell from "./decorators/HoverableCell";
import KeyboardShortcuts from "./decorators/kbd-shortcuts";
import "./default.css";
import "./NotebookRenderer.less";
import IFrameOutputs from "./outputs/IFrameOutputs";
import Prompt from "./Prompt";
import { promptContent } from "./PromptContent";
import StatusBar from "./StatusBar";
import CellToolbar from "./Toolbar";
export interface NotebookRendererBaseProps { export interface NotebookRendererBaseProps {
contentRef: any; contentRef: any;
@@ -112,6 +107,16 @@ class BaseNotebookRenderer extends React.Component<NotebookRendererProps> {
</Prompt> </Prompt>
), ),
toolbar: () => <CellToolbar id={id} contentRef={contentRef} />, toolbar: () => <CellToolbar id={id} contentRef={contentRef} />,
outputs: userContext.features.sandboxNotebookOutputs
? (props: any) => (
<IFrameOutputs id={id} contentRef={contentRef}>
<TransformMedia output_type={"display_data"} id={id} contentRef={contentRef} />
<TransformMedia output_type={"execute_result"} id={id} contentRef={contentRef} />
<KernelOutputError />
<StreamText />
</IFrameOutputs>
)
: undefined,
}} }}
</CodeCell> </CodeCell>
), ),

View File

@@ -0,0 +1,70 @@
import { AppState, ContentRef, selectors } from "@nteract/core";
import { Output } from "@nteract/outputs";
import Immutable from "immutable";
import React from "react";
import { connect } from "react-redux";
import { SandboxFrame } from "./SandboxFrame";
// Adapted from https://github.com/nteract/nteract/blob/main/packages/stateful-components/src/outputs/index.tsx
// to add support for sandboxing using <iframe>
interface ComponentProps {
id: string;
contentRef: ContentRef;
children: React.ReactNode;
}
interface StateProps {
hidden: boolean;
expanded: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
outputs: Immutable.List<any>;
}
export class IFrameOutputs extends React.PureComponent<ComponentProps & StateProps> {
render(): JSX.Element {
const { outputs, children, hidden, expanded } = this.props;
return (
<SandboxFrame
style={{ border: "none", width: "100%" }}
sandbox="allow-downloads allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-popups-to-escape-sandbox"
>
<div className={`nteract-cell-outputs ${hidden ? "hidden" : ""} ${expanded ? "expanded" : ""}`}>
{outputs.map((output, index) => (
<Output output={output} key={index}>
{children}
</Output>
))}
</div>
</SandboxFrame>
);
}
}
export const makeMapStateToProps = (
initialState: AppState,
ownProps: ComponentProps
): ((state: AppState) => StateProps) => {
const mapStateToProps = (state: AppState): StateProps => {
let outputs = Immutable.List();
let hidden = false;
let expanded = false;
const { contentRef, id } = ownProps;
const model = selectors.model(state, { contentRef });
if (model && model.type === "notebook") {
const cell = selectors.notebook.cellById(model, { id });
if (cell) {
outputs = cell.get("outputs", Immutable.List());
hidden = cell.cell_type === "code" && cell.getIn(["metadata", "jupyter", "outputs_hidden"]);
expanded = cell.cell_type === "code" && cell.getIn(["metadata", "collapsed"]) === false;
}
}
return { outputs, hidden, expanded };
};
return mapStateToProps;
};
export default connect<StateProps, void, ComponentProps, AppState>(makeMapStateToProps)(IFrameOutputs);

View File

@@ -0,0 +1,64 @@
import React from "react";
import ReactDOM from "react-dom";
import { copyStyles } from "../../../../Utils/StyleUtils";
interface SandboxFrameProps {
style: React.CSSProperties;
sandbox: string;
}
interface SandboxFrameState {
frame: HTMLIFrameElement;
frameBody: HTMLElement;
frameHeight: number;
}
export class SandboxFrame extends React.PureComponent<SandboxFrameProps, SandboxFrameState> {
private resizeObserver: ResizeObserver;
constructor(props: SandboxFrameProps) {
super(props);
this.state = {
frame: undefined,
frameBody: undefined,
frameHeight: 0,
};
}
render(): JSX.Element {
return (
<iframe
ref={(ele) => this.setState({ frame: ele })}
srcDoc={`<!DOCTYPE html>`}
onLoad={(event) => this.onFrameLoad(event)}
style={this.props.style}
sandbox={this.props.sandbox}
height={this.state.frameHeight}
>
{this.state.frameBody && ReactDOM.createPortal(this.props.children, this.state.frameBody)}
</iframe>
);
}
componentWillUnmount() {
this.resizeObserver?.disconnect();
}
onFrameLoad(event: React.SyntheticEvent<HTMLIFrameElement, Event>): void {
const doc = (event.target as HTMLIFrameElement).contentDocument;
copyStyles(document, doc);
this.setState({
frameBody: doc.body,
frameHeight: doc.body.scrollHeight,
});
this.resizeObserver = new ResizeObserver(() =>
this.setState({
frameHeight: this.state.frameBody.scrollHeight,
})
);
this.resizeObserver.observe(doc.body);
}
}

View File

@@ -0,0 +1,26 @@
import { Media } from "@nteract/outputs";
import React from "react";
interface Props {
/**
* The JavaScript code that we would like to execute.
*/
data: string;
/**
* The media type associated with our component.
*/
mediaType: "text/javascript";
}
export class JavaScript extends React.PureComponent<Props> {
static defaultProps = {
data: "",
mediaType: "application/javascript",
};
render(): JSX.Element {
return <Media.HTML data={`<script>${this.props.data}</script>`} />;
}
}
export default JavaScript;

View File

@@ -9,7 +9,6 @@ import SetupNotebooksPaneTemplate from "./SetupNotebooksPane.html";
import StringInputPaneTemplate from "./StringInputPane.html"; import StringInputPaneTemplate from "./StringInputPane.html";
import TableAddEntityPaneTemplate from "./Tables/TableAddEntityPane.html"; import TableAddEntityPaneTemplate from "./Tables/TableAddEntityPane.html";
import TableEditEntityPaneTemplate from "./Tables/TableEditEntityPane.html"; import TableEditEntityPaneTemplate from "./Tables/TableEditEntityPane.html";
import TableQuerySelectPaneTemplate from "./Tables/TableQuerySelectPane.html";
export class PaneComponent { export class PaneComponent {
constructor(data: any) { constructor(data: any) {
@@ -79,16 +78,6 @@ export class TableEditEntityPaneComponent {
}; };
} }
} }
export class TableQuerySelectPaneComponent {
constructor() {
return {
viewModel: PaneComponent,
template: TableQuerySelectPaneTemplate,
};
}
}
export class CassandraAddCollectionPaneComponent { export class CassandraAddCollectionPaneComponent {
constructor() { constructor() {
return { return {

View File

@@ -152,3 +152,6 @@
.removeIcon { .removeIcon {
color: @InfoIconColor; color: @InfoIconColor;
} }
.column-select-view {
margin: 20px 0px 0px 0px;
}

View File

@@ -238,27 +238,6 @@ exports[`Settings Pane should render Default properly 1`] = `
"title": [Function], "title": [Function],
"visible": [Function], "visible": [Function],
}, },
QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
NewVertexPane { NewVertexPane {
"buildString": [Function], "buildString": [Function],
"container": [Circular], "container": [Circular],
@@ -716,27 +695,6 @@ exports[`Settings Pane should render Default properly 1`] = `
"queriesClient": QueriesClient { "queriesClient": QueriesClient {
"container": [Circular], "container": [Circular],
}, },
"querySelectPane": QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
"refreshDatabaseAccount": [Function], "refreshDatabaseAccount": [Function],
"refreshNotebookList": [Function], "refreshNotebookList": [Function],
"refreshTreeTitle": [Function], "refreshTreeTitle": [Function],
@@ -1163,27 +1121,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
"title": [Function], "title": [Function],
"visible": [Function], "visible": [Function],
}, },
QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
NewVertexPane { NewVertexPane {
"buildString": [Function], "buildString": [Function],
"container": [Circular], "container": [Circular],
@@ -1641,27 +1578,6 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
"queriesClient": QueriesClient { "queriesClient": QueriesClient {
"container": [Circular], "container": [Circular],
}, },
"querySelectPane": QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
"refreshDatabaseAccount": [Function], "refreshDatabaseAccount": [Function],
"refreshNotebookList": [Function], "refreshNotebookList": [Function],
"refreshTreeTitle": [Function], "refreshTreeTitle": [Function],

View File

@@ -1,174 +0,0 @@
import * as ko from "knockout";
import _ from "underscore";
import * as Constants from "../../Tables/Constants";
import QueryViewModel from "../../Tables/QueryBuilder/QueryViewModel";
import * as ViewModels from "../../../Contracts/ViewModels";
import { ContextualPaneBase } from "../ContextualPaneBase";
export interface ISelectColumn {
columnName: ko.Observable<string>;
selected: ko.Observable<boolean>;
editable: ko.Observable<boolean>;
}
export class QuerySelectPane extends ContextualPaneBase {
public titleLabel: string = "Select Columns";
public instructionLabel: string = "Select the columns that you want to query.";
public availableColumnsTableQueryLabel: string = "Available Columns";
public noColumnSelectedWarning: string = "At least one column should be selected.";
public columnOptions: ko.ObservableArray<ISelectColumn>;
public anyColumnSelected: ko.Computed<boolean>;
public canSelectAll: ko.Computed<boolean>;
public allSelected: ko.Computed<boolean>;
private selectedColumnOption: ISelectColumn = null;
public queryViewModel: QueryViewModel;
constructor(options: ViewModels.PaneOptions) {
super(options);
this.columnOptions = ko.observableArray<ISelectColumn>();
this.anyColumnSelected = ko.computed<boolean>(() => {
return _.some(this.columnOptions(), (value: ISelectColumn) => {
return value.selected();
});
});
this.canSelectAll = ko.computed<boolean>(() => {
return _.some(this.columnOptions(), (value: ISelectColumn) => {
return !value.selected();
});
});
this.allSelected = ko.pureComputed<boolean>({
read: () => {
return !this.canSelectAll();
},
write: (value) => {
if (value) {
this.selectAll();
} else {
this.clearAll();
}
},
owner: this,
});
}
public submit() {
this.queryViewModel.selectText(this.getParameters());
this.queryViewModel.getSelectMessage();
this.close();
}
public open() {
this.setTableColumns(this.queryViewModel.columnOptions());
this.setDisplayedColumns(this.queryViewModel.selectText(), this.columnOptions());
super.open();
}
private getParameters(): string[] {
if (this.canSelectAll() === false) {
return [];
}
var selectedColumns = this.columnOptions().filter((value: ISelectColumn) => value.selected() === true);
var columns: string[] = selectedColumns.map((value: ISelectColumn) => {
var name: string = value.columnName();
return name;
});
return columns;
}
public setTableColumns(columnNames: string[]): void {
var columns: ISelectColumn[] = columnNames.map((value: string) => {
var columnOption: ISelectColumn = {
columnName: ko.observable<string>(value),
selected: ko.observable<boolean>(true),
editable: ko.observable<boolean>(this.isEntityEditable(value)),
};
return columnOption;
});
this.columnOptions(columns);
}
public setDisplayedColumns(querySelect: string[], columns: ISelectColumn[]): void {
if (querySelect == null || _.isEmpty(querySelect)) {
return;
}
this.setSelected(querySelect, columns);
}
private setSelected(querySelect: string[], columns: ISelectColumn[]): void {
this.clearAll();
querySelect &&
querySelect.forEach((value: string) => {
for (var i = 0; i < columns.length; i++) {
if (value === columns[i].columnName()) {
columns[i].selected(true);
}
}
});
}
public availableColumnsCheckboxClick(): boolean {
if (this.canSelectAll()) {
return this.selectAll();
} else {
return this.clearAll();
}
}
public selectAll(): boolean {
const columnOptions = this.columnOptions && this.columnOptions();
columnOptions &&
columnOptions.forEach((value: ISelectColumn) => {
value.selected(true);
});
return true;
}
public clearAll(): boolean {
const columnOptions = this.columnOptions && this.columnOptions();
columnOptions &&
columnOptions.forEach((column: ISelectColumn) => {
if (this.isEntityEditable(column.columnName())) {
column.selected(false);
} else {
column.selected(true);
}
});
return true;
}
public handleClick = (data: ISelectColumn, event: KeyboardEvent): boolean => {
this.selectTargetItem($(event.currentTarget), data);
return true;
};
private selectTargetItem($target: JQuery, targetColumn: ISelectColumn): void {
this.selectedColumnOption = targetColumn;
$(".list-item.selected").removeClass("selected");
$target.addClass("selected");
}
private isEntityEditable(name: string) {
if (this.queryViewModel.queryTablesTab.container.isPreferredApiCassandra()) {
const cassandraKeys = this.queryViewModel.queryTablesTab.collection.cassandraKeys.partitionKeys
.concat(this.queryViewModel.queryTablesTab.collection.cassandraKeys.clusteringKeys)
.map((key) => key.property);
return !_.contains<string>(cassandraKeys, name);
}
return !(
name === Constants.EntityKeyNames.PartitionKey ||
name === Constants.EntityKeyNames.RowKey ||
name === Constants.EntityKeyNames.Timestamp
);
}
}

View File

@@ -1,79 +0,0 @@
<div data-bind="visible: visible">
<div
class="contextual-pane-out"
data-bind="
click: cancel,
clickBubble: false"
></div>
<div class="contextual-pane" id="queryselectpane">
<!-- Query Select form - Start -->
<div class="contextual-pane-in">
<form
class="paneContentContainer"
data-bind="
submit: submit"
>
<!-- Query Select header - Start -->
<div class="firstdivbg headerline">
Select Column
<div
class="closeImg"
role="button"
aria-label="Close pane"
tabindex="0"
data-bind="
click: cancel, event: { keydown: onCloseKeyPress }"
>
<img src="../../../../images/close-black.svg" title="Close" alt="Close" />
</div>
</div>
<!-- Query Select header - End -->
<div class="paneMainContent paneContentContainer">
<!--<div class="row">
<label id="instructionLabel" data-bind="text: instructionLabel"></label>
</div>-->
<div><span>Select the columns that you want to query.</span></div>
<div class="column-options">
<div class="columns-border">
<input class="all-select-check" type="checkbox" data-bind="checked: allSelected" />
<label
style="font-weight: 700"
id="availableColumnsTableQueryLabel"
data-bind="text: availableColumnsTableQueryLabel"
></label>
</div>
<div class="content">
<section>
<ul data-bind="foreach: columnOptions" aria-labelledby="availableColumnsTableQueryLabel" tabindex="0">
<!-- ko template: {if: editable} -->
<li
class="list-item columns-border"
data-bind="attr: { title: columnName }, click: $parent.handleClick "
>
<input type="checkbox" data-bind="attr: { title: columnName }, checked: selected" />
<span data-bind="text: columnName"></span>
</li>
<!--/ko-->
<!-- ko template: {ifnot: editable} -->
<li class="list-item columns-border" data-bind="attr: { title: columnName } ">
<input type="checkbox" disabled data-bind="checked: selected" />
<span data-bind="text: columnName"></span>
</li>
<!--/ko-->
</ul>
</section>
</div>
</div>
<div class="row-label" data-bind="style: { visibility: anyColumnSelected() ? 'hidden': 'visible' }">
<label class="warning" role="alert" aria-atomic="true" data-bind="text: noColumnSelectedWarning"></label>
</div>
</div>
<div class="paneFooter">
<div class="leftpanel-okbut"><input type="submit" value="OK" class="btncreatecoll1" /></div>
</div>
</form>
</div>
<!-- Query Select form - End -->
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
import { mount } from "enzyme";
import * as ko from "knockout";
import React from "react";
import Explorer from "../../../Explorer";
import QueryViewModel from "../../../Tables/QueryBuilder/QueryViewModel";
import { TableQuerySelectPanel } from "./index";
describe("Table query select Panel", () => {
const fakeExplorer = {} as Explorer;
const fakeQueryViewModal = {} as QueryViewModel;
fakeQueryViewModal.columnOptions = ko.observableArray<string>([""]);
const props = {
explorer: fakeExplorer,
closePanel: (): void => undefined,
queryViewModel: fakeQueryViewModal,
};
it("should render Default properly", () => {
const wrapper = mount(<TableQuerySelectPanel {...props} />);
expect(wrapper).toMatchSnapshot();
});
it("Should exist availableCheckbox by default", () => {
const wrapper = mount(<TableQuerySelectPanel {...props} />);
expect(wrapper.exists("#availableCheckbox")).toBe(true);
});
it("Should checked availableCheckbox by default", () => {
const wrapper = mount(<TableQuerySelectPanel {...props} />);
expect(wrapper.find("#availableCheckbox").first().props()).toEqual({
id: "availableCheckbox",
label: "Available Columns",
checked: true,
onChange: expect.any(Function),
});
});
});

View File

@@ -0,0 +1,155 @@
import { Checkbox, Text } from "office-ui-fabric-react";
import React, { FunctionComponent, useEffect, useState } from "react";
import { userContext } from "../../../../UserContext";
import Explorer from "../../../Explorer";
import * as Constants from "../../../Tables/Constants";
import QueryViewModel from "../../../Tables/QueryBuilder/QueryViewModel";
import { GenericRightPaneComponent, GenericRightPaneProps } from "../../GenericRightPaneComponent";
interface TableQuerySelectPanelProps {
explorer: Explorer;
closePanel: () => void;
queryViewModel: QueryViewModel;
}
interface ISelectColumn {
columnName: string;
selected: boolean;
editable: boolean;
}
export const TableQuerySelectPanel: FunctionComponent<TableQuerySelectPanelProps> = ({
explorer,
closePanel,
queryViewModel,
}: TableQuerySelectPanelProps): JSX.Element => {
const [columnOptions, setColumnOptions] = useState<ISelectColumn[]>([
{ columnName: "", selected: true, editable: false },
]);
const [isAvailableColumnChecked, setIsAvailableColumnChecked] = useState<boolean>(true);
const genericPaneProps: GenericRightPaneProps = {
container: explorer,
formError: "",
formErrorDetail: "",
id: "querySelectPane",
isExecuting: false,
title: "Select Column",
submitButtonText: "OK",
onClose: () => closePanel(),
onSubmit: () => submit(),
};
const submit = (): void => {
queryViewModel.selectText(getParameters());
queryViewModel.getSelectMessage();
closePanel();
};
const handleClick = (isChecked: boolean, selectedColumn: string): void => {
const columns = columnOptions.map((column) => {
if (column.columnName === selectedColumn) {
column.selected = isChecked;
return { ...column };
}
return { ...column };
});
canSelectAll();
setColumnOptions(columns);
};
useEffect(() => {
queryViewModel && setTableColumns(queryViewModel.columnOptions());
}, []);
const setTableColumns = (columnNames: string[]): void => {
const columns: ISelectColumn[] =
columnNames &&
columnNames.length &&
columnNames.map((value: string) => {
const columnOption: ISelectColumn = {
columnName: value,
selected: true,
editable: isEntityEditable(value),
};
return columnOption;
});
setColumnOptions(columns);
};
const isEntityEditable = (name: string): boolean => {
if (userContext.apiType === "Cassandra") {
const cassandraKeys = queryViewModel.queryTablesTab.collection.cassandraKeys.partitionKeys
.concat(queryViewModel.queryTablesTab.collection.cassandraKeys.clusteringKeys)
.map((key) => key.property);
return !cassandraKeys.includes(name);
}
return !(
name === Constants.EntityKeyNames.PartitionKey ||
name === Constants.EntityKeyNames.RowKey ||
name === Constants.EntityKeyNames.Timestamp
);
};
const availableColumnsCheckboxClick = (event: React.FormEvent<HTMLElement>, isChecked: boolean): void => {
setIsAvailableColumnChecked(isChecked);
selectClearAll(isChecked);
};
const selectClearAll = (isChecked: boolean): void => {
const columns: ISelectColumn[] = columnOptions.map((column: ISelectColumn) => {
if (isEntityEditable(column.columnName)) {
column.selected = isChecked;
return { ...column };
}
return { ...column };
});
setColumnOptions(columns);
};
const getParameters = (): string[] => {
const selectedColumns = columnOptions.filter((value: ISelectColumn) => value.selected === true);
const columns: string[] = selectedColumns.map((value: ISelectColumn) => {
const name: string = value.columnName;
return name;
});
return columns;
};
const canSelectAll = (): void => {
const canSelectAllColumn: boolean = columnOptions.some((value: ISelectColumn) => {
return !value.selected;
});
setIsAvailableColumnChecked(!canSelectAllColumn);
};
return (
<GenericRightPaneComponent {...genericPaneProps}>
<div className="panelFormWrapper">
<div className="panelMainContent">
<Text>Select the columns that you want to query.</Text>
<div className="column-select-view">
<Checkbox
id="availableCheckbox"
label="Available Columns"
checked={isAvailableColumnChecked}
onChange={availableColumnsCheckboxClick}
/>
{columnOptions.map((column) => {
return (
<Checkbox
label={column.columnName}
onChange={(_event, isChecked: boolean) => handleClick(isChecked, column.columnName)}
key={column.columnName}
checked={column.selected}
disabled={!column.editable}
/>
);
})}
</div>
</div>
</div>
</GenericRightPaneComponent>
);
};

View File

@@ -238,27 +238,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
"title": [Function], "title": [Function],
"visible": [Function], "visible": [Function],
}, },
QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
NewVertexPane { NewVertexPane {
"buildString": [Function], "buildString": [Function],
"container": [Circular], "container": [Circular],
@@ -716,27 +695,6 @@ exports[`Upload Items Pane should render Default properly 1`] = `
"queriesClient": QueriesClient { "queriesClient": QueriesClient {
"container": [Circular], "container": [Circular],
}, },
"querySelectPane": QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
"refreshDatabaseAccount": [Function], "refreshDatabaseAccount": [Function],
"refreshNotebookList": [Function], "refreshNotebookList": [Function],
"refreshTreeTitle": [Function], "refreshTreeTitle": [Function],

View File

@@ -30,7 +30,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
verticalAlign="start" verticalAlign="start"
> >
<div <div
className="ms-Stack panelInfoErrorContainer css-140" className="ms-Stack panelInfoErrorContainer css-108"
> >
<StyledIconBase <StyledIconBase
className="panelWarningIcon" className="panelWarningIcon"
@@ -317,7 +317,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
> >
<i <i
aria-hidden={true} aria-hidden={true}
className="panelWarningIcon root-142" className="panelWarningIcon root-110"
data-icon-name="WarningSolid" data-icon-name="WarningSolid"
> >
@@ -333,7 +333,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variant="small" variant="small"
> >
<span <span
className="panelWarningErrorMessage css-143" className="panelWarningErrorMessage css-111"
> >
Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources. Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources.
@@ -358,7 +358,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variant="small" variant="small"
> >
<span <span
className="css-143" className="css-111"
> >
Confirm by typing the collection id Confirm by typing the collection id
</span> </span>
@@ -659,18 +659,18 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
validateOnLoad={true} validateOnLoad={true}
> >
<div <div
className="ms-TextField root-145" className="ms-TextField root-113"
> >
<div <div
className="ms-TextField-wrapper" className="ms-TextField-wrapper"
> >
<div <div
className="ms-TextField-fieldGroup fieldGroup-146" className="ms-TextField-fieldGroup fieldGroup-114"
> >
<input <input
aria-invalid={false} aria-invalid={false}
autoFocus={true} autoFocus={true}
className="ms-TextField-field field-147" className="ms-TextField-field field-115"
id="confirmCollectionId" id="confirmCollectionId"
onBlur={[Function]} onBlur={[Function]}
onChange={[Function]} onChange={[Function]}
@@ -693,7 +693,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variant="small" variant="small"
> >
<span <span
className="css-156" className="css-124"
> >
Help us improve Azure Cosmos DB! Help us improve Azure Cosmos DB!
</span> </span>
@@ -703,7 +703,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variant="small" variant="small"
> >
<span <span
className="css-156" className="css-124"
> >
What is the reason why you are deleting this container? What is the reason why you are deleting this container?
</span> </span>
@@ -1006,17 +1006,17 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
validateOnLoad={true} validateOnLoad={true}
> >
<div <div
className="ms-TextField ms-TextField--multiline root-145" className="ms-TextField ms-TextField--multiline root-113"
> >
<div <div
className="ms-TextField-wrapper" className="ms-TextField-wrapper"
> >
<div <div
className="ms-TextField-fieldGroup fieldGroup-157" className="ms-TextField-fieldGroup fieldGroup-125"
> >
<textarea <textarea
aria-invalid={false} aria-invalid={false}
className="ms-TextField-field field-158" className="ms-TextField-field field-126"
id="deleteCollectionFeedbackInput" id="deleteCollectionFeedbackInput"
onBlur={[Function]} onBlur={[Function]}
onChange={[Function]} onChange={[Function]}
@@ -2708,7 +2708,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
variantClassName="ms-Button--primary" variantClassName="ms-Button--primary"
> >
<button <button
className="ms-Button ms-Button--primary root-160" className="ms-Button ms-Button--primary root-128"
data-is-focusable={true} data-is-focusable={true}
id="sidePanelOkButton" id="sidePanelOkButton"
onClick={[Function]} onClick={[Function]}
@@ -2720,14 +2720,14 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect
type="submit" type="submit"
> >
<span <span
className="ms-Button-flexContainer flexContainer-161" className="ms-Button-flexContainer flexContainer-129"
data-automationid="splitbuttonprimary" data-automationid="splitbuttonprimary"
> >
<span <span
className="ms-Button-textContainer textContainer-162" className="ms-Button-textContainer textContainer-130"
> >
<span <span
className="ms-Button-label label-164" className="ms-Button-label label-132"
id="id__6" id="id__6"
key="id__6" key="id__6"
> >

View File

@@ -239,27 +239,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
"title": [Function], "title": [Function],
"visible": [Function], "visible": [Function],
}, },
QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
NewVertexPane { NewVertexPane {
"buildString": [Function], "buildString": [Function],
"container": [Circular], "container": [Circular],
@@ -720,27 +699,6 @@ exports[`Delete Database Confirmation Pane submit() Should call delete database
"queriesClient": QueriesClient { "queriesClient": QueriesClient {
"container": [Circular], "container": [Circular],
}, },
"querySelectPane": QuerySelectPane {
"allSelected": [Function],
"anyColumnSelected": [Function],
"availableColumnsTableQueryLabel": "Available Columns",
"canSelectAll": [Function],
"columnOptions": [Function],
"container": [Circular],
"firstFieldHasFocus": [Function],
"formErrors": [Function],
"formErrorsDetails": [Function],
"handleClick": [Function],
"id": "queryselectpane",
"instructionLabel": "Select the columns that you want to query.",
"isExecuting": [Function],
"isTemplateReady": [Function],
"noColumnSelectedWarning": "At least one column should be selected.",
"selectedColumnOption": null,
"title": [Function],
"titleLabel": "Select Columns",
"visible": [Function],
},
"refreshAllDatabases": [Function], "refreshAllDatabases": [Function],
"refreshDatabaseAccount": [Function], "refreshDatabaseAccount": [Function],
"refreshNotebookList": [Function], "refreshNotebookList": [Function],

View File

@@ -1,13 +1,12 @@
import * as ko from "knockout"; import * as ko from "knockout";
import * as _ from "underscore"; import * as _ from "underscore";
import { KeyCodes } from "../../../Common/Constants";
import QueryTablesTab from "../../Tabs/QueryTablesTab";
import { getQuotedCqlIdentifier } from "../CqlUtilities";
import * as DataTableUtilities from "../DataTable/DataTableUtilities";
import TableEntityListViewModel from "../DataTable/TableEntityListViewModel";
import QueryBuilderViewModel from "./QueryBuilderViewModel"; import QueryBuilderViewModel from "./QueryBuilderViewModel";
import QueryClauseViewModel from "./QueryClauseViewModel"; import QueryClauseViewModel from "./QueryClauseViewModel";
import TableEntityListViewModel from "../DataTable/TableEntityListViewModel";
import QueryTablesTab from "../../Tabs/QueryTablesTab";
import * as DataTableUtilities from "../DataTable/DataTableUtilities";
import { KeyCodes } from "../../../Common/Constants";
import { getQuotedCqlIdentifier } from "../CqlUtilities";
export default class QueryViewModel { export default class QueryViewModel {
public topValueLimitMessage: string = "Please input a number between 0 and 1000."; public topValueLimitMessage: string = "Please input a number between 0 and 1000.";
@@ -198,8 +197,7 @@ export default class QueryViewModel {
}; };
public selectQueryOptions(): Promise<any> { public selectQueryOptions(): Promise<any> {
this.queryTablesTab.container.querySelectPane.queryViewModel = this; this.queryTablesTab.container.openTableSelectQueryPanel(this);
this.queryTablesTab.container.querySelectPane.open();
return null; return null;
} }

View File

@@ -236,7 +236,6 @@ const App: React.FunctionComponent = () => {
<div data-bind='component: { name: "graph-styling-pane", params: { data: graphStylingPane} }' /> <div data-bind='component: { name: "graph-styling-pane", params: { data: graphStylingPane} }' />
<div data-bind='component: { name: "table-add-entity-pane", params: { data: addTableEntityPane} }' /> <div data-bind='component: { name: "table-add-entity-pane", params: { data: addTableEntityPane} }' />
<div data-bind='component: { name: "table-edit-entity-pane", params: { data: editTableEntityPane} }' /> <div data-bind='component: { name: "table-edit-entity-pane", params: { data: editTableEntityPane} }' />
<div data-bind='component: { name: "table-query-select-pane", params: { data: querySelectPane} }' />
<div data-bind='component: { name: "cassandra-add-collection-pane", params: { data: cassandraAddCollectionPane} }' /> <div data-bind='component: { name: "cassandra-add-collection-pane", params: { data: cassandraAddCollectionPane} }' />
<div data-bind='component: { name: "string-input-pane", params: { data: stringInputPane} }' /> <div data-bind='component: { name: "string-input-pane", params: { data: stringInputPane} }' />
<div data-bind='component: { name: "setup-notebooks-pane", params: { data: setupNotebooksPane} }' /> <div data-bind='component: { name: "setup-notebooks-pane", params: { data: setupNotebooksPane} }' />

View File

@@ -1,87 +0,0 @@
import { Callout, DefaultButton, DirectionalHint, Stack, TextField } from "office-ui-fabric-react";
import React from "react";
export interface DropdownItem {
key: string;
text: string;
}
export interface SearchableDropdownProps {
items: DropdownItem[];
onItemSelected: (selectedItem: DropdownItem) => void;
defaultSelectedItem?: DropdownItem;
placeholder?: string;
title?: string;
}
export interface SearchableDropdownState {
isDropdownExpanded: boolean;
selectedItem: DropdownItem;
filteredItems: DropdownItem[];
}
export class SearchableDropdown extends React.Component<SearchableDropdownProps, SearchableDropdownState> {
constructor(props: SearchableDropdownProps) {
super(props);
this.state = {
isDropdownExpanded: false,
selectedItem: props.defaultSelectedItem,
filteredItems: props.items,
};
}
public render(): JSX.Element {
return this.state.isDropdownExpanded ? (
<Stack>
<TextField
className="dropdownTextField"
title={this.props.title}
onChange={(event, newInput?: string) => this.onSearchInputChange(newInput)}
placeholder={this.props.placeholder}
autoFocus
/>
<Callout
isBeakVisible={false}
target=".dropdownTextField"
directionalHint={DirectionalHint.rightTopEdge}
onDismiss={() => this.setState({ isDropdownExpanded: false })}
gapSpace={0}
>
<Stack>
{this.state.filteredItems?.map((item) => (
<DefaultButton
key={item.key}
text={item.text}
style={{ border: "none", textAlign: "left" }}
styles={{ label: { fontWeight: "normal" } }}
onClick={() => this.onItemSelected(item)}
/>
))}
</Stack>
</Callout>
</Stack>
) : (
<TextField
className="dropdownTextField"
title={this.props.title}
onClick={() => this.setState({ isDropdownExpanded: true, filteredItems: this.props.items })}
value={this.state.selectedItem?.text || ""}
placeholder={this.props.placeholder}
readOnly
/>
);
}
private onSearchInputChange(newInput: string): void {
const filteredItems = this.props.items.filter((item: DropdownItem) =>
item.text.toLocaleLowerCase().includes(newInput.toLocaleLowerCase())
);
this.setState({ filteredItems });
}
private onItemSelected(item: DropdownItem): void {
this.setState({ selectedItem: item, isDropdownExpanded: false });
this.props.onItemSelected(item);
}
}

View File

@@ -1,6 +1,7 @@
import React from "react"; import { Dropdown } from "office-ui-fabric-react/lib/Dropdown";
import * as React from "react";
import { FunctionComponent } from "react";
import { DatabaseAccount } from "../../../Contracts/DataModels"; import { DatabaseAccount } from "../../../Contracts/DataModels";
import { DropdownItem, SearchableDropdown } from "./SearchableDropdown";
interface Props { interface Props {
accounts: DatabaseAccount[]; accounts: DatabaseAccount[];
@@ -9,32 +10,30 @@ interface Props {
dismissMenu: () => void; dismissMenu: () => void;
} }
export const SwitchAccount: React.FunctionComponent<Props> = ({ export const SwitchAccount: FunctionComponent<Props> = ({
accounts, accounts,
setSelectedAccountName, setSelectedAccountName,
selectedAccount, selectedAccount,
dismissMenu, dismissMenu,
}: Props) => { }: Props) => {
const accountItems = accounts?.map((account) => ({
key: account.name,
text: account.name,
}));
const defaultAccount = selectedAccount && {
key: selectedAccount.name,
text: selectedAccount.name,
};
return ( return (
<SearchableDropdown <Dropdown
items={accountItems} label="Cosmos DB Account Name"
title="Cosmos DB Account Name" className="accountSwitchAccountDropdown"
defaultSelectedItem={defaultAccount} options={accounts?.map((account) => ({
placeholder={accounts?.length === 0 ? "No Accounts Found" : "Select an Account"} key: account.name,
onItemSelected={(accountItem: DropdownItem) => { text: account.name,
setSelectedAccountName(accountItem.key); data: account,
}))}
onChange={(_, option) => {
setSelectedAccountName(String(option.key));
dismissMenu(); dismissMenu();
}} }}
defaultSelectedKey={selectedAccount?.name}
placeholder={accounts && accounts.length === 0 ? "No Accounts Found" : "Select an Account"}
styles={{
callout: "accountSwitchAccountDropdownMenu",
}}
/> />
); );
}; };

View File

@@ -1,6 +1,7 @@
import React from "react"; import { Dropdown } from "office-ui-fabric-react/lib/Dropdown";
import * as React from "react";
import { FunctionComponent } from "react";
import { Subscription } from "../../../Contracts/DataModels"; import { Subscription } from "../../../Contracts/DataModels";
import { DropdownItem, SearchableDropdown } from "./SearchableDropdown";
interface Props { interface Props {
subscriptions: Subscription[]; subscriptions: Subscription[];
@@ -8,28 +9,30 @@ interface Props {
setSelectedSubscriptionId: (id: string) => void; setSelectedSubscriptionId: (id: string) => void;
} }
export const SwitchSubscription: React.FunctionComponent<Props> = ({ export const SwitchSubscription: FunctionComponent<Props> = ({
subscriptions, subscriptions,
setSelectedSubscriptionId, setSelectedSubscriptionId,
selectedSubscription, selectedSubscription,
}: Props) => { }: Props) => {
const subscriptionItems = subscriptions?.map((sub) => ({
key: sub.subscriptionId,
text: sub.displayName,
}));
const defaultSubscription = selectedSubscription && {
key: selectedSubscription.subscriptionId,
text: selectedSubscription.displayName,
};
return ( return (
<SearchableDropdown <Dropdown
items={subscriptionItems} label="Subscription"
title="Subscription" className="accountSwitchSubscriptionDropdown"
defaultSelectedItem={defaultSubscription} options={subscriptions?.map((sub) => {
placeholder={subscriptions?.length === 0 ? "No Subscriptions Found" : "Select a Subscription"} return {
onItemSelected={(subscriptionItem: DropdownItem) => setSelectedSubscriptionId(subscriptionItem.key)} key: sub.subscriptionId,
text: sub.displayName,
data: sub,
};
})}
onChange={(_, option) => {
setSelectedSubscriptionId(String(option.key));
}}
defaultSelectedKey={selectedSubscription?.subscriptionId}
placeholder={subscriptions && subscriptions.length === 0 ? "No Subscriptions Found" : "Select a Subscription"}
styles={{
callout: "accountSwitchSubscriptionDropdownMenu",
}}
/> />
); );
}; };

View File

@@ -17,12 +17,14 @@ export type Features = {
readonly notebookBasePath?: string; readonly notebookBasePath?: string;
readonly notebookServerToken?: string; readonly notebookServerToken?: string;
readonly notebookServerUrl?: string; readonly notebookServerUrl?: string;
readonly sandboxNotebookOutputs: boolean;
readonly selfServeType?: string; readonly selfServeType?: string;
readonly pr?: string;
readonly showMinRUSurvey: boolean; readonly showMinRUSurvey: boolean;
readonly ttl90Days: boolean; readonly ttl90Days: boolean;
}; };
export function extractFeatures(given = new URLSearchParams()): Features { export function extractFeatures(given = new URLSearchParams(window.location.search)): Features {
const downcased = new URLSearchParams(); const downcased = new URLSearchParams();
const set = (value: string, key: string) => downcased.set(key.toLowerCase(), value); const set = (value: string, key: string) => downcased.set(key.toLowerCase(), value);
const get = (key: string) => downcased.get("feature." + key) ?? undefined; const get = (key: string) => downcased.get("feature." + key) ?? undefined;
@@ -54,7 +56,9 @@ export function extractFeatures(given = new URLSearchParams()): Features {
notebookBasePath: get("notebookbasepath"), notebookBasePath: get("notebookbasepath"),
notebookServerToken: get("notebookservertoken"), notebookServerToken: get("notebookservertoken"),
notebookServerUrl: get("notebookserverurl"), notebookServerUrl: get("notebookserverurl"),
sandboxNotebookOutputs: "true" === get("sandboxnotebookoutputs"),
selfServeType: get("selfservetype"), selfServeType: get("selfservetype"),
pr: get("pr"),
showMinRUSurvey: "true" === get("showminrusurvey"), showMinRUSurvey: "true" === get("showminrusurvey"),
ttl90Days: "true" === get("ttl90days"), ttl90Days: "true" === get("ttl90days"),
}; };

23
src/Utils/StyleUtils.ts Normal file
View File

@@ -0,0 +1,23 @@
// Adapted from https://gist.github.com/davidgilbertson/ed3c8bb8569bc64b094b87aa88bed5fa
export function copyStyles(sourceDoc: Document, targetDoc: Document): void {
Array.from(sourceDoc.styleSheets).forEach((styleSheet) => {
if (styleSheet.href) {
// for <link> elements loading CSS from a URL
const newLinkEl = sourceDoc.createElement("link");
newLinkEl.rel = "stylesheet";
newLinkEl.href = styleSheet.href;
targetDoc.head.appendChild(newLinkEl);
} else if (styleSheet.cssRules && styleSheet.cssRules.length > 0) {
// for <style> elements
const newStyleEl = sourceDoc.createElement("style");
Array.from(styleSheet.cssRules).forEach((cssRule) => {
// write the text of each rule into the body of the style element
newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText));
});
targetDoc.head.appendChild(newStyleEl);
}
});
}

View File

@@ -201,11 +201,12 @@ async function configurePortal(explorerParams: ExplorerParams): Promise<Explorer
if (process.env.NODE_ENV === "development" && !window.location.search.includes("disablePortalInitCache")) { if (process.env.NODE_ENV === "development" && !window.location.search.includes("disablePortalInitCache")) {
const initMessage = sessionStorage.getItem("portalDataExplorerInitMessage"); const initMessage = sessionStorage.getItem("portalDataExplorerInitMessage");
if (initMessage) { if (initMessage) {
const message = JSON.parse(initMessage); const message = JSON.parse(initMessage) as DataExplorerInputsFrame;
console.warn( console.warn(
"Loaded cached portal iframe message from session storage. Do a full page refresh to get a new message" "Loaded cached portal iframe message from session storage. Do a full page refresh to get a new message"
); );
console.dir(message); console.dir(message);
updateContextsFromPortalMessage(message);
const explorer = new Explorer(explorerParams); const explorer = new Explorer(explorerParams);
explorer.configure(message); explorer.configure(message);
resolve(explorer); resolve(explorer);
@@ -237,29 +238,7 @@ async function configurePortal(explorerParams: ExplorerParams): Promise<Explorer
inputs.extensionEndpoint = configContext.PROXY_PATH; inputs.extensionEndpoint = configContext.PROXY_PATH;
} }
const authorizationToken = inputs.authorizationToken || ""; updateContextsFromPortalMessage(inputs);
const masterKey = inputs.masterKey || "";
const databaseAccount = inputs.databaseAccount;
updateConfigContext({
BACKEND_ENDPOINT: inputs.extensionEndpoint || configContext.BACKEND_ENDPOINT,
ARM_ENDPOINT: normalizeArmEndpoint(inputs.csmEndpoint || configContext.ARM_ENDPOINT),
});
updateUserContext({
authorizationToken,
masterKey,
databaseAccount,
resourceGroup: inputs.resourceGroup,
subscriptionId: inputs.subscriptionId,
subscriptionType: inputs.subscriptionType,
quotaId: inputs.quotaId,
portalEnv: inputs.serverId as PortalEnv,
hasWriteAccess: inputs.hasWriteAccess ?? true,
addCollectionFlight:
inputs.addCollectionDefaultFlight || CollectionCreation.DefaultAddCollectionDefaultFlight,
});
const explorer = new Explorer(explorerParams); const explorer = new Explorer(explorerParams);
explorer.configure(inputs); explorer.configure(inputs);
resolve(explorer); resolve(explorer);
@@ -299,6 +278,38 @@ function shouldProcessMessage(event: MessageEvent): boolean {
return true; return true;
} }
function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
if (
configContext.BACKEND_ENDPOINT &&
configContext.platform === Platform.Portal &&
process.env.NODE_ENV === "development"
) {
inputs.extensionEndpoint = configContext.PROXY_PATH;
}
const authorizationToken = inputs.authorizationToken || "";
const masterKey = inputs.masterKey || "";
const databaseAccount = inputs.databaseAccount;
updateConfigContext({
BACKEND_ENDPOINT: inputs.extensionEndpoint || configContext.BACKEND_ENDPOINT,
ARM_ENDPOINT: normalizeArmEndpoint(inputs.csmEndpoint || configContext.ARM_ENDPOINT),
});
updateUserContext({
authorizationToken,
masterKey,
databaseAccount,
resourceGroup: inputs.resourceGroup,
subscriptionId: inputs.subscriptionId,
subscriptionType: inputs.subscriptionType,
quotaId: inputs.quotaId,
portalEnv: inputs.serverId as PortalEnv,
hasWriteAccess: inputs.hasWriteAccess ?? true,
addCollectionFlight: inputs.addCollectionDefaultFlight || CollectionCreation.DefaultAddCollectionDefaultFlight,
});
}
interface PortalMessage { interface PortalMessage {
openAction?: DataExplorerAction; openAction?: DataExplorerAction;
actionType?: ActionType; actionType?: ActionType;

View File

@@ -1 +0,0 @@
node_modules

View File

@@ -1,25 +0,0 @@
Borrowed from https://github.com/mjbvz/vscode-strict-null-check-migration-tools/tree/f1da7c12fe6e93618a310cb3662e2ade808be0c4
Scripts to help [migrate VS Code to use strict null checks](https://github.com/Microsoft/vscode/issues/60565)
## Usage
```bash
$ npm install
```
**index.js**
The main script prints of list of files that are eligible for strict null checks. This includes all files that only import files thare are already strict null checked.
```bash
$ node index.js /path/to/vscode
```
**autoAdd.js**
Very simple script that tries to auto add any eligible file to the `tsconfig.strictNullChecks.json`. This iteratively compiles the `tsconfig` project with just that file added. If there are no errors, it is added to the `tsconfig`
```bash
$ node autoAdd.js /path/to/vscode
```

View File

@@ -1,55 +0,0 @@
// @ts-check
const path = require("path");
const fs = require("fs");
const child_process = require("child_process");
const config = require("./src/config");
const { forStrictNullCheckEligibleFiles } = require("./src/getStrictNullCheckEligibleFiles");
const vscodeRoot = path.join(process.cwd());
const srcRoot = path.join(vscodeRoot, "src");
const buildCompletePattern = /Found (\d+) errors?\. Watching for file changes\./gi;
forStrictNullCheckEligibleFiles(vscodeRoot, () => {}).then(async files => {
const tsconfigPath = path.join(srcRoot, config.targetTsconfig);
const child = child_process.spawn("tsc", ["-p", tsconfigPath, "--watch"]);
for (const file of files) {
await tryAutoAddStrictNulls(child, tsconfigPath, file);
}
child.kill();
});
function tryAutoAddStrictNulls(child, tsconfigPath, file) {
return new Promise(resolve => {
const relativeFilePath = path.relative(srcRoot, file);
console.log(`Trying to auto add ./src/${relativeFilePath}`);
const originalConifg = JSON.parse(fs.readFileSync(tsconfigPath).toString());
originalConifg.files = Array.from(new Set(originalConifg.files.sort()));
// Config on accept
const newConfig = Object.assign({}, originalConifg);
newConfig.files = Array.from(new Set(originalConifg.files.concat("./src/" + relativeFilePath).sort()));
fs.writeFileSync(tsconfigPath, JSON.stringify(newConfig, null, "\t"));
const listener = data => {
const textOut = data.toString();
const match = buildCompletePattern.exec(textOut);
if (match) {
const errorCount = +match[1];
if (errorCount === 0) {
console.log(`👍`);
fs.writeFileSync(tsconfigPath, JSON.stringify(newConfig, null, "\t"));
} else {
console.log(`💥 - ${errorCount}`);
fs.writeFileSync(tsconfigPath, JSON.stringify(originalConifg, null, "\t"));
}
resolve();
child.stdout.removeListener("data", listener);
}
};
child.stdout.on("data", listener);
});
}

View File

@@ -1,53 +0,0 @@
// @ts-check
const path = require("path");
const glob = require("glob");
const { forStrictNullCheckEligibleFiles, forEachFileInSrc } = require("./src/getStrictNullCheckEligibleFiles");
const { getImportsForFile } = require("./src/tsHelper");
const projectRoot = path.join(process.cwd());
const srcRoot = path.join(projectRoot, "src");
let sort = true;
let filter;
let printDependedOnCount = true;
let includeTests = false;
// if (false) {
// // Generate test files listing
// sort = false;
// filter = x => x.endsWith(".test.ts");
// printDependedOnCount = false;
// includeTests = true;
// }
forStrictNullCheckEligibleFiles(projectRoot, () => {}, { includeTests }).then(async eligibleFiles => {
console.log(eligibleFiles);
// const eligibleSet = new Set(eligibleFiles);
// const dependedOnCount = new Map(eligibleFiles.map(file => [file, 0]));
// for (const file of await forEachFileInSrc(srcRoot)) {
// if (eligibleSet.has(file)) {
// // Already added
// continue;
// }
// for (const imp of getImportsForFile(file, srcRoot)) {
// if (dependedOnCount.has(imp)) {
// dependedOnCount.set(imp, dependedOnCount.get(imp) + 1);
// }
// }
// }
// let out = Array.from(dependedOnCount.entries());
// if (filter) {
// out = out.filter(x => filter(x[0]));
// }
// if (sort) {
// out = out.sort((a, b) => b[1] - a[1]);
// }
// for (const pair of out) {
// console.log(toFormattedFilePath(pair[0]) + (printDependedOnCount ? ` — Depended on by **${pair[1]}** files` : ""));
// }
});
// function toFormattedFilePath(file) {
// // return `"./${path.relative(srcRoot, file)}",`;
// return `- [ ] \`"./${path.relative(srcRoot, file)}"\``;
// }

View File

@@ -1,90 +0,0 @@
{
"name": "vscode-strict-null-tools",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"typescript": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.2.tgz",
"integrity": "sha512-gOoGJWbNnFAfP9FlrSV63LYD5DJqYJHG5ky1kOXSl3pCImn4rqWy/flyq1BRd4iChQsoCqjbQaqtmXO4yCVPCA=="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}

View File

@@ -1,17 +0,0 @@
{
"name": "vscode-strict-null-tools",
"private": true,
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Matt Bierner",
"license": "MIT",
"dependencies": {
"typescript": "3.1.2",
"glob": "^7.1.3"
}
}

View File

@@ -1,3 +0,0 @@
module.exports.targetTsconfig = "../tsconfig.strict.json";
module.exports.skippedFiles = new Set([]);

View File

@@ -1,94 +0,0 @@
// @ts-check
const path = require("path");
const fs = require("fs");
const { getImportsForFile } = require("./tsHelper");
const glob = require("glob");
const config = require("./config");
/**
* @param {string} srcRoot
* @param {{ includeTests: boolean }} [options]
*/
const forEachFileInSrc = (srcRoot, options) => {
return new Promise((resolve, reject) => {
glob(`${srcRoot}/**/*.ts`, (err, files) => {
if (err) {
return reject(err);
}
return resolve(
files.filter(
file => !file.endsWith(".d.ts") && (options && options.includeTests ? true : !file.endsWith(".test.ts"))
)
);
});
});
};
module.exports.forEachFileInSrc = forEachFileInSrc;
/**
* @param {string} vscodeRoot
* @param {(file: string) => void} forEach
* @param {{ includeTests: boolean }} [options]
*/
module.exports.forStrictNullCheckEligibleFiles = async (vscodeRoot, forEach, options) => {
const srcRoot = path.join(vscodeRoot, "src");
const tsconfig = JSON.parse(fs.readFileSync(path.join(srcRoot, config.targetTsconfig)).toString());
const checkedFiles = await getCheckedFiles(tsconfig, vscodeRoot);
const imports = new Map();
const getMemoizedImportsForFile = (file, srcRoot) => {
if (imports.has(file)) {
return imports.get(file);
}
const importList = getImportsForFile(file, srcRoot);
imports.set(file, importList);
return importList;
};
const files = await forEachFileInSrc(srcRoot, options);
return files
.filter(file => !checkedFiles.has(file))
.filter(file => !config.skippedFiles.has(path.relative(srcRoot, file)))
.filter(file => {
const allProjImports = getMemoizedImportsForFile(file, srcRoot);
const nonCheckedImports = allProjImports
.filter(x => x !== file)
.filter(imp => {
if (checkedFiles.has(imp)) {
return false;
}
// Don't treat cycles as blocking
const impImports = getMemoizedImportsForFile(imp, srcRoot);
return impImports.filter(x => x !== file).filter(x => !checkedFiles.has(x)).length !== 0;
});
const isEdge = nonCheckedImports.length === 0;
if (isEdge) {
forEach(file);
}
return isEdge;
});
};
async function getCheckedFiles(tsconfig, srcRoot) {
const set = new Set(tsconfig.files.map(include => path.join(srcRoot, include)));
const includes = tsconfig.include.map(include => {
return new Promise((resolve, reject) => {
glob(path.join(srcRoot, include), (err, files) => {
if (err) {
return reject(err);
}
for (const file of files) {
set.add(file);
}
resolve();
});
});
});
await Promise.all(includes);
return set;
}

View File

@@ -1,44 +0,0 @@
// @ts-check
const path = require("path");
const ts = require("typescript");
const fs = require("fs");
module.exports.getImportsForFile = function getImportsForFile(file, srcRoot) {
const fileInfo = ts.preProcessFile(fs.readFileSync(file).toString());
return fileInfo.importedFiles
.map(importedFile => importedFile.fileName)
.filter(fileName => !/svg|gif|png|html|less|json|externals|css|ico/.test(fileName)) // remove image imports
.filter(x => /\//.test(x)) // remove node modules (the import must contain '/')
.filter(x => !/\@/.test(x)) // remove @ scoped modules
.filter(
x =>
!/url-polyfill|office-ui-fabric|rxjs|\@nteract|bootstrap|promise-polyfill|abort-controller|es6-object-assign|es6-symbol|webcrypto-liner|promise.prototype.finally|object.entries/.test(
x
)
) // remove other modules
.filter(x => !/worker-loader/.test(x)) // remove other modules
.map(fileName => {
if (/(^\.\/)|(^\.\.\/)/.test(fileName)) {
return path.join(path.dirname(file), fileName);
}
if (/^vs/.test(fileName)) {
return path.join(srcRoot, fileName);
}
return fileName;
})
.map(fileName => {
if (fs.existsSync(`${fileName}.ts`)) {
return `${fileName}.ts`;
}
if (fs.existsSync(`${fileName}.js`)) {
return `${fileName}.js`;
}
if (fs.existsSync(`${fileName}.d.ts`)) {
return `${fileName}.d.ts`;
}
if (fs.existsSync(`${fileName}.tsx`)) {
return `${fileName}.tsx`;
}
throw new Error(`Unresolved import ${fileName} in ${file}`);
});
};

View File

@@ -0,0 +1,8 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
-->
Scripts to help migrate to use strict null checks.
Modified from [strict-null-checks](https://github.com/microsoft/accessibility-insights-web/tree/f2ec74cc5f09d41a4b617b0eb941b6da332a4343/tools/strict-null-checks)

View File

@@ -0,0 +1,100 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// @ts-check
const child_process = require("child_process");
const fs = require("fs");
const path = require("path");
const { collapseCompletedDirectories } = require("./collapse-completed-directories");
const config = require("./config");
const { getUncheckedLeafFiles } = require("./eligible-file-finder");
const { writeTsconfigSync } = require("./write-tsconfig");
const repoRoot = config.repoRoot;
const tscPath = path.join(repoRoot, "node_modules", "typescript", "bin", "tsc");
const tsconfigPath = path.join(repoRoot, config.targetTsconfig);
async function main() {
console.log("## Initializing tsc --watch process...");
const tscWatchProcess = child_process.spawn("node", [tscPath, "-p", tsconfigPath, "--watch"]);
await waitForBuildComplete(tscWatchProcess);
const alreadyAttempted = new Set();
for (let pass = 1; ; pass += 1) {
let successesThisPass = 0;
const uncheckedLeafFiles = await getUncheckedLeafFiles();
const candidateFiles = uncheckedLeafFiles.filter((f) => !alreadyAttempted.has(f));
const candidateCount = candidateFiles.length;
console.log(`## Starting pass ${pass} with ${candidateCount} candidate files`);
for (const file of candidateFiles) {
alreadyAttempted.add(file);
if (await tryAutoAddStrictNulls(tscWatchProcess, tsconfigPath, file)) {
successesThisPass += 1;
}
}
console.log(`### Finished pass ${pass} (added ${successesThisPass}/${candidateCount})`);
if (successesThisPass === 0) {
break;
}
}
console.log("## Stopping tsc --watch process...");
tscWatchProcess.kill();
console.log('## Collapsing fully null-checked directories into "include" patterns...');
collapseCompletedDirectories(tsconfigPath);
}
async function tryAutoAddStrictNulls(child, tsconfigPath, file) {
const relativeFilePath = path.relative(repoRoot, file).replace(/\\/g, "/");
const originalConfig = JSON.parse(fs.readFileSync(tsconfigPath).toString());
originalConfig.files = Array.from(new Set(originalConfig.files.sort()));
// Config on accept
const newConfig = Object.assign({}, originalConfig);
newConfig.files = Array.from(new Set(originalConfig.files.concat("./" + relativeFilePath).sort()));
const buildCompetePromise = waitForBuildComplete(child);
writeTsconfigSync(tsconfigPath, newConfig);
const errorCount = await buildCompetePromise;
const success = errorCount === 0;
if (success) {
console.log(`${relativeFilePath}: added`);
} else {
console.log(`${relativeFilePath}: ${errorCount} error(s), skipped`);
writeTsconfigSync(tsconfigPath, originalConfig);
}
return success;
}
const buildCompletePattern = /Found (\d+) errors?\. Watching for file changes\./gi;
async function waitForBuildComplete(tscWatchProcess) {
const match = await waitForStdoutMatching(tscWatchProcess, buildCompletePattern);
const errorCount = +match[1];
return errorCount;
}
async function waitForStdoutMatching(child, pattern) {
return new Promise((resolve) => {
const listener = (data) => {
const textOut = data.toString();
const match = pattern.exec(textOut);
if (match) {
child.stdout.removeListener("data", listener);
resolve(match);
}
};
child.stdout.on("data", listener);
});
}
main().catch((error) => {
console.error(error.stack);
process.exit(1);
});

View File

@@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// @ts-check
const fs = require("fs");
const path = require("path");
const config = require("./config");
const { writeTsconfigSync } = require("./write-tsconfig");
const repoRoot = config.repoRoot;
function collapseCompletedDirectories(tsconfigPath) {
const tsconfigContent = JSON.parse(fs.readFileSync(tsconfigPath).toString());
const listedFiles = Array.from(new Set(tsconfigContent.files.sort()));
const listedIncludes = Array.from(new Set(tsconfigContent.include.sort()));
const listedDirectories = listedIncludes.map(includeToDirectory);
const completedSet = new Set([...listedFiles, ...listedDirectories]);
reduceCompletedSet(completedSet, "./src");
const completedPaths = Array.from(completedSet).sort();
tsconfigContent.files = completedPaths.filter(isTsFile);
tsconfigContent.include = completedPaths.filter(isSourceDirectory).map(directoryToInclude);
writeTsconfigSync(tsconfigPath, tsconfigContent);
}
// convert from src/common/styles/**/* to ./src/common/styles
function includeToDirectory(include) {
return "./" + include.replace("/**/*", "");
}
// convert from ./src/common/styles to src/common/styles/**/*
function directoryToInclude(directory) {
return directory.substring(2) + "/**/*";
}
function reduceCompletedSet(completedSet, root) {
if (completedSet.has(root)) {
return true;
}
if (!isSourceDirectory(root)) {
return false;
}
const children = listRelevantChildren(root);
let allChildrenReduced = true;
for (const child of children) {
const childReduced = reduceCompletedSet(completedSet, child);
allChildrenReduced = allChildrenReduced && childReduced;
}
if (allChildrenReduced) {
for (const child of children) {
completedSet.delete(child);
}
completedSet.add(root);
}
return allChildrenReduced;
}
function isSourceDirectory(relativePath) {
// this assumes directories don't have .s in their names, which isn't robust generally
// but happens to be true in our repo
const isDirectory = -1 === relativePath.indexOf(".", 1);
return isDirectory && !relativePath.includes("__snapshots__");
}
const isTsFileRegex = /\.(ts|tsx)$/;
function isTsFile(relativePath) {
return isTsFileRegex.test(relativePath);
}
function listRelevantChildren(relativePath) {
const rawReaddir = fs.readdirSync(path.join(repoRoot, relativePath));
const directories = rawReaddir.filter(isSourceDirectory);
const tsFiles = rawReaddir.filter(isTsFile);
return [...directories, ...tsFiles].map((name) => relativePath + "/" + name);
}
module.exports = {
collapseCompletedDirectories,
};

View File

@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const path = require("path");
const repoRoot = path.join(__dirname, "../").replace(/\\/g, "/");
module.exports = {
repoRoot: repoRoot,
srcRoot: `${repoRoot}/src`,
targetTsconfig: "tsconfig.strict.json",
skippedFiles: new Set([]),
};

View File

@@ -0,0 +1,70 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// @ts-check
const fs = require("fs");
const path = require("path");
const glob = require("glob");
const config = require("./config");
const { getMemoizedImportsForFile } = require("./import-finder");
// "Eligible" means "a file that we might want to list in tsconfig.strictNullChecks.json"
// "Checked" means "a file that is currently listed in tsconfig.strictNullChecks.json"
// It is possible for an ineligible file to be checked (eg, a png under /src/icons/**)
const isEligibleFile = (file) => !config.skippedFiles.has(path.relative(config.srcRoot, file));
function globAsync(pattern) {
return new Promise((resolve, reject) => glob(pattern, (err, files) => (err ? reject(err) : resolve(files))));
}
// Includes both checked and unchecked files (ie, doesn't care about inclusion in tsconfig.strictNullChecks.json)
async function getAllEligibleFiles() {
const tsFiles = await globAsync(`${config.srcRoot}/**/*.@(ts|tsx)`);
return tsFiles.filter(isEligibleFile);
}
// Includes ineligible files that are listed under glob patterns in tsconfig.strictNullChecks
async function getAllCheckedFiles() {
const tsconfigPath = path.join(config.repoRoot, config.targetTsconfig);
const tsconfigContent = JSON.parse(fs.readFileSync(tsconfigPath).toString());
const set = new Set(tsconfigContent.files.map((f) => path.join(config.repoRoot, f).replace(/\\/g, "/")));
await Promise.all(
tsconfigContent.include.map(async (include) => {
const includePath = path.join(config.repoRoot, include);
const files = await globAsync(includePath);
for (const file of files) {
set.add(file);
}
})
);
return set;
}
async function getUncheckedLeafFiles() {
const checkedFiles = await getAllCheckedFiles();
const eligibleFiles = await getAllEligibleFiles();
const eligibleFileSet = new Set(eligibleFiles);
const allUncheckedFiles = eligibleFiles.filter((file) => !checkedFiles.has(file));
const areAllImportsChecked = (file) => {
const allImports = getMemoizedImportsForFile(file, config.srcRoot);
const uncheckedImports = allImports.filter((imp) => !checkedFiles.has(imp));
const ineligibleUncheckedImports = uncheckedImports.filter((imp) => !eligibleFileSet.has(imp));
if (ineligibleUncheckedImports.length > 0) {
console.warn(
`Eligible file ${file} with unchecked ineligible imports [${ineligibleUncheckedImports.join(", ")}]`
);
}
return uncheckedImports.length === 0;
};
return allUncheckedFiles.filter(areAllImportsChecked);
}
module.exports = {
getAllEligibleFiles,
getUncheckedLeafFiles,
getAllCheckedFiles,
};

View File

@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// @ts-check
const path = require("path");
const process = require("process");
const { srcRoot } = require("./config");
const { getUncheckedLeafFiles, getAllEligibleFiles } = require("./eligible-file-finder");
const { getImportsForFile } = require("./import-finder");
if (process.argv.includes("--help")) {
console.log("yarn null:find [--sort=name|count] [--show-count] [--filter file_path_substring]");
process.exit(0);
}
const sortBy = process.argv.includes("--sort=name") ? "name" : "count";
const printDependedOnCount = process.argv.includes("--show-count");
const filterArgIndex = process.argv.indexOf("--filter") + 1;
const filterArg = filterArgIndex === 0 ? null : process.argv[filterArgIndex];
const filter = filterArg && ((file) => file.includes(filterArg));
async function main() {
const eligibleFiles = await getUncheckedLeafFiles();
const eligibleSet = new Set(eligibleFiles);
const dependedOnCount = new Map(eligibleFiles.map((file) => [file, 0]));
for (const file of await getAllEligibleFiles()) {
if (eligibleSet.has(file)) {
// Already added
continue;
}
for (const imp of getImportsForFile(file, srcRoot)) {
if (dependedOnCount.has(imp)) {
dependedOnCount.set(imp, dependedOnCount.get(imp) + 1);
}
}
}
let out = Array.from(dependedOnCount.entries());
if (filter) {
out = out.filter((x) => filter(x[0]));
}
if (sortBy === "count") {
out = out.sort((a, b) => b[1] - a[1]);
} else if (sortBy === "name") {
out = out.sort((a, b) => a[0].localeCompare(b[0]));
}
for (const pair of out) {
console.log(toFormattedFilePath(pair[0]) + (printDependedOnCount ? ` — Depended on by **${pair[1]}** files` : ""));
}
}
function toFormattedFilePath(file) {
const relativePath = path.relative(srcRoot, file).replace(/\\/g, "/");
return `"./src/${relativePath}",`;
}
main().catch((error) => {
console.error(error.stack);
process.exit(1);
});

View File

@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// @ts-check
const fs = require("fs");
const path = require("path");
const ts = require("typescript");
const imports = new Map();
const getMemoizedImportsForFile = (file, srcRoot) => {
if (imports.has(file)) {
return imports.get(file);
}
const importList = getImportsForFile(file, srcRoot);
imports.set(file, importList);
return importList;
};
function getImportsForFile(parent, srcRoot) {
return ts
.preProcessFile(fs.readFileSync(parent).toString())
.importedFiles.map(({ fileName }) => fileName)
.filter((base) => /\//.test(base)) // remove node modules (the import must contain '/')
.map((base) => (/(^\.\/)|(^\.\.\/)/.test(base) ? path.join(path.dirname(parent), base) : path.join(srcRoot, base)))
.map((base) => (fs.existsSync(base) ? path.join(base, "index") : base))
.map((base) => base.replace(/\\/g, "/"))
.map((base) => ["ts", "tsx", "d.ts", "js", "jsx"].map((ext) => `${base}.${ext}`).find(fs.existsSync))
.filter((base) => base && base !== parent);
}
module.exports = {
getImportsForFile,
getMemoizedImportsForFile,
};

View File

@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const fs = require("fs");
module.exports = {
writeTsconfigSync: (tsconfigPath, content) => {
let serializedContent = JSON.stringify(content, null, " ");
serializedContent += "\n";
fs.writeFileSync(tsconfigPath, serializedContent);
},
};

View File

@@ -15,107 +15,111 @@
"./src/Common/DocumentUtility.ts", "./src/Common/DocumentUtility.ts",
"./src/Common/EnvironmentUtility.ts", "./src/Common/EnvironmentUtility.ts",
"./src/Common/HashMap.ts", "./src/Common/HashMap.ts",
"./src/Common/HeadersUtility.test.ts",
"./src/Common/HeadersUtility.ts", "./src/Common/HeadersUtility.ts",
"./src/Common/Logger.ts", "./src/Common/Logger.ts",
"./src/Common/MessageHandler.test.ts",
"./src/Common/MessageHandler.ts", "./src/Common/MessageHandler.ts",
"./src/Common/MongoUtility.ts", "./src/Common/MongoUtility.ts",
"./src/Common/ObjectCache.test.ts",
"./src/Common/ObjectCache.ts", "./src/Common/ObjectCache.ts",
"./src/Common/OfferUtility.test.ts",
"./src/Common/OfferUtility.ts", "./src/Common/OfferUtility.ts",
"./src/Common/Splitter.ts",
"./src/Common/ThemeUtility.ts", "./src/Common/ThemeUtility.ts",
"./src/Common/UrlUtility.ts", "./src/Common/UrlUtility.ts",
"./src/Common/Splitter.ts",
"./src/ConfigContext.ts", "./src/ConfigContext.ts",
"./src/Contracts/ActionContracts.ts", "./src/Contracts/ActionContracts.ts",
"./src/Contracts/DataModels.ts", "./src/Contracts/DataModels.ts",
"./src/Contracts/Diagnostics.ts", "./src/Contracts/Diagnostics.ts",
"./src/Contracts/ExplorerContracts.ts", "./src/Contracts/ExplorerContracts.ts",
"./src/Contracts/SelfServeContracts.ts",
"./src/Contracts/SubscriptionType.ts", "./src/Contracts/SubscriptionType.ts",
"./src/Contracts/Versions.ts", "./src/Contracts/Versions.ts",
"./src/Controls/Heatmap/Heatmap.ts",
"./src/Controls/Heatmap/HeatmapDatatypes.ts",
"./src/DefaultAccountExperienceType.ts", "./src/DefaultAccountExperienceType.ts",
"./src/Definitions/globals.d.ts",
"./src/Definitions/html.d.ts",
"./src/Definitions/jquery-ui.d.ts",
"./src/Definitions/jquery.d.ts",
"./src/Definitions/plotly.js-cartesian-dist.d-min.ts",
"./src/Definitions/svg.d.ts",
"./src/Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.ts",
"./src/Explorer/Controls/GitHub/GitHubStyleConstants.ts", "./src/Explorer/Controls/GitHub/GitHubStyleConstants.ts",
"./src/Explorer/Controls/InputTypeahead/InputTypeahead.ts", "./src/Explorer/Controls/InputTypeahead/InputTypeahead.ts",
"./src/Explorer/Controls/SmartUi/InputUtils.ts", "./src/Explorer/Controls/SmartUi/InputUtils.ts",
"./src/Explorer/Graph/GraphExplorerComponent/__mocks__/GremlinClient.ts", "./src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.test.ts",
"./src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts", "./src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts",
"./src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts", "./src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts",
"./src/Explorer/Graph/GraphExplorerComponent/GraphData.ts", "./src/Explorer/Graph/GraphExplorerComponent/GraphData.ts",
"./src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts",
"./src/Explorer/Notebook/FileSystemUtil.ts", "./src/Explorer/Notebook/FileSystemUtil.ts",
"./src/Explorer/Notebook/NTeractUtil.ts", "./src/Explorer/Notebook/NTeractUtil.ts",
"./src/Explorer/Notebook/NotebookComponent/actions.ts", "./src/Explorer/Notebook/NotebookComponent/actions.ts",
"./src/Explorer/Notebook/NotebookComponent/loadTransform.ts", "./src/Explorer/Notebook/NotebookComponent/loadTransform.ts",
"./src/Explorer/Notebook/NotebookComponent/reducers.ts", "./src/Explorer/Notebook/NotebookComponent/reducers.ts",
"./src/Explorer/Notebook/NotebookComponent/types.ts", "./src/Explorer/Notebook/NotebookComponent/types.ts",
"./src/Explorer/Notebook/NotebookContentClient.ts",
"./src/Explorer/Notebook/NotebookContentItem.ts", "./src/Explorer/Notebook/NotebookContentItem.ts",
"./src/Explorer/Notebook/NotebookRenderer/AzureTheme.tsx",
"./src/Explorer/Notebook/NotebookRenderer/decorators/CellCreator.tsx",
"./src/Explorer/Notebook/NotebookUtil.ts", "./src/Explorer/Notebook/NotebookUtil.ts",
"./src/Explorer/Tree/AccessibleVerticalList.ts",
"./src/Explorer/Panes/PaneComponents.ts", "./src/Explorer/Panes/PaneComponents.ts",
"./src/Explorer/Panes/PanelFooterComponent.tsx",
"./src/Explorer/Panes/PanelLoadingScreen.tsx",
"./src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts", "./src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts",
"./src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts", "./src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts",
"./src/Explorer/Tables/Constants.ts", "./src/Explorer/Tables/Constants.ts",
"./src/Explorer/Tables/CqlUtilities.ts", "./src/Explorer/Tables/CqlUtilities.ts",
"./src/Explorer/Tables/QueryBuilder/DateTimeUtilities.ts",
"./src/Explorer/Tables/DataTable/CacheBase.ts", "./src/Explorer/Tables/DataTable/CacheBase.ts",
"./src/Explorer/Tables/Entities.ts", "./src/Explorer/Tables/Entities.ts",
"./src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts", "./src/Explorer/Tables/QueryBuilder/DateTimeUtilities.test.ts",
"./src/Explorer/Notebook/NotebookContentClient.ts", "./src/Explorer/Tables/QueryBuilder/DateTimeUtilities.ts",
"./src/Explorer/Tree/AccessibleVerticalList.ts",
"./src/GitHub/GitHubConnector.ts", "./src/GitHub/GitHubConnector.ts",
"./src/HostedExplorerChildFrame.ts",
"./src/Index.ts", "./src/Index.ts",
"./src/NotebookWorkspaceManager/NotebookWorkspaceResourceProviderMockClients.ts", "./src/NotebookWorkspaceManager/NotebookWorkspaceResourceProviderMockClients.ts",
"./src/Platform/Hosted/Components/SignInButton.tsx",
"./src/Platform/Hosted/extractFeatures.test.ts",
"./src/Platform/Hosted/extractFeatures.ts",
"./src/ReactDevTools.ts", "./src/ReactDevTools.ts",
"./src/ResourceProvider/IResourceProviderClient.ts", "./src/ResourceProvider/IResourceProviderClient.ts",
"./src/SelfServe/SelfServeStyles.tsx",
"./src/SelfServe/SqlX/SqlxTypes.ts",
"./src/Shared/Constants.ts", "./src/Shared/Constants.ts",
"./src/Shared/DefaultExperienceUtility.ts",
"./src/Shared/ExplorerSettings.ts", "./src/Shared/ExplorerSettings.ts",
"./src/Shared/PriceEstimateCalculator.ts", "./src/Shared/PriceEstimateCalculator.ts",
"./src/Shared/StorageUtility.test.ts",
"./src/Shared/StorageUtility.ts", "./src/Shared/StorageUtility.ts",
"./src/Shared/StringUtility.test.ts",
"./src/Shared/StringUtility.ts", "./src/Shared/StringUtility.ts",
"./src/Shared/Telemetry/TelemetryConstants.ts",
"./src/Shared/Telemetry/TelemetryProcessor.ts",
"./src/Shared/appInsights.ts", "./src/Shared/appInsights.ts",
"./src/Shared/DefaultExperienceUtility.ts",
"./src/Terminal/index.ts",
"./src/Terminal/JupyterLabAppFactory.ts",
"./src/UserContext.ts", "./src/UserContext.ts",
"./src/Utils/AutoPilotUtils.ts",
"./src/Utils/Base64Utils.test.ts",
"./src/Utils/Base64Utils.ts", "./src/Utils/Base64Utils.ts",
"./src/Utils/BlobUtils.ts", "./src/Utils/BlobUtils.ts",
"./src/Utils/GitHubUtils.test.ts",
"./src/Utils/GitHubUtils.ts", "./src/Utils/GitHubUtils.ts",
"./src/Utils/MessageValidation.test.ts",
"./src/Utils/MessageValidation.ts", "./src/Utils/MessageValidation.ts",
"./src/Utils/StringUtils.ts",
"./src/Utils/WindowUtils.ts",
"./src/Utils/arm/generatedClients/2020-04-01/types.ts",
"./src/Utils/AutoPilotUtils.ts",
"./src/Utils/PricingUtils.ts", "./src/Utils/PricingUtils.ts",
"./src/Utils/arm/generatedClients/2020-04-01/cassandraResources.ts", "./src/Utils/StringUtils.ts",
"./src/Utils/arm/generatedClients/2020-04-01/collection.ts", "./src/Utils/WindowUtils.test.ts",
"./src/Utils/arm/generatedClients/2020-04-01/collectionPartition.ts", "./src/Utils/WindowUtils.ts",
"./src/Utils/arm/generatedClients/2020-04-01/collectionPartitionRegion.ts", "./src/hooks/useDirectories.tsx",
"./src/Utils/arm/generatedClients/2020-04-01/collectionRegion.ts", "./src/i18n.ts",
"./src/Utils/arm/generatedClients/2020-04-01/database.ts",
"./src/Utils/arm/generatedClients/2020-04-01/databaseAccountRegion.ts",
"./src/Utils/arm/generatedClients/2020-04-01/databaseAccounts.ts",
"./src/Utils/arm/generatedClients/2020-04-01/gremlinResources.ts",
"./src/Utils/arm/generatedClients/2020-04-01/mongoDBResources.ts",
"./src/Utils/arm/generatedClients/2020-04-01/operations.ts",
"./src/Utils/arm/generatedClients/2020-04-01/partitionKeyRangeId.ts",
"./src/Utils/arm/generatedClients/2020-04-01/partitionKeyRangeIdRegion.ts",
"./src/Utils/arm/generatedClients/2020-04-01/percentile.ts",
"./src/Utils/arm/generatedClients/2020-04-01/percentileSourceTarget.ts",
"./src/Utils/arm/generatedClients/2020-04-01/percentileTarget.ts",
"./src/Utils/arm/generatedClients/2020-04-01/sqlResources.ts",
"./src/Utils/arm/generatedClients/2020-04-01/tableResources.ts",
"./src/Utils/arm/request.ts",
"./src/quickstart.ts", "./src/quickstart.ts",
"./src/setupTests.ts", "./src/setupTests.ts",
"./src/userContext.test.ts",
"./src/workers/upload/definitions.ts" "./src/workers/upload/definitions.ts"
], ],
"include": [] "include": [
"src/Controls/**/*",
"src/Definitions/**/*",
"src/Explorer/Controls/ErrorDisplayComponent/**/*",
"src/Explorer/Controls/RadioSwitchComponent/**/*",
"src/Explorer/Controls/ResizeSensorReactComponent/**/*",
"src/Explorer/Graph/GraphExplorerComponent/__mocks__/**/*",
"src/Explorer/Notebook/NotebookComponent/__mocks__/**/*",
"src/Libs/**/*",
"src/Localization/**/*",
"src/Platform/Emulator/**/*",
"src/Shared/Telemetry/**/*",
"src/Terminal/**/*",
"src/Utils/arm/**/*"
]
} }