Compare commits

..

29 Commits

Author SHA1 Message Date
Sung-Hyun Kang
82f50f6b1f Added VNext feature matrix 2025-08-14 20:46:45 -05:00
Sung-Hyun Kang
d385764027 Rebase master 2025-08-06 12:56:36 -05:00
Lei Jiang
5db83f9a85 Revert version changes on http-proxy-middleware 2025-04-24 20:33:58 -07:00
Lei Jiang
f4276adca9 Update package-lock.json on http-proxy-middleware versions 2025-04-24 15:39:35 -07:00
Lei Jiang
36ded95be0 Update package-lock.json to update package version path-to-regexp 2025-04-14 15:09:52 -07:00
Lei Jiang
41d132b041 Update package-lock.json to update package version path-to-regexp 2025-04-14 14:46:40 -07:00
Lei Jiang
f6c8db9f37 Apply minimum changes on package-lock.json for package update 2025-04-14 13:51:04 -07:00
Lei Jiang
46e6695cba Revert package.json 2025-04-14 13:41:42 -07:00
Lei Jiang
392d0edcd1 Revert package-lock.json 2025-04-14 13:41:08 -07:00
Lei Jiang
b2ea464b6c Update package-lock.json to fix package vulnerabilities 2025-04-14 13:08:31 -07:00
Lei Jiang
c84beeb8c3 Update package.json to patch for vulnerabilities 2025-04-14 13:07:51 -07:00
Chris Anderson
3bf0f09d37 remove old local-dep 2024-12-21 00:31:14 +00:00
Chris Anderson
eb85b2959d added https support 2024-12-21 00:30:58 +00:00
Chris Anderson
ec472a3d4c exclude local proxy from ts 2024-12-06 18:38:37 +00:00
Chris Anderson
e3055b121f complete proxy with instructions 2024-12-03 21:43:29 +00:00
Chris Anderson
e489a66ae2 chores 2024-12-03 21:43:12 +00:00
Chris Anderson
601a335839 misc fixes 2024-12-03 01:17:31 +00:00
Chris Anderson
0029b04af1 add local proxy 2024-12-03 00:47:26 +00:00
Theo van Kraay
f4aa74ad6f comment out or change SDK code to remove errors 2024-12-03 00:47:26 +00:00
Theo van Kraay
2afb2d82e4 add copy of cosmos node.js SDK as local dependency 2024-12-03 00:47:26 +00:00
Chris Anderson
679e4e56df fix templates 2024-12-03 00:12:22 +00:00
Chris Anderson
df2c1b2345 update cosmos dep in lock file 2024-12-02 23:03:44 +00:00
Chris Anderson
7007368a1a temp: dynamically redirect quickstart template based on tls flag 2024-12-02 22:54:40 +00:00
Chris Anderson
079965d199 fix: explorer use emulator tls setting 2024-12-02 22:54:40 +00:00
Chris Anderson
fc774e1089 feat: enable http emulator support 2024-12-02 22:54:40 +00:00
Theo van Kraay
d9d90ac6d9 temp revert back changes that break even legacy emulator 2024-12-02 22:54:16 +00:00
Theo van Kraay
c101f7de74 comment out or change SDK code to remove errors 2024-12-02 22:54:16 +00:00
Theo van Kraay
ca396cdfbe add copy of cosmos node.js SDK as local dependency 2024-12-02 22:54:16 +00:00
Theo van Kraay
ff1e733679 pgcosmos 2024-12-02 22:52:32 +00:00
47 changed files with 2675 additions and 1169 deletions

View File

@@ -143,4 +143,6 @@ src/Explorer/Tree/ResourceTreeAdapter.tsx
__mocks__/monaco-editor.ts
src/Explorer/Tree/ResourceTree.tsx
src/Utils/EndpointUtils.ts
src/Utils/PriorityBasedExecutionUtils.ts
src/Utils/PriorityBasedExecutionUtils.ts
utils/local-proxy/**

View File

@@ -0,0 +1,3 @@
{
"EMULATOR_ENDPOINT": "http://localhost:8081"
}

91
package-lock.json generated
View File

@@ -205,6 +205,58 @@
"webpack-dev-server": "4.15.2"
}
},
"canvas": {
"version": "1.0.0",
"extraneous": true,
"license": "ISC"
},
"local_dependencies/@azure/cosmos": {
"version": "4.0.1-beta.3",
"extraneous": true,
"license": "MIT",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
"@azure/core-rest-pipeline": "^1.2.0",
"@azure/core-tracing": "^1.0.0",
"debug": "^4.1.1",
"fast-json-stable-stringify": "^2.1.0",
"jsbi": "^3.1.3",
"node-abort-controller": "^3.0.0",
"priorityqueuejs": "^2.0.0",
"semaphore": "^1.0.5",
"tslib": "^2.2.0",
"universal-user-agent": "^6.0.0",
"uuid": "^8.3.0"
},
"engines": {
"node": ">=18.0.0"
}
},
"local_dependencies/cosmos": {
"name": "@azure/cosmos",
"version": "4.0.1-beta.3",
"extraneous": true,
"license": "MIT",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
"@azure/core-rest-pipeline": "^1.2.0",
"@azure/core-tracing": "^1.0.0",
"debug": "^4.1.1",
"fast-json-stable-stringify": "^2.1.0",
"jsbi": "^3.1.3",
"node-abort-controller": "^3.0.0",
"priorityqueuejs": "^2.0.0",
"semaphore": "^1.0.5",
"tslib": "^2.2.0",
"universal-user-agent": "^6.0.0",
"uuid": "^8.3.0"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
"version": "1.2.6",
"license": "MIT",
@@ -391,9 +443,15 @@
"license": "0BSD"
},
"node_modules/@azure/cosmos": {
<<<<<<< HEAD
"version": "4.2.0-beta.1",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0-beta.1.tgz",
"integrity": "sha512-mREONehm1DxjEKXGaNU6Wmpf9Ckb9IrhKFXhDFVs45pxmoEb3y2s/Ub0owuFmqlphpcS1zgtYQn5exn+lwnJuQ==",
=======
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.5.0.tgz",
"integrity": "sha512-JsTh4twb6FcwP7rJwxQiNZQ/LGtuF6gmciaxY9Rnp6/A325Lhsw/SH4R2ArpT0yCvozbZpweIwdPfUkXVBtp5w==",
>>>>>>> master
"license": "MIT",
"dependencies": {
"@azure/abort-controller": "^2.1.2",
@@ -441,7 +499,12 @@
"node_modules/@azure/cosmos/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
<<<<<<< HEAD
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
=======
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
>>>>>>> master
},
"node_modules/@azure/identity": {
"version": "4.5.0",
@@ -7893,7 +7956,7 @@
}
},
"node_modules/@nteract/data-explorer/node_modules/cross-spawn": {
"version": "6.0.5",
"version": "7.0.5",
"license": "MIT",
"dependencies": {
"nice-try": "^1.0.4",
@@ -8017,7 +8080,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"cross-spawn": "^6.0.0",
"cross-spawn": "^7.0.5",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
@@ -16445,7 +16508,7 @@
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"version": "7.0.5",
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -18359,7 +18422,7 @@
"@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"cross-spawn": "^7.0.5",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
@@ -18946,7 +19009,7 @@
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
"devOptional": true,
"dependencies": {
"cross-spawn": "^7.0.3",
"cross-spawn": "^7.0.5",
"get-stream": "^6.0.0",
"human-signals": "^2.1.0",
"is-stream": "^2.0.0",
@@ -19211,7 +19274,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
@@ -19247,7 +19310,7 @@
"license": "MIT"
},
"node_modules/express/node_modules/path-to-regexp": {
"version": "0.1.7",
"version": "0.1.12",
"dev": true,
"license": "MIT"
},
@@ -30535,7 +30598,7 @@
"@yarnpkg/lockfile": "^1.1.0",
"chalk": "^4.1.2",
"ci-info": "^3.7.0",
"cross-spawn": "^7.0.3",
"cross-spawn": "^7.0.5",
"find-yarn-workspace-root": "^2.0.0",
"fs-extra": "^9.0.0",
"json-stable-stringify": "^1.0.2",
@@ -31513,7 +31576,7 @@
"address": "^1.1.2",
"browserslist": "^4.18.1",
"chalk": "^4.1.2",
"cross-spawn": "^7.0.3",
"cross-spawn": "^7.0.5",
"detect-port-alt": "^1.1.6",
"escape-string-regexp": "^4.0.0",
"filesize": "^8.0.6",
@@ -32969,7 +33032,7 @@
}
},
"node_modules/sane/node_modules/cross-spawn": {
"version": "6.0.5",
"version": "7.0.5",
"license": "MIT",
"dependencies": {
"nice-try": "^1.0.4",
@@ -32986,7 +33049,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"cross-spawn": "^6.0.0",
"cross-spawn": "^7.0.5",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
@@ -35955,7 +36018,7 @@
"@webpack-cli/serve": "^2.0.5",
"colorette": "^2.0.14",
"commander": "^10.0.1",
"cross-spawn": "^7.0.3",
"cross-spawn": "^7.0.5",
"envinfo": "^7.7.3",
"fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2",
@@ -36480,7 +36543,7 @@
}
},
"node_modules/windows-release/node_modules/cross-spawn": {
"version": "6.0.5",
"version": "7.0.5",
"license": "MIT",
"dependencies": {
"nice-try": "^1.0.4",
@@ -36497,7 +36560,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"cross-spawn": "^6.0.0",
"cross-spawn": "^7.0.5",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",

View File

@@ -206,9 +206,11 @@
"build:dataExplorer:ci": "npm run build:ci",
"build": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToConsumers",
"build:ci": "npm run format:check && npm run lint && npm run compile && npm run compile:strict && npm run pack:fast",
"build:proxy": "npm run compile && npm run compile:strict && npm run pack:prod && npm run copyToProxy",
"pack:prod": "webpack --mode production",
"pack:fast": "webpack --mode development --progress",
"copyToConsumers": "node copyToConsumers",
"copyToProxy": "rm -rf ./utils/local-proxy/dist && cp -r ./dist ./utils/local-proxy",
"test": "rimraf coverage && jest",
"test:debug": "jest --runInBand",
"test:e2e": "jest -c ./jest.config.playwright.js --detectOpenHandles",

View File

@@ -19077,9 +19077,9 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
@@ -35537,12 +35537,420 @@
"node": ">= 0.8"
}
},
"node_modules/verror": {
"version": "1.10.0",
"engines": [
"node >=0.6.0"
],
"license": "MIT",
"@types/node": {
"version": "14.14.37",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.37.tgz",
"integrity": "sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw=="
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"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"
}
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"requires": {
"fill-range": "^7.0.1"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"camelcase": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
"integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg=="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"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=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"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.12",
"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"
}
},
"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==",
"requires": {
"to-regex-range": "^5.0.1"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"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"
}
},
"follow-redirects": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz",
"integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA=="
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"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==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"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==",
"requires": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
"requires-port": "^1.0.0"
}
},
"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==",
"requires": {
"@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"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"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=="
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
},
"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==",
"requires": {
"is-extglob": "^2.1.1"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
},
"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=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
}
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"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=="
},
"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==",
"requires": {
"mime-db": "1.46.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"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": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg=="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"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=="
},
"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==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"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=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"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"
},
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",

View File

@@ -765,10 +765,3 @@ export const ShortenedQueryCopilotSampleContainerSchema = {
userPrompt: "find all products",
};
export enum MongoGuidRepresentation {
Standard = "Standard",
CSharpLegacy = "CSharpLegacy",
JavaLegacy = "JavaLegacy",
PythonLegacy = "PythonLegacy",
}

View File

@@ -1,5 +1,4 @@
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
import { getMongoGuidRepresentation } from "Shared/StorageUtility";
import { AuthType } from "../AuthType";
import { configContext } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels";
@@ -140,9 +139,6 @@ export function readDocument(
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperties?.[0]
: "",
clientSettings: {
guidRepresentation: getMongoGuidRepresentation(),
},
};
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
@@ -185,9 +181,6 @@ export function createDocument(
partitionKey:
collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
documentContent: JSON.stringify(documentContent),
clientSettings: {
guidRepresentation: getMongoGuidRepresentation(),
},
};
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
@@ -235,9 +228,6 @@ export function updateDocument(
? documentId.partitionKeyProperties?.[0]
: "",
documentContent,
clientSettings: {
guidRepresentation: getMongoGuidRepresentation(),
},
};
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);
@@ -284,9 +274,6 @@ export function deleteDocuments(
subscriptionID: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
databaseAccountName: databaseAccount.name,
clientSettings: {
guidRepresentation: getMongoGuidRepresentation(),
},
};
const endpoint = getEndpoint(configContext.MONGO_PROXY_ENDPOINT);

View File

@@ -19,6 +19,7 @@ export enum Platform {
Hosted = "Hosted",
Emulator = "Emulator",
Fabric = "Fabric",
VNextEmulator = "VNextEmulator",
}
export interface ConfigContext {
@@ -225,6 +226,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
case Platform.Fabric:
case Platform.Hosted:
case Platform.Emulator:
case Platform.VNextEmulator:
updateConfigContext({ platform });
}
}

View File

@@ -7,7 +7,6 @@ export enum FabricMessageTypes {
GetAccessToken = "GetAccessToken",
Ready = "Ready",
OpenSettings = "OpenSettings",
NewShortcut = "NewShortcut",
}
export interface AuthorizationToken {

View File

@@ -84,11 +84,9 @@ export type FabricMessageV3 =
}
| {
type: "refreshResourceTree";
}
| {
type: "shortcutCreated";
message: {
shortcutId: string;
id: string;
error: string | undefined;
};
};

View File

@@ -15,6 +15,7 @@ import { useDatabases } from "Explorer/useDatabases";
import { isFabricNative } from "Platform/Fabric/FabricUtil";
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
import { isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
import * as React from "react";
import DiscardIcon from "../../../../images/discard.svg";
import SaveIcon from "../../../../images/save-cosmos.svg";
@@ -60,15 +61,15 @@ import {
AddMongoIndexProps,
ChangeFeedPolicyState,
GeospatialConfigType,
MongoIndexTypes,
SettingsV2TabTypes,
TtlType,
getMongoNotification,
getTabTitle,
hasDatabaseSharedThroughput,
isDirty,
MongoIndexTypes,
parseConflictResolutionMode,
parseConflictResolutionProcedure,
SettingsV2TabTypes,
TtlType,
} from "./SettingsUtils";
interface SettingsV2TabInfo {
@@ -276,14 +277,14 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.saveSettingsButton = {
isEnabled: this.isSaveSettingsButtonEnabled,
isVisible: () => {
return true;
return isFeatureSupported(PlatformFeature.UpdateCollection);
},
};
this.discardSettingsChangesButton = {
isEnabled: this.isDiscardSettingsButtonEnabled,
isVisible: () => {
return true;
return isFeatureSupported(PlatformFeature.UpdateCollection);
},
};

View File

@@ -3,9 +3,8 @@ import { Link } from "@fluentui/react/lib/Link";
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
import { Environment, getEnvironment } from "Common/EnvironmentUtility";
import { sendMessage } from "Common/MessageHandler";
import { Platform, configContext } from "ConfigContext";
import { configContext, Platform } from "ConfigContext";
import { MessageTypes } from "Contracts/ExplorerContracts";
import { AddFabricShortcutPanel } from "Explorer/Panes/AddCollectionPanel/AddFabricShortcutPanel";
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { IGalleryItem } from "Juno/JunoClient";
@@ -19,6 +18,7 @@ import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
import { featureRegistered } from "Utils/FeatureRegistrationUtils";
import { isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import * as ko from "knockout";
@@ -1113,14 +1113,6 @@ export default class Explorer {
}
}
public async onNewFabricShortcutClicked(param: {
shortcutId: string;
}): Promise<void> {
useSidePanel
.getState()
.openSidePanel("Configure Shortcut", <AddFabricShortcutPanel explorer={this} shortcutId={param.shortcutId} />);
}
private refreshCommandBarButtons(): void {
const activeTab = useTabs.getState().activeTab;
if (activeTab) {
@@ -1196,6 +1188,7 @@ export default class Explorer {
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
const isNotebookEnabled =
isFeatureSupported(PlatformFeature.Notebooks) &&
configContext.platform !== Platform.Fabric &&
(userContext.features.notebooksDownBanner ||
useNotebook.getState().isPhoenixNotebooks ||
@@ -1203,7 +1196,11 @@ export default class Explorer {
useNotebook.getState().setIsNotebookEnabled(isNotebookEnabled);
useNotebook
.getState()
.setIsShellEnabled(useNotebook.getState().isPhoenixFeatures && isPublicInternetAccessAllowed());
.setIsShellEnabled(
isFeatureSupported(PlatformFeature.CloudShell) &&
useNotebook.getState().isPhoenixFeatures &&
isPublicInternetAccessAllowed(),
);
TelemetryProcessor.trace(Action.NotebookEnabled, ActionModifiers.Mark, {
isNotebookEnabled,
@@ -1224,6 +1221,7 @@ export default class Explorer {
public async configureCopilot(): Promise<void> {
if (
!isFeatureSupported(PlatformFeature.Copilot) ||
userContext.apiType !== "SQL" ||
!userContext.subscriptionId ||
![Environment.Development, Environment.Mpac, Environment.Prod].includes(getEnvironment())

View File

@@ -1,5 +1,6 @@
import { KeyboardAction } from "KeyboardShortcuts";
import { isDataplaneRbacSupported } from "Utils/APITypeUtils";
import { areAdvancedScriptsSupported, isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
import * as React from "react";
import { useEffect, useState } from "react";
import AddSqlQueryIcon from "../../../../images/AddSqlQuery_16x16.svg";
@@ -17,7 +18,7 @@ import SynapseIcon from "../../../../images/synapse-link.svg";
import VSCodeIcon from "../../../../images/vscode.svg";
import { AuthType } from "../../../AuthType";
import * as Constants from "../../../Common/Constants";
import { Platform, configContext } from "../../../ConfigContext";
import { configContext, Platform } from "../../../ConfigContext";
import * as ViewModels from "../../../Contracts/ViewModels";
import { userContext } from "../../../UserContext";
import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils";
@@ -63,7 +64,9 @@ export function createStaticCommandBarButtons(
}
if (userContext.apiType !== "Gremlin") {
const addVsCode = createOpenVsCodeDialogButton(container);
buttons.push(addVsCode);
if (addVsCode) {
buttons.push(addVsCode);
}
}
}
@@ -242,11 +245,17 @@ export function createDivider(): CommandButtonComponentProps {
function areScriptsSupported(): boolean {
return (
configContext.platform !== Platform.Fabric && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
areAdvancedScriptsSupported() &&
configContext.platform !== Platform.Fabric &&
(userContext.apiType === "SQL" || userContext.apiType === "Gremlin")
);
}
function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonComponentProps {
if (!isFeatureSupported(PlatformFeature.SynapseLink)) {
return undefined;
}
if (configContext.platform === Platform.Emulator) {
return undefined;
}
@@ -274,6 +283,10 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
}
function createOpenVsCodeDialogButton(container: Explorer): CommandButtonComponentProps {
if (!isFeatureSupported(PlatformFeature.VSCodeIntegration)) {
return undefined;
}
const label = "Visual Studio Code";
return {
iconSrc: VSCodeIcon,

View File

@@ -50,6 +50,7 @@ import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext";
import { getCollectionName } from "Utils/APITypeUtils";
import { isCapabilityEnabled, isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
import { getUpsellMessage } from "Utils/PricingUtils";
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
@@ -1160,11 +1161,15 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
}
private shouldShowVectorSearchParameters() {
return isVectorSearchEnabled() && (isServerlessAccount() || this.shouldShowCollectionThroughputInput());
return (
isFeatureSupported(PlatformFeature.VectorSearch) &&
isVectorSearchEnabled() &&
(isServerlessAccount() || this.shouldShowCollectionThroughputInput())
);
}
private shouldShowFullTextSearchParameters() {
return !isFabricNative() && this.showFullTextSearch;
return isFeatureSupported(PlatformFeature.FullTextSearch) && !isFabricNative() && this.showFullTextSearch;
}
private parseUniqueKeys(): DataModels.UniqueKeyPolicy {

View File

@@ -6,6 +6,7 @@ import { getFullTextLanguageOptions } from "Explorer/Controls/FullTextSeach/Full
import { isFabricNative } from "Platform/Fabric/FabricUtil";
import React from "react";
import { userContext } from "UserContext";
import { isFeatureSupported, PlatformFeature } from "Utils/PlatformFeatureUtils";
export function getPartitionKeyTooltipText(): string {
if (userContext.apiType === "Mongo") {
@@ -85,7 +86,11 @@ export function UniqueKeysHeader(): JSX.Element {
}
export function shouldShowAnalyticalStoreOptions(): boolean {
if (isFabricNative() || configContext.platform === Platform.Emulator) {
if (
!isFeatureSupported(PlatformFeature.AnalyticalStore) ||
isFabricNative() ||
configContext.platform === Platform.Emulator
) {
return false;
}

View File

@@ -1,800 +0,0 @@
import {
ChoiceGroup,
DefaultButton,
DirectionalHint,
Icon,
IconButton,
IDropdownOption,
Link,
ProgressIndicator,
Separator,
Stack,
TeachingBubble,
Text,
TooltipHost
} from "@fluentui/react";
import * as Constants from "Common/Constants";
import { createCollection } from "Common/dataAccess/createCollection";
import { getNewDatabaseSharedThroughputDefault } from "Common/DatabaseUtility";
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
import { configContext, Platform } from "ConfigContext";
import * as DataModels from "Contracts/DataModels";
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
import {
AllPropertiesIndexed,
ContainerVectorPolicyTooltipContent,
FullTextPolicyDefault,
getPartitionKey,
getPartitionKeyName,
getPartitionKeyPlaceHolder,
getPartitionKeyTooltipText,
isFreeTierAccount,
isSynapseLinkEnabled,
parseUniqueKeys,
scrollToSection,
SharedDatabaseDefault,
shouldShowAnalyticalStoreOptions
} from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
import { useSidePanel } from "hooks/useSidePanel";
import { useTeachingBubble } from "hooks/useTeachingBubble";
import { isFabricNative } from "Platform/Fabric/FabricUtil";
import React from "react";
import { CollectionCreation } from "Shared/Constants";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext";
import { getCollectionName } from "Utils/APITypeUtils";
import { isCapabilityEnabled, isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import { CollapsibleSectionComponent } from "../../Controls/CollapsiblePanel/CollapsibleSectionComponent";
import { ContainerSampleGenerator } from "../../DataSamples/ContainerSampleGenerator";
import Explorer from "../../Explorer";
import { useDatabases } from "../../useDatabases";
import { PanelFooterComponent } from "../PanelFooterComponent";
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
import { PanelLoadingScreen } from "../PanelLoadingScreen";
interface AddFabricShortcutPanelProps {
explorer: Explorer;
shortcutId: string;
databaseId?: string;
isQuickstart?: boolean;
}
export const DefaultVectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy = {
vectorEmbeddings: [],
};
export interface AddCollectionPanelState {
createNewDatabase: boolean;
newDatabaseId: string;
isSharedThroughputChecked: boolean;
selectedDatabaseId: string;
collectionId: string;
enableIndexing: boolean;
isSharded: boolean;
partitionKey: string;
subPartitionKeys: string[];
enableDedicatedThroughput: boolean;
createMongoWildCardIndex: boolean;
useHashV1: boolean;
enableAnalyticalStore: boolean;
uniqueKeys: string[];
errorMessage: string;
showErrorDetails: boolean;
isExecuting: boolean;
isThroughputCapExceeded: boolean;
teachingBubbleStep: number;
vectorIndexingPolicy: DataModels.VectorIndex[];
vectorEmbeddingPolicy: DataModels.VectorEmbedding[];
vectorPolicyValidated: boolean;
fullTextPolicy: DataModels.FullTextPolicy;
fullTextIndexes: DataModels.FullTextIndex[];
fullTextPolicyValidated: boolean;
}
export class AddFabricShortcutPanel extends React.Component<AddFabricShortcutPanelProps, AddCollectionPanelState> {
private newDatabaseThroughput: number;
private isNewDatabaseAutoscale: boolean;
private collectionThroughput: number;
private isCollectionAutoscale: boolean;
private isCostAcknowledged: boolean;
private showFullTextSearch: boolean;
constructor(props: AddFabricShortcutPanelProps) {
super(props);
this.state = {
createNewDatabase:
userContext.apiType !== "Tables" && configContext.platform !== Platform.Fabric && !this.props.databaseId,
newDatabaseId: props.isQuickstart ? this.getSampleDBName() : "",
isSharedThroughputChecked: getNewDatabaseSharedThroughputDefault(),
selectedDatabaseId:
userContext.apiType === "Tables" ? CollectionCreation.TablesAPIDefaultDatabase : this.props.databaseId,
collectionId: props.isQuickstart ? `Sample${getCollectionName()}` : "",
enableIndexing: true,
isSharded: userContext.apiType !== "Tables",
partitionKey: getPartitionKey(props.isQuickstart),
subPartitionKeys: [],
enableDedicatedThroughput: isFabricNative(), // Dedicated throughput is only enabled in Fabric Native by default
createMongoWildCardIndex:
isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport"),
useHashV1: false,
enableAnalyticalStore: false,
uniqueKeys: [],
errorMessage: "",
showErrorDetails: false,
isExecuting: false,
isThroughputCapExceeded: false,
teachingBubbleStep: 0,
vectorEmbeddingPolicy: [],
vectorIndexingPolicy: [],
vectorPolicyValidated: true,
fullTextPolicy: FullTextPolicyDefault,
fullTextIndexes: [],
fullTextPolicyValidated: true,
};
this.showFullTextSearch = userContext.apiType === "SQL";
}
componentDidMount(): void {
if (this.state.teachingBubbleStep === 0 && this.props.isQuickstart) {
this.setState({ teachingBubbleStep: 1 });
}
}
componentDidUpdate(_prevProps: AddFabricShortcutPanelProps, prevState: AddCollectionPanelState): void {
if (this.state.errorMessage && this.state.errorMessage !== prevState.errorMessage) {
scrollToSection("panelContainer");
}
}
render(): JSX.Element {
const options: IChoiceGroupOption[] = [
{ key: 'continuous', text: 'Continuous' },
{ key: 'manual', text: 'Manual' },
];
return (
<form className="panelFormWrapper" onSubmit={this.submit.bind(this)} id="panelContainer">
{this.state.errorMessage && (
<PanelInfoErrorComponent
message={this.state.errorMessage}
messageType="error"
showErrorDetails={this.state.showErrorDetails}
/>
)}
<div className="panelMainContent">
<Stack>
<Stack horizontal style={{ marginTop: -5, marginBottom: -4 }}>
<Text className="panelTextBold" variant="small">Container id</Text>
<TooltipHost directionalHint={DirectionalHint.bottomLeftEdge} content={getPartitionKeyTooltipText()}>
<Icon
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={getPartitionKeyTooltipText()}
/>
</TooltipHost>
</Stack>
<div>{this.props.shortcutId}</div>
<div>publicHolidays</div>
<Separator className="panelSeparator" style={{ marginTop: 2, marginBottom: -4 }} />
</Stack>
<Stack>
<Stack horizontal style={{ marginTop: -5, marginBottom: -4 }}>
<span className="mandatoryStar">*&nbsp;</span>
<Text className="panelTextBold" variant="small">
{getPartitionKeyName()}
</Text>
<TooltipHost directionalHint={DirectionalHint.bottomLeftEdge} content={getPartitionKeyTooltipText()}>
<Icon
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={getPartitionKeyTooltipText()}
/>
</TooltipHost>
</Stack>
<Text variant="small">{this.getPartitionKeySubtext()}</Text>
<input
type="text"
id="addCollection-partitionKeyValue"
aria-required
required
size={40}
className="panelTextField"
placeholder={getPartitionKeyPlaceHolder()}
aria-label={getPartitionKeyName()}
pattern={userContext.apiType === "Gremlin" ? "^/[^/]*" : ".*"}
title={userContext.apiType === "Gremlin" ? "May not use composite partition key" : ""}
value={this.state.partitionKey}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (
userContext.apiType !== "Mongo" &&
!this.state.partitionKey &&
!event.target.value.startsWith("/")
) {
this.setState({ partitionKey: "/" + event.target.value });
} else {
this.setState({ partitionKey: event.target.value });
}
}}
/>
{userContext.apiType === "SQL" &&
this.state.subPartitionKeys.map((subPartitionKey: string, index: number) => {
return (
<Stack style={{ marginBottom: 2, marginTop: -5 }} key={`uniqueKey${index}`} horizontal>
<div
style={{
width: "20px",
border: "solid",
borderWidth: "0px 0px 1px 1px",
marginRight: "5px",
}}
></div>
<input
type="text"
id="addCollection-partitionKeyValue"
key={`addCollection-partitionKeyValue_${index}`}
aria-required
required
size={40}
tabIndex={index > 0 ? 1 : 0}
className="panelTextField"
autoComplete="off"
placeholder={getPartitionKeyPlaceHolder(index)}
aria-label={getPartitionKeyName()}
pattern={".*"}
title={""}
value={subPartitionKey}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const subPartitionKeys = [...this.state.subPartitionKeys];
if (!this.state.subPartitionKeys[index] && !event.target.value.startsWith("/")) {
subPartitionKeys[index] = "/" + event.target.value.trim();
this.setState({ subPartitionKeys });
} else {
subPartitionKeys[index] = event.target.value.trim();
this.setState({ subPartitionKeys });
}
}}
/>
<IconButton
iconProps={{ iconName: "Delete" }}
style={{ height: 27 }}
onClick={() => {
const subPartitionKeys = this.state.subPartitionKeys.filter((uniqueKey, j) => index !== j);
this.setState({ subPartitionKeys });
}}
/>
</Stack>
);
})}
{userContext.apiType === "SQL" && (
<Stack className="panelGroupSpacing">
<DefaultButton
styles={{ root: { padding: 0, width: 200, height: 30 }, label: { fontSize: 12 } }}
hidden={this.state.useHashV1}
disabled={this.state.subPartitionKeys.length >= Constants.BackendDefaults.maxNumMultiHashPartition}
onClick={() => this.setState({ subPartitionKeys: [...this.state.subPartitionKeys, ""] })}
>
Add hierarchical partition key
</DefaultButton>
{this.state.subPartitionKeys.length > 0 && (
<Text variant="small">
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} /> This feature allows you to
partition your data with up to three levels of keys for better data distribution. Requires .NET
V3, Java V4 SDK, or preview JavaScript V3 SDK.{" "}
<Link href="https://aka.ms/cosmos-hierarchical-partitioning" target="_blank">
Learn more
</Link>
</Text>
)}
</Stack>
)}
<Separator className="panelSeparator" style={{ marginTop: 2, marginBottom: -4 }} />
</Stack>
<Stack>
<CollapsibleSectionComponent
title="Container Vector Policy"
isExpandedByDefault={false}
onExpand={() => {
scrollToSection("collapsibleVectorPolicySectionContent");
}}
tooltipContent={ContainerVectorPolicyTooltipContent()}
>
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
<Stack styles={{ root: { paddingLeft: 40 } }}>
<VectorEmbeddingPoliciesComponent
vectorEmbeddings={this.state.vectorEmbeddingPolicy}
vectorIndexes={this.state.vectorIndexingPolicy}
onVectorEmbeddingChange={(
vectorEmbeddingPolicy: DataModels.VectorEmbedding[],
vectorIndexingPolicy: DataModels.VectorIndex[],
vectorPolicyValidated: boolean,
) => {
this.setState({ vectorEmbeddingPolicy, vectorIndexingPolicy, vectorPolicyValidated });
}}
/>
</Stack>
</Stack>
</CollapsibleSectionComponent>
<Separator className="panelSeparator" style={{ marginTop: 2, marginBottom: -4 }} />
</Stack>
<Stack>
<CollapsibleSectionComponent
title="ETL Frequency"
isExpandedByDefault={false}
onExpand={() => {
scrollToSection("collapsibleVectorPolicySectionContent");
}}
tooltipContent={ContainerVectorPolicyTooltipContent()}
>
<ChoiceGroup defaultSelectedKey="continuous" options={options} onChange={() => { }} required={true} />
</CollapsibleSectionComponent>
</Stack>
</div>
<PanelFooterComponent buttonLabel="OK" isButtonDisabled={this.state.isThroughputCapExceeded} />
{this.state.isExecuting && (
<div>
<PanelLoadingScreen />
{this.state.teachingBubbleStep === 5 && (
<TeachingBubble
headline="Creating sample container"
target={"#loadingScreen"}
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
styles={{ footer: { width: "100%" } }}
>
A sample container is now being created and we are adding sample data for you. It should take about 1
minute.
<br />
<br />
Once the sample container is created, review your sample dataset and follow next steps
<br />
<br />
<ProgressIndicator
styles={{
itemName: { color: "white" },
progressTrack: { backgroundColor: "#A6A6A6" },
progressBar: { background: "white" },
}}
label="Adding sample data set"
/>
</TeachingBubble>
)}
</div>
)}
</form>
);
}
private getDatabaseOptions(): IDropdownOption[] {
return useDatabases.getState().databases?.map((database) => ({
key: database.id(),
text: database.id(),
}));
}
private onCreateNewDatabaseRadioBtnChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.checked && !this.state.createNewDatabase) {
this.setState({
createNewDatabase: true,
});
}
}
private onUseExistingDatabaseRadioBtnChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.checked && this.state.createNewDatabase) {
this.setState({
createNewDatabase: false,
});
}
}
private onUnshardedRadioBtnChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.checked && this.state.isSharded) {
this.setState({
isSharded: false,
});
}
}
private onShardedRadioBtnChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.checked && !this.state.isSharded) {
this.setState({
isSharded: true,
});
}
}
private onEnableAnalyticalStoreRadioBtnChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.checked && !this.state.enableAnalyticalStore) {
this.setState({
enableAnalyticalStore: true,
});
}
}
private onDisableAnalyticalStoreRadioBtnChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.checked && this.state.enableAnalyticalStore) {
this.setState({
enableAnalyticalStore: false,
});
}
}
private onTurnOnIndexing(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.checked && !this.state.enableIndexing) {
this.setState({
enableIndexing: true,
});
}
}
private onTurnOffIndexing(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.checked && this.state.enableIndexing) {
this.setState({
enableIndexing: false,
});
}
}
private setVectorEmbeddingPolicy(vectorEmbeddingPolicy: DataModels.VectorEmbedding[]): void {
this.setState({
vectorEmbeddingPolicy,
});
}
private setVectorIndexingPolicy(vectorIndexingPolicy: DataModels.VectorIndex[]): void {
this.setState({
vectorIndexingPolicy,
});
}
private isSelectedDatabaseSharedThroughput(): boolean {
if (!this.state.selectedDatabaseId) {
return false;
}
const selectedDatabase = useDatabases
.getState()
.databases?.find((database) => database.id() === this.state.selectedDatabaseId);
return !!selectedDatabase?.offer();
}
private getFreeTierIndexingText(): string {
return this.state.enableIndexing
? "All properties in your documents will be indexed by default for flexible and efficient queries."
: "Indexing will be turned off. Recommended if you don't need to run queries or only have key value operations.";
}
private getPartitionKeySubtext(): string {
if (
userContext.features.partitionKeyDefault &&
(userContext.apiType === "SQL" || userContext.apiType === "Mongo")
) {
const subtext = "For small workloads, the item ID is a suitable choice for the partition key.";
return subtext;
}
return "";
}
//TODO: uncomment when learn more text becomes available
// private getContainerFullTextPolicyTooltipContent(): JSX.Element {
// return (
// <Text variant="small">
// Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
// magna aliqua.{" "}
// <Link target="_blank" href="https://aka.ms/CosmosFullTextSearch">
// Learn more
// </Link>
// </Text>
// );
// }
private shouldShowCollectionThroughputInput(): boolean {
if (isServerlessAccount()) {
return false;
}
if (this.state.createNewDatabase) {
return !this.state.isSharedThroughputChecked;
}
if (this.state.enableDedicatedThroughput) {
return true;
}
return this.state.selectedDatabaseId && !this.isSelectedDatabaseSharedThroughput();
}
private shouldShowIndexingOptionsForFreeTierAccount(): boolean {
if (!isFreeTierAccount()) {
return false;
}
return this.state.createNewDatabase
? this.state.isSharedThroughputChecked
: this.isSelectedDatabaseSharedThroughput();
}
private shouldShowVectorSearchParameters() {
return isVectorSearchEnabled() && (isServerlessAccount() || this.shouldShowCollectionThroughputInput());
}
private shouldShowFullTextSearchParameters() {
return !isFabricNative() && this.showFullTextSearch;
}
private parseUniqueKeys(): DataModels.UniqueKeyPolicy {
if (this.state.uniqueKeys?.length === 0) {
return undefined;
}
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = { uniqueKeys: [] };
this.state.uniqueKeys.forEach((uniqueKey) => {
if (uniqueKey) {
const validPaths: string[] = uniqueKey.split(",")?.filter((path) => path?.length > 0);
const trimmedPaths: string[] = validPaths?.map((path) => path.trim());
if (trimmedPaths?.length > 0) {
if (userContext.apiType === "Mongo") {
trimmedPaths.map((path) => {
const transformedPath = path.split(".").join("/");
if (transformedPath[0] !== "/") {
return "/" + transformedPath;
}
return transformedPath;
});
}
uniqueKeyPolicy.uniqueKeys.push({ paths: trimmedPaths });
}
}
});
return uniqueKeyPolicy;
}
private validateInputs(): boolean {
if (!this.state.createNewDatabase && !this.state.selectedDatabaseId) {
this.setState({ errorMessage: "Please select an existing database" });
return false;
}
const throughput = this.state.createNewDatabase ? this.newDatabaseThroughput : this.collectionThroughput;
if (throughput > CollectionCreation.DefaultCollectionRUs100K && !this.isCostAcknowledged) {
const errorMessage = this.isNewDatabaseAutoscale
? "Please acknowledge the estimated monthly spend."
: "Please acknowledge the estimated daily spend.";
this.setState({ errorMessage });
return false;
}
if (throughput > CollectionCreation.MaxRUPerPartition && !this.state.isSharded) {
this.setState({ errorMessage: "Unsharded collections support up to 10,000 RUs" });
return false;
}
if (
userContext.apiType === "Gremlin" &&
(this.state.partitionKey === "/id" || this.state.partitionKey === "/label")
) {
this.setState({ errorMessage: "/id and /label as partition keys are not allowed for graph." });
return false;
}
if (this.shouldShowVectorSearchParameters()) {
if (!this.state.vectorPolicyValidated) {
this.setState({ errorMessage: "Please fix errors in container vector policy" });
return false;
}
if (!this.state.fullTextPolicyValidated) {
this.setState({ errorMessage: "Please fix errors in container full text search polilcy" });
return false;
}
}
return true;
}
private getAnalyticalStorageTtl(): number {
if (!isSynapseLinkEnabled()) {
return undefined;
}
if (!shouldShowAnalyticalStoreOptions()) {
return undefined;
}
if (this.state.enableAnalyticalStore) {
// TODO: always default to 90 days once the backend hotfix is deployed
return userContext.features.ttl90Days
? Constants.AnalyticalStorageTtl.Days90
: Constants.AnalyticalStorageTtl.Infinite;
}
return Constants.AnalyticalStorageTtl.Disabled;
}
private getSampleDBName(): string {
const existingSampleDBs = useDatabases
.getState()
.databases?.filter((database) => database.id().startsWith("SampleDB"));
const existingSampleDBNames = existingSampleDBs?.map((database) => database.id());
if (!existingSampleDBNames || existingSampleDBNames.length === 0) {
return "SampleDB";
}
let i = 1;
while (existingSampleDBNames.indexOf(`SampleDB${i}`) !== -1) {
i++;
}
return `SampleDB${i}`;
}
private async submit(event?: React.FormEvent<HTMLFormElement>): Promise<void> {
event?.preventDefault();
if (!this.validateInputs()) {
return;
}
const collectionId: string = this.state.collectionId.trim();
let databaseId = this.state.createNewDatabase ? this.state.newDatabaseId.trim() : this.state.selectedDatabaseId;
let partitionKeyString = this.state.isSharded ? this.state.partitionKey.trim() : undefined;
if (userContext.apiType === "Tables") {
// Table require fixed Database: TablesDB, and fixed Partition Key: /'$pk'
databaseId = CollectionCreation.TablesAPIDefaultDatabase;
partitionKeyString = "/'$pk'";
}
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = parseUniqueKeys(this.state.uniqueKeys);
const partitionKeyVersion = this.state.useHashV1 ? undefined : 2;
const partitionKey: DataModels.PartitionKey = partitionKeyString
? {
paths: [
partitionKeyString,
...(userContext.apiType === "SQL" && this.state.subPartitionKeys.length > 0
? this.state.subPartitionKeys
: []),
],
kind: userContext.apiType === "SQL" && this.state.subPartitionKeys.length > 0 ? "MultiHash" : "Hash",
version: partitionKeyVersion,
}
: undefined;
const indexingPolicy: DataModels.IndexingPolicy = this.state.enableIndexing
? AllPropertiesIndexed
: SharedDatabaseDefault;
let vectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy;
if (this.shouldShowVectorSearchParameters()) {
indexingPolicy.vectorIndexes = this.state.vectorIndexingPolicy;
vectorEmbeddingPolicy = {
vectorEmbeddings: this.state.vectorEmbeddingPolicy,
};
}
if (this.showFullTextSearch) {
indexingPolicy.fullTextIndexes = this.state.fullTextIndexes;
}
const telemetryData = {
database: {
id: databaseId,
new: this.state.createNewDatabase,
shared: this.state.createNewDatabase
? this.state.isSharedThroughputChecked
: this.isSelectedDatabaseSharedThroughput(),
},
collection: {
id: this.state.collectionId,
throughput: this.collectionThroughput,
isAutoscale: this.isCollectionAutoscale,
partitionKey,
uniqueKeyPolicy,
collectionWithDedicatedThroughput: this.state.enableDedicatedThroughput,
},
subscriptionQuotaId: userContext.quotaId,
dataExplorerArea: Constants.Areas.ContextualPane,
useIndexingForSharedThroughput: this.state.enableIndexing,
isQuickstart: !!this.props.isQuickstart,
};
const startKey: number = TelemetryProcessor.traceStart(Action.CreateCollection, telemetryData);
const databaseLevelThroughput: boolean = this.state.createNewDatabase
? this.state.isSharedThroughputChecked
: this.isSelectedDatabaseSharedThroughput() && !this.state.enableDedicatedThroughput;
let offerThroughput: number;
let autoPilotMaxThroughput: number;
// Throughput
if (isFabricNative()) {
// Fabric Native accounts are always autoscale and have a fixed throughput of 5K
autoPilotMaxThroughput = AutoPilotUtils.autoPilotThroughput5K;
offerThroughput = undefined;
} else if (databaseLevelThroughput) {
if (this.state.createNewDatabase) {
if (this.isNewDatabaseAutoscale) {
autoPilotMaxThroughput = this.newDatabaseThroughput;
} else {
offerThroughput = this.newDatabaseThroughput;
}
}
} else {
if (this.isCollectionAutoscale) {
autoPilotMaxThroughput = this.collectionThroughput;
} else {
offerThroughput = this.collectionThroughput;
}
}
const createCollectionParams: DataModels.CreateCollectionParams = {
createNewDatabase: this.state.createNewDatabase,
collectionId,
databaseId,
databaseLevelThroughput,
offerThroughput,
autoPilotMaxThroughput,
analyticalStorageTtl: this.getAnalyticalStorageTtl(),
indexingPolicy,
partitionKey,
uniqueKeyPolicy,
createMongoWildcardIndex: this.state.createMongoWildCardIndex,
vectorEmbeddingPolicy,
fullTextPolicy: this.state.fullTextPolicy,
};
this.setState({ isExecuting: true });
try {
await createCollection(createCollectionParams);
await this.props.explorer.refreshAllDatabases();
if (this.props.isQuickstart) {
const database = useDatabases.getState().findDatabaseWithId(databaseId);
if (database) {
database.isSampleDB = true;
// populate sample container with sample data
await database.loadCollections();
const collection = database.findCollectionWithId(collectionId);
collection.isSampleCollection = true;
useTeachingBubble.getState().setSampleCollection(collection);
const sampleGenerator = await ContainerSampleGenerator.createSampleGeneratorAsync(this.props.explorer);
await sampleGenerator.populateContainerAsync(collection, partitionKeyString);
// auto-expand sample database + container and show teaching bubble
await database.expandDatabase();
collection.expandCollection();
useDatabases.getState().updateDatabase(database);
useTeachingBubble.getState().setIsSampleDBExpanded(true);
TelemetryProcessor.traceOpen(Action.LaunchUITour);
}
}
this.setState({ isExecuting: false });
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
useSidePanel.getState().closeSidePanel();
} catch (error) {
const errorMessage: string = getErrorMessage(error);
this.setState({ isExecuting: false, errorMessage, showErrorDetails: true });
const failureTelemetryData = { ...telemetryData, error: errorMessage, errorStack: getErrorStack(error) };
TelemetryProcessor.traceFailure(Action.CreateCollection, failureTelemetryData, startKey);
}
}
}

View File

@@ -199,12 +199,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
LocalStorageUtility.getEntryString(StorageKey.CopilotSampleDBEnabled) === "true",
);
const [mongoGuidRepresentation, setMongoGuidRepresentation] = useState<Constants.MongoGuidRepresentation>(
LocalStorageUtility.hasItem(StorageKey.MongoGuidRepresentation)
? (LocalStorageUtility.getEntryString(StorageKey.MongoGuidRepresentation) as Constants.MongoGuidRepresentation)
: Constants.MongoGuidRepresentation.CSharpLegacy,
);
const styles = useStyles();
const explorerVersion = configContext.gitSha;
@@ -267,8 +261,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
useDatabases.getState().sampleDataResourceTokenCollection &&
!isEmulator;
const shouldShowMongoGuidRepresentationOption = userContext.apiType === "Mongo";
const handlerOnSubmit = async () => {
setIsExecuting(true);
@@ -420,10 +412,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
);
}
if (shouldShowMongoGuidRepresentationOption) {
LocalStorageUtility.setEntryString(StorageKey.MongoGuidRepresentation, mongoGuidRepresentation);
}
setIsExecuting(false);
logConsoleInfo(
`Updated items per page setting to ${LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage)}`,
@@ -445,14 +433,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
);
}
if (shouldShowMongoGuidRepresentationOption) {
logConsoleInfo(
`Updated Mongo Guid Representation to ${LocalStorageUtility.getEntryString(
StorageKey.MongoGuidRepresentation,
)}`,
);
}
refreshExplorer && (await explorer.refreshExplorer());
closeSidePanel();
};
@@ -497,13 +477,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
{ key: SplitterDirection.Horizontal, text: "Horizontal" },
];
const mongoGuidRepresentationDropdownOptions: IDropdownOption[] = [
{ key: Constants.MongoGuidRepresentation.CSharpLegacy, text: Constants.MongoGuidRepresentation.CSharpLegacy },
{ key: Constants.MongoGuidRepresentation.JavaLegacy, text: Constants.MongoGuidRepresentation.JavaLegacy },
{ key: Constants.MongoGuidRepresentation.PythonLegacy, text: Constants.MongoGuidRepresentation.PythonLegacy },
{ key: Constants.MongoGuidRepresentation.Standard, text: Constants.MongoGuidRepresentation.Standard },
];
const handleOnPriorityLevelOptionChange = (
ev: React.FormEvent<HTMLInputElement>,
option: IChoiceGroupOption,
@@ -586,13 +559,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
setRefreshExplorer(false);
};
const handleOnMongoGuidRepresentationOptionChange = (
ev: React.FormEvent<HTMLInputElement>,
option: IDropdownOption,
): void => {
setMongoGuidRepresentation(option.key as Constants.MongoGuidRepresentation);
};
const choiceButtonStyles = {
root: {
clear: "both",
@@ -1099,15 +1065,15 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
This is a sample database and collection with synthetic product data you can use to explore using
NoSQL queries. This will appear as another database in the Data Explorer UI, and is created by,
and maintained by Microsoft at no cost to you.
NoSQL queries and Query Advisor. This will appear as another database in the Data Explorer UI, and
is created by, and maintained by Microsoft at no cost to you.
</div>
<Checkbox
styles={{
label: { padding: 0 },
}}
className="padding"
ariaLabel="Enable sample db for query exploration"
ariaLabel="Enable sample db for Query Advisor"
checked={copilotSampleDBEnabled}
onChange={handleSampleDatabaseChange}
label="Enable sample database"
@@ -1116,27 +1082,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</AccordionPanel>
</AccordionItem>
)}
{shouldShowMongoGuidRepresentationOption && (
<AccordionItem value="14">
<AccordionHeader>
<div className={styles.header}>Guid Representation</div>
</AccordionHeader>
<AccordionPanel>
<div className={styles.settingsSectionContainer}>
<div className={styles.settingsSectionDescription}>
GuidRepresentation in MongoDB refers to how Globally Unique Identifiers (GUIDs) are serialized and
deserialized when stored in BSON documents. This will apply to all document operations.
</div>
<Dropdown
aria-labelledby="mongoGuidRepresentation"
selectedKey={mongoGuidRepresentation}
options={mongoGuidRepresentationDropdownOptions}
onChange={handleOnMongoGuidRepresentationOptionChange}
/>
</div>
</AccordionPanel>
</AccordionItem>
)}
</Accordion>
)}

View File

@@ -1,9 +1,11 @@
/* eslint-disable no-console */
import { Stack } from "@fluentui/react";
import { QueryCopilotSampleContainerId, QueryCopilotSampleDatabaseId } from "Common/Constants";
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
import { SaveQueryPane } from "Explorer/Panes/SaveQueryPane/SaveQueryPane";
import { QueryCopilotPromptbar } from "Explorer/QueryCopilot/QueryCopilotPromptbar";
import { readCopilotToggleStatus, saveCopilotToggleStatus } from "Explorer/QueryCopilot/QueryCopilotUtilities";
import { OnExecuteQueryClick } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
@@ -11,6 +13,7 @@ import { QueryCopilotResults } from "Explorer/QueryCopilot/Shared/QueryCopilotRe
import { userContext } from "UserContext";
import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
import { useSidePanel } from "hooks/useSidePanel";
import { ReactTabKind, TabsState, useTabs } from "hooks/useTabs";
import React, { useState } from "react";
import SplitterLayout from "react-splitter-layout";
import QueryCommandIcon from "../../../images/CopilotCommand.svg";
@@ -23,8 +26,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
const [copilotActive, setCopilotActive] = useState<boolean>(() =>
readCopilotToggleStatus(userContext.databaseAccount),
);
//TODO: Uncomment this useState when query copilot is reinstated in DE
// const [tabActive, setTabActive] = useState<boolean>(true);
const [tabActive, setTabActive] = useState<boolean>(true);
const getCommandbarButtons = (): CommandButtonComponentProps[] => {
const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query";
@@ -68,18 +70,17 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
useCommandBar.getState().setContextButtons(getCommandbarButtons());
}, [query, selectedQuery, copilotActive]);
//TODO: Uncomment this effect when query copilot is reinstated in DE
// React.useEffect(() => {
// return () => {
// useTabs.subscribe((state: TabsState) => {
// if (state.activeReactTab === ReactTabKind.QueryCopilot) {
// setTabActive(true);
// } else {
// setTabActive(false);
// }
// });
// };
// }, []);
React.useEffect(() => {
return () => {
useTabs.subscribe((state: TabsState) => {
if (state.activeReactTab === ReactTabKind.QueryCopilot) {
setTabActive(true);
} else {
setTabActive(false);
}
});
};
}, []);
const toggleCopilot = (toggle: boolean) => {
setCopilotActive(toggle);
@@ -89,7 +90,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
return (
<Stack className="tab-pane" style={{ width: "100%" }}>
<div style={isGeneratingQuery ? { height: "100%" } : { overflowY: "auto", height: "100%" }}>
{/*TODO: Uncomment this section when query copilot is reinstated in DE
{tabActive && copilotActive && (
<QueryCopilotPromptbar
explorer={explorer}
@@ -97,7 +97,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
databaseId={QueryCopilotSampleDatabaseId}
containerId={QueryCopilotSampleContainerId}
></QueryCopilotPromptbar>
)} */}
)}
<Stack className="tabPaneContentContainer">
<SplitterLayout percentage={true} vertical={true} primaryIndex={0} primaryMinSize={30} secondaryMinSize={70}>
<EditorReact

View File

@@ -15,9 +15,7 @@ import {
import { Add16Regular, ArrowSync12Regular, ChevronLeft12Regular, ChevronRight12Regular } from "@fluentui/react-icons";
import { GlobalSecondaryIndexLabels } from "Common/Constants";
import { isGlobalSecondaryIndexEnabled } from "Common/DatabaseAccountUtility";
import { sendMessage } from "Common/MessageHandler";
import { configContext, Platform } from "ConfigContext";
import { FabricMessageTypes } from "Contracts/FabricMessageTypes";
import Explorer from "Explorer/Explorer";
import { AddDatabasePanel } from "Explorer/Panes/AddDatabasePanel/AddDatabasePanel";
import {
@@ -173,19 +171,6 @@ const GlobalCommands: React.FC<GlobalCommandsProps> = ({ explorer }) => {
},
];
if (isFabricNative()) {
actions.push({
id: "new_fabric_shortcut",
label: "New Shortcut",
icon: <Add16Regular />,
onClick: () => {
// explorer.onNewFabricShortcutClicked({ shortcutId: "blah" });
// TODO: add telemetry
sendMessage({ type: FabricMessageTypes.NewShortcut, });
},
});
}
if (configContext.platform !== Platform.Fabric && userContext.apiType !== "Tables") {
actions.push({
id: "new_database",

View File

@@ -1,8 +1,8 @@
/**
* Accordion top class
*/
import { Link, makeStyles, tokens } from "@fluentui/react-components";
import { DocumentAddRegular, LinkMultipleRegular, OpenRegular } from "@fluentui/react-icons";
import { makeStyles, tokens } from "@fluentui/react-components";
import { DocumentAddRegular, LinkMultipleRegular } from "@fluentui/react-icons";
import { SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
import { isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
@@ -119,7 +119,7 @@ const FabricHomeScreenButton: React.FC<FabricHomeScreenButtonProps & { className
}) => {
const styles = useStyles();
return (
<div role="button" className={`${styles.buttonContainer} ${className}`} onClick={onClick} tabIndex={0}>
<div role="button" className={`${styles.buttonContainer} ${className}`} onClick={onClick}>
<div className={styles.buttonUpperPart}>{icon}</div>
<div aria-label={title} className={styles.buttonLowerPart}>
<div>{title}</div>
@@ -147,7 +147,7 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
{
title: "Sample data",
description: "Automatically load sample data in your database",
icon: <img src={CosmosDbBlackIcon} alt={"Azure Cosmos DB icon"} aria-hidden="true" />,
icon: <img src={CosmosDbBlackIcon} />,
onClick: () => setOpenSampleDataImportDialog(true),
},
{
@@ -181,18 +181,16 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
explorer={props.explorer}
databaseName={userContext.fabricContext?.databaseName}
/>
<div className={styles.title} role="heading" aria-label={title} aria-level={1}>
<div className={styles.title} role="heading" aria-label={title}>
{title}
</div>
{getSplashScreenButtons()}
{
<div className={styles.footer}>
Need help?{" "}
<Link href="https://learn.microsoft.com/fabric/database/cosmos-db/overview" target="_blank">
Learn more <OpenRegular />
</Link>
</div>
}
{/* <div className={styles.footer}>
Need help?{" "}
<Link href="https://aka.ms/cosmosdbfabricdocs" target="_blank">
Learn more <img src={LinkIcon} alt="Learn more" />
</Link>
</div> */}
</CosmosFluentProvider>
</>
);

View File

@@ -24,7 +24,6 @@ import { ReactTabKind, useTabs } from "hooks/useTabs";
import * as React from "react";
import ConnectIcon from "../../../images/Connect_color.svg";
import ContainersIcon from "../../../images/Containers.svg";
import CosmosDBIcon from "../../../images/CosmosDB-logo.svg";
import LinkIcon from "../../../images/Link_blue.svg";
import PowerShellIcon from "../../../images/PowerShell.svg";
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
@@ -121,7 +120,11 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
};
private getSplashScreenButtons = (): JSX.Element => {
if (userContext.apiType === "SQL") {
if (
userContext.apiType === "SQL" &&
useQueryCopilot.getState().copilotEnabled &&
useDatabases.getState().sampleDataResourceTokenCollection
) {
return (
<Stack
className="splashStackContainer"
@@ -149,18 +152,25 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
/>
</Stack>
<Stack className="splashStackRow" horizontal>
<SplashScreenButton
imgSrc={CosmosDBIcon}
imgSize={35}
title={"Azure Cosmos DB Samples Gallery"}
description={
"Discover samples that showcase scalable, intelligent app patterns. Try one now to see how fast you can go from concept to code with Cosmos DB"
}
onClick={() => {
window.open("https://azurecosmosdb.github.io/gallery/?tags=example", "_blank");
traceOpen(Action.LearningResourcesClicked, { apiType: userContext.apiType });
}}
/>
{useQueryCopilot.getState().copilotEnabled && (
<SplashScreenButton
imgSrc={CopilotIcon}
title={"Query faster with Query Advisor"}
description={
"Query Advisor is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
}
onClick={() => {
const copilotVersion = userContext.features.copilotVersion;
if (copilotVersion === "v1.0") {
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
} else if (copilotVersion === "v2.0") {
const sampleCollection = useDatabases.getState().sampleDataResourceTokenCollection;
sampleCollection.onNewQueryClick(sampleCollection, undefined);
}
traceOpen(Action.OpenQueryCopilotFromSplashScreen, { apiType: userContext.apiType });
}}
/>
)}
<SplashScreenButton
imgSrc={ConnectIcon}
title={"Connect"}
@@ -202,7 +212,6 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
sample data, query.
</TeachingBubble>
)}
{/*TODO: convert below to use SplashScreenButton */}
{mainItems.map((item) => (
<Stack
id={`mainButton-${item.id}`}
@@ -468,34 +477,6 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
};
}
//TODO: Re-enable lint rule when query copilot is reinstated in DE
/* eslint-disable-next-line no-unused-vars */
private getQueryCopilotCard = (): JSX.Element => {
return (
<>
{useQueryCopilot.getState().copilotEnabled && (
<SplashScreenButton
imgSrc={CopilotIcon}
title={"Query faster with Query Advisor"}
description={
"Query Advisor is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
}
onClick={() => {
const copilotVersion = userContext.features.copilotVersion;
if (copilotVersion === "v1.0") {
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
} else if (copilotVersion === "v2.0") {
const sampleCollection = useDatabases.getState().sampleDataResourceTokenCollection;
sampleCollection.onNewQueryClick(sampleCollection, undefined);
}
traceOpen(Action.OpenQueryCopilotFromSplashScreen, { apiType: userContext.apiType });
}}
/>
)}
</>
);
};
private decorateOpenCollectionActivity({ databaseId, collectionId }: MostRecentActivity.OpenCollectionItem) {
return {
iconSrc: CollectionIcon,

View File

@@ -7,7 +7,6 @@ interface SplashScreenButtonProps {
title: string;
description: string;
onClick: () => void;
imgSize?: number;
}
export const SplashScreenButton: React.FC<SplashScreenButtonProps> = ({
@@ -15,7 +14,6 @@ export const SplashScreenButton: React.FC<SplashScreenButtonProps> = ({
title,
description,
onClick,
imgSize,
}: SplashScreenButtonProps): JSX.Element => {
return (
<Stack
@@ -41,7 +39,7 @@ export const SplashScreenButton: React.FC<SplashScreenButtonProps> = ({
role="button"
>
<div>
<img src={imgSrc} alt={title} aria-hidden="true" {...(imgSize ? { height: imgSize, width: imgSize } : {})} />
<img src={imgSrc} alt={title} aria-hidden="true" />
</div>
<Stack style={{ marginLeft: 16 }}>
<Text style={{ fontSize: 18, fontWeight: 600 }}>{title}</Text>

View File

@@ -22,7 +22,6 @@ import { formatErrorMessage, formatInfoMessage, formatWarningMessage } from "./U
// Constants
const DEFAULT_CLOUDSHELL_REGION = "westus";
const DEFAULT_FAIRFAX_CLOUDSHELL_REGION = "usgovvirginia";
const POLLING_INTERVAL_MS = 2000;
const MAX_RETRY_COUNT = 10;
const MAX_PING_COUNT = 120 * 60; // 120 minutes (60 seconds/minute)
@@ -154,9 +153,7 @@ export const ensureCloudShellProviderRegistered = async (): Promise<void> => {
* Determines the appropriate CloudShell region
*/
export const determineCloudShellRegion = (): string => {
const defaultRegion =
userContext.portalEnv === "fairfax" ? DEFAULT_FAIRFAX_CLOUDSHELL_REGION : DEFAULT_CLOUDSHELL_REGION;
return getNormalizedRegion(userContext.databaseAccount?.location, defaultRegion);
return getNormalizedRegion(userContext.databaseAccount?.location, DEFAULT_CLOUDSHELL_REGION);
};
/**

View File

@@ -33,7 +33,6 @@ jest.mock("../../../../UserContext", () => ({
}));
jest.mock("../Utils/CommonUtils", () => ({
...jest.requireActual("../Utils/CommonUtils"),
getHostFromUrl: jest.fn().mockReturnValue("test-mongo.documents.azure.com"),
}));
@@ -125,10 +124,7 @@ describe("MongoShellHandler", () => {
describe("getTerminalSuppressedData", () => {
it("should return the correct warning message", () => {
expect(mongoShellHandler.getTerminalSuppressedData()).toEqual([
"Warning: Non-Genuine MongoDB Detected",
"Telemetry is now disabled.",
]);
expect(mongoShellHandler.getTerminalSuppressedData()).toEqual(["Warning: Non-Genuine MongoDB Detected"]);
});
});
});

View File

@@ -1,11 +1,10 @@
import { userContext } from "../../../../UserContext";
import { filterAndCleanTerminalOutput, getHostFromUrl, getMongoShellRemoveInfoText } from "../Utils/CommonUtils";
import { getHostFromUrl } from "../Utils/CommonUtils";
import { AbstractShellHandler, DISABLE_TELEMETRY_COMMAND } from "./AbstractShellHandler";
export class MongoShellHandler extends AbstractShellHandler {
private _key: string;
private _endpoint: string | undefined;
private _removeInfoText: string[] = getMongoShellRemoveInfoText();
constructor(private key: string) {
super();
this._key = key;
@@ -45,10 +44,6 @@ export class MongoShellHandler extends AbstractShellHandler {
}
public getTerminalSuppressedData(): string[] {
return ["Warning: Non-Genuine MongoDB Detected", "Telemetry is now disabled."];
}
updateTerminalData(data: string): string {
return filterAndCleanTerminalOutput(data, this._removeInfoText);
return ["Warning: Non-Genuine MongoDB Detected"];
}
}

View File

@@ -1,10 +1,13 @@
import { userContext } from "../../../../UserContext";
import { filterAndCleanTerminalOutput, getMongoShellRemoveInfoText } from "../Utils/CommonUtils";
import { AbstractShellHandler, DISABLE_TELEMETRY_COMMAND } from "./AbstractShellHandler";
export class VCoreMongoShellHandler extends AbstractShellHandler {
private _endpoint: string | undefined;
private _removeInfoText: string[] = getMongoShellRemoveInfoText();
private _textFilterRules: string[] = [
"For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/",
"disableTelemetry() command",
"https://www.mongodb.com/legal/privacy-policy",
];
constructor() {
super();
@@ -35,7 +38,12 @@ export class VCoreMongoShellHandler extends AbstractShellHandler {
return ["Warning: Non-Genuine MongoDB Detected", "Telemetry is now disabled."];
}
updateTerminalData(data: string): string {
return filterAndCleanTerminalOutput(data, this._removeInfoText);
updateTerminalData(content: string): string {
const updatedContent = content
.split("\n")
.filter((line) => !this._textFilterRules.some((part) => line.includes(part)))
.filter((line, idx, arr) => (arr.length > 3 && idx <= arr.length - 3 ? !["", "\r"].includes(line) : true)) // Filter out empty lines and carriage returns, but keep the last 3 lines if they exist
.join("\n");
return updatedContent;
}
}

View File

@@ -135,11 +135,7 @@ export class AttachAddon implements ITerminalAddon {
}
if (this._allowTerminalWrite) {
const updatedData =
typeof this._shellHandler?.updateTerminalData === "function"
? this._shellHandler.updateTerminalData(data)
: data;
const updatedData = this._shellHandler?.updateTerminalData(data) ?? data;
const suppressedData = this._shellHandler?.getTerminalSuppressedData();
const shouldNotWrite = suppressedData.filter(Boolean).some((item) => updatedData.includes(item));

View File

@@ -50,34 +50,3 @@ export const getShellNameForDisplay = (terminalKind: TerminalKind): string => {
return "";
}
};
/**
* Get MongoDB shell information text that should be removed from terminal output
*/
export const getMongoShellRemoveInfoText = (): string[] => {
return [
"For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/",
"disableTelemetry() command",
"https://www.mongodb.com/legal/privacy-policy",
];
};
export const filterAndCleanTerminalOutput = (data: string, removeInfoText: string[]): string => {
if (!data || removeInfoText.length === 0) {
return data;
}
const lines = data.split("\n");
const filteredLines: string[] = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const shouldRemove = removeInfoText.some((text) => line.includes(text));
if (!shouldRemove) {
filteredLines.push(line);
}
}
return filteredLines.join("\n").replace(/((\r\n)|\n|\r){2,}/g, "\r\n");
};

View File

@@ -8,8 +8,6 @@ const validCloudShellRegions = new Set([
"centralindia",
"southeastasia",
"westcentralus",
"usgovvirginia",
"usgovarizona",
]);
/**

View File

@@ -106,6 +106,6 @@ describe("QueryTabComponent", () => {
<QueryTabCopilotComponent {...propsMock} />
</CopilotProvider>,
);
expect(container.find(QueryCopilotPromptbar).exists()).toBe(false);
expect(container.find(QueryCopilotPromptbar).exists()).toBe(true);
});
});

View File

@@ -9,6 +9,7 @@ import { useDialog } from "Explorer/Controls/Dialog";
import { monaco } from "Explorer/LazyMonaco";
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
import { useCopilotStore } from "Explorer/QueryCopilot/QueryCopilotContext";
import { QueryCopilotPromptbar } from "Explorer/QueryCopilot/QueryCopilotPromptbar";
import { readCopilotToggleStatus, saveCopilotToggleStatus } from "Explorer/QueryCopilot/QueryCopilotUtilities";
import { OnExecuteQueryClick, QueryDocumentsPerPage } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { QueryCopilotSidebar } from "Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar";
@@ -27,9 +28,8 @@ import { TabsState, useTabs } from "hooks/useTabs";
import React, { Fragment, createRef } from "react";
import "react-splitter-layout/lib/index.css";
import { format } from "react-string-format";
//TODO: Uncomment next two lines when query copilot is reinstated in DE
// import QueryCommandIcon from "../../../../images/CopilotCommand.svg";
// import LaunchCopilot from "../../../../images/CopilotTabIcon.svg";
import QueryCommandIcon from "../../../../images/CopilotCommand.svg";
import LaunchCopilot from "../../../../images/CopilotTabIcon.svg";
import DownloadQueryIcon from "../../../../images/DownloadQuery.svg";
import CancelQueryIcon from "../../../../images/Entity_cancel.svg";
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
@@ -494,55 +494,53 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
});
}
//TODO: Uncomment next section when query copilot is reinstated in DE
// if (this.launchCopilotButton.visible && this.isCopilotTabActive) {
// const mainButtonLabel = "Launch Copilot";
// const chatPaneLabel = "Open Copilot in chat pane (ALT+C)";
// const copilotSettingLabel = "Copilot settings";
if (this.launchCopilotButton.visible && this.isCopilotTabActive) {
const mainButtonLabel = "Launch Copilot";
const chatPaneLabel = "Open Copilot in chat pane (ALT+C)";
const copilotSettingLabel = "Copilot settings";
// const openCopilotChatButton: CommandButtonComponentProps = {
// iconAlt: chatPaneLabel,
// onCommandClick: this.launchQueryCopilotChat,
// commandButtonLabel: chatPaneLabel,
// ariaLabel: chatPaneLabel,
// hasPopup: false,
// };
const openCopilotChatButton: CommandButtonComponentProps = {
iconAlt: chatPaneLabel,
onCommandClick: this.launchQueryCopilotChat,
commandButtonLabel: chatPaneLabel,
ariaLabel: chatPaneLabel,
hasPopup: false,
};
// const copilotSettingsButton: CommandButtonComponentProps = {
// iconAlt: copilotSettingLabel,
// onCommandClick: () => undefined,
// commandButtonLabel: copilotSettingLabel,
// ariaLabel: copilotSettingLabel,
// hasPopup: false,
// };
const copilotSettingsButton: CommandButtonComponentProps = {
iconAlt: copilotSettingLabel,
onCommandClick: () => undefined,
commandButtonLabel: copilotSettingLabel,
ariaLabel: copilotSettingLabel,
hasPopup: false,
};
// const launchCopilotButton: CommandButtonComponentProps = {
// iconSrc: LaunchCopilot,
// iconAlt: mainButtonLabel,
// onCommandClick: this.launchQueryCopilotChat,
// commandButtonLabel: mainButtonLabel,
// ariaLabel: mainButtonLabel,
// hasPopup: false,
// children: [openCopilotChatButton, copilotSettingsButton],
// };
// buttons.push(launchCopilotButton);
// }
const launchCopilotButton: CommandButtonComponentProps = {
iconSrc: LaunchCopilot,
iconAlt: mainButtonLabel,
onCommandClick: this.launchQueryCopilotChat,
commandButtonLabel: mainButtonLabel,
ariaLabel: mainButtonLabel,
hasPopup: false,
children: [openCopilotChatButton, copilotSettingsButton],
};
buttons.push(launchCopilotButton);
}
//TODO: Uncomment next section when query copilot is reinstated in DE
// if (this.props.copilotEnabled) {
// const toggleCopilotButton: CommandButtonComponentProps = {
// iconSrc: QueryCommandIcon,
// iconAlt: "Query Advisor",
// keyboardAction: KeyboardAction.TOGGLE_COPILOT,
// onCommandClick: () => {
// this._toggleCopilot(!this.state.copilotActive);
// },
// commandButtonLabel: this.state.copilotActive ? "Disable Query Advisor" : "Enable Query Advisor",
// ariaLabel: this.state.copilotActive ? "Disable Query Advisor" : "Enable Query Advisor",
// hasPopup: false,
// };
// buttons.push(toggleCopilotButton);
// }
if (this.props.copilotEnabled) {
const toggleCopilotButton: CommandButtonComponentProps = {
iconSrc: QueryCommandIcon,
iconAlt: "Query Advisor",
keyboardAction: KeyboardAction.TOGGLE_COPILOT,
onCommandClick: () => {
this._toggleCopilot(!this.state.copilotActive);
},
commandButtonLabel: this.state.copilotActive ? "Disable Query Advisor" : "Enable Query Advisor",
ariaLabel: this.state.copilotActive ? "Disable Query Advisor" : "Enable Query Advisor",
hasPopup: false,
};
buttons.push(toggleCopilotButton);
}
if (!this.props.isPreferredApiMongoDB && this.state.isExecuting) {
const label = "Cancel query";
@@ -727,7 +725,6 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
return (
<Fragment>
<CosmosFluentProvider id={this.props.tabId} className={this.props.styles.queryTab} role="tabpanel">
{/*TODO: Uncomment this section when query copilot is reinstated in DE
{this.props.copilotEnabled && this.state.currentTabActive && this.state.copilotActive && (
<QueryCopilotPromptbar
explorer={this.props.collection.container}
@@ -735,7 +732,7 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
databaseId={this.props.collection.databaseId}
containerId={this.props.collection.id()}
></QueryCopilotPromptbar>
)} */}
)}
{/* Set 'key' to the value of vertical to force re-rendering when vertical changes, to work around https://github.com/johnwalley/allotment/issues/457 */}
<Allotment
key={vertical.toString()}

View File

@@ -21,7 +21,7 @@ import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
import { useSelectedNode } from "../useSelectedNode";
export const shouldShowScriptNodes = (): boolean => {
return !isFabric() && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
return !isFabric() && configContext.platform !== Platform.Emulator && (userContext.apiType === "SQL" || userContext.apiType === "Gremlin");
};
const TreeDatabaseIcon = <DatabaseRegular fontSize={16} />;

View File

@@ -1,4 +1,3 @@
import { MongoGuidRepresentation } from "Common/Constants";
import { SplitterDirection } from "Common/Splitter";
import * as LocalStorageUtility from "./LocalStorageUtility";
import * as SessionStorageUtility from "./SessionStorageUtility";
@@ -34,7 +33,6 @@ export enum StorageKey {
DocumentsTabPrefs,
DefaultQueryResultsView,
AppState,
MongoGuidRepresentation,
}
export const hasRUThresholdBeenConfigured = (): boolean => {
@@ -67,13 +65,4 @@ export const getDefaultQueryResultsView = (): SplitterDirection => {
return SplitterDirection.Horizontal;
};
export const getMongoGuidRepresentation = (): MongoGuidRepresentation => {
const mongoGuidRepresentation: string | null = LocalStorageUtility.getEntryString(StorageKey.MongoGuidRepresentation);
if (mongoGuidRepresentation) {
return mongoGuidRepresentation as MongoGuidRepresentation;
}
return MongoGuidRepresentation.CSharpLegacy;
};
export const DefaultRUThreshold = 5000;

View File

@@ -79,7 +79,22 @@ export const defaultAllowedCassandraProxyEndpoints: ReadonlyArray<string> = [
CassandraProxyEndpoints.Mooncake,
];
export const allowedEmulatorEndpoints: ReadonlyArray<string> = ["https://localhost:8081"];
export const allowedCassandraProxyEndpoints_ToBeDeprecated: ReadonlyArray<string> = [
"https://main.documentdb.ext.azure.com",
"https://main.documentdb.ext.azure.cn",
"https://main.documentdb.ext.azure.us",
"https://main.cosmos.ext.azure",
"https://localhost:12901",
];
export const CassandraProxyOutboundIPs: { [key: string]: string[] } = {
[CassandraProxyEndpoints.Mpac]: ["40.113.96.14", "104.42.11.145"],
[CassandraProxyEndpoints.Prod]: ["137.117.230.240", "168.61.72.237"],
[CassandraProxyEndpoints.Fairfax]: ["52.244.50.101", "52.227.165.24"],
[CassandraProxyEndpoints.Mooncake]: ["40.73.99.146", "143.64.62.47"],
};
export const allowedEmulatorEndpoints: ReadonlyArray<string> = ["https://localhost:8081", "http://localhost:8081"];
export const allowedArcadiaEndpoints: ReadonlyArray<string> = ["https://workspaceartifacts.projectarcadia.net"];

View File

@@ -0,0 +1,112 @@
import { Platform, configContext } from "../ConfigContext";
/**
* Feature flags enumeration - centralized feature definitions
*/
export enum PlatformFeature {
// UI/Core Features
Queries = "Queries",
Notebooks = "Notebooks",
SynapseLink = "SynapseLink",
VSCodeIntegration = "VSCodeIntegration",
GlobalSecondaryIndex = "GlobalSecondaryIndex",
DataPlaneRbac = "DataPlaneRbac",
EntraIDLogin = "EntraIDLogin",
EntreIDRbac = "EntreIDRbac",
RetrySettings = "RetrySettings",
GraphAutoVizOption = "GraphAutoVizOption",
CrossPartitionOption = "CrossPartitionOption",
EnhancedQueryControl = "EnhancedQueryControl",
ParallelismOption = "ParallelismOption",
EnableEntraIdRbac = "EnableEntraIdRbac",
PriorityBasedExecution = "PriorityBasedExecution",
RegionSelection = "RegionSelection",
Copilot = "Copilot",
CloudShell = "CloudShell",
ContainerPagination = "ContainerPagination",
FullTextSearch = "FullTextSearch",
VectorSearch = "VectorSearch",
ThroughputBucketing = "ThroughputBucketing",
ComputedProperties = "ComputedProperties",
AnalyticalStore = "AnalyticalStore",
// CRUD Operations - Database
CreateDatabase = "CreateDatabase",
ReadDatabase = "ReadDatabase",
DeleteDatabase = "DeleteDatabase",
// CRUD Operations - Collection
CreateCollection = "CreateCollection",
ReadCollection = "ReadCollection",
UpdateCollection = "UpdateCollection",
DeleteCollection = "DeleteCollection",
// CRUD Operations - Document
CreateDocument = "CreateDocument",
ReadDocument = "ReadDocument",
UpdateDocument = "UpdateDocument",
DeleteDocument = "DeleteDocument",
// Advanced Database Features
StoredProcedures = "StoredProcedures",
UDF = "UDF",
Trigger = "Trigger",
}
/**
* Feature matrix per platform.
* - Only list platforms that have restrictions. If a platform is not present, all features are considered supported.
* - Start with VNextEmulator today; add more platforms/flags here later without touching calling code.
*/
const FEATURE_MATRIX: ReadonlyMap<Platform, ReadonlySet<PlatformFeature>> = new Map([
[
Platform.VNextEmulator,
new Set<PlatformFeature>([
PlatformFeature.Queries,
PlatformFeature.CreateDatabase,
PlatformFeature.ReadDatabase,
PlatformFeature.DeleteDatabase,
PlatformFeature.CreateCollection,
PlatformFeature.ReadCollection,
PlatformFeature.UpdateCollection,
PlatformFeature.DeleteCollection,
PlatformFeature.CreateDocument,
PlatformFeature.ReadDocument,
PlatformFeature.UpdateDocument,
PlatformFeature.DeleteDocument,
]),
],
]);
/**
* Central feature flag function - checks if a feature is enabled for current platform
* @param feature The feature to check
* @param platform Optional platform override, defaults to current platform
* @returns True if the feature is enabled for the platform, false otherwise
*/
export const isFeatureSupported = (feature: PlatformFeature, platform?: Platform): boolean => {
const currentPlatform = platform ?? configContext.platform;
if (currentPlatform !== Platform.VNextEmulator) {
return true;
}
// VNextEmulator: check from the feature matrix
const vnextFeatures = FEATURE_MATRIX.get(Platform.VNextEmulator);
return vnextFeatures?.has(feature) ?? false;
};
export const areAdvancedScriptsSupported = (platform?: Platform): boolean => {
const currentPlatform = platform ?? configContext.platform;
if (currentPlatform !== Platform.VNextEmulator) {
return true;
}
// Otherwise, require all script features to be enabled
return (
isFeatureSupported(PlatformFeature.StoredProcedures, currentPlatform) &&
isFeatureSupported(PlatformFeature.UDF, currentPlatform) &&
isFeatureSupported(PlatformFeature.Trigger, currentPlatform)
);
};

View File

@@ -86,7 +86,7 @@ export function useKnockoutExplorer(platform: Platform): Explorer {
let explorer: Explorer;
if (platform === Platform.Hosted) {
explorer = await configureHosted();
} else if (platform === Platform.Emulator) {
} else if (platform === Platform.Emulator || platform === Platform.VNextEmulator) {
explorer = configureEmulator();
} else if (platform === Platform.Portal) {
explorer = await configurePortal();
@@ -220,10 +220,6 @@ async function configureFabric(): Promise<Explorer> {
explorer.onRefreshResourcesClick();
break;
}
case "shortcutCreated": {
explorer.onNewFabricShortcutClicked({ shortcutId: data.message.shortcutId });
break;
}
default:
console.error(`Unknown Fabric message type: ${JSON.stringify(data)}`);
break;

View File

@@ -0,0 +1,291 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0" />
<title>Azure Cosmos DB Emulator</title>
</head>
<body>
<div id="divQuickStart">
<div class="Introlines">
<p class="Introline1">Congratulations! Your Azure Cosmos DB emulator is running.</p>
<p class="Introline2">Now, let's connect a sample app to it.</p>
<div id="divQuickStartConnections">
<p class="Introline2">URI</p>
<input type="text" class="codeblock" readonly="readonly" value="http://localhost:8081" />
<p class="Introline2">Primary Key</p>
<input
type="text"
class="codeblock"
readonly="readonly"
value="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
/>
<p class="Introline2">Primary Connection String</p>
<input
type="text"
class="codeblock"
readonly="readonly"
value="AccountEndpoint=http://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
/>
</div>
<p class="Introline3"><b>Choose a platform</b></p>
</div>
<div class="container-fluid">
<ul class="nav nav-tabs qslevel">
<li class="active">
<a data-toggle="tab" href="#net"
><img class="qsmenuicons" src="../images/dotnet.png" alt=".NET platform" />.NET</a
>
</li>
<li>
<a data-toggle="tab" href="#corenet"
><img class="qsmenuicons" src="../images/dotnet.png" alt=".NET Core platform" />.NET Core</a
>
</li>
<li>
<a data-toggle="tab" href="#Java"
><img class="qsmenuicons" src="../images/java.png" alt="Java platform" />Java</a
>
</li>
<li>
<a data-toggle="tab" href="#NodeJs"
><img class="qsmenuicons" src="../images/nodejs.png" alt="Node.js platform" />Node.js</a
>
</li>
<li>
<a data-toggle="tab" href="#Python"
><img class="qsmenuicons" src="../images/python.png" alt="Python platform" />Python</a
>
</li>
</ul>
<div class="tab-content tab-content-override">
<div id="net" class="tab-pane fade in active">
<div class="netApp">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Open and run a sample .NET app
<p>
We created a sample .NET app connected to your Azure Cosmos DB Emulator instance. Download, extract,
build and run the app.
</p>
<a href="quickstart/DocumentDB-Quickstart-DotNet.zip"
><button class="btncreatecoll">Download</button></a
>
</div>
</div>
<div class="netApp">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB
<ul>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/dotnet"
>Code Samples</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity Planner</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
</li>
</ul>
</div>
</div>
</div>
<div id="corenet" class="tab-pane fade">
<div class="netApp">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Open and run a sample .NET Core app
<p>
We created a sample .NET Core app connected to your Azure Cosmos DB Emulator instance. Download,
extract, build and run the app.
</p>
<a href="quickstart/DocumentDB-Quickstart-DotNetCore.zip"
><button class="btncreatecoll">Download</button></a
>
</div>
</div>
<div class="netApp">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB.
<ul>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/dotnet"
>Code Samples</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity Planner</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
</li>
</ul>
</div>
</div>
</div>
<div id="Java" class="tab-pane fade">
<div class="step1">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Open and run a sample Java app
<p>
We created a sample Java app connected to your Azure Cosmos DB Emulator instance. Download, extract,
build and run the app.
</p>
<a href="quickstart/DocumentDB-Quickstart-Java.zip"><button class="btncreatecoll">Download</button></a>
<p>
Follow instructions in the readme.md to setup prerequisites needed to run Java web apps, if you
havent already.
</p>
</div>
</div>
<div class="step1">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB.
<ul>
<!--<li><a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/java">Code Samples</a></li>-->
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity Planner</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
</li>
</ul>
</div>
</div>
</div>
<div id="NodeJs" class="tab-pane fade">
<div class="step1">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Open and run a sample Node.js app
<p>
We created a sample Node.js app connected to your Azure Cosmos DB Emulator instance. Download,
extract, build and run the app.
</p>
<a href="quickstart/DocumentDB-Quickstart-NodeJs.zip"
><button class="btncreatecoll">Download</button></a
>
<p>
Run <strong>npm install</strong> and <strong>npm start</strong>, and navigate to
<a href="http://localhost:3000" _targe="blank">http://localhost:3000</a>.
</p>
</div>
</div>
<div class="step1">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB.
<ul>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/nodejs"
>Code Samples</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity Planner</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
</li>
</ul>
</div>
</div>
</div>
<div id="Python" class="tab-pane fade">
<div class="pythonApp">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Create a new Python app.
<p>
Follow this
<a href="https://aka.ms/cosmos-db-emulator/tutorial/python" target="_blank">tutorial</a>
to create a new Python app connected to Azure Cosmos DB.
</p>
</div>
</div>
<div class="pythonApp">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB.
<ul>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/python"
>Code Samples</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity planner</a
>
</li>
<li>
<a
target="_blank"
class="atags"
href="https://social.msdn.microsoft.com/forums/azure/home?forum=AzureDocumentDB"
>Forum</a
>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,291 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0" />
<title>Azure Cosmos DB Emulator</title>
</head>
<body>
<div id="divQuickStart">
<div class="Introlines">
<p class="Introline1">Congratulations! Your Azure Cosmos DB emulator is running.</p>
<p class="Introline2">Now, let's connect a sample app to it.</p>
<div id="divQuickStartConnections">
<p class="Introline2">URI</p>
<input type="text" class="codeblock" readonly="readonly" value="https://localhost:8081" />
<p class="Introline2">Primary Key</p>
<input
type="text"
class="codeblock"
readonly="readonly"
value="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
/>
<p class="Introline2">Primary Connection String</p>
<input
type="text"
class="codeblock"
readonly="readonly"
value="AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
/>
</div>
<p class="Introline3"><b>Choose a platform</b></p>
</div>
<div class="container-fluid">
<ul class="nav nav-tabs qslevel">
<li class="active">
<a data-toggle="tab" href="#net"
><img class="qsmenuicons" src="../images/dotnet.png" alt=".NET platform" />.NET</a
>
</li>
<li>
<a data-toggle="tab" href="#corenet"
><img class="qsmenuicons" src="../images/dotnet.png" alt=".NET Core platform" />.NET Core</a
>
</li>
<li>
<a data-toggle="tab" href="#Java"
><img class="qsmenuicons" src="../images/java.png" alt="Java platform" />Java</a
>
</li>
<li>
<a data-toggle="tab" href="#NodeJs"
><img class="qsmenuicons" src="../images/nodejs.png" alt="Node.js platform" />Node.js</a
>
</li>
<li>
<a data-toggle="tab" href="#Python"
><img class="qsmenuicons" src="../images/python.png" alt="Python platform" />Python</a
>
</li>
</ul>
<div class="tab-content tab-content-override">
<div id="net" class="tab-pane fade in active">
<div class="netApp">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Open and run a sample .NET app
<p>
We created a sample .NET app connected to your Azure Cosmos DB Emulator instance. Download, extract,
build and run the app.
</p>
<a href="quickstart/DocumentDB-Quickstart-DotNet.zip"
><button class="btncreatecoll">Download</button></a
>
</div>
</div>
<div class="netApp">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB
<ul>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/dotnet"
>Code Samples</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity Planner</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
</li>
</ul>
</div>
</div>
</div>
<div id="corenet" class="tab-pane fade">
<div class="netApp">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Open and run a sample .NET Core app
<p>
We created a sample .NET Core app connected to your Azure Cosmos DB Emulator instance. Download,
extract, build and run the app.
</p>
<a href="quickstart/DocumentDB-Quickstart-DotNetCore.zip"
><button class="btncreatecoll">Download</button></a
>
</div>
</div>
<div class="netApp">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB.
<ul>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/dotnet"
>Code Samples</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity Planner</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
</li>
</ul>
</div>
</div>
</div>
<div id="Java" class="tab-pane fade">
<div class="step1">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Open and run a sample Java app
<p>
We created a sample Java app connected to your Azure Cosmos DB Emulator instance. Download, extract,
build and run the app.
</p>
<a href="quickstart/DocumentDB-Quickstart-Java.zip"><button class="btncreatecoll">Download</button></a>
<p>
Follow instructions in the readme.md to setup prerequisites needed to run Java web apps, if you
havent already.
</p>
</div>
</div>
<div class="step1">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB.
<ul>
<!--<li><a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/java">Code Samples</a></li>-->
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity Planner</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
</li>
</ul>
</div>
</div>
</div>
<div id="NodeJs" class="tab-pane fade">
<div class="step1">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Open and run a sample Node.js app
<p>
We created a sample Node.js app connected to your Azure Cosmos DB Emulator instance. Download,
extract, build and run the app.
</p>
<a href="quickstart/DocumentDB-Quickstart-NodeJs.zip"
><button class="btncreatecoll">Download</button></a
>
<p>
Run <strong>npm install</strong> and <strong>npm start</strong>, and navigate to
<a href="http://localhost:3000" _targe="blank">http://localhost:3000</a>.
</p>
</div>
</div>
<div class="step1">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB.
<ul>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/nodejs"
>Code Samples</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity Planner</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/stackoverflow">Forum</a>
</li>
</ul>
</div>
</div>
</div>
<div id="Python" class="tab-pane fade">
<div class="pythonApp">
<div class="numbersize numbersizePadding">1</div>
<div class="numberheading">
Create a new Python app.
<p>
Follow this
<a href="https://aka.ms/cosmos-db-emulator/tutorial/python" target="_blank">tutorial</a>
to create a new Python app connected to Azure Cosmos DB.
</p>
</div>
</div>
<div class="pythonApp">
<div class="numbersize">2</div>
<div class="numberheading">
Learn more about Azure Cosmos DB.
<ul>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/samples/python"
>Code Samples</a
>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/docs">Documentation</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/pricing">Pricing</a>
</li>
<li>
<a target="_blank" class="atags" href="https://aka.ms/cosmos-db-emulator/capacity-planner"
>Capacity planner</a
>
</li>
<li>
<a
target="_blank"
class="atags"
href="https://social.msdn.microsoft.com/forums/azure/home?forum=AzureDocumentDB"
>Forum</a
>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -15,18 +15,12 @@
"target": "es2017",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"lib": [
"es5",
"es6",
"dom"
],
"lib": ["es5", "es6", "dom"],
"jsx": "react",
"moduleResolution": "node",
"resolveJsonModule": true,
"noEmit": true,
"types": [
"jest"
],
"types": ["jest"],
"baseUrl": "src"
},
"typedocOptions": {
@@ -43,17 +37,11 @@
"includes": "./src/SelfServe/Documentation",
"disableSources": true
},
"include": [
"src",
"./src/**/*",
"./utils/**/*"
],
"exclude": [
"./src/**/__mocks__/**/*"
],
"include": ["src", "./src/**/*", "./utils/**/*"],
"exclude": ["./src/**/__mocks__/**/*", "./utils/local-proxy/**/*"],
"ts-node": {
"compilerOptions": {
"module": "CommonJS"
}
}
}
}

6
utils/local-proxy/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
dist*
node_modules
*.cert
*.key
*.pfx
*.log

177
utils/local-proxy/index.js Normal file
View File

@@ -0,0 +1,177 @@
const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware");
const { inspect } = require("util");
const fs = require("fs");
const https = require("https");
const conf = {};
conf.PORT = process.env.EXPLORER_PORT || 1234;
conf.LOG_LEVEL = process.env.LOG_LEVEL || "info";
conf.EMULATOR_ENDPOINT = process.env.EMULATOR_ENDPOINT || "http://localhost:8081";
conf.ENDPOINT_DISCOVERY_ENABLED = (process.env.ENDPOINT_DISCOVERY_ENABLED || "false").toLowerCase() === "true";
conf.GATEWAY_TLS_ENABLED = (process.env.GATEWAY_TLS_ENABLED || "false").toLowerCase() === "true";
conf.CERT_PATH = process.env.CERT_PATH;
conf.CERT_SECRET = process.env.CERT_SECRET;
const LOG_NUM = levelToNumber(conf.LOG_LEVEL);
function _log(level, msg, color) {
if (levelToNumber(level) >= LOG_NUM) {
console.log(`${colorToCode(color)}[${level || "debug"}]${msg}\x1b[0m`);
}
}
function _debug(msg, color) {
_log("debug", msg, color);
}
function _info(msg, color) {
_log("info", msg, color);
}
function _warn(msg, color) {
_log("warn", msg, color || "yellow");
}
function _err(msg, color) {
_log("error", msg, color || "red");
}
function levelToNumber(level) {
switch (level) {
case "debug":
return 0;
case "info":
return 1;
case "warn":
return 2;
case "error":
return 3;
default:
return 0;
}
}
function colorToCode(color) {
switch (color) {
case "red":
return "\x1b[31m";
case "green":
return "\x1b[32m";
case "blue":
return "\x1b[34m";
case "yellow":
return "\x1b[33m";
default:
return "\x1b[0m";
}
}
function statusToColor(status) {
if (status < 300) {
return "green";
} else if (status < 400) {
return "blue";
} else {
return "red";
}
}
const testEndpoint = () => {
fetch(conf.EMULATOR_ENDPOINT)
.then(async (res) => {
const body = await res.json();
_info("[EMU] Emulator is accessible");
})
.catch((e) => {
_warn("[EMU] Emulator is not accessible");
_warn(`[EMU] ${inspect(e)}`);
});
};
testEndpoint();
const app = express();
app.use((e, req, res, next) => {
_err(`[APP] ${inspect(e)}`);
res.status(500).json({ error: _err.message });
});
app.use((req, res, next) => {
req.startTime = new Date();
res.append("Access-Control-Allow-Origin", "*");
res.append("Access-Control-Allow-Credentials", "true");
res.append("Access-Control-Max-Age", "3600");
res.append("Access-Control-Allow-Headers", "*");
res.append("Access-Control-Allow-Methods", "*");
res.once("finish", () => {
const ms = new Date() - req.startTime;
(res.statusCode < 400 ? _debug : _err)(
`[APP] ${req.method} ${req.url} ${res.statusCode} - ${ms}ms`,
statusToColor(res.statusCode),
);
});
next();
});
app.get("/_ready", (_, res) => {
res.status(200).send("Compilation complete.");
});
const appConf = {
PROXY_PATH: "/proxy",
EMULATOR_ENDPOINT: conf.EMULATOR_ENDPOINT,
platform: "VNextEmulator",
};
app.get("/config.json", (_, res) => {
res.status(200).json(appConf).end();
});
const proxyProxy = createProxyMiddleware({
target: "https://cdb-ms-mpac-pbe.cosmos.azure.com",
changeOrigin: true,
secure: false,
logLevel: conf.LOG_LEVEL,
pathRewrite: { "^/proxy": "" },
router: (req) => {
if (conf.ENDPOINT_DISCOVERY_ENABLED) {
let newTarget = req.headers["x-ms-proxy-target"];
return newTarget;
} else {
return conf.EMULATOR_ENDPOINT;
}
},
});
app.use("/proxy", proxyProxy);
const unsupported = (req, res) => {
res.status(404).send("Unexpected operation. Please create issue.");
};
// TODO: andersonc - I don't believe these are needed for emulator, should confirm and remove.
app.use("/api", unsupported);
app.use("/_explorer", unsupported);
app.use("/explorerProxy", unsupported);
app.use(`/${conf.AZURE_TENANT_ID}`, unsupported);
app.use(express.static("dist"));
_info(`[EMU] Expecting emulator on [${conf.EMULATOR_ENDPOINT}]`);
_info(`[APP] Listening on [${conf.PORT}]`);
if (conf.GATEWAY_TLS_ENABLED) {
if (!conf.CERT_PATH || !conf.CERT_SECRET) {
_err("[APP] Certificate path or secret not provided");
process.exit(1);
}
const options = {
pfx: fs.readFileSync(conf.CERT_PATH),
passphrase: conf.CERT_SECRET,
};
const server = https.createServer(options, app);
server.listen(conf.PORT);
} else {
app.listen(conf.PORT);
}

View File

@@ -0,0 +1 @@
require('./index.js');

984
utils/local-proxy/package-lock.json generated Normal file
View File

@@ -0,0 +1,984 @@
{
"name": "local-proxy",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "local-proxy",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"express": "^4.21.1",
"http-proxy-middleware": "^3.0.3"
}
},
"node_modules/@types/http-proxy": {
"version": "1.17.15",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz",
"integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "22.10.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz",
"integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"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": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"license": "MIT",
"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": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"license": "MIT"
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"license": "MIT",
"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==",
"license": "MIT"
},
"node_modules/express": {
"version": "4.21.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.19.0",
"serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"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.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/finalhandler": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.1.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"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==",
"license": "MIT",
"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": "3.0.3",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.3.tgz",
"integrity": "sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==",
"license": "MIT",
"dependencies": {
"@types/http-proxy": "^1.17.15",
"debug": "^4.3.6",
"http-proxy": "^1.18.1",
"is-glob": "^4.0.3",
"is-plain-object": "^5.0.0",
"micromatch": "^4.0.8"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/http-proxy-middleware/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/http-proxy-middleware/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"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==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"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==",
"license": "MIT",
"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": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"license": "MIT",
"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==",
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-inspect": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
"integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"license": "MIT",
"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==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"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==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"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": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"license": "MIT"
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/send": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/serve-static": {
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"license": "MIT",
"dependencies": {
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.19.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
"node_modules/side-channel": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"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==",
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"license": "MIT",
"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==",
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/undici-types": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"license": "MIT"
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"license": "MIT",
"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": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
}
}
}

View File

@@ -0,0 +1,17 @@
{
"name": "local-proxy",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node main.js",
"pack": "cd ../.. && npm run build:proxy && cd utils/local-proxy"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"express": "^4.21.1",
"http-proxy-middleware": "^3.0.3"
}
}

View File

@@ -0,0 +1,39 @@
# local-proxy
Lightweight host for Cosmos Explorer
## Quickstart
1. Pre-req - install packages for root project (`cd ../.. && npm ci && cd utils/local-proxy`)
2. Install - install packages for local-proxy (`npm ci`)
3. Pack - `npm run pack` - builds and packs Cosmos Explorer and copies files into project
4. Start - `npm start` - starts the proxy
```bash
cd ../..
npm ci
cd utils/local-proxy
npm ci
npm run pack
npm start
```
## Config
All config is current set via environment variables
| Name | Options (Default) | Description |
| ---------------------------- | ----------------------------------------- | ------------------------------------------------------------ |
| `PORT` | number (`1234`) | The port on which the proxy runs. |
| `LOG_LEVEL` | `debug`, `info`, `warn`, `error` (`info`) | The logging level for the proxy. |
| `EMULATOR_ENDPOINT` | string (`http://localhost:8081`) | The endpoint for the emulator which will be proxied. |
| `ENDPOINT_DISCOVERY_ENABLED` | boolean (`false`) | Determine whether the proxy will rewrite the endpoint or not |
## Dependenies
Node.js v20+
npm (optional)
## Deployment
Copy the entire local-proxy directory to wherever you'd like. If you have npm, you can use `npm start`, else `node main.js`

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
pushd $(dirname $0) > /dev/null
echo Creating self-signed certificate
# Create a self-signed certificate
export CERT_SECRET=$(openssl rand -base64 20)
openssl genrsa 2048 > host.key
chmod 400 host.key
#openssl req -new -x509 -nodes -sha256 -days 365 -key host.key -out host.cert --passin env:CERT_SECRET -subj "/C=US/ST=WA/L=BELLEVUE/O=Microsoft/OU=Azure Cosmos DB/CN=CHRIS ANDERSON/emailAddress=andersonc@microsoft.com"
openssl pkcs12 -export -out host.pfx -inkey host.key -in host.cert --passout env:CERT_SECRET --name "CHRIS ANDERSON"
export CERT_PATH=$(realpath host.pfx)
echo CERT_PATH=$CERT_PATH
popd > /dev/null
export GATEWAY_TLS_ENABLED=true
export EXPLORER_PORT=12345
# Use node to start so we can kill it later
node main.js > ./https-test.log &
node_pid=$!
echo node pid=$node_pid
sleep .5
output=$(curl --insecure -s "https://localhost:12345/_ready")
kill -KILL $node_pid
if [ "$output" != "Compilation complete." ]; then
echo "Failed to start HTTPS server"
cat ./https-test.log
exit 1
fi
echo https test completed

View File

@@ -24,6 +24,8 @@ const AZURE_TENANT_ID = "72f988bf-86f1-41af-91ab-2d7cd011db47";
const RESOURCE_GROUP = "de-e2e-tests";
const AZURE_CLIENT_SECRET = process.env.AZURE_CLIENT_SECRET || process.env.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET; // TODO Remove. Exists for backwards compat with old .env files. Prefer AZURE_CLIENT_SECRET
const ishttps = process.env.GATEWAY_TLS_ENABLED !== "false"; // false -> false, true -> true, default -> true
if (!AZURE_CLIENT_SECRET) {
console.warn("AZURE_CLIENT_SECRET is not set. testExplorer.html will not work.");
}
@@ -131,11 +133,18 @@ module.exports = function (_env = {}, argv = {}) {
template: "src/Terminal/index.html",
chunks: ["terminal"],
}),
new HtmlWebpackPlugin({
filename: "quickstart.html",
template: "src/quickstart.html",
chunks: ["quickstart"],
}),
//todo - dynamically include apis
ishttps
? new HtmlWebpackPlugin({
filename: "quickstart.html",
template: "src/quickstart-sql-only.html",
chunks: ["quickstart"],
})
: new HtmlWebpackPlugin({
filename: "quickstart.html",
template: "src/quickstart-sql-only-http.html",
chunks: ["quickstart"],
}),
new HtmlWebpackPlugin({
filename: "index.html",
template: "src/index.html",
@@ -216,6 +225,14 @@ module.exports = function (_env = {}, argv = {}) {
new EnvironmentPlugin(envVars),
];
if (process.env.EXPLORER_CONFIG_PATH) {
plugins.push(
new CopyWebpackPlugin({
patterns: [{ from: process.env.EXPLORER_CONFIG_PATH, to: "config.json" }],
}),
);
}
if (argv.analyze) {
plugins.push(new BundleAnalyzerPlugin());
}
@@ -280,7 +297,7 @@ module.exports = function (_env = {}, argv = {}) {
// disableHostCheck: true,
liveReload: !isCI,
server: {
type: "https",
type: ishttps ? "https" : "http",
},
host: "0.0.0.0",
port: envVars.PORT,