Compare commits

...

8 Commits

Author SHA1 Message Date
Asier Isayas
32600db3f8 vector index work 2026-03-06 06:52:54 -08:00
sindhuba
67250f0f6b Fix timeout for flaky tests (#2378)
* Add changes for Load more option to work

* Remove localhost

* Add Mongo Pagination tests

* Run npm format

* Fix timeout in tests

* Revert "Fix timeout in tests"

This reverts commit 418b81c490.

* Fix timeout in tests

* Minor fix

* Revert "Minor fix"

This reverts commit 87fe289e3a.

* Revert "Fix timeout in tests"

This reverts commit 11c5748a31.

* Fix timeout
2026-02-05 16:14:37 -08:00
asier-isayas
df6312038a Dependabot Part 2 (#2370)
* dependabot part 1

* fix npm ci

* remove overrides

* undo playwright test update

* dependabot part 2

* cross-spawn

---------

Co-authored-by: Asier Isayas <aisayas@microsoft.com>
2026-02-05 08:45:21 -08:00
sindhuba
e6c27f39be Fix pagination issue (#2298)
* Add changes for Load more option to work

* Remove localhost

* Add Mongo Pagination tests

* Run npm format

* Fix error in tests

* Cleanup CORSByPass

* Revert "Cleanup CORSByPass"

This reverts commit de11ece337.
2026-02-03 17:02:23 -08:00
asier-isayas
937989a47d Dependabot part 1 (#2362)
* dependabot part 1

* fix npm ci

* remove overrides

* undo playwright test update

---------

Co-authored-by: Asier Isayas <aisayas@microsoft.com>
2026-02-03 13:21:10 -08:00
sakshigupta12feb
f166ef9c66 Add Playwright E2E tests for Dynamic Data Masking feature (#2348) (#2349)
* Add Playwright E2E tests for Dynamic Data Masking feature (#2348)

* Initial plan

* Add DDM Playwright test with comprehensive test cases

Co-authored-by: sakshigupta12feb <206070758+sakshigupta12feb@users.noreply.github.com>

* Remove disable DDM test - DDM cannot be disabled once enabled

Co-authored-by: sakshigupta12feb <206070758+sakshigupta12feb@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sakshigupta12feb <206070758+sakshigupta12feb@users.noreply.github.com>

* Fix format and lint errors in DDM Playwright tests (#2350)

* Initial plan

* Fix format and lint errors in dataMasking.spec.ts

Co-authored-by: sakshigupta12feb <206070758+sakshigupta12feb@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sakshigupta12feb <206070758+sakshigupta12feb@users.noreply.github.com>

* playwright testfor DDM

* fixed errors

* fixed playwright based on isPolicyEnabled(removal)

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sakshigupta12feb <206070758+sakshigupta12feb@users.noreply.github.com>
Co-authored-by: Sakshi Gupta <sakshig@microsoft.com>
2026-02-02 13:02:36 +05:30
sakshigupta12feb
b83f54e253 DDM login issue fixed while logging in via connection string (#2353)
* DDM login issue fixed while logging in via connection string

* Removed isPolicyEnabled. The includePaths field should be mandatory and, if present, must be an array

* updated the included value validation and excluded to be optional

---------

Co-authored-by: Sakshi Gupta <sakshig@microsoft.com>
2026-02-02 11:28:49 +05:30
sindhuba
a255dd502b Add sharedThroughput db tests (#2287)
* Add sharedThroughput db tests

* Run npm commands

* Run npm format

* Fix error with adding container to shared db test

* Add changes for Load more option to work

* Remove localhost

* Add Mongo Pagination tests

* Run npm format

* Revert "Add Mongo Pagination tests"

This reverts commit 50a244e6f9.

* Minor fixes

* Fix errors

* Minor fix

* Fix tests

* Address comments

* Address comments
2026-01-27 12:00:56 -08:00
54 changed files with 4615 additions and 6998 deletions

9621
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
"description": "Cosmos Explorer",
"main": "index.js",
"dependencies": {
"@azure/arm-cosmosdb": "9.1.0",
"@azure/arm-cosmosdb": "16.4.0",
"@azure/cosmos": "4.7.0",
"@azure/cosmos-language-service": "0.0.5",
"@azure/identity": "4.5.0",
@@ -14,12 +14,12 @@
"@fluentui/react": "8.119.0",
"@fluentui/react-components": "9.54.2",
"@jupyterlab/services": "6.0.2",
"@jupyterlab/terminal": "3.0.3",
"@jupyterlab/terminal": "3.6.1",
"@microsoft/applicationinsights-web": "2.6.1",
"@nteract/commutable": "7.5.1",
"@nteract/connected-components": "6.8.2",
"@nteract/core": "15.1.9",
"@nteract/data-explorer": "8.0.3",
"@nteract/data-explorer": "8.2.12",
"@nteract/directory-listing": "2.0.6",
"@nteract/dropdown-menu": "1.0.1",
"@nteract/editor": "10.1.12",
@@ -31,7 +31,7 @@
"@nteract/monaco-editor": "3.2.2",
"@nteract/octicons": "2.0.0",
"@nteract/outputs": "3.0.9",
"@nteract/presentational-components": "3.0.7",
"@nteract/presentational-components": "3.4.12",
"@nteract/stateful-components": "1.7.0",
"@nteract/styles": "2.0.2",
"@nteract/transform-geojson": "5.1.8",
@@ -51,13 +51,13 @@
"allotment": "1.20.2",
"applicationinsights": "1.8.0",
"bootstrap": "3.4.1",
"canvas": "2.11.2",
"canvas": "3.2.1",
"clean-webpack-plugin": "4.0.0",
"clipboard-copy": "4.0.1",
"copy-webpack-plugin": "11.0.0",
"crossroads": "0.12.2",
"css-element-queries": "1.1.1",
"d3": "7.8.5",
"d3": "7.9.0",
"datatables.net-colreorder-dt": "1.7.0",
"datatables.net-dt": "1.13.8",
"date-fns": "1.29.0",
@@ -70,7 +70,7 @@
"html2canvas": "1.0.0-rc.5",
"i18next": "23.11.5",
"i18next-browser-languagedetector": "6.0.1",
"i18next-http-backend": "1.0.23",
"i18next-http-backend": "3.0.2",
"iframe-resizer-react": "1.1.0",
"immer": "9.0.6",
"immutable": "4.0.0-rc.12",
@@ -79,7 +79,8 @@
"jquery-typeahead": "2.11.1",
"jquery-ui-dist": "1.13.2",
"knockout": "3.5.1",
"loader-utils": "2.0.3",
"lodash": "4.17.23",
"lodash-es": "4.17.23",
"mkdirp": "1.0.4",
"monaco-editor": "0.44.0",
"ms": "2.1.3",
@@ -103,7 +104,7 @@
"react-youtube": "9.0.1",
"reflect-metadata": "0.1.13",
"rx-jupyter": "5.5.12",
"sanitize-html": "2.3.3",
"sanitize-html": "2.17.0",
"shell-quote": "1.7.3",
"styled-components": "5.0.1",
"swr": "0.4.0",
@@ -111,12 +112,17 @@
"tinykeys": "2.1.0",
"underscore": "1.12.1",
"utility-types": "3.10.0",
"web-vitals": "4.2.4",
"uuid": "9.0.0",
"web-vitals": "4.2.4",
"ws": "8.17.1",
"zustand": "3.5.0"
},
"overrides": {
"d3-color": "3.1.0",
"cross-spawn": "7.0.6"
},
"devDependencies": {
"@babel/core": "7.24.7",
"@babel/core": "7.29.0",
"@babel/preset-env": "7.24.7",
"@babel/preset-react": "7.24.7",
"@babel/preset-typescript": "7.24.7",
@@ -134,7 +140,7 @@
"@types/hasher": "0.0.31",
"@types/jest": "29.5.12",
"@types/jquery": "3.5.29",
"@types/node": "12.11.1",
"@types/node": "18.19.0",
"@types/post-robot": "10.0.1",
"@types/q": "1.5.1",
"@types/react": "17.0.44",
@@ -177,7 +183,7 @@
"jest-html-loader": "1.0.0",
"jest-react-hooks-shallow": "1.5.1",
"jest-trx-results-processor": "3.0.2",
"less": "3.8.1",
"less": "4.5.1",
"less-loader": "11.1.3",
"less-vars-loader": "1.1.0",
"mini-css-extract-plugin": "2.1.0",
@@ -195,11 +201,12 @@
"typedoc": "0.26.2",
"typescript": "4.9.5",
"url-loader": "4.1.1",
"wait-on": "4.0.2",
"webpack": "5.88.2",
"webpack-bundle-analyzer": "4.9.1",
"wait-on": "9.0.3",
"webpack": "5.104.1",
"webpack-bundle-analyzer": "5.2.0",
"webpack-cli": "5.1.4",
"webpack-dev-server": "4.15.2"
"webpack-dev-server": "5.2.3",
"ws": "8.17.1"
},
"scripts": {
"postinstall": "patch-package",

View File

@@ -8,9 +8,10 @@
"name": "cosmos-explorer-preview",
"version": "1.0.0",
"dependencies": {
"body-parser": "^1.20.3",
"express": "^4.21.2",
"http-proxy-middleware": "^3.0.3",
"body-parser": "^2.2.2",
"express": "^5.2.1",
"follow-redirects": "^1.15.6",
"http-proxy-middleware": "^3.0.5",
"node": "^20.19.5",
"node-fetch": "^2.6.1",
"path-to-regexp": "^0.1.12"
@@ -18,8 +19,7 @@
},
"node_modules/@types/http-proxy": {
"version": "1.17.16",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz",
"integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
@@ -29,50 +29,40 @@
"license": "MIT"
},
"node_modules/accepts": {
"version": "1.3.8",
"license": "MIT",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
"mime-types": "^3.0.0",
"negotiator": "^1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"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==",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
"integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
"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"
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.3",
"http-errors": "^2.0.0",
"iconv-lite": "^0.7.0",
"on-finished": "^2.4.1",
"qs": "^6.14.1",
"raw-body": "^3.0.1",
"type-is": "^2.0.1"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/body-parser/node_modules/debug": {
"version": "2.6.9",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"license": "MIT"
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -83,8 +73,7 @@
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
@@ -95,8 +84,7 @@
},
"node_modules/call-bound": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0"
@@ -109,13 +97,15 @@
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
"engines": {
"node": ">= 0.6"
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/content-type": {
@@ -127,20 +117,22 @@
},
"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",
"license": "MIT"
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
"engines": {
"node": ">=6.6.0"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
@@ -160,18 +152,9 @@
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
@@ -195,24 +178,21 @@
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"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/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
@@ -238,104 +218,77 @@
"license": "MIT"
},
"node_modules/express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"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"
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"depd": "^2.0.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"finalhandler": "^2.1.0",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"merge-descriptors": "^2.0.0",
"mime-types": "^3.0.0",
"on-finished": "^2.4.1",
"once": "^1.4.0",
"parseurl": "^1.3.3",
"proxy-addr": "^2.0.7",
"qs": "^6.14.0",
"range-parser": "^1.2.1",
"router": "^2.2.0",
"send": "^1.1.0",
"serve-static": "^2.2.0",
"statuses": "^2.0.1",
"type-is": "^2.0.1",
"vary": "^1.1.2"
},
"engines": {
"node": ">= 0.10.0"
"node": ">= 18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/express/node_modules/array-flatten": {
"version": "1.1.1",
"license": "MIT"
},
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
"license": "MIT"
},
"node_modules/finalhandler": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
"integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
"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"
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"on-finished": "^2.4.1",
"parseurl": "^1.3.3",
"statuses": "^2.0.1"
},
"engines": {
"node": ">= 0.8"
"node": ">= 18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/finalhandler/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/follow-redirects": {
"version": "1.15.3",
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
@@ -353,25 +306,23 @@
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
"engines": {
"node": ">= 0.6"
"node": ">= 0.8"
}
},
"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.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
@@ -393,8 +344,7 @@
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
@@ -405,8 +355,7 @@
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -416,8 +365,7 @@
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -427,8 +375,7 @@
},
"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"
},
@@ -437,17 +384,22 @@
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"license": "MIT",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/http-proxy": {
@@ -480,8 +432,7 @@
},
"node_modules/http-proxy-middleware/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"
},
@@ -491,8 +442,7 @@
},
"node_modules/http-proxy-middleware/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"
},
@@ -502,16 +452,14 @@
},
"node_modules/http-proxy-middleware/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/http-proxy-middleware/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"
@@ -522,8 +470,7 @@
},
"node_modules/http-proxy-middleware/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"
},
@@ -532,14 +479,18 @@
}
},
"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==",
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/inherits": {
@@ -570,62 +521,58 @@
"node": ">=0.10.0"
}
},
"node_modules/is-promise": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"license": "MIT",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
"engines": {
"node": ">= 0.6"
"node": ">= 0.8"
}
},
"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==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/methods": {
"version": "1.1.2",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"license": "MIT",
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"license": "MIT",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"dependencies": {
"mime-db": "1.52.0"
"mime-db": "^1.54.0"
},
"engines": {
"node": ">= 0.6"
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/ms": {
@@ -633,32 +580,27 @@
"license": "MIT"
},
"node_modules/negotiator": {
"version": "0.6.3",
"license": "MIT",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/node": {
"version": "20.19.5",
"resolved": "https://registry.npmjs.org/node/-/node-20.19.5.tgz",
"integrity": "sha512-9fJOHEP8AVrwpbhlUxnbudW8IbkseQVxl4yNQyI/rDfP+gNwKEmfPtBc/Luyf677i5Y0HKIBHiApiB9S9vvxKw==",
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
"node-bin-setup": "^1.0.0"
},
"bin": {
"node": "bin/node"
"node": "bin/node.exe"
},
"engines": {
"npm": ">=5.0.0"
}
},
"node_modules/node-bin-setup": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/node-bin-setup/-/node-bin-setup-1.1.4.tgz",
"integrity": "sha512-vWNHOne0ZUavArqPP5LJta50+S8R261Fr5SvGul37HbEDcowvLjwdvd0ZeSr0r2lTSrPxl6okq9QUw8BFGiAxA=="
},
"node_modules/node-fetch": {
"version": "2.6.7",
"license": "MIT",
@@ -677,10 +619,13 @@
}
}
},
"node_modules/node/node_modules/node-bin-setup": {
"version": "1.1.4",
"license": "ISC"
},
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -698,6 +643,14 @@
"node": ">= 0.8"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -708,8 +661,7 @@
},
"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",
@@ -740,11 +692,10 @@
}
},
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"version": "6.14.1",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
@@ -762,40 +713,46 @@
}
},
"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==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.7.0",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
"node": ">= 0.10"
}
},
"node_modules/requires-port": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"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/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
"dependencies": {
"debug": "^4.4.0",
"depd": "^2.0.0",
"is-promise": "^4.0.0",
"parseurl": "^1.3.3",
"path-to-regexp": "^8.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/router/node_modules/path-to-regexp": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/safer-buffer": {
"version": "2.1.2",
@@ -803,61 +760,46 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
"integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
"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"
"debug": "^4.4.3",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"fresh": "^2.0.0",
"http-errors": "^2.0.1",
"mime-types": "^3.0.2",
"ms": "^2.1.3",
"on-finished": "^2.4.1",
"range-parser": "^1.2.1",
"statuses": "^2.0.2"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/send/node_modules/debug/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"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==",
"engines": {
"node": ">= 0.8"
"node": ">= 18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"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==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
"integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
"dependencies": {
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.19.0"
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"parseurl": "^1.3.3",
"send": "^1.2.0"
},
"engines": {
"node": ">= 0.8.0"
"node": ">= 18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/setprototypeof": {
@@ -866,8 +808,7 @@
},
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3",
@@ -884,8 +825,7 @@
},
"node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
@@ -899,8 +839,7 @@
},
"node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
@@ -916,8 +855,7 @@
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
@@ -933,8 +871,9 @@
}
},
"node_modules/statuses": {
"version": "2.0.1",
"license": "MIT",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"engines": {
"node": ">= 0.8"
}
@@ -951,11 +890,13 @@
"license": "MIT"
},
"node_modules/type-is": {
"version": "1.6.18",
"license": "MIT",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
"content-type": "^1.0.5",
"media-typer": "^1.1.0",
"mime-types": "^3.0.0"
},
"engines": {
"node": ">= 0.6"
@@ -968,13 +909,6 @@
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"license": "MIT",
@@ -993,6 +927,11 @@
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
}
}

View File

@@ -11,9 +11,10 @@
"keywords": [],
"author": "Microsoft Corporation",
"dependencies": {
"body-parser": "^1.20.3",
"express": "^4.21.2",
"http-proxy-middleware": "^3.0.3",
"body-parser": "^2.2.2",
"express": "^5.2.1",
"follow-redirects": "^1.15.6",
"http-proxy-middleware": "^3.0.5",
"node": "^20.19.5",
"node-fetch": "^2.6.1",
"path-to-regexp": "^0.1.12"

View File

@@ -38,7 +38,7 @@ export function queryIterator(databaseId: string, collection: Collection, query:
let continuationToken: string;
return {
fetchNext: () => {
return queryDocuments(databaseId, collection, false, query).then((response) => {
return queryDocuments(databaseId, collection, false, query, continuationToken).then((response) => {
continuationToken = response.continuationToken;
const headers: { [key: string]: string | number } = {};
response.headers.forEach((value, key) => {

View File

@@ -275,8 +275,7 @@ export interface DataMaskingPolicy {
startPosition: number;
length: number;
}>;
excludedPaths: string[];
isPolicyEnabled: boolean;
excludedPaths?: string[];
}
export interface MaterializedView {
@@ -441,7 +440,7 @@ export interface VectorEmbeddingPolicy {
}
export interface VectorEmbedding {
dataType: "float32" | "uint8" | "int8";
dataType: "float32" | "uint8" | "int8" | "float16";
dimensions: number;
distanceFunction: "euclidean" | "cosine" | "dotproduct";
path: string;

View File

@@ -12,6 +12,7 @@ export interface CollapsibleSectionProps {
showDelete?: boolean;
onDelete?: () => void;
disabled?: boolean;
disableDelete?: boolean;
}
export interface CollapsibleSectionState {
@@ -75,7 +76,7 @@ export class CollapsibleSectionComponent extends React.Component<CollapsibleSect
{this.props.showDelete && (
<Stack.Item style={{ marginLeft: "auto" }}>
<IconButton
disabled={this.props.disabled}
disabled={this.props.disableDelete ?? this.props.disabled}
id={`delete-${this.props.title.split(" ").join("-")}`}
iconProps={{ iconName: "Delete" }}
style={{ height: 27, marginRight: "20px" }}

View File

@@ -30,7 +30,6 @@ jest.mock("../../../Common/dataAccess/updateCollection", () => ({
dataMaskingPolicy: {
includedPaths: [],
excludedPaths: ["/excludedPath"],
isPolicyEnabled: true,
},
indexes: [],
}),
@@ -307,12 +306,10 @@ describe("SettingsComponent", () => {
dataMaskingContent: {
includedPaths: [],
excludedPaths: ["/excludedPath"],
isPolicyEnabled: true,
},
dataMaskingContentBaseline: {
includedPaths: [],
excludedPaths: [],
isPolicyEnabled: false,
},
isDataMaskingDirty: true,
});
@@ -326,7 +323,6 @@ describe("SettingsComponent", () => {
expect(wrapper.state("dataMaskingContentBaseline")).toEqual({
includedPaths: [],
excludedPaths: ["/excludedPath"],
isPolicyEnabled: true,
});
});
@@ -340,7 +336,6 @@ describe("SettingsComponent", () => {
const invalidPolicy: InvalidPolicy = {
includedPaths: "invalid",
excludedPaths: [],
isPolicyEnabled: false,
};
// Use type assertion since we're deliberately testing with invalid data
settingsComponentInstance["onDataMaskingContentChange"](invalidPolicy as unknown as DataModels.DataMaskingPolicy);
@@ -349,7 +344,6 @@ describe("SettingsComponent", () => {
expect(wrapper.state("dataMaskingContent")).toEqual({
includedPaths: "invalid",
excludedPaths: [],
isPolicyEnabled: false,
});
expect(wrapper.state("dataMaskingValidationErrors")).toEqual(["includedPaths must be an array"]);
@@ -364,7 +358,6 @@ describe("SettingsComponent", () => {
},
],
excludedPaths: ["/excludedPath"],
isPolicyEnabled: true,
};
settingsComponentInstance["onDataMaskingContentChange"](validPolicy);
@@ -388,7 +381,6 @@ describe("SettingsComponent", () => {
},
],
excludedPaths: ["/excludedPath1"],
isPolicyEnabled: false,
};
const modifiedPolicy = {
@@ -401,7 +393,6 @@ describe("SettingsComponent", () => {
},
],
excludedPaths: ["/excludedPath2"],
isPolicyEnabled: true,
};
// Set initial state

View File

@@ -16,7 +16,7 @@ import {
import { useIndexingPolicyStore } from "Explorer/Tabs/QueryTab/ResultsView";
import { useDatabases } from "Explorer/useDatabases";
import { isFabricNative } from "Platform/Fabric/FabricUtil";
import { isCapabilityEnabled, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
import * as React from "react";
import DiscardIcon from "../../../../images/discard.svg";
@@ -70,6 +70,7 @@ import {
getMongoNotification,
getTabTitle,
hasDatabaseSharedThroughput,
isDataMaskingEnabled,
isDirty,
parseConflictResolutionMode,
parseConflictResolutionProcedure,
@@ -122,6 +123,7 @@ export interface SettingsComponentState {
vectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy;
vectorEmbeddingPolicyBaseline: DataModels.VectorEmbeddingPolicy;
isVectorEmbeddingPolicyValid: boolean;
fullTextPolicy: DataModels.FullTextPolicy;
fullTextPolicyBaseline: DataModels.FullTextPolicy;
shouldDiscardContainerPolicies: boolean;
@@ -243,6 +245,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
vectorEmbeddingPolicy: undefined,
vectorEmbeddingPolicyBaseline: undefined,
isVectorEmbeddingPolicyValid: true,
fullTextPolicy: undefined,
fullTextPolicyBaseline: undefined,
shouldDiscardContainerPolicies: false,
@@ -370,6 +373,10 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
return false;
}
if (!this.state.isVectorEmbeddingPolicyValid) {
return false;
}
return (
this.state.isScaleSaveable ||
this.state.isSubSettingsSaveable ||
@@ -504,6 +511,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
changeFeedPolicy: this.state.changeFeedPolicyBaseline,
autoPilotThroughput: this.state.autoPilotThroughputBaseline,
isAutoPilotSelected: this.state.wasAutopilotOriginallySet,
isVectorEmbeddingPolicyValid: true,
shouldDiscardContainerPolicies: true,
shouldDiscardIndexingPolicy: true,
isScaleSaveable: false,
@@ -648,6 +656,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
private onVectorEmbeddingPolicyDirtyChange = (isVectorEmbeddingPolicyDirty: boolean): void =>
this.setState({ isContainerPolicyDirty: isVectorEmbeddingPolicyDirty });
private onVectorEmbeddingPolicyValidationChange = (isVectorEmbeddingPolicyValid: boolean): void =>
this.setState({ isVectorEmbeddingPolicyValid });
private onFullTextPolicyDirtyChange = (isFullTextPolicyDirty: boolean): void =>
this.setState({ isContainerPolicyDirty: isFullTextPolicyDirty });
@@ -686,22 +697,14 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.setState({ isComputedPropertiesDirty: isComputedPropertiesDirty });
private onDataMaskingContentChange = (newDataMasking: DataModels.DataMaskingPolicy): void => {
if (!newDataMasking.excludedPaths) {
newDataMasking.excludedPaths = [];
}
if (!newDataMasking.includedPaths) {
newDataMasking.includedPaths = [];
}
const validationErrors = [];
if (!Array.isArray(newDataMasking.includedPaths)) {
if (newDataMasking.includedPaths === undefined || newDataMasking.includedPaths === null) {
validationErrors.push("includedPaths is required");
} else if (!Array.isArray(newDataMasking.includedPaths)) {
validationErrors.push("includedPaths must be an array");
}
if (!Array.isArray(newDataMasking.excludedPaths)) {
validationErrors.push("excludedPaths must be an array");
}
if (typeof newDataMasking.isPolicyEnabled !== "boolean") {
validationErrors.push("isPolicyEnabled must be a boolean");
if (newDataMasking.excludedPaths !== undefined && !Array.isArray(newDataMasking.excludedPaths)) {
validationErrors.push("excludedPaths must be an array if provided");
}
this.setState({
@@ -842,7 +845,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
const dataMaskingContent: DataModels.DataMaskingPolicy = {
includedPaths: this.collection.dataMaskingPolicy?.()?.includedPaths || [],
excludedPaths: this.collection.dataMaskingPolicy?.()?.excludedPaths || [],
isPolicyEnabled: this.collection.dataMaskingPolicy?.()?.isPolicyEnabled ?? true,
};
const conflictResolutionPolicy: DataModels.ConflictResolutionPolicy =
this.collection.conflictResolutionPolicy && this.collection.conflictResolutionPolicy();
@@ -1073,8 +1075,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
newCollection.fullTextPolicy = this.state.fullTextPolicy;
// Only send data masking policy if it was modified (dirty)
if (this.state.isDataMaskingDirty && isCapabilityEnabled(Constants.CapabilityNames.EnableDynamicDataMasking)) {
// Only send data masking policy if it was modified (dirty) and data masking is enabled
if (this.state.isDataMaskingDirty && isDataMaskingEnabled(this.collection.dataMaskingPolicy?.())) {
newCollection.dataMaskingPolicy = this.state.dataMaskingContent;
}
@@ -1326,6 +1328,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
vectorEmbeddingPolicyBaseline: this.state.vectorEmbeddingPolicyBaseline,
onVectorEmbeddingPolicyChange: this.onVectorEmbeddingPolicyChange,
onVectorEmbeddingPolicyDirtyChange: this.onVectorEmbeddingPolicyDirtyChange,
onVectorEmbeddingPolicyValidationChange: this.onVectorEmbeddingPolicyValidationChange,
isVectorSearchEnabled: this.isVectorSearchEnabled,
fullTextPolicy: this.state.fullTextPolicy,
fullTextPolicyBaseline: this.state.fullTextPolicyBaseline,
@@ -1463,15 +1466,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
});
}
// Check if DDM should be enabled
const shouldEnableDDM = (): boolean => {
const hasDataMaskingCapability = isCapabilityEnabled(Constants.CapabilityNames.EnableDynamicDataMasking);
const isSqlAccount = userContext.apiType === "SQL";
return isSqlAccount && hasDataMaskingCapability; // Only show for SQL accounts with DDM capability
};
if (shouldEnableDDM()) {
if (isDataMaskingEnabled(this.collection.dataMaskingPolicy?.())) {
const dataMaskingComponentProps: DataMaskingComponentProps = {
shouldDiscardDataMasking: this.state.shouldDiscardDataMasking,
resetShouldDiscardDataMasking: this.resetShouldDiscardDataMasking,

View File

@@ -14,6 +14,7 @@ export interface ContainerPolicyComponentProps {
vectorEmbeddingPolicyBaseline: VectorEmbeddingPolicy;
onVectorEmbeddingPolicyChange: (newVectorEmbeddingPolicy: VectorEmbeddingPolicy) => void;
onVectorEmbeddingPolicyDirtyChange: (isVectorEmbeddingPolicyDirty: boolean) => void;
onVectorEmbeddingPolicyValidationChange: (isValid: boolean) => void;
isVectorSearchEnabled: boolean;
fullTextPolicy: FullTextPolicy;
fullTextPolicyBaseline: FullTextPolicy;
@@ -30,6 +31,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
vectorEmbeddingPolicyBaseline,
onVectorEmbeddingPolicyChange,
onVectorEmbeddingPolicyDirtyChange,
onVectorEmbeddingPolicyValidationChange,
isVectorSearchEnabled,
fullTextPolicy,
fullTextPolicyBaseline,
@@ -42,8 +44,12 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
const [selectedTab, setSelectedTab] = React.useState<ContainerPolicyTabTypes>(
ContainerPolicyTabTypes.VectorPolicyTab,
);
const [vectorEmbeddings, setVectorEmbeddings] = React.useState<VectorEmbedding[]>();
const [vectorEmbeddingsBaseline, setVectorEmbeddingsBaseline] = React.useState<VectorEmbedding[]>();
const [vectorEmbeddings, setVectorEmbeddings] = React.useState<VectorEmbedding[]>(
vectorEmbeddingPolicy?.vectorEmbeddings ?? [],
);
const [vectorEmbeddingsBaseline, setVectorEmbeddingsBaseline] = React.useState<VectorEmbedding[]>(
vectorEmbeddingPolicyBaseline?.vectorEmbeddings ?? [],
);
const [discardVectorChanges, setDiscardVectorChanges] = React.useState<boolean>(false);
const [fullTextSearchPolicy, setFullTextSearchPolicy] = React.useState<FullTextPolicy>();
const [fullTextSearchPolicyBaseline, setFullTextSearchPolicyBaseline] = React.useState<FullTextPolicy>();
@@ -52,7 +58,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
React.useEffect(() => {
setVectorEmbeddings(vectorEmbeddingPolicy?.vectorEmbeddings);
setVectorEmbeddingsBaseline(vectorEmbeddingPolicyBaseline?.vectorEmbeddings);
}, [vectorEmbeddingPolicy]);
}, [vectorEmbeddingPolicy, vectorEmbeddingPolicyBaseline]);
React.useEffect(() => {
setFullTextSearchPolicy(fullTextPolicy);
@@ -69,12 +75,15 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
}
});
const checkAndSendVectorEmbeddingPoliciesToSettings = (newVectorEmbeddings: VectorEmbedding[]): void => {
if (isDirty(newVectorEmbeddings, vectorEmbeddingsBaseline)) {
onVectorEmbeddingPolicyDirtyChange(true);
const checkAndSendVectorEmbeddingPoliciesToSettings = (
newVectorEmbeddings: VectorEmbedding[],
validationPassed: boolean,
): void => {
onVectorEmbeddingPolicyValidationChange(validationPassed);
const isVectorDirty: boolean = isDirty(newVectorEmbeddings, vectorEmbeddingsBaseline);
onVectorEmbeddingPolicyDirtyChange(isVectorDirty);
if (isVectorDirty) {
onVectorEmbeddingPolicyChange({ vectorEmbeddings: newVectorEmbeddings });
} else {
resetShouldDiscardContainerPolicyChange();
}
};
@@ -156,18 +165,18 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
headerText="Vector Policy"
>
<Stack {...titleAndInputStackProps} styles={{ root: { position: "relative", maxWidth: "400px" } }}>
{vectorEmbeddings && (
<VectorEmbeddingPoliciesComponent
disabled={true}
vectorEmbeddings={vectorEmbeddings}
vectorIndexes={undefined}
onVectorEmbeddingChange={(vectorEmbeddings: VectorEmbedding[]) =>
checkAndSendVectorEmbeddingPoliciesToSettings(vectorEmbeddings)
}
discardChanges={discardVectorChanges}
onChangesDiscarded={onVectorChangesDiscarded}
/>
)}
<VectorEmbeddingPoliciesComponent
vectorEmbeddingsBaseline={vectorEmbeddingsBaseline}
vectorEmbeddings={vectorEmbeddings}
vectorIndexes={undefined}
onVectorEmbeddingChange={(
vectorEmbeddings: VectorEmbedding[],
_vectorIndexingPolicies,
validationPassed: boolean,
) => checkAndSendVectorEmbeddingPoliciesToSettings(vectorEmbeddings, validationPassed)}
discardChanges={discardVectorChanges}
onChangesDiscarded={onVectorChangesDiscarded}
/>
</Stack>
</PivotItem>
)}

View File

@@ -53,7 +53,6 @@ describe("DataMaskingComponent", () => {
},
],
excludedPaths: [],
isPolicyEnabled: false,
};
let changeContentCallback: () => void;
@@ -78,7 +77,7 @@ describe("DataMaskingComponent", () => {
<DataMaskingComponent
{...mockProps}
dataMaskingContent={samplePolicy}
dataMaskingContentBaseline={{ ...samplePolicy, isPolicyEnabled: true }}
dataMaskingContentBaseline={{ ...samplePolicy, excludedPaths: ["/excluded"] }}
/>,
);
@@ -123,7 +122,7 @@ describe("DataMaskingComponent", () => {
});
it("resets content when shouldDiscardDataMasking is true", async () => {
const baselinePolicy = { ...samplePolicy, isPolicyEnabled: true };
const baselinePolicy = { ...samplePolicy, excludedPaths: ["/excluded"] };
const wrapper = mount(
<DataMaskingComponent
@@ -159,7 +158,7 @@ describe("DataMaskingComponent", () => {
wrapper.update();
// Update baseline to trigger componentDidUpdate
const newBaseline = { ...samplePolicy, isPolicyEnabled: true };
const newBaseline = { ...samplePolicy, excludedPaths: ["/excluded"] };
wrapper.setProps({ dataMaskingContentBaseline: newBaseline });
expect(mockProps.onDataMaskingDirtyChange).toHaveBeenCalledWith(true);
@@ -174,7 +173,6 @@ describe("DataMaskingComponent", () => {
const invalidPolicy: Record<string, unknown> = {
includedPaths: "not an array",
excludedPaths: [] as string[],
isPolicyEnabled: "not a boolean",
};
mockGetValue.mockReturnValue(JSON.stringify(invalidPolicy));
@@ -197,7 +195,7 @@ describe("DataMaskingComponent", () => {
wrapper.update();
// First change
const modifiedPolicy1 = { ...samplePolicy, isPolicyEnabled: true };
const modifiedPolicy1 = { ...samplePolicy, excludedPaths: ["/path1"] };
mockGetValue.mockReturnValue(JSON.stringify(modifiedPolicy1));
changeContentCallback();
expect(mockProps.onDataMaskingDirtyChange).toHaveBeenCalledWith(true);

View File

@@ -1,12 +1,10 @@
import { MessageBar, MessageBarType, Stack } from "@fluentui/react";
import * as monaco from "monaco-editor";
import * as React from "react";
import * as Constants from "../../../../Common/Constants";
import * as DataModels from "../../../../Contracts/DataModels";
import { isCapabilityEnabled } from "../../../../Utils/CapabilityUtils";
import { loadMonaco } from "../../../LazyMonaco";
import { titleAndInputStackProps, unsavedEditorWarningMessage } from "../SettingsRenderUtils";
import { isDirty as isContentDirty } from "../SettingsUtils";
import { isDirty as isContentDirty, isDataMaskingEnabled } from "../SettingsUtils";
export interface DataMaskingComponentProps {
shouldDiscardDataMasking: boolean;
@@ -24,16 +22,8 @@ interface DataMaskingComponentState {
}
const emptyDataMaskingPolicy: DataModels.DataMaskingPolicy = {
includedPaths: [
{
path: "/",
strategy: "Default",
startPosition: 0,
length: -1,
},
],
includedPaths: [],
excludedPaths: [],
isPolicyEnabled: true,
};
export class DataMaskingComponent extends React.Component<DataMaskingComponentProps, DataMaskingComponentState> {
@@ -140,7 +130,7 @@ export class DataMaskingComponent extends React.Component<DataMaskingComponentPr
};
public render(): JSX.Element {
if (!isCapabilityEnabled(Constants.CapabilityNames.EnableDynamicDataMasking)) {
if (!isDataMaskingEnabled(this.props.dataMaskingContent)) {
return null;
}

View File

@@ -2,6 +2,8 @@ import * as Constants from "../../../Common/Constants";
import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels";
import { isFabricNative } from "../../../Platform/Fabric/FabricUtil";
import { userContext } from "../../../UserContext";
import { isCapabilityEnabled } from "../../../Utils/CapabilityUtils";
import { MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
const zeroValue = 0;
@@ -88,6 +90,19 @@ export const hasDatabaseSharedThroughput = (collection: ViewModels.Collection):
return database?.isDatabaseShared() && !collection.offer();
};
export const isDataMaskingEnabled = (dataMaskingPolicy?: DataModels.DataMaskingPolicy): boolean => {
const isSqlAccount = userContext.apiType === "SQL";
if (!isSqlAccount) {
return false;
}
const hasDataMaskingCapability = isCapabilityEnabled(Constants.CapabilityNames.EnableDynamicDataMasking);
const hasDataMaskingPolicyFromCollection =
dataMaskingPolicy?.includedPaths?.length > 0 || dataMaskingPolicy?.excludedPaths?.length > 0;
return hasDataMaskingCapability || hasDataMaskingPolicyFromCollection;
};
export const parseConflictResolutionMode = (modeFromBackend: string): DataModels.ConflictResolutionMode => {
// Backend can contain different casing as it does case-insensitive comparisson
if (!modeFromBackend) {

View File

@@ -68,7 +68,6 @@ export const collection = {
dataMaskingPolicy: ko.observable<DataModels.DataMaskingPolicy>({
includedPaths: [],
excludedPaths: ["/excludedPath"],
isPolicyEnabled: true,
}),
readSettings: () => {
return;

View File

@@ -604,6 +604,58 @@ exports[`SettingsComponent renders 1`] = `
/>
</Stack>
</PivotItem>
<PivotItem
headerButtonProps={
{
"data-test": "settings-tab-header/DataMaskingTab",
}
}
headerText="Masking Policy (preview)"
itemKey="DataMaskingTab"
key="DataMaskingTab"
style={
{
"backgroundColor": "var(--colorNeutralBackground1)",
"color": "var(--colorNeutralForeground1)",
"marginTop": 20,
}
}
>
<Stack
styles={
{
"root": {
"backgroundColor": "var(--colorNeutralBackground1)",
"color": "var(--colorNeutralForeground1)",
},
}
}
>
<DataMaskingComponent
dataMaskingContent={
{
"excludedPaths": [
"/excludedPath",
],
"includedPaths": [],
}
}
dataMaskingContentBaseline={
{
"excludedPaths": [
"/excludedPath",
],
"includedPaths": [],
}
}
onDataMaskingContentChange={[Function]}
onDataMaskingDirtyChange={[Function]}
resetShouldDiscardDataMasking={[Function]}
shouldDiscardDataMasking={false}
validationErrors={[]}
/>
</Stack>
</PivotItem>
<PivotItem
headerButtonProps={
{

View File

@@ -18,6 +18,7 @@ describe("AddVectorEmbeddingPolicyForm", () => {
beforeEach(() => {
component = render(
<VectorEmbeddingPoliciesComponent
vectorEmbeddingsBaseline={mockVectorEmbedding}
vectorEmbeddings={mockVectorEmbedding}
vectorIndexes={mockVectorIndex}
onVectorEmbeddingChange={mockOnVectorEmbeddingChange}

View File

@@ -20,6 +20,7 @@ import {
import React, { FunctionComponent, useState } from "react";
export interface IVectorEmbeddingPoliciesComponentProps {
vectorEmbeddingsBaseline: VectorEmbedding[];
vectorEmbeddings: VectorEmbedding[];
onVectorEmbeddingChange: (
vectorEmbeddings: VectorEmbedding[],
@@ -29,7 +30,6 @@ export interface IVectorEmbeddingPoliciesComponentProps {
vectorIndexes?: VectorIndex[];
discardChanges?: boolean;
onChangesDiscarded?: () => void;
disabled?: boolean;
isGlobalSecondaryIndex?: boolean;
}
@@ -85,14 +85,28 @@ const dropdownStyles = {
};
export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddingPoliciesComponentProps> = ({
vectorEmbeddingsBaseline,
vectorEmbeddings,
vectorIndexes,
onVectorEmbeddingChange,
discardChanges,
onChangesDiscarded,
disabled,
isGlobalSecondaryIndex,
}): JSX.Element => {
const isExistingPolicy = (policy: VectorEmbeddingPolicyData): boolean => {
if (!vectorEmbeddingsBaseline || vectorEmbeddingsBaseline.length === 0) {
return false;
}
return vectorEmbeddingsBaseline.some(
(baseline) =>
baseline.path === policy.path &&
baseline.dataType === policy.dataType &&
baseline.dimensions === policy.dimensions &&
baseline.distanceFunction === policy.distanceFunction,
);
};
const onVectorEmbeddingPathError = (path: string, index?: number): string => {
let error = "";
if (!path) {
@@ -139,7 +153,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
const initializeData = (vectorEmbeddings: VectorEmbedding[], vectorIndexes: VectorIndex[]) => {
const mergedData: VectorEmbeddingPolicyData[] = [];
vectorEmbeddings.forEach((embedding) => {
vectorEmbeddings?.forEach((embedding) => {
const matchingIndex = displayIndexes ? vectorIndexes.find((index) => index.path === embedding.path) : undefined;
mergedData.push({
...embedding,
@@ -151,6 +165,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
dimensionsError: onVectorEmbeddingDimensionError(embedding.dimensions, matchingIndex?.type || "none"),
});
});
return mergedData;
};
@@ -192,6 +207,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
const validationPassed = vectorEmbeddingPolicyData.every(
(policy: VectorEmbeddingPolicyData) => policy.pathError === "" && policy.dimensionsError === "",
);
onVectorEmbeddingChange(vectorEmbeddings, vectorIndexes, validationPassed);
};
@@ -300,12 +316,13 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
vectorEmbeddingPolicyData.length > 0 &&
vectorEmbeddingPolicyData.map((vectorEmbeddingPolicy: VectorEmbeddingPolicyData, index: number) => (
<CollapsibleSectionComponent
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
key={index}
isExpandedByDefault={true}
title={`Vector embedding ${index + 1}`}
showDelete={true}
onDelete={() => onDelete(index)}
disableDelete={false}
>
<Stack horizontal tokens={{ childrenGap: 4 }}>
<Stack
@@ -319,11 +336,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
}}
>
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Path
</Label>
<TextField
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
id={`vector-policy-path-${index + 1}`}
required={true}
placeholder="/vector1"
@@ -334,11 +351,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
/>
</Stack>
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Data type
</Label>
<Dropdown
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
required={true}
styles={dropdownStyles}
options={getDataTypeOptions()}
@@ -349,11 +366,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
></Dropdown>
</Stack>
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Distance function
</Label>
<Dropdown
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
required={true}
styles={dropdownStyles}
options={getDistanceFunctionOptions()}
@@ -364,11 +381,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
></Dropdown>
</Stack>
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Dimensions
</Label>
<TextField
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
id={`vector-policy-dimension-${index + 1}`}
required={true}
styles={textFieldStyles}
@@ -381,11 +398,11 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
</Stack>
{displayIndexes && (
<Stack>
<Label disabled={disabled} styles={labelStyles}>
<Label disabled={isExistingPolicy(vectorEmbeddingPolicy)} styles={labelStyles}>
Index type
</Label>
<Dropdown
disabled={disabled}
disabled={isExistingPolicy(vectorEmbeddingPolicy)}
required={true}
styles={dropdownStyles}
options={getIndexTypeOptions()}
@@ -397,7 +414,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
<Stack style={{ marginLeft: "10px" }}>
<Label
disabled={
disabled ||
isExistingPolicy(vectorEmbeddingPolicy) ||
(vectorEmbeddingPolicy.indexType !== "quantizedFlat" &&
vectorEmbeddingPolicy.indexType !== "diskANN")
}
@@ -408,7 +425,7 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
</Label>
<TextField
disabled={
disabled ||
isExistingPolicy(vectorEmbeddingPolicy) ||
(vectorEmbeddingPolicy.indexType !== "quantizedFlat" &&
vectorEmbeddingPolicy.indexType !== "diskANN")
}
@@ -421,11 +438,18 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
/>
</Stack>
<Stack style={{ marginLeft: "10px" }}>
<Label disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"} styles={labelStyles}>
<Label
disabled={
isExistingPolicy(vectorEmbeddingPolicy) || vectorEmbeddingPolicy.indexType !== "diskANN"
}
styles={labelStyles}
>
Indexing search list size
</Label>
<TextField
disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"}
disabled={
isExistingPolicy(vectorEmbeddingPolicy) || vectorEmbeddingPolicy.indexType !== "diskANN"
}
id={`vector-policy-indexingSearchListSize-${index + 1}`}
styles={textFieldStyles}
value={String(vectorEmbeddingPolicy.indexingSearchListSize || "")}
@@ -435,11 +459,18 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
/>
</Stack>
<Stack style={{ marginLeft: "10px" }}>
<Label disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"} styles={labelStyles}>
<Label
disabled={
isExistingPolicy(vectorEmbeddingPolicy) || vectorEmbeddingPolicy.indexType !== "diskANN"
}
styles={labelStyles}
>
Vector index shard key
</Label>
<TextField
disabled={disabled || vectorEmbeddingPolicy.indexType !== "diskANN"}
disabled={
isExistingPolicy(vectorEmbeddingPolicy) || vectorEmbeddingPolicy.indexType !== "diskANN"
}
id={`vector-policy-vectorIndexShardKey-${index + 1}`}
styles={textFieldStyles}
value={String(vectorEmbeddingPolicy.vectorIndexShardKey?.[0] ?? "")}
@@ -453,7 +484,6 @@ export const VectorEmbeddingPoliciesComponent: FunctionComponent<IVectorEmbeddin
</CollapsibleSectionComponent>
))}
<DefaultButton
disabled={disabled}
id={`add-vector-policy`}
styles={{ root: { maxWidth: 170, fontSize: 12 } }}
onClick={onAdd}

View File

@@ -1,6 +1,6 @@
import { IDropdownOption } from "@fluentui/react";
const dataTypes = ["float32", "uint8", "int8"];
const dataTypes = ["float32", "uint8", "int8", "float16"];
const distanceFunctions = ["euclidean", "cosine", "dotproduct"];
const indexTypes = ["none", "flat", "diskANN", "quantizedFlat"];

View File

@@ -888,6 +888,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
<Stack styles={{ root: { paddingLeft: 40 } }}>
<VectorEmbeddingPoliciesComponent
vectorEmbeddingsBaseline={[]}
vectorEmbeddings={this.state.vectorEmbeddingPolicy}
vectorIndexes={this.state.vectorIndexingPolicy}
onVectorEmbeddingChange={(

View File

@@ -40,6 +40,7 @@ export const VectorSearchComponent = (props: VectorSearchComponentProps): JSX.El
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
<Stack styles={{ root: { paddingLeft: 40 } }}>
<VectorEmbeddingPoliciesComponent
vectorEmbeddingsBaseline={[]}
vectorEmbeddings={vectorEmbeddingPolicy}
vectorIndexes={vectorIndexingPolicy}
onVectorEmbeddingChange={(

View File

@@ -141,7 +141,6 @@ export default class Collection implements ViewModels.Collection {
const defaultDataMaskingPolicy: DataModels.DataMaskingPolicy = {
includedPaths: Array<{ path: string; strategy: string; startPosition: number; length: number }>(),
excludedPaths: Array<string>(),
isPolicyEnabled: true,
};
const observablePolicy = ko.observable(data.dataMaskingPolicy || defaultDataMaskingPolicy);
observablePolicy.subscribe(() => {});

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Lists the Cassandra keyspaces under an existing Azure Cosmos DB database account. */
export async function listCassandraKeyspaces(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account and collection. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given collection, split by partition. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given collection and region, split by partition. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account, collection and region. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account and database. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account and region. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the properties of an existing Azure Cosmos DB database account. */
export async function get(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Lists the graphs under an existing Azure Cosmos DB database account. */
export async function listGraphs(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Lists the Gremlin databases under an existing Azure Cosmos DB database account. */
export async function listGremlinDatabases(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* List Cosmos DB locations and their properties */
export async function list(subscriptionId: string): Promise<Types.LocationListResult | Types.CloudError> {

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Lists the MongoDB databases under an existing Azure Cosmos DB database account. */
export async function listMongoDBDatabases(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Lists all of the available Cosmos DB Resource Provider operations. */
export async function list(): Promise<Types.OperationListResult> {

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given partition key range id. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given partition key range id and region. */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given database account. This url is only for PBS and Replication Latency data */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given account, source and target region. This url is only for PBS and Replication Latency data */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Retrieves the metrics determined by the given filter for the given account target region. This url is only for PBS and Replication Latency data */
export async function listMetrics(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Lists the SQL databases under an existing Azure Cosmos DB database account. */
export async function listSqlDatabases(

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
import { armRequest } from "../../request";
import * as Types from "./types";
import { configContext } from "../../../../ConfigContext";
const apiVersion = "2025-05-01-preview";
const apiVersion = "2025-11-01-preview";
/* Lists the Tables under an existing Azure Cosmos DB database account. */
export async function listTables(

View File

@@ -3,7 +3,7 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2025-05-01-preview/cosmos-db.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-11-01-preview/cosmos-db.json
*/
/* The List operation response, that contains the client encryption keys and their properties. */
@@ -573,6 +573,8 @@ export interface DatabaseAccountGetProperties {
/* Indicates the status of the Customer Managed Key feature on the account. In case there are errors, the property provides troubleshooting guidance. */
customerManagedKeyStatus?: string;
/* The version of the Customer Managed Key currently being used by the account */
readonly keyVaultKeyUriVersion?: string;
/* Flag to indicate enabling/disabling of Priority Based Execution Preview feature on the account */
enablePriorityBasedExecution?: boolean;
/* Enum to indicate default Priority Level of request for Priority Based Execution. */
@@ -582,6 +584,10 @@ export interface DatabaseAccountGetProperties {
enablePerRegionPerPartitionAutoscale?: boolean;
/* Flag to indicate if All Versions and Deletes Change feed feature is enabled on the account */
enableAllVersionsAndDeletesChangeFeed?: boolean;
/* Total dedicated throughput (RU/s) for database account. Represents the sum of all manual provisioned throughput and all autoscale max RU/s across all shared throughput databases and dedicated throughput containers in the account for 1 region. READ ONLY. */
throughputPoolDedicatedRUs?: number;
/* When this account is part of a fleetspace with throughput pooling enabled, this is the maximum additional throughput (RU/s) that can be consumed from the pool, summed across all shared throughput databases and dedicated throughput containers in the account for 1 region. READ ONLY. */
throughputPoolMaxConsumableRUs?: number;
}
/* Properties to create and update Azure Cosmos DB database accounts. */
@@ -1105,7 +1111,7 @@ export interface ThroughputSettingsResource {
readonly instantMaximumThroughput?: string;
/* The maximum throughput value or the maximum maxThroughput value (for autoscale) that can be specified */
readonly softAllowedMaximumThroughput?: string;
/* Array of Throughput Bucket limits to be applied to the Cosmos DB container */
/* Array of throughput bucket limits to be applied to the Cosmos DB container */
throughputBuckets?: ThroughputBucketResource[];
}
@@ -1140,6 +1146,8 @@ export interface ThroughputBucketResource {
id: number;
/* Represents maximum percentage throughput that can be used by the bucket */
maxThroughputPercentage: number;
/* Indicates whether this is the default throughput bucket */
isDefaultBucket?: boolean;
}
/* Cosmos DB options resource object */
@@ -1296,6 +1304,9 @@ export interface SqlContainerResource {
/* Materialized Views defined on the container. */
materializedViews?: MaterializedViewDetails[];
/* Materialized Views Properties defined for source container. */
materializedViewsProperties?: MaterializedViewsProperties;
/* List of computed properties */
computedProperties?: ComputedProperty[];
@@ -1304,6 +1315,9 @@ export interface SqlContainerResource {
/* The FullText policy for the container. */
fullTextPolicy?: FullTextPolicy;
/* The Data Masking policy for the container. */
dataMaskingPolicy?: DataMaskingPolicy;
}
/* Cosmos DB indexing policy */
@@ -1327,6 +1341,9 @@ export interface IndexingPolicy {
/* List of paths to include in the vector indexing */
vectorIndexes?: VectorIndex[];
/* List of paths to include in the full text indexing */
fullTextIndexes?: FullTextIndexPath[];
}
/* Cosmos DB Vector Embedding Policy */
@@ -1374,6 +1391,13 @@ export interface VectorIndex {
path: string;
/* The index type of the vector. Currently, flat, diskANN, and quantizedFlat are supported. */
type: "flat" | "diskANN" | "quantizedFlat";
/* The number of bytes used in product quantization of the vectors. A larger value may result in better recall for vector searches at the expense of latency. This is only applicable for the quantizedFlat and diskANN vector index types. */
quantizationByteSize?: number;
/* This is the size of the candidate list of approximate neighbors stored while building the DiskANN index as part of the optimization processes. Large values may improve recall at the expense of latency. This is only applicable for the diskANN vector index type. */
indexingSearchListSize?: number;
/* Array of shard keys for the vector index. This is only applicable for the quantizedFlat and diskANN vector index types. */
vectorIndexShardKey?: unknown[];
}
/* Represents a vector embedding. A vector embedding is used to define a vector field in the documents. */
@@ -1381,7 +1405,7 @@ export interface VectorEmbedding {
/* The path to the vector field in the document. */
path: string;
/* Indicates the data type of vector. */
dataType: "float32" | "uint8" | "int8";
dataType: "float32" | "uint8" | "int8" | "float16";
/* The distance function to use for distance calculation in between vectors. */
distanceFunction: "euclidean" | "cosine" | "dotproduct";
@@ -1390,6 +1414,12 @@ export interface VectorEmbedding {
dimensions: number;
}
/* Represents the full text index path. */
export interface FullTextIndexPath {
/* The path to the full text field in the document. */
path: string;
}
/* Represents the full text path specification. */
export interface FullTextPath {
/* The path to the full text field in the document. */
@@ -1489,6 +1519,18 @@ export interface ClientEncryptionIncludedPath {
encryptionAlgorithm: string;
}
/* Data masking policy for the container. */
export interface DataMaskingPolicy {
/* List of JSON paths to include in the masking policy. */
includedPaths?: unknown[];
/* List of JSON paths to exclude from masking. */
excludedPaths?: unknown[];
/* Flag indicating whether the data masking policy is enabled. */
isPolicyEnabled?: boolean;
}
/* Materialized View definition for the container. */
export interface MaterializedViewDefinition {
/* An unique identifier for the source collection. This is a system generated property. */
@@ -1497,6 +1539,14 @@ export interface MaterializedViewDefinition {
sourceCollectionId: string;
/* The definition should be an SQL query which would be used to fetch data from the source container to populate into the Materialized View container. */
definition: string;
/* Throughput bucket assigned for the materialized view operations on target container. */
throughputBucketForBuild?: number;
}
/* Materialized Views Properties for the source container. */
export interface MaterializedViewsProperties {
/* Throughput bucket assigned for the materialized view operations on source container. */
throughputBucketForBuild?: number;
}
/* MaterializedViewDetails, contains Id & _rid fields of materialized view. */

View File

@@ -2,20 +2,54 @@ import { Page } from "@playwright/test";
export async function setupCORSBypass(page: Page) {
await page.route("**/api/mongo/explorer{,/**}", async (route) => {
const request = route.request();
const origin = request.headers()["origin"];
// If there's no origin, it's not a CORS request. Let it proceed without modification.
if (!origin) {
await route.continue();
return;
}
//// Handle preflight (OPTIONS) requests separately.
// These should not be forwarded to the target server.
if (request.method() === "OPTIONS") {
await route.fulfill({
status: 204, // No Content
headers: {
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,OPTIONS,HEAD",
"Access-Control-Request-Headers": "*, x-ms-continuation",
"Access-Control-Max-Age": "86400", // Cache preflight response for 1 day
Vary: "Origin",
},
});
return;
}
// Handle the actual GET/POST request
const response = await route.fetch({
headers: {
...route.request().headers(),
...request.headers(),
},
});
const responseHeaders = response.headers();
// Clean up any pre-existing CORS headers from the real response to avoid conflicts.
delete responseHeaders["access-control-allow-origin"];
delete responseHeaders["access-control-allow-credentials"];
await route.fulfill({
status: response.status(),
headers: {
...response.headers(),
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "*",
...responseHeaders,
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,OPTIONS,HEAD",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Credentials": "*",
"Access-Control-Expose-Headers": "x-ms-continuation,x-ms-request-charge,x-ms-session-token",
Vary: "Origin",
},
body: await response.body(),
});

View File

@@ -58,7 +58,9 @@ export const defaultAccounts: Record<TestAccount, string> = {
export const resourceGroupName = process.env.DE_TEST_RESOURCE_GROUP ?? "de-e2e-tests";
export const subscriptionId = process.env.DE_TEST_SUBSCRIPTION_ID ?? "69e02f2d-f059-4409-9eac-97e8a276ae2c";
export const TEST_AUTOSCALE_THROUGHPUT_RU = 1000;
export const TEST_MANUAL_THROUGHPUT_RU = 800;
export const TEST_AUTOSCALE_MAX_THROUGHPUT_RU_2K = 2000;
export const TEST_AUTOSCALE_MAX_THROUGHPUT_RU_4K = 4000;
export const TEST_MANUAL_THROUGHPUT_RU_2K = 2000;
export const ONE_MINUTE_MS: number = 60 * 1000;
@@ -248,7 +250,7 @@ class TreeNode {
// Try three times to wait for the node to expand.
for (let i = 0; i < RETRY_COUNT; i++) {
try {
await tree.waitFor({ state: "visible" });
await tree.waitFor({ state: "visible", timeout: 30000 });
// The tree has expanded, let's get out of here
return true;
} catch {

View File

@@ -0,0 +1,132 @@
import { expect, test } from "@playwright/test";
import { setupCORSBypass } from "../CORSBypass";
import { DataExplorer, QueryTab, TestAccount, CommandBarButton, Editor } from "../fx";
import { serializeMongoToJson } from "../testData";
const databaseId = "test-e2etests-mongo-pagination";
const collectionId = "test-coll-mongo-pagination";
let explorer: DataExplorer = null!;
test.setTimeout(5 * 60 * 1000);
test.describe("Test Mongo Pagination", () => {
let queryTab: QueryTab;
let queryEditor: Editor;
test.beforeEach("Open query tab", async ({ page }) => {
await setupCORSBypass(page);
explorer = await DataExplorer.open(page, TestAccount.MongoReadonly);
const containerNode = await explorer.waitForContainerNode(databaseId, collectionId);
await containerNode.expand();
const containerMenuNode = await explorer.waitForContainerDocumentsNode(databaseId, collectionId);
await containerMenuNode.openContextMenu();
await containerMenuNode.contextMenuItem("New Query").click();
queryTab = explorer.queryTab("tab0");
queryEditor = queryTab.editor();
await queryEditor.locator.waitFor({ timeout: 30 * 1000 });
await queryTab.executeCTA.waitFor();
await explorer.frame.getByTestId("NotificationConsole/ExpandCollapseButton").click();
await explorer.frame.getByTestId("NotificationConsole/Contents").waitFor();
});
test("should execute a query and load more results", async ({ page }) => {
const query = "{}";
await queryEditor.locator.click();
await queryEditor.setText(query);
const executeQueryButton = explorer.commandBarButton(CommandBarButton.ExecuteQuery);
await executeQueryButton.click();
// Wait for query execution to complete
await expect(queryTab.resultsView).toBeVisible({ timeout: 60000 });
await expect(queryTab.resultsEditor.locator).toBeAttached({ timeout: 30000 });
// Get initial results
const resultText = await queryTab.resultsEditor.text();
if (!resultText || resultText.trim() === "" || resultText.trim() === "[]") {
throw new Error("Query returned no results - the collection appears to be empty");
}
const resultData = serializeMongoToJson(resultText);
if (resultData.length === 0) {
throw new Error("Parsed results contain 0 documents - collection is empty");
}
if (resultData.length < 100) {
expect(resultData.length).toBeGreaterThan(0);
return;
}
expect(resultData.length).toBe(100);
// Pagination test
let totalPagesLoaded = 1;
const maxLoadMoreAttempts = 10;
for (let loadMoreAttempts = 0; loadMoreAttempts < maxLoadMoreAttempts; loadMoreAttempts++) {
const loadMoreButton = queryTab.resultsView.getByText("Load more");
try {
await expect(loadMoreButton).toBeVisible({ timeout: 5000 });
} catch {
// Load more button not visible - pagination complete
break;
}
const beforeClickText = await queryTab.resultsEditor.text();
const beforeClickHash = Buffer.from(beforeClickText || "")
.toString("base64")
.substring(0, 50);
await loadMoreButton.click();
// Wait for content to update
let editorContentChanged = false;
for (let waitAttempt = 1; waitAttempt <= 3; waitAttempt++) {
await page.waitForTimeout(2000);
const currentEditorText = await queryTab.resultsEditor.text();
const currentHash = Buffer.from(currentEditorText || "")
.toString("base64")
.substring(0, 50);
if (currentHash !== beforeClickHash) {
editorContentChanged = true;
break;
}
}
if (editorContentChanged) {
totalPagesLoaded++;
} else {
// No content change detected, stop pagination
break;
}
await page.waitForTimeout(1000);
}
// Final verification
const finalIndicator = queryTab.resultsView.locator("text=/\\d+ - \\d+/");
const finalIndicatorText = await finalIndicator.textContent();
if (finalIndicatorText) {
const match = finalIndicatorText.match(/(\d+) - (\d+)/);
if (match) {
const totalDocuments = parseInt(match[2]);
expect(totalDocuments).toBe(405);
expect(totalPagesLoaded).toBe(5);
} else {
throw new Error(`Invalid results indicator format: ${finalIndicatorText}`);
}
} else {
expect(totalPagesLoaded).toBe(5);
}
});
});

View File

@@ -2,7 +2,6 @@ import { expect, test } from "@playwright/test";
import { CosmosDBManagementClient } from "@azure/arm-cosmosdb";
import { CosmosClient, PermissionMode } from "@azure/cosmos";
import { AzureIdentityCredentialAdapter } from "@azure/ms-rest-js";
import {
DataExplorer,
TestAccount,
@@ -18,8 +17,7 @@ test("SQL account using Resource token", async ({ page }) => {
test.skip(nosqlAccountRbacToken.length > 0, "Resource tokens not supported when using data plane RBAC.");
const credentials = getAzureCLICredentials();
const adaptedCredentials = new AzureIdentityCredentialAdapter(credentials);
const armClient = new CosmosDBManagementClient(adaptedCredentials, subscriptionId);
const armClient = new CosmosDBManagementClient(credentials, subscriptionId);
const accountName = getAccountName(TestAccount.SQL);
const account = await armClient.databaseAccounts.get(resourceGroupName, accountName);
const keys = await armClient.databaseAccounts.listKeys(resourceGroupName, accountName);

View File

@@ -0,0 +1,139 @@
import { expect, test } from "@playwright/test";
import { CommandBarButton, DataExplorer, ONE_MINUTE_MS, TestAccount } from "../../../fx";
import { createTestSQLContainer, TestContainerContext } from "../../../testData";
test.describe("Vector Policy under Scale & Settings", () => {
let context: TestContainerContext = null!;
let explorer: DataExplorer = null!;
test.beforeAll("Create Test Database", async () => {
context = await createTestSQLContainer();
});
test.beforeEach("Open Container Policy tab under Scale & Settings", async ({ page }) => {
explorer = await DataExplorer.open(page, TestAccount.SQL);
await explorer.waitForContainerNode(context.database.id, context.container.id);
// Click Scale & Settings and open Container Policy tab
await explorer.openScaleAndSettings(context);
const containerPolicyTab = explorer.frame.getByTestId("settings-tab-header/ContainerVectorPolicyTab");
await containerPolicyTab.click();
// Click on Vector Policy tab
const vectorPolicyTab = explorer.frame.getByRole("tab", { name: "Vector Policy" });
await vectorPolicyTab.click();
});
test.afterAll("Delete Test Database", async () => {
await context?.dispose();
});
test("Add new vector embedding policy", async () => {
// Click Add vector embedding button
const addButton = explorer.frame.locator("#add-vector-policy");
await addButton.click();
// Fill in path
const pathInput = explorer.frame.locator("#vector-policy-path-1");
await pathInput.fill("/embedding");
// Fill in dimensions
const dimensionsInput = explorer.frame.locator("#vector-policy-dimension-1");
await dimensionsInput.fill("1500");
// Save changes
const saveButton = explorer.commandBarButton(CommandBarButton.Save);
await expect(saveButton).toBeEnabled();
await saveButton.click();
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated container ${context.container.id}`,
{
timeout: 2 * ONE_MINUTE_MS,
},
);
});
test("Existing vector embedding policy fields are disabled", async () => {
// First add a vector embedding policy
const addButton = explorer.frame.locator("#add-vector-policy");
await addButton.click();
const pathInput = explorer.frame.locator("#vector-policy-path-1");
await pathInput.fill("/existingEmbedding");
const dimensionsInput = explorer.frame.locator("#vector-policy-dimension-1");
await dimensionsInput.fill("700");
// Save the policy
const saveButton = explorer.commandBarButton(CommandBarButton.Save);
await saveButton.click();
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated container ${context.container.id}`,
{
timeout: 2 * ONE_MINUTE_MS,
},
);
// Verify the path field is disabled for the existing policy
const existingPathInput = explorer.frame.locator("#vector-policy-path-1");
await expect(existingPathInput).toBeDisabled();
// Verify the dimensions field is disabled for the existing policy
const existingDimensionsInput = explorer.frame.locator("#vector-policy-dimension-1");
await expect(existingDimensionsInput).toBeDisabled();
});
test("New vector embedding policy fields are enabled while existing are disabled", async () => {
// First, create an existing policy
const addButton = explorer.frame.locator("#add-vector-policy");
await addButton.click();
const firstPathInput = explorer.frame.locator("#vector-policy-path-1");
await firstPathInput.fill("/existingPolicy");
const firstDimensionsInput = explorer.frame.locator("#vector-policy-dimension-1");
await firstDimensionsInput.fill("500");
// Save the policy to make it "existing"
const saveButton = explorer.commandBarButton(CommandBarButton.Save);
await saveButton.click();
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated container ${context.container.id}`,
{
timeout: 2 * ONE_MINUTE_MS,
},
);
// Now add a new policy
await addButton.click();
// Verify the existing policy fields are disabled
const existingPathInput = explorer.frame.locator("#vector-policy-path-1");
const existingDimensionsInput = explorer.frame.locator("#vector-policy-dimension-1");
await expect(existingPathInput).toBeDisabled();
await expect(existingDimensionsInput).toBeDisabled();
// Verify the new policy fields are enabled
const newPathInput = explorer.frame.locator("#vector-policy-path-2");
const newDimensionsInput = explorer.frame.locator("#vector-policy-dimension-2");
await expect(newPathInput).toBeEnabled();
await expect(newDimensionsInput).toBeEnabled();
});
test("Validation error for empty path", async () => {
const addButton = explorer.frame.locator("#add-vector-policy");
await addButton.click();
// Leave path empty, just fill dimensions
const dimensionsInput = explorer.frame.locator("#vector-policy-dimension-1");
await dimensionsInput.fill("512");
// Check for validation error on path
const pathError = explorer.frame.locator("text=Path should not be empty");
await expect(pathError).toBeVisible();
// Verify save button is disabled due to validation error
const saveButton = explorer.commandBarButton(CommandBarButton.Save);
await expect(saveButton).toBeDisabled();
});
});

View File

@@ -0,0 +1,127 @@
import { expect, test, type Page } from "@playwright/test";
import { DataExplorer, TestAccount } from "../../fx";
import { createTestSQLContainer, TestContainerContext } from "../../testData";
/**
* Tests for Dynamic Data Masking (DDM) feature.
*
* Prerequisites:
* - Test account must have the EnableDynamicDataMasking capability enabled
* - If the capability is not enabled, the DataMaskingTab will not be visible and tests will be skipped
*
* Important Notes:
* - Tests focus on enabling DDM and modifying the masking policy configuration
*/
let testContainer: TestContainerContext;
let DATABASE_ID: string;
let CONTAINER_ID: string;
test.beforeAll(async () => {
testContainer = await createTestSQLContainer();
DATABASE_ID = testContainer.database.id;
CONTAINER_ID = testContainer.container.id;
});
// Clean up test database after all tests
test.afterAll(async () => {
if (testContainer) {
await testContainer.dispose();
}
});
// Helper function to navigate to Data Masking tab
async function navigateToDataMaskingTab(page: Page, explorer: DataExplorer): Promise<boolean> {
// Refresh the tree to see the newly created database
const refreshButton = explorer.frame.getByTestId("Sidebar/RefreshButton");
await refreshButton.click();
await page.waitForTimeout(3000);
// Expand database and container nodes
const databaseNode = await explorer.waitForNode(DATABASE_ID);
await databaseNode.expand();
await page.waitForTimeout(2000);
const containerNode = await explorer.waitForNode(`${DATABASE_ID}/${CONTAINER_ID}`);
await containerNode.expand();
await page.waitForTimeout(1000);
// Click Scale & Settings or Settings (depending on container type)
let settingsNode = explorer.frame.getByTestId(`TreeNode:${DATABASE_ID}/${CONTAINER_ID}/Scale & Settings`);
const isScaleAndSettings = await settingsNode.isVisible().catch(() => false);
if (!isScaleAndSettings) {
settingsNode = explorer.frame.getByTestId(`TreeNode:${DATABASE_ID}/${CONTAINER_ID}/Settings`);
}
await settingsNode.click();
await page.waitForTimeout(2000);
// Check if Data Masking tab is available
const dataMaskingTab = explorer.frame.getByTestId("settings-tab-header/DataMaskingTab");
const isTabVisible = await dataMaskingTab.isVisible().catch(() => false);
if (!isTabVisible) {
return false;
}
await dataMaskingTab.click();
await page.waitForTimeout(1000);
return true;
}
test.describe("Data Masking under Scale & Settings", () => {
test("Data Masking tab should be visible and show JSON editor", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.SQL);
const isTabAvailable = await navigateToDataMaskingTab(page, explorer);
if (!isTabAvailable) {
test.skip(
true,
"Data Masking tab is not available. Test account may not have EnableDynamicDataMasking capability.",
);
}
// Verify the Data Masking editor is visible
const dataMaskingEditor = explorer.frame.locator(".settingsV2Editor");
await expect(dataMaskingEditor).toBeVisible();
});
test("Data Masking editor should contain default policy structure", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.SQL);
const isTabAvailable = await navigateToDataMaskingTab(page, explorer);
if (!isTabAvailable) {
test.skip(
true,
"Data Masking tab is not available. Test account may not have EnableDynamicDataMasking capability.",
);
}
// Verify the editor contains the expected JSON structure fields
const editorContent = explorer.frame.locator(".settingsV2Editor");
await expect(editorContent).toBeVisible();
// Check that the editor contains key policy fields (default policy has empty arrays)
await expect(editorContent).toContainText("includedPaths");
await expect(editorContent).toContainText("excludedPaths");
});
test("Data Masking editor should have correct default policy values", async ({ page }) => {
const explorer = await DataExplorer.open(page, TestAccount.SQL);
const isTabAvailable = await navigateToDataMaskingTab(page, explorer);
if (!isTabAvailable) {
test.skip(
true,
"Data Masking tab is not available. Test account may not have EnableDynamicDataMasking capability.",
);
}
const editorContent = explorer.frame.locator(".settingsV2Editor");
await expect(editorContent).toBeVisible();
// Default policy should have empty includedPaths and excludedPaths arrays
await expect(editorContent).toContainText("[]");
});
});

View File

@@ -0,0 +1,229 @@
import { Locator, expect, test } from "@playwright/test";
import {
CommandBarButton,
DataExplorer,
ONE_MINUTE_MS,
TEST_AUTOSCALE_MAX_THROUGHPUT_RU_4K,
TEST_MANUAL_THROUGHPUT_RU,
TestAccount,
} from "../../fx";
import { TestDatabaseContext, createTestDB } from "../../testData";
test.describe("Database with Shared Throughput", () => {
let dbContext: TestDatabaseContext = null!;
let explorer: DataExplorer = null!;
const containerId = "sharedcontainer";
// Helper methods
const getThroughputInput = (type: "manual" | "autopilot"): Locator => {
return explorer.frame.getByTestId(`${type}-throughput-input`);
};
test.afterEach("Delete Test Database", async () => {
await dbContext?.dispose();
});
test.describe("Manual Throughput Tests", () => {
test.beforeEach(async ({ page }) => {
explorer = await DataExplorer.open(page, TestAccount.SQL);
});
test("Create database with shared manual throughput and verify Scale node in UI", async () => {
test.setTimeout(120000); // 2 minutes timeout
// Create database with shared manual throughput (400 RU/s)
dbContext = await createTestDB({ throughput: 400 });
// Verify database node appears in the tree
const databaseNode = await explorer.waitForNode(dbContext.database.id);
expect(databaseNode).toBeDefined();
// Expand the database node to see child nodes
await databaseNode.expand();
// Verify that "Scale" node appears under the database
const scaleNode = await explorer.waitForNode(`${dbContext.database.id}/Scale`);
expect(scaleNode).toBeDefined();
await expect(scaleNode.element).toBeVisible();
});
test("Add container to shared database without dedicated throughput", async () => {
// Create database with shared manual throughput
dbContext = await createTestDB({ throughput: 400 });
// Wait for the database to appear in the tree
await explorer.waitForNode(dbContext.database.id);
// Add a container to the shared database via UI
const newContainerButton = await explorer.globalCommandButton("New Container");
await newContainerButton.click();
await explorer.whilePanelOpen(
"New Container",
async (panel, okButton) => {
// Select "Use existing" database
const useExistingRadio = panel.getByRole("radio", { name: /Use existing/i });
await useExistingRadio.click();
// Select the database from dropdown using the new data-testid
const databaseDropdown = panel.getByRole("combobox", { name: "Choose an existing database" });
await databaseDropdown.click();
await explorer.frame.getByRole("option", { name: dbContext.database.id }).click();
// Now you can target the specific database option by its data-testid
//await panel.getByTestId(`database-option-${dbContext.database.id}`).click();
// Fill container id
await panel.getByRole("textbox", { name: "Container id, Example Container1" }).fill(containerId);
// Fill partition key
await panel.getByRole("textbox", { name: "Partition key" }).fill("/pk");
// Ensure "Provision dedicated throughput" is NOT checked
const dedicatedThroughputCheckbox = panel.getByRole("checkbox", {
name: /Provision dedicated throughput for this container/i,
});
if (await dedicatedThroughputCheckbox.isVisible()) {
const isChecked = await dedicatedThroughputCheckbox.isChecked();
if (isChecked) {
await dedicatedThroughputCheckbox.uncheck();
}
}
await okButton.click();
},
{ closeTimeout: 5 * ONE_MINUTE_MS },
);
// Verify container was created under the database
const containerNode = await explorer.waitForContainerNode(dbContext.database.id, containerId);
expect(containerNode).toBeDefined();
});
test("Scale shared database manual throughput", async () => {
// Create database with shared manual throughput (400 RU/s)
dbContext = await createTestDB({ throughput: 400 });
// Navigate to the scale settings by clicking the "Scale" node in the tree
const databaseNode = await explorer.waitForNode(dbContext.database.id);
await databaseNode.expand();
const scaleNode = await explorer.waitForNode(`${dbContext.database.id}/Scale`);
await scaleNode.element.click();
// Update manual throughput from 400 to 800
await getThroughputInput("manual").fill(TEST_MANUAL_THROUGHPUT_RU.toString());
// Save changes
await explorer.commandBarButton(CommandBarButton.Save).click();
// Verify success message
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated offer for database ${dbContext.database.id}`,
{
timeout: 2 * ONE_MINUTE_MS,
},
);
});
test("Scale shared database from manual to autoscale", async () => {
// Create database with shared manual throughput (400 RU/s)
dbContext = await createTestDB({ throughput: 400 });
// Open database settings by clicking the "Scale" node
const databaseNode = await explorer.waitForNode(dbContext.database.id);
await databaseNode.expand();
const scaleNode = await explorer.waitForNode(`${dbContext.database.id}/Scale`);
await scaleNode.element.click();
// Switch to Autoscale
const autoscaleRadio = explorer.frame.getByText("Autoscale", { exact: true });
await autoscaleRadio.click();
// Set autoscale max throughput to 1000
//await getThroughputInput("autopilot").fill(TEST_AUTOSCALE_THROUGHPUT_RU.toString());
// Save changes
await explorer.commandBarButton(CommandBarButton.Save).click();
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated offer for database ${dbContext.database.id}`,
{
timeout: 2 * ONE_MINUTE_MS,
},
);
});
});
test.describe("Autoscale Throughput Tests", () => {
test.beforeEach(async ({ page }) => {
explorer = await DataExplorer.open(page, TestAccount.SQL);
});
test("Create database with shared autoscale throughput and verify Scale node in UI", async () => {
test.setTimeout(120000); // 2 minutes timeout
// Create database with shared autoscale throughput (max 1000 RU/s)
dbContext = await createTestDB({ maxThroughput: 1000 });
// Verify database node appears
const databaseNode = await explorer.waitForNode(dbContext.database.id);
expect(databaseNode).toBeDefined();
// Expand the database node to see child nodes
await databaseNode.expand();
// Verify that "Scale" node appears under the database
const scaleNode = await explorer.waitForNode(`${dbContext.database.id}/Scale`);
expect(scaleNode).toBeDefined();
await expect(scaleNode.element).toBeVisible();
});
test("Scale shared database autoscale throughput", async () => {
// Create database with shared autoscale throughput (max 1000 RU/s)
dbContext = await createTestDB({ maxThroughput: 1000 });
// Open database settings
const databaseNode = await explorer.waitForNode(dbContext.database.id);
await databaseNode.expand();
const scaleNode = await explorer.waitForNode(`${dbContext.database.id}/Scale`);
await scaleNode.element.click();
// Update autoscale max throughput from 1000 to 4000
await getThroughputInput("autopilot").fill(TEST_AUTOSCALE_MAX_THROUGHPUT_RU_4K.toString());
// Save changes
await explorer.commandBarButton(CommandBarButton.Save).click();
// Verify success message
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated offer for database ${dbContext.database.id}`,
{
timeout: 2 * ONE_MINUTE_MS,
},
);
});
test("Scale shared database from autoscale to manual", async () => {
// Create database with shared autoscale throughput (max 1000 RU/s)
dbContext = await createTestDB({ maxThroughput: 1000 });
// Open database settings
const databaseNode = await explorer.waitForNode(dbContext.database.id);
await databaseNode.expand();
const scaleNode = await explorer.waitForNode(`${dbContext.database.id}/Scale`);
await scaleNode.element.click();
// Switch to Manual
const manualRadio = explorer.frame.getByText("Manual", { exact: true });
await manualRadio.click();
// Save changes
await explorer.commandBarButton(CommandBarButton.Save).click();
// Verify success message
await expect(explorer.getConsoleHeaderStatus()).toContainText(
`Successfully updated offer for database ${dbContext.database.id}`,
{ timeout: 2 * ONE_MINUTE_MS },
);
});
});
});

View File

@@ -1,9 +1,7 @@
import crypto from "crypto";
import { CosmosDBManagementClient } from "@azure/arm-cosmosdb";
import { BulkOperationType, Container, CosmosClient, CosmosClientOptions, Database, JSONObject } from "@azure/cosmos";
import { AzureIdentityCredentialAdapter } from "@azure/ms-rest-js";
import { Buffer } from "node:buffer";
import { webcrypto } from "node:crypto";
import {
generateUniqueName,
getAccountName,
@@ -12,6 +10,7 @@ import {
subscriptionId,
TestAccount,
} from "./fx";
globalThis.crypto = webcrypto as Crypto;
export interface TestItem {
id: string;
@@ -60,8 +59,9 @@ function createTestItems(): TestItem[] {
// Document IDs cannot contain '/', '\', or '#'
function createSafeRandomString(byteLength: number): string {
return crypto
.randomBytes(byteLength)
const bytes = new Uint8Array(byteLength);
crypto.getRandomValues(bytes);
return Buffer.from(bytes)
.toString("base64")
.replace(/[/\\#]/g, "_");
}
@@ -82,6 +82,74 @@ export class TestContainerContext {
}
}
export class TestDatabaseContext {
constructor(
public armClient: CosmosDBManagementClient,
public client: CosmosClient,
public database: Database,
) {}
async dispose() {
await this.database.delete();
}
}
export interface CreateTestDBOptions {
throughput?: number;
maxThroughput?: number; // For autoscale
}
// Helper function to create ARM client and Cosmos client for SQL account
async function createCosmosClientForSQLAccount(
accountType: TestAccount.SQL | TestAccount.SQLContainerCopyOnly = TestAccount.SQL,
): Promise<{ armClient: CosmosDBManagementClient; client: CosmosClient }> {
const credentials = getAzureCLICredentials();
const armClient = new CosmosDBManagementClient(credentials, subscriptionId);
const accountName = getAccountName(accountType);
const account = await armClient.databaseAccounts.get(resourceGroupName, accountName);
const clientOptions: CosmosClientOptions = {
endpoint: account.documentEndpoint!,
};
const rbacToken =
accountType === TestAccount.SQL
? process.env.NOSQL_TESTACCOUNT_TOKEN
: accountType === TestAccount.SQLContainerCopyOnly
? process.env.NOSQL_CONTAINERCOPY_TESTACCOUNT_TOKEN
: "";
if (rbacToken) {
clientOptions.tokenProvider = async (): Promise<string> => {
const AUTH_PREFIX = `type=aad&ver=1.0&sig=`;
const authorizationToken = `${AUTH_PREFIX}${rbacToken}`;
return authorizationToken;
};
} else {
const keys = await armClient.databaseAccounts.listKeys(resourceGroupName, accountName);
clientOptions.key = keys.primaryMasterKey;
}
const client = new CosmosClient(clientOptions);
return { armClient, client };
}
export async function createTestDB(options?: CreateTestDBOptions): Promise<TestDatabaseContext> {
const databaseId = generateUniqueName("db");
const { armClient, client } = await createCosmosClientForSQLAccount();
// Create database with provisioned throughput (shared throughput)
// This checks the "Provision database throughput" option
const { database } = await client.databases.create({
id: databaseId,
throughput: options?.throughput, // Manual throughput (e.g., 400)
maxThroughput: options?.maxThroughput, // Autoscale max throughput (e.g., 1000)
});
return new TestDatabaseContext(armClient, client, database);
}
type createTestSqlContainerConfig = {
includeTestData?: boolean;
partitionKey?: string;
@@ -104,34 +172,7 @@ export async function createMultipleTestContainers({
const creationPromises: Promise<TestContainerContext>[] = [];
const databaseId = databaseName ? databaseName : generateUniqueName("db");
const credentials = getAzureCLICredentials();
const adaptedCredentials = new AzureIdentityCredentialAdapter(credentials);
const armClient = new CosmosDBManagementClient(adaptedCredentials, subscriptionId);
const accountName = getAccountName(accountType);
const account = await armClient.databaseAccounts.get(resourceGroupName, accountName);
const clientOptions: CosmosClientOptions = {
endpoint: account.documentEndpoint!,
};
const rbacToken =
accountType === TestAccount.SQL
? process.env.NOSQL_TESTACCOUNT_TOKEN
: accountType === TestAccount.SQLContainerCopyOnly
? process.env.NOSQL_CONTAINERCOPY_TESTACCOUNT_TOKEN
: "";
if (rbacToken) {
clientOptions.tokenProvider = async (): Promise<string> => {
const AUTH_PREFIX = `type=aad&ver=1.0&sig=`;
const authorizationToken = `${AUTH_PREFIX}${rbacToken}`;
return authorizationToken;
};
} else {
const keys = await armClient.databaseAccounts.listKeys(resourceGroupName, accountName);
clientOptions.key = keys.primaryMasterKey;
}
const client = new CosmosClient(clientOptions);
const { armClient, client } = await createCosmosClientForSQLAccount(accountType);
const { database } = await client.databases.createIfNotExists({ id: databaseId });
try {
@@ -158,29 +199,8 @@ export async function createTestSQLContainer({
}: createTestSqlContainerConfig = {}) {
const databaseId = databaseName ? databaseName : generateUniqueName("db");
const containerId = "testcontainer"; // A unique container name isn't needed because the database is unique
const credentials = getAzureCLICredentials();
const adaptedCredentials = new AzureIdentityCredentialAdapter(credentials);
const armClient = new CosmosDBManagementClient(adaptedCredentials, subscriptionId);
const accountName = getAccountName(TestAccount.SQL);
const account = await armClient.databaseAccounts.get(resourceGroupName, accountName);
const { armClient, client } = await createCosmosClientForSQLAccount();
const clientOptions: CosmosClientOptions = {
endpoint: account.documentEndpoint!,
};
const nosqlAccountRbacToken = process.env.NOSQL_TESTACCOUNT_TOKEN;
if (nosqlAccountRbacToken) {
clientOptions.tokenProvider = async (): Promise<string> => {
const AUTH_PREFIX = `type=aad&ver=1.0&sig=`;
const authorizationToken = `${AUTH_PREFIX}${nosqlAccountRbacToken}`;
return authorizationToken;
};
} else {
const keys = await armClient.databaseAccounts.listKeys(resourceGroupName, accountName);
clientOptions.key = keys.primaryMasterKey;
}
const client = new CosmosClient(clientOptions);
const { database } = await client.databases.createIfNotExists({ id: databaseId });
try {
const { container } = await database.containers.createIfNotExists({
@@ -211,7 +231,7 @@ export async function createTestSQLContainer({
}
export const setPartitionKeys = (partitionKeys: PartitionKey[]) => {
const result = {};
const result: Record<string, unknown> = {};
partitionKeys.forEach((partitionKey) => {
const { key: keyPath, value: keyValue } = partitionKey;
@@ -224,7 +244,7 @@ export const setPartitionKeys = (partitionKeys: PartitionKey[]) => {
current[key] = keyValue;
} else {
current[key] = current[key] || {};
current = current[key];
current = current[key] as Record<string, unknown>;
}
});
});

View File

@@ -16,14 +16,14 @@ Results of this file should be checked into the repo.
*/
// CHANGE THESE VALUES TO GENERATE NEW CLIENTS
const version = "2025-05-01-preview";
const version = "2025-11-01-preview";
/* The following are legal options for resourceName but you generally will only use cosmos:
"cosmos" | "managedCassandra" | "mongorbac" | "notebook" | "privateEndpointConnection" | "privateLinkResources" |
"rbac" | "restorable" | "services" | "dataTransferService"
*/
const githubResourceName = "cosmos-db";
const deResourceName = "cosmos";
const schemaURL = `https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/${version}/${githubResourceName}.json`;
const schemaURL = `https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/${version}/${githubResourceName}.json`;
const outputDir = path.join(__dirname, `../../src/Utils/arm/generatedClients/${deResourceName}`);
// Array of strings to use for eventual output

View File

@@ -1,66 +1,103 @@
{
"name": "deployment-status",
"version": "1.0.0",
"lockfileVersion": 1,
"lockfileVersion": 3,
"requires": true,
"dependencies": {
"@types/color-name": {
"packages": {
"": {
"name": "deployment-status",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"chalk": "^4.1.0",
"moment": "^2.30.1",
"node-fetch": "^2.6.1"
}
},
"node_modules/@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
},
"ansi-styles": {
"node_modules/ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"requires": {
"dependencies": {
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"chalk": {
"node_modules/chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"requires": {
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"color-convert": {
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"color-name": {
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"has-flag": {
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"engines": {
"node": ">=8"
}
},
"moment": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
},
"node-fetch": {
"node_modules/node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
"engines": {
"node": "4.x || >=6.0.0"
}
},
"supports-color": {
"node_modules/supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
"requires": {
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
}
}

View File

@@ -11,7 +11,7 @@
"license": "ISC",
"dependencies": {
"chalk": "^4.1.0",
"moment": "^2.27.0",
"moment": "^2.30.1",
"node-fetch": "^2.6.1"
}
}

View File

@@ -303,51 +303,53 @@ module.exports = function (_env = {}, argv = {}) {
}
});
// Only handle OPTIONS for /api (preflight)
if (server?.app) {
server.app.options("/api", (_req, res) => res.sendStatus(200));
server.app.options("/api/*", (_req, res) => res.sendStatus(200));
}
return middlewares;
},
proxy: {
"/api": {
proxy: [
{
context: ["/api"],
target: "https://cdb-ms-mpac-pbe.cosmos.azure.com",
changeOrigin: true,
logLevel: "debug",
bypass: (req, res) => {
if (req.method === "OPTIONS") {
res.statusCode = 200;
res.send();
}
},
},
"/proxy": {
{
context: ["/proxy"],
target: "https://cdb-ms-mpac-pbe.cosmos.azure.com",
changeOrigin: true,
secure: false,
logLevel: "debug",
pathRewrite: { "^/proxy": "" },
router: (req) => {
let newTarget = req.headers["x-ms-proxy-target"];
return newTarget;
},
router: (req) => req.headers["x-ms-proxy-target"],
},
"/_explorer": {
{
context: ["/_explorer"],
target: process.env.EMULATOR_ENDPOINT || "https://localhost:8081/",
changeOrigin: true,
secure: false,
logLevel: "debug",
},
"/explorerProxy": {
{
context: ["/explorerProxy"],
target: process.env.EMULATOR_ENDPOINT || "https://localhost:8081/",
pathRewrite: { "^/explorerProxy": "" },
changeOrigin: true,
secure: false,
logLevel: "debug",
},
[`/${AZURE_TENANT_ID}`]: {
{
context: [`/${AZURE_TENANT_ID}`],
target: "https://login.microsoftonline.com/",
changeOrigin: true,
secure: false,
logLevel: "debug",
},
},
],
},
stats: "minimal",
};