diff --git a/.github/.prettierrc.yaml b/.github/.prettierrc.yaml index 6fa04cdc80ad..caa7e1c7a610 100644 --- a/.github/.prettierrc.yaml +++ b/.github/.prettierrc.yaml @@ -1,5 +1,8 @@ # Keep in sync with eng/tools/.prettierrc.yaml +plugins: + - prettier-plugin-organize-imports + # Aligned with microsoft/typespec printWidth: 100 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cc61e016a1b3..c94b23b5fdfc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -67,7 +67,7 @@ # PRLabel: %Compute /specification/compute/ @bilaakpan-ms @sandido @dkulkarni-ms @haagha @MS-syh2qs @grizzlytheodore @mabhard @danielli90 @smotwani @ppatwa @vikramd-ms @yunusm @ZhidongPeng @nkuchta @maheshnemichand @najams @changov -/specification/consumption/ @kjeur @panda-wang +/specification/consumption/ @arusing @micahbresette # PRLabel: %Container Instances /specification/containerinstance/ @novinc @@ -134,13 +134,13 @@ /specification/iothub/ @rkmanda # PRLabel: %KeyVault -/specification/keyvault/resource-manager/ @vickm @chen-karen @cheathamb36 @lgonsoulin +/specification/keyvault/resource-manager/ @vickm @chen-karen @cheathamb36 @Azure/azure-sdk-write-keyvault # PRLabel: %KeyVault -/specification/keyvault/Security.*/ @vickm @chen-karen @cheathamb36 @lgonsoulin @heaths @chlowell +/specification/keyvault/Security.*/ @vickm @chen-karen @cheathamb36 @Azure/azure-sdk-write-keyvault @heaths @chlowell # PRLabel: %KeyVault -/specification/keyvault/data-plane/ @vickm @chen-karen @cheathamb36 @lgonsoulin @heaths +/specification/keyvault/data-plane/ @vickm @chen-karen @cheathamb36 @Azure/azure-sdk-write-keyvault @heaths # PRLabel: %Logic App /specification/logic/ @pankajsn @tonytang-microsoft-com @@ -292,4 +292,6 @@ ## Copilot /.github/copilot-instructions.md @praveenkuttappan @maririos -/.github/prompts/ @praveenkuttappan @maririos +/.github/prompts/ @praveenkuttappan @maririos +/.github/instructions/ @praveenkuttappan @maririos +/.github/chatmodes/ @praveenkuttappan @maririos diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5eea4332f74d..2b0dd39d1399 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -184,7 +184,7 @@ from the list of paths. If user does not have a TypeSpec project, then prompt us - Confirm with the user if they want to change the repo owner or target branch, and prompt for new values if needed. 6. **Exclusions**: - - Exclude changes in `.github` and `.vscode` folders from API spec and SDK pull requests. + - Exclude changes to the `.gitignore` file and contents within the `.github` and `.vscode` folders from API spec and SDK pull requests. 7. **Working Branch Rule**: - Ensure the TypeSpec project repository and the current working repository are not on the `main` branch: @@ -201,7 +201,7 @@ from the list of paths. If user does not have a TypeSpec project, then prompt us By following these rules, the SDK release process will remain clear, structured, and user-friendly. ## Steps to generate SDK from TypeSpec API specification -Follow `/typespec-to-sdk` prompt to generate and release SDK from TypeSpec API specification. The process is divided into several steps, each with specific actions to ensure a smooth SDK generation and release process. +Follow [typespec to sdk](.\prompts\typespec-to-sdk.prompt.md) to generate and release SDK from TypeSpec API specification. The process is divided into several steps, each with specific actions to ensure a smooth SDK generation and release process. Do not skip the step that choose SDK generation method to ensure the user selects the appropriate method for SDK generation, either locally or using the SDK generation pipeline. Do not repeat the steps. 1. **Identify TypeSpec Project**: Locate the TypeSpec project root path by checking for `tspconfig.yaml` or `main.tsp` files. @@ -210,12 +210,12 @@ Do not skip the step that choose SDK generation method to ensure the user select 4. **Review and Commit Changes**: Stage and commit TypeSpec modifications, ensuring the current branch is not "main". Do not create pull request yet. 5. **Create Specification Pull Request**: Create a pull request for TypeSpec changes if not already created. This is required only if there are TypeSpec changes in current branch. 6. **Choose SDK Generation Method**: Determine how to generate SDKs (locally or via pipeline). Only Python is supported for local SDK generation at this time. -7. **Generate SDKs via Pipeline**: Generate SDKs using `/run-sdk-gen-pipeline` prompt, monitor the pipeline status and displaying generated SDK PR links. +7. **Generate SDKs via Pipeline**: Generate SDKs using [run sdk gen pipeline](.\prompts\run-sdk-gen-pipeline.prompt.md), monitor the pipeline status and displaying generated SDK PR links. 8. **Show generated SDK PR**: Display the generated SDK pull request links for review. -9. **Create a release plan**: Create a release plan for the generated SDKs using spec pull request. +9. **Create a release plan**: To create a release plan refer to [create release plan](.\prompts\create-release-plan.prompt.md) 10. **Prompt user to change spec pull request to ready for review from draft status**: Update spec pull request to change it to ready for review. 11. **Release package**: Release the SDK package using `ReleaseSdkPackage` tool. ## Release readiness of SDK and information about the release pipeline -Run `/check-package-readiness` prompt to check the release readiness of an SDK package. This prompt will collect the required information from the user, execute the readiness check, and present the results. \ No newline at end of file +Run [check package readiness](.\prompts\check-package-readiness.prompt.md) to check the release readiness of an SDK package. This prompt will collect the required information from the user, execute the readiness check, and present the results. \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 027cabf407a8..e69bac5f8f86 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,13 +1,10 @@ version: 2 updates: - # For monorepos, only the root folder should be listed (not the sub-folders). - # We may also need to set "versioning-strategy" to "increase" rather than "increase-if-necessary", - # to update both package.json and package-lock.json files. In our case, we don't really care - # if the package.json files are updated or not, just that package-lock.json is update. - # https://github.com/dependabot/dependabot-core/issues/4993#issuecomment-1289133027 - package-ecosystem: "npm" directories: - "/" + - "/eng/tools" + - "/eng/tools/**" schedule: interval: "daily" ignore: @@ -41,7 +38,9 @@ updates: eslint: patterns: - "*eslint*" - # Leave the constraint if the original constraint allows the new version, otherwise, bump the constraint. + openapi-validator: + patterns: + - "@microsoft.azure/openapi-validator*" versioning-strategy: increase-if-necessary - package-ecosystem: "npm" directories: @@ -59,9 +58,10 @@ updates: - dependency-name: "typescript" # Points to "github:actions/github-script" since package isn't published to npmjs - dependency-name: "@types/github-script" + # Stay on ^7 until ^10 increases in adoption + - dependency-name: "cross-env" groups: eslint: patterns: - "*eslint*" - # Leave the constraint if the original constraint allows the new version, otherwise, bump the constraint. versioning-strategy: increase-if-necessary diff --git a/.github/eslint.config.js b/.github/eslint.config.js index d3c33d8d947d..85ab9868ef40 100644 --- a/.github/eslint.config.js +++ b/.github/eslint.config.js @@ -1,5 +1,8 @@ -import pluginJs from "@eslint/js"; +import eslint from "@eslint/js"; import globals from "globals"; +import tseslint from "typescript-eslint"; /** @type {import('eslint').Linter.Config[]} */ -export default [{ languageOptions: { globals: globals.node } }, pluginJs.configs.recommended]; +export default tseslint.config(eslint.configs.recommended, tseslint.configs.recommended, { + languageOptions: { globals: globals.node }, +}); diff --git a/.github/matchers/actionlint.json b/.github/matchers/actionlint.json new file mode 100644 index 000000000000..4613e1617bfe --- /dev/null +++ b/.github/matchers/actionlint.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "actionlint", + "pattern": [ + { + "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$", + "file": 1, + "line": 2, + "column": 3, + "message": 4, + "code": 5 + } + ] + } + ] +} diff --git a/.github/package-lock.json b/.github/package-lock.json index 5ccca37c5b16..f2097d22dd70 100644 --- a/.github/package-lock.json +++ b/.github/package-lock.json @@ -5,11 +5,11 @@ "packages": { "": { "dependencies": { - "@apidevtools/json-schema-ref-parser": "^14.1.0", + "@apidevtools/json-schema-ref-parser": "^14.1.1", "debug": "^4.4.0", "js-yaml": "^4.1.0", "markdown-table": "^3.0.4", - "marked": "^16.0.0", + "marked": "^16.1.1", "simple-git": "^3.27.0", "zod": "^4.0.2" }, @@ -17,6 +17,7 @@ "@actions/github-script": "github:actions/github-script", "@eslint/js": "^9.22.0", "@octokit/rest": "^22.0.0", + "@octokit/types": "^14.1.0", "@octokit/webhooks-types": "^7.5.1", "@tsconfig/node20": "^20.1.4", "@types/debug": "^4.1.12", @@ -27,8 +28,10 @@ "eslint": "^9.22.0", "globals": "^16.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", + "typescript-eslint": "^8.38.0", "vitest": "^3.0.7" } }, @@ -185,9 +188,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -209,9 +212,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -226,9 +229,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -243,9 +246,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -260,9 +263,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -277,9 +280,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -294,9 +297,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -311,9 +314,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -328,9 +331,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -345,9 +348,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -362,9 +365,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -379,9 +382,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -396,9 +399,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -413,9 +416,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -430,9 +433,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -447,9 +450,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -464,9 +467,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -481,9 +484,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -498,9 +501,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -515,9 +518,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -532,9 +535,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -549,9 +552,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -566,9 +569,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", "cpu": [ "arm64" ], @@ -583,9 +586,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -600,9 +603,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -617,9 +620,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -634,9 +637,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -708,9 +711,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -718,9 +721,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -768,9 +771,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", "dev": true, "license": "MIT", "engines": { @@ -791,13 +794,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -962,6 +965,44 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "license": "MIT" }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@octokit/auth-token": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", @@ -991,6 +1032,23 @@ "node": ">= 18" } }, + "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/core/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, "node_modules/@octokit/endpoint": { "version": "9.0.6", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", @@ -1005,6 +1063,23 @@ "node": ">= 18" } }, + "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/endpoint/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, "node_modules/@octokit/graphql": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", @@ -1020,13 +1095,30 @@ "node": ">= 18" } }, - "node_modules/@octokit/openapi-types": { + "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { "version": "24.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "dev": true, "license": "MIT" }, + "node_modules/@octokit/graphql/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", + "dev": true, + "license": "MIT" + }, "node_modules/@octokit/plugin-paginate-rest": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", @@ -1124,6 +1216,23 @@ "@octokit/core": "5" } }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, "node_modules/@octokit/request": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", @@ -1155,6 +1264,40 @@ "node": ">= 18" } }, + "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, "node_modules/@octokit/rest": { "version": "22.0.0", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.0.tgz", @@ -1229,13 +1372,6 @@ "node": ">= 20" } }, - "node_modules/@octokit/rest/node_modules/@octokit/openapi-types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", - "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", - "dev": true, - "license": "MIT" - }, "node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": { "version": "13.1.1", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz", @@ -1311,16 +1447,6 @@ "node": ">= 20" } }, - "node_modules/@octokit/rest/node_modules/@octokit/types": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", - "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/openapi-types": "^25.1.0" - } - }, "node_modules/@octokit/rest/node_modules/before-after-hook": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", @@ -1336,13 +1462,13 @@ "license": "ISC" }, "node_modules/@octokit/types": { - "version": "13.10.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", - "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^24.2.0" + "@octokit/openapi-types": "^25.1.0" } }, "node_modules/@octokit/webhooks-types": { @@ -1364,9 +1490,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.1.tgz", + "integrity": "sha512-oENme6QxtLCqjChRUUo3S6X8hjCXnWmJWnedD7VbGML5GUtaOtAyx+fEEXnBXVf0CBZApMQU0Idwi0FmyxzQhw==", "cpu": [ "arm" ], @@ -1378,9 +1504,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.1.tgz", + "integrity": "sha512-OikvNT3qYTl9+4qQ9Bpn6+XHM+ogtFadRLuT2EXiFQMiNkXFLQfNVppi5o28wvYdHL2s3fM0D/MZJ8UkNFZWsw==", "cpu": [ "arm64" ], @@ -1392,9 +1518,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.1.tgz", + "integrity": "sha512-EFYNNGij2WllnzljQDQnlFTXzSJw87cpAs4TVBAWLdkvic5Uh5tISrIL6NRcxoh/b2EFBG/TK8hgRrGx94zD4A==", "cpu": [ "arm64" ], @@ -1406,9 +1532,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.1.tgz", + "integrity": "sha512-ZaNH06O1KeTug9WI2+GRBE5Ujt9kZw4a1+OIwnBHal92I8PxSsl5KpsrPvthRynkhMck4XPdvY0z26Cym/b7oA==", "cpu": [ "x64" ], @@ -1420,9 +1546,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.1.tgz", + "integrity": "sha512-n4SLVebZP8uUlJ2r04+g2U/xFeiQlw09Me5UFqny8HGbARl503LNH5CqFTb5U5jNxTouhRjai6qPT0CR5c/Iig==", "cpu": [ "arm64" ], @@ -1434,9 +1560,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.1.tgz", + "integrity": "sha512-8vu9c02F16heTqpvo3yeiu7Vi1REDEC/yES/dIfq3tSXe6mLndiwvYr3AAvd1tMNUqE9yeGYa5w7PRbI5QUV+w==", "cpu": [ "x64" ], @@ -1448,9 +1574,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.1.tgz", + "integrity": "sha512-K4ncpWl7sQuyp6rWiGUvb6Q18ba8mzM0rjWJ5JgYKlIXAau1db7hZnR0ldJvqKWWJDxqzSLwGUhA4jp+KqgDtQ==", "cpu": [ "arm" ], @@ -1462,9 +1588,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.1.tgz", + "integrity": "sha512-YykPnXsjUjmXE6j6k2QBBGAn1YsJUix7pYaPLK3RVE0bQL2jfdbfykPxfF8AgBlqtYbfEnYHmLXNa6QETjdOjQ==", "cpu": [ "arm" ], @@ -1476,9 +1602,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.1.tgz", + "integrity": "sha512-kKvqBGbZ8i9pCGW3a1FH3HNIVg49dXXTsChGFsHGXQaVJPLA4f/O+XmTxfklhccxdF5FefUn2hvkoGJH0ScWOA==", "cpu": [ "arm64" ], @@ -1490,9 +1616,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.1.tgz", + "integrity": "sha512-zzX5nTw1N1plmqC9RGC9vZHFuiM7ZP7oSWQGqpbmfjK7p947D518cVK1/MQudsBdcD84t6k70WNczJOct6+hdg==", "cpu": [ "arm64" ], @@ -1504,9 +1630,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.1.tgz", + "integrity": "sha512-O8CwgSBo6ewPpktFfSDgB6SJN9XDcPSvuwxfejiddbIC/hn9Tg6Ai0f0eYDf3XvB/+PIWzOQL+7+TZoB8p9Yuw==", "cpu": [ "loong64" ], @@ -1517,10 +1643,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.1.tgz", + "integrity": "sha512-JnCfFVEKeq6G3h3z8e60kAp8Rd7QVnWCtPm7cxx+5OtP80g/3nmPtfdCXbVl063e3KsRnGSKDHUQMydmzc/wBA==", "cpu": [ "ppc64" ], @@ -1532,9 +1658,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.1.tgz", + "integrity": "sha512-dVxuDqS237eQXkbYzQQfdf/njgeNw6LZuVyEdUaWwRpKHhsLI+y4H/NJV8xJGU19vnOJCVwaBFgr936FHOnJsQ==", "cpu": [ "riscv64" ], @@ -1546,9 +1672,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.1.tgz", + "integrity": "sha512-CvvgNl2hrZrTR9jXK1ye0Go0HQRT6ohQdDfWR47/KFKiLd5oN5T14jRdUVGF4tnsN8y9oSfMOqH6RuHh+ck8+w==", "cpu": [ "riscv64" ], @@ -1560,9 +1686,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.1.tgz", + "integrity": "sha512-x7ANt2VOg2565oGHJ6rIuuAon+A8sfe1IeUx25IKqi49OjSr/K3awoNqr9gCwGEJo9OuXlOn+H2p1VJKx1psxA==", "cpu": [ "s390x" ], @@ -1574,9 +1700,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.1.tgz", + "integrity": "sha512-9OADZYryz/7E8/qt0vnaHQgmia2Y0wrjSSn1V/uL+zw/i7NUhxbX4cHXdEQ7dnJgzYDS81d8+tf6nbIdRFZQoQ==", "cpu": [ "x64" ], @@ -1588,9 +1714,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.1.tgz", + "integrity": "sha512-NuvSCbXEKY+NGWHyivzbjSVJi68Xfq1VnIvGmsuXs6TCtveeoDRKutI5vf2ntmNnVq64Q4zInet0UDQ+yMB6tA==", "cpu": [ "x64" ], @@ -1602,9 +1728,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.1.tgz", + "integrity": "sha512-mWz+6FSRb82xuUMMV1X3NGiaPFqbLN9aIueHleTZCc46cJvwTlvIh7reQLk4p97dv0nddyewBhwzryBHH7wtPw==", "cpu": [ "arm64" ], @@ -1616,9 +1742,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.1.tgz", + "integrity": "sha512-7Thzy9TMXDw9AU4f4vsLNBxh7/VOKuXi73VH3d/kHGr0tZ3x/ewgL9uC7ojUKmH1/zvmZe2tLapYcZllk3SO8Q==", "cpu": [ "ia32" ], @@ -1630,9 +1756,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.1.tgz", + "integrity": "sha512-7GVB4luhFmGUNXXJhH2jJwZCFB3pIOixv2E3s17GQHBFUOQaISlt7aGcQgqvCaDSxTZJUzlK/QJ1FN8S94MrzQ==", "cpu": [ "x64" ], @@ -1705,15 +1831,273 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.8.tgz", - "integrity": "sha512-HzbgCY53T6bfu4tT7Aq3TvViJyHjLjPNaAS3HOuMc9pw97KHsUtXNX4L+wu59g1WnjsZSko35MbEqnO58rihhw==", + "version": "20.19.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", + "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz", + "integrity": "sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/type-utils": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.39.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz", + "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz", + "integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.39.1", + "@typescript-eslint/types": "^8.39.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz", + "integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz", + "integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.1.tgz", + "integrity": "sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz", + "integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.39.1", + "@typescript-eslint/tsconfig-utils": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/visitor-keys": "8.39.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz", + "integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.1", + "@typescript-eslint/types": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz", + "integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.39.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vitest/coverage-v8": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", @@ -1992,6 +2376,19 @@ "concat-map": "0.0.1" } }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -2180,9 +2577,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2193,32 +2590,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/escape-string-regexp": { @@ -2235,20 +2632,20 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2433,6 +2830,36 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2447,19 +2874,14 @@ "dev": true, "license": "MIT" }, - "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" } }, "node_modules/file-entry-cache": { @@ -2475,6 +2897,19 @@ "node": ">=16.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2618,6 +3053,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2705,6 +3147,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2870,9 +3322,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", "dev": true, "license": "MIT" }, @@ -2932,9 +3384,9 @@ } }, "node_modules/marked": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.0.0.tgz", - "integrity": "sha512-MUKMXDjsD/eptB7GPzxo4xcnLS6oo7/RHimUMHEDRhUooPwmN9BEpMl7AEOJv3bmso169wHI2wUF9VQgL7zfmA==", + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.1.2.tgz", + "integrity": "sha512-rNQt5EvRinalby7zJZu/mB+BvaAY2oz3wCuCjt1RDrWNpS1Pdf9xqMOeC9Hm5adBdcV/3XZPJpG58eT+WBc0XQ==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -2943,6 +3395,30 @@ "node": ">= 20" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3140,13 +3616,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -3207,6 +3683,23 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-organize-imports": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prettier": ">=2.0", + "typescript": ">=2.9", + "vue-tsc": "^2.1.0 || 3" + }, + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3217,6 +3710,27 @@ "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "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/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3227,10 +3741,21 @@ "node": ">=4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.1.tgz", + "integrity": "sha512-33xGNBsDJAkzt0PvninskHlWnTIPgDtTwhg0U38CUoNP/7H6wI2Cz6dUeoNPbjdTdsYTGuiFFASuUOWovH0SyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3244,29 +3769,53 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.46.1", + "@rollup/rollup-android-arm64": "4.46.1", + "@rollup/rollup-darwin-arm64": "4.46.1", + "@rollup/rollup-darwin-x64": "4.46.1", + "@rollup/rollup-freebsd-arm64": "4.46.1", + "@rollup/rollup-freebsd-x64": "4.46.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.1", + "@rollup/rollup-linux-arm-musleabihf": "4.46.1", + "@rollup/rollup-linux-arm64-gnu": "4.46.1", + "@rollup/rollup-linux-arm64-musl": "4.46.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.1", + "@rollup/rollup-linux-ppc64-gnu": "4.46.1", + "@rollup/rollup-linux-riscv64-gnu": "4.46.1", + "@rollup/rollup-linux-riscv64-musl": "4.46.1", + "@rollup/rollup-linux-s390x-gnu": "4.46.1", + "@rollup/rollup-linux-x64-gnu": "4.46.1", + "@rollup/rollup-linux-x64-musl": "4.46.1", + "@rollup/rollup-win32-arm64-msvc": "4.46.1", + "@rollup/rollup-win32-ia32-msvc": "4.46.1", + "@rollup/rollup-win32-x64-msvc": "4.46.1", "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "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", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", @@ -3577,6 +4126,34 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", @@ -3607,6 +4184,32 @@ "node": ">=14.0.0" } }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -3644,6 +4247,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.1.tgz", + "integrity": "sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.39.1", + "@typescript-eslint/parser": "8.39.1", + "@typescript-eslint/typescript-estree": "8.39.1", + "@typescript-eslint/utils": "8.39.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/undici": { "version": "5.29.0", "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", @@ -3682,15 +4309,15 @@ } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" @@ -3779,6 +4406,34 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vitest": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", @@ -3852,6 +4507,19 @@ } } }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4011,9 +4679,9 @@ } }, "node_modules/zod": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.5.tgz", - "integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.17.tgz", + "integrity": "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/.github/package.json b/.github/package.json index 54a098101716..276b367f67b8 100644 --- a/.github/package.json +++ b/.github/package.json @@ -6,10 +6,10 @@ "dependencies2": "All runtime and dev dependencies in this file, must be a superset of shared/package.json" }, "dependencies": { - "@apidevtools/json-schema-ref-parser": "^14.1.0", + "@apidevtools/json-schema-ref-parser": "^14.1.1", "debug": "^4.4.0", "js-yaml": "^4.1.0", - "marked": "^16.0.0", + "marked": "^16.1.1", "markdown-table": "^3.0.4", "simple-git": "^3.27.0", "zod": "^4.0.2" @@ -17,8 +17,9 @@ "devDependencies": { "@actions/github-script": "github:actions/github-script", "@eslint/js": "^9.22.0", - "@octokit/webhooks-types": "^7.5.1", "@octokit/rest": "^22.0.0", + "@octokit/types": "^14.1.0", + "@octokit/webhooks-types": "^7.5.1", "@tsconfig/node20": "^20.1.4", "@types/debug": "^4.1.12", "@types/js-yaml": "^4.0.9", @@ -28,8 +29,10 @@ "eslint": "^9.22.0", "globals": "^16.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", + "typescript-eslint": "^8.38.0", "vitest": "^3.0.7" }, "scripts": { @@ -40,6 +43,7 @@ "format:check": "prettier . --check", "format:check:ci": "prettier . --check --log-level debug", "test": "vitest", - "test:ci": "vitest run --coverage --reporter=verbose" + "test:ci": "vitest run --coverage --reporter=verbose", + "validate": "npm run lint && npm run format:check && npm run test:ci" } } diff --git a/.github/prompts/create-release-plan.prompt.md b/.github/prompts/create-release-plan.prompt.md index 0cee3bedde3b..0381c886031b 100644 --- a/.github/prompts/create-release-plan.prompt.md +++ b/.github/prompts/create-release-plan.prompt.md @@ -20,8 +20,7 @@ Follow these steps in order to create or manage a release plan for an API specif - If no release plan exists, proceed to Step 3 ## Step 3: Gather Release Plan Information -Collect the following required information from the user. Do not use non GUID valid for product and service tree Id. Do not create release plan with temporary values. -Do not assume or use default for service tree Id and product service tree Id. Always show the values to user and ask them to confirm it's a valid value in service tree. +Collect the following required information from the user. Do not create a release plan with temporary values. Confirm the values with the user before proceeding to create the release plan. If any details are missing, prompt the user accordingly: - **API Lifecycle Stage**: Must be one of: @@ -41,11 +40,11 @@ If any details are missing, prompt the user accordingly: - Provide this resource: [Release Plan Creation Guide](https://eng.ms/docs/products/azure-developer-experience/plan/release-plan-create) - Once all information is gathered, use `CreateReleasePlan` to create the release plan - Display the newly created release plan details to the user for confirmation -- Run `/sdk-details-in-release-plan` to identify languages configured in the TypeSpec project and add them to the release plan +- Refer [sdk details in release plan](sdk-details-in-release-plan.prompt.md) to identify languages configured in the TypeSpec project and add them to the release plan ## Step 5: Update SDK Details in Release Plan -- Run `/sdk-details-in-release-plan.prompt.md` to add languages and package names to the release plan -- If the TypeSpec project is for a management plane, run `/verify-namespace-approval` if this is first release of SDK. +- Run [sdk details in release plan](sdk-details-in-release-plan.prompt.md) to add languages and package names to the release plan +- If the TypeSpec project is for a management plane, run [verify namespace approval](verify-namespace-approval.prompt.md) if this is first release of SDK. ## Step 6: Link SDK Pull Requests (if applicable) - Ask the user if they have already created SDK pull requests locally for any programming language diff --git a/.github/prompts/typespec-to-sdk.prompt.md b/.github/prompts/typespec-to-sdk.prompt.md index 304910be1399..6844c6640958 100644 --- a/.github/prompts/typespec-to-sdk.prompt.md +++ b/.github/prompts/typespec-to-sdk.prompt.md @@ -21,7 +21,7 @@ Your goal is to guide user through the process of generating SDKs from TypeSpec ## Step 2: Validate TypeSpec Specification **Goal**: Ensure TypeSpec specification compiles without errors **Actions**: -1. Run `/validate-typespec` command +1. Run [validate typespec](validate-typespec.prompt.md) command 2. If validation succeeds, proceed to Step 3 3. If validation fails: - Display all compilation errors to user @@ -61,7 +61,7 @@ Your goal is to guide user through the process of generating SDKs from TypeSpec - Option A: "Generate SDK locally". This is currently supported only for Python. Do not recommend this for other languages. - Option B: "Use SDK generation pipeline" 2. Based on selection: - - If Option A: Run `/create-sdk-locally` and then proceed to Step 6 + - If Option A: Run [create sdk locally](create-sdk-locally.prompt.md) and then proceed to Step 6 - If Option B: Continue to Step 6 **Success Criteria**: SDK generation method selected @@ -71,7 +71,7 @@ Your goal is to guide user through the process of generating SDKs from TypeSpec 1. Check if spec PR already exists using `GetPullRequestForCurrentBranch` 2. If PR exists, display PR details and proceed to Step 7 3. If no PR exists: - - Run `/create-spec-pullrequest` + - Run [create spec pullrequest](create-spec-pullrequest.prompt.md) - Wait for PR creation confirmation - Display created PR details **Success Criteria**: Specification pull request exists @@ -79,12 +79,12 @@ Your goal is to guide user through the process of generating SDKs from TypeSpec ## Step 7: Generate SDKs via Pipeline **Goal**: Create release plan and generate SDKs **Actions**: -1. Run `/create-release-plan` +1. Run [create release plan](create-release-plan.prompt.md) 2. If SDK PRs exist, link them to the release plan -3. Run `/sdk-details-in-release-plan` to add languages and package names to the release plan -4. If TypeSpec project is for management plane, Run `/verify-namespace-approval` to check package namespace approval. +3. Run [sdk details in release plan](sdk-details-in-release-plan.prompt.md) to add languages and package names to the release plan +4. If TypeSpec project is for management plane, Run [verify namespace approval](verify-namespace-approval.prompt.md) to check package namespace approval. This step should not check package readiness to verify namespace approval for management plane SDK. -5. Run `/run-sdk-gen-pipeline` with the spec PR +5. Run [run sdk gen pipeline](run-sdk-gen-pipeline.prompt.md) with the spec PR 6. Monitor pipeline status and provide updates 7. Display generated SDK PR links when available **Success Criteria**: SDK generation pipeline initiated and SDKs generated @@ -97,7 +97,7 @@ This step should not check package readiness to verify namespace approval for ma ## Step 9: Create release plan **Goal**: Create a release plan for the generated SDKs **Actions**: -1. Run `/create-release-plan` to create a release plan using the spec pull request. +1. Run [create release plan](create-release-plan.prompt.md) to create a release plan using the spec pull request. 2. If the release plan already exists, display the existing plan details. ## Step 10: Mark Spec PR as Ready for Review diff --git a/.github/shared/cmd/api-doc-preview.js b/.github/shared/cmd/api-doc-preview.js new file mode 100755 index 000000000000..3c6a75d63fc5 --- /dev/null +++ b/.github/shared/cmd/api-doc-preview.js @@ -0,0 +1,142 @@ +#!/usr/bin/env node +// @ts-check + +import { mkdir, writeFile } from "fs/promises"; +import { dirname, join, resolve } from "path"; +import { fileURLToPath } from "url"; +import { parseArgs } from "util"; + +import { getChangedFilesStatuses, swagger } from "../src/changed-files.js"; + +import { + getSwaggersToProcess, + indexMd, + mappingJSONTemplate, + repoJSONTemplate, +} from "../src/doc-preview.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +function usage() { + console.log(`Usage: +npx api-doc-preview --output + +parameters: + --output Directory to write documentation artifacts to. + --build-id Build ID, used in the documentation index. Defaults to BUILD_BUILDID environment variable. + --spec-repo-name Name of the repository containing the swagger files of the form /. Defaults to BUILD_REPOSITORY_NAME environment variable. + --spec-repo-pr-number PR number of the repository containing the swagger files. Defaults to SYSTEM_PULLREQUEST_PULLREQUESTNUMBER environment variable. + --spec-repo-root Root path of the repository containing the swagger files. Defaults to the root of the repository containing this script.`); +} + +const { + values: { + output: outputDir, + "build-id": buildId, + "spec-repo-name": specRepoName, + "spec-repo-pr-number": specRepoPrNumber, + "spec-repo-root": specRepoRoot, + }, +} = parseArgs({ + options: { + output: { type: "string", default: "" }, + "build-id": { + type: "string", + default: process.env.BUILD_BUILDID || "", + }, + "spec-repo-name": { + type: "string", + default: process.env.BUILD_REPOSITORY_NAME || "", + }, + "spec-repo-pr-number": { + type: "string", + default: process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER || "", + }, + "spec-repo-root": { + type: "string", + default: resolve(__dirname, "../../../"), + }, + }, + allowPositionals: false, +}); + +let validArgs = true; + +if (!outputDir) { + console.log(`Missing required parameter --output. Value given: ${outputDir || ""}`); + validArgs = false; +} + +if (!specRepoName) { + console.log( + `Missing required parameter --spec-repo-name. Value given: ${specRepoName || ""}`, + ); + validArgs = false; +} + +if (!specRepoPrNumber) { + console.log( + `Missing required parameter --spec-repo-pr-number. Value given: ${specRepoPrNumber || ""}`, + ); + validArgs = false; +} + +if (!specRepoRoot) { + console.log(`Invalid parameter --spec-repo-root. Value given: ${specRepoRoot || ""}`); + validArgs = false; +} + +if (!validArgs) { + usage(); + process.exit(1); +} + +// Get selected version and swaggers to process + +const changedFileStatuses = await getChangedFilesStatuses({ + cwd: specRepoRoot, + paths: ["specification"], +}); + +// Exclude deleted files as they are not relevant for generating documentation. +const changedFiles = [ + ...changedFileStatuses.additions, + ...changedFileStatuses.modifications, + // Current names of renamed files are interesting, previous names are not. + ...changedFileStatuses.renames.map((r) => r.to), +]; +console.log(`Found ${changedFiles.length} relevant changed files in ${specRepoRoot}`); +console.log("Changed files:"); +changedFiles.forEach((file) => console.log(` - ${file}`)); +const swaggerPaths = changedFiles.filter(swagger); + +if (swaggerPaths.length === 0) { + console.log("No eligible swagger files found. No documentation artifacts will be written."); + process.exit(0); +} + +const { selectedVersion, swaggersToProcess } = getSwaggersToProcess(swaggerPaths); + +if (swaggersToProcess.length === 0) { + console.log("No swagger files to process. No documentation artifacts will be written."); + process.exit(0); +} + +const repoName = specRepoName; +const prNumber = specRepoPrNumber; + +await mkdir(outputDir, { recursive: true }); +await writeFile( + join(outputDir, "repo.json"), + JSON.stringify(repoJSONTemplate(repoName, prNumber), null, 2), +); +await writeFile( + join(outputDir, "mapping.json"), + JSON.stringify(mappingJSONTemplate(swaggersToProcess), null, 2), +); +await writeFile(join(outputDir, "index.md"), indexMd(buildId, repoName, prNumber)); +console.log(`Documentation preview artifacts written to ${outputDir}`); + +console.log(`Doc preview for API version ${selectedVersion} includes:`); +swaggersToProcess.forEach((swagger) => console.log(` - ${swagger}`)); +console.log(`Artifacts written to: ${outputDir}`); diff --git a/.github/shared/package-lock.json b/.github/shared/package-lock.json index a35c217b74db..4c72b3d8cc4d 100644 --- a/.github/shared/package-lock.json +++ b/.github/shared/package-lock.json @@ -6,13 +6,14 @@ "": { "name": "@azure-tools/specs-shared", "dependencies": { - "@apidevtools/json-schema-ref-parser": "^14.1.0", + "@apidevtools/json-schema-ref-parser": "^14.1.1", "debug": "^4.4.0", "js-yaml": "^4.1.0", - "marked": "^16.0.0", + "marked": "^16.1.1", "simple-git": "^3.27.0" }, "bin": { + "api-doc-preview": "cmd/api-doc-preview.js", "spec-model": "cmd/spec-model.js" }, "devDependencies": { @@ -26,6 +27,7 @@ "eslint": "^9.22.0", "globals": "^16.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", "vitest": "^3.0.7" @@ -98,9 +100,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -122,9 +124,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -139,9 +141,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -156,9 +158,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -173,9 +175,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -190,9 +192,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -207,9 +209,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -224,9 +226,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -241,9 +243,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -258,9 +260,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -275,9 +277,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -292,9 +294,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -309,9 +311,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -326,9 +328,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -343,9 +345,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -360,9 +362,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -377,9 +379,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -394,9 +396,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -411,9 +413,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -428,9 +430,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -445,9 +447,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -462,9 +464,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -479,9 +481,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", "cpu": [ "arm64" ], @@ -496,9 +498,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -513,9 +515,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -530,9 +532,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -547,9 +549,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -621,9 +623,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -631,9 +633,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -681,9 +683,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", "dev": true, "license": "MIT", "engines": { @@ -704,13 +706,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -877,9 +879,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.1.tgz", + "integrity": "sha512-oENme6QxtLCqjChRUUo3S6X8hjCXnWmJWnedD7VbGML5GUtaOtAyx+fEEXnBXVf0CBZApMQU0Idwi0FmyxzQhw==", "cpu": [ "arm" ], @@ -891,9 +893,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.1.tgz", + "integrity": "sha512-OikvNT3qYTl9+4qQ9Bpn6+XHM+ogtFadRLuT2EXiFQMiNkXFLQfNVppi5o28wvYdHL2s3fM0D/MZJ8UkNFZWsw==", "cpu": [ "arm64" ], @@ -905,9 +907,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.1.tgz", + "integrity": "sha512-EFYNNGij2WllnzljQDQnlFTXzSJw87cpAs4TVBAWLdkvic5Uh5tISrIL6NRcxoh/b2EFBG/TK8hgRrGx94zD4A==", "cpu": [ "arm64" ], @@ -919,9 +921,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.1.tgz", + "integrity": "sha512-ZaNH06O1KeTug9WI2+GRBE5Ujt9kZw4a1+OIwnBHal92I8PxSsl5KpsrPvthRynkhMck4XPdvY0z26Cym/b7oA==", "cpu": [ "x64" ], @@ -933,9 +935,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.1.tgz", + "integrity": "sha512-n4SLVebZP8uUlJ2r04+g2U/xFeiQlw09Me5UFqny8HGbARl503LNH5CqFTb5U5jNxTouhRjai6qPT0CR5c/Iig==", "cpu": [ "arm64" ], @@ -947,9 +949,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.1.tgz", + "integrity": "sha512-8vu9c02F16heTqpvo3yeiu7Vi1REDEC/yES/dIfq3tSXe6mLndiwvYr3AAvd1tMNUqE9yeGYa5w7PRbI5QUV+w==", "cpu": [ "x64" ], @@ -961,9 +963,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.1.tgz", + "integrity": "sha512-K4ncpWl7sQuyp6rWiGUvb6Q18ba8mzM0rjWJ5JgYKlIXAau1db7hZnR0ldJvqKWWJDxqzSLwGUhA4jp+KqgDtQ==", "cpu": [ "arm" ], @@ -975,9 +977,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.1.tgz", + "integrity": "sha512-YykPnXsjUjmXE6j6k2QBBGAn1YsJUix7pYaPLK3RVE0bQL2jfdbfykPxfF8AgBlqtYbfEnYHmLXNa6QETjdOjQ==", "cpu": [ "arm" ], @@ -989,9 +991,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.1.tgz", + "integrity": "sha512-kKvqBGbZ8i9pCGW3a1FH3HNIVg49dXXTsChGFsHGXQaVJPLA4f/O+XmTxfklhccxdF5FefUn2hvkoGJH0ScWOA==", "cpu": [ "arm64" ], @@ -1003,9 +1005,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.1.tgz", + "integrity": "sha512-zzX5nTw1N1plmqC9RGC9vZHFuiM7ZP7oSWQGqpbmfjK7p947D518cVK1/MQudsBdcD84t6k70WNczJOct6+hdg==", "cpu": [ "arm64" ], @@ -1017,9 +1019,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.1.tgz", + "integrity": "sha512-O8CwgSBo6ewPpktFfSDgB6SJN9XDcPSvuwxfejiddbIC/hn9Tg6Ai0f0eYDf3XvB/+PIWzOQL+7+TZoB8p9Yuw==", "cpu": [ "loong64" ], @@ -1030,10 +1032,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.1.tgz", + "integrity": "sha512-JnCfFVEKeq6G3h3z8e60kAp8Rd7QVnWCtPm7cxx+5OtP80g/3nmPtfdCXbVl063e3KsRnGSKDHUQMydmzc/wBA==", "cpu": [ "ppc64" ], @@ -1045,9 +1047,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.1.tgz", + "integrity": "sha512-dVxuDqS237eQXkbYzQQfdf/njgeNw6LZuVyEdUaWwRpKHhsLI+y4H/NJV8xJGU19vnOJCVwaBFgr936FHOnJsQ==", "cpu": [ "riscv64" ], @@ -1059,9 +1061,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.1.tgz", + "integrity": "sha512-CvvgNl2hrZrTR9jXK1ye0Go0HQRT6ohQdDfWR47/KFKiLd5oN5T14jRdUVGF4tnsN8y9oSfMOqH6RuHh+ck8+w==", "cpu": [ "riscv64" ], @@ -1073,9 +1075,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.1.tgz", + "integrity": "sha512-x7ANt2VOg2565oGHJ6rIuuAon+A8sfe1IeUx25IKqi49OjSr/K3awoNqr9gCwGEJo9OuXlOn+H2p1VJKx1psxA==", "cpu": [ "s390x" ], @@ -1087,9 +1089,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.1.tgz", + "integrity": "sha512-9OADZYryz/7E8/qt0vnaHQgmia2Y0wrjSSn1V/uL+zw/i7NUhxbX4cHXdEQ7dnJgzYDS81d8+tf6nbIdRFZQoQ==", "cpu": [ "x64" ], @@ -1101,9 +1103,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.1.tgz", + "integrity": "sha512-NuvSCbXEKY+NGWHyivzbjSVJi68Xfq1VnIvGmsuXs6TCtveeoDRKutI5vf2ntmNnVq64Q4zInet0UDQ+yMB6tA==", "cpu": [ "x64" ], @@ -1115,9 +1117,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.1.tgz", + "integrity": "sha512-mWz+6FSRb82xuUMMV1X3NGiaPFqbLN9aIueHleTZCc46cJvwTlvIh7reQLk4p97dv0nddyewBhwzryBHH7wtPw==", "cpu": [ "arm64" ], @@ -1129,9 +1131,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.1.tgz", + "integrity": "sha512-7Thzy9TMXDw9AU4f4vsLNBxh7/VOKuXi73VH3d/kHGr0tZ3x/ewgL9uC7ojUKmH1/zvmZe2tLapYcZllk3SO8Q==", "cpu": [ "ia32" ], @@ -1143,9 +1145,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.1.tgz", + "integrity": "sha512-7GVB4luhFmGUNXXJhH2jJwZCFB3pIOixv2E3s17GQHBFUOQaISlt7aGcQgqvCaDSxTZJUzlK/QJ1FN8S94MrzQ==", "cpu": [ "x64" ], @@ -1218,9 +1220,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.8.tgz", - "integrity": "sha512-HzbgCY53T6bfu4tT7Aq3TvViJyHjLjPNaAS3HOuMc9pw97KHsUtXNX4L+wu59g1WnjsZSko35MbEqnO58rihhw==", + "version": "20.19.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", + "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", "dev": true, "license": "MIT", "dependencies": { @@ -1672,9 +1674,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1685,32 +1687,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/escape-string-regexp": { @@ -1727,20 +1729,20 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2345,9 +2347,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", "dev": true, "license": "MIT" }, @@ -2397,9 +2399,9 @@ } }, "node_modules/marked": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.0.0.tgz", - "integrity": "sha512-MUKMXDjsD/eptB7GPzxo4xcnLS6oo7/RHimUMHEDRhUooPwmN9BEpMl7AEOJv3bmso169wHI2wUF9VQgL7zfmA==", + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.1.2.tgz", + "integrity": "sha512-rNQt5EvRinalby7zJZu/mB+BvaAY2oz3wCuCjt1RDrWNpS1Pdf9xqMOeC9Hm5adBdcV/3XZPJpG58eT+WBc0XQ==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -2662,6 +2664,23 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-organize-imports": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prettier": ">=2.0", + "typescript": ">=2.9", + "vue-tsc": "^2.1.0 || 3" + }, + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2683,9 +2702,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.1.tgz", + "integrity": "sha512-33xGNBsDJAkzt0PvninskHlWnTIPgDtTwhg0U38CUoNP/7H6wI2Cz6dUeoNPbjdTdsYTGuiFFASuUOWovH0SyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2699,26 +2718,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.46.1", + "@rollup/rollup-android-arm64": "4.46.1", + "@rollup/rollup-darwin-arm64": "4.46.1", + "@rollup/rollup-darwin-x64": "4.46.1", + "@rollup/rollup-freebsd-arm64": "4.46.1", + "@rollup/rollup-freebsd-x64": "4.46.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.1", + "@rollup/rollup-linux-arm-musleabihf": "4.46.1", + "@rollup/rollup-linux-arm64-gnu": "4.46.1", + "@rollup/rollup-linux-arm64-musl": "4.46.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.1", + "@rollup/rollup-linux-ppc64-gnu": "4.46.1", + "@rollup/rollup-linux-riscv64-gnu": "4.46.1", + "@rollup/rollup-linux-riscv64-musl": "4.46.1", + "@rollup/rollup-linux-s390x-gnu": "4.46.1", + "@rollup/rollup-linux-x64-gnu": "4.46.1", + "@rollup/rollup-linux-x64-musl": "4.46.1", + "@rollup/rollup-win32-arm64-msvc": "4.46.1", + "@rollup/rollup-win32-ia32-msvc": "4.46.1", + "@rollup/rollup-win32-x64-msvc": "4.46.1", "fsevents": "~2.3.2" } }, @@ -3107,15 +3126,15 @@ } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" diff --git a/.github/shared/package.json b/.github/shared/package.json index 5c94d416ddfb..58b7b4d5a503 100644 --- a/.github/shared/package.json +++ b/.github/shared/package.json @@ -9,30 +9,34 @@ "./equality": "./src/equality.js", "./error-reporting": "./src/error-reporting.js", "./exec": "./src/exec.js", + "./github": "./src/github.js", "./logger": "./src/logger.js", "./path": "./src/path.js", "./readme": "./src/readme.js", "./sdk-types": "./src/sdk-types.js", + "./set": "./src/set.js", "./sleep": "./src/sleep.js", "./sort": "./src/sort.js", "./spec-model-error": "./src/spec-model-error.js", "./spec-model": "./src/spec-model.js", "./swagger": "./src/swagger.js", "./tag": "./src/tag.js", + "./simple-git": "./src/simple-git.js", "./test/examples": "./test/examples.js" }, "bin": { - "spec-model": "./cmd/spec-model.js" + "spec-model": "./cmd/spec-model.js", + "api-doc-preview": "./cmd/api-doc-preview.js" }, "_comments": { "dependencies": "Runtime dependencies must be kept to an absolute minimum for performance, ideally with no transitive dependencies", "dependencies2": "All runtime and dev dependencies in this file, must be a subset of ../package.json" }, "dependencies": { - "@apidevtools/json-schema-ref-parser": "^14.1.0", + "@apidevtools/json-schema-ref-parser": "^14.1.1", "debug": "^4.4.0", "js-yaml": "^4.1.0", - "marked": "^16.0.0", + "marked": "^16.1.1", "simple-git": "^3.27.0" }, "devDependencies": { @@ -46,6 +50,7 @@ "eslint": "^9.22.0", "globals": "^16.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", "vitest": "^3.0.7" diff --git a/.github/shared/src/array.js b/.github/shared/src/array.js index 1279df5e3858..0b8e4ee5aacf 100644 --- a/.github/shared/src/array.js +++ b/.github/shared/src/array.js @@ -23,6 +23,8 @@ export async function flatMapAsync(array, asyncMapper) { } /** + * Returns true if `array` includes no elements from `values` + * * @template T,U * @param {T[]} array * @param {(item: T, index: number, array: T[]) => Promise} asyncMapper @@ -31,3 +33,25 @@ export async function flatMapAsync(array, asyncMapper) { export async function mapAsync(array, asyncMapper) { return Promise.all(array.map(asyncMapper)); } + +/** + * Returns true if `array` includes every element from `values` + * + * @template T + * @param {T[]} array + * @param {T[]} values + * @returns {boolean} + */ +export function includesEvery(array, values) { + return values.every((value) => array.includes(value)); +} + +/** + * @template T + * @param {T[]} array + * @param {T[]} values + * @returns {boolean} + */ +export function includesNone(array, values) { + return values.every((value) => !array.includes(value)); +} diff --git a/.github/shared/src/changed-files.js b/.github/shared/src/changed-files.js index 433ca87ca7ef..4561c394116f 100644 --- a/.github/shared/src/changed-files.js +++ b/.github/shared/src/changed-files.js @@ -2,30 +2,38 @@ import debug from "debug"; import { simpleGit } from "simple-git"; +import { includesFolder } from "./path.js"; // Enable simple-git debug logging to improve console output debug.enable("simple-git"); /** + * Get a list of changed files in a git repository + * * @param {Object} [options] * @param {string} [options.baseCommitish] Default: "HEAD^". * @param {string} [options.cwd] Current working directory. Default: process.cwd(). * @param {string} [options.headCommitish] Default: "HEAD". * @param {import('./logger.js').ILogger} [options.logger] - * @returns {Promise} List of changed files, using posix paths, relative to options.cwd. Example: ["specification/foo/Microsoft.Foo/main.tsp"]. + * @param {string[]} [options.paths] Limits the diff to the named paths. If not set, includes all paths in repo. Default: [] + * @returns {Promise} List of changed files, using posix paths, relative to repo root. Example: ["specification/foo/Microsoft.Foo/main.tsp"]. */ export async function getChangedFiles(options = {}) { - const { baseCommitish = "HEAD^", cwd, headCommitish = "HEAD", logger } = options; + const { baseCommitish = "HEAD^", cwd, headCommitish = "HEAD", logger, paths = [] } = options; + + if (paths.length > 0) { + // Use "--" to separate paths from revisions + paths.unshift("--"); + } // TODO: If we need to filter based on status, instead of passing an argument to `--diff-filter, // consider using "--name-status" instead of "--name-only", and return an array of objects like // { name: "/foo/baz.js", status: Status.Renamed, previousName: "/foo/bar.js"}. // Then add filter functions to filter based on status. This is more flexible and lets consumers // filter based on status with a single call to `git diff`. - const result = await simpleGit(cwd).diff(["--name-only", baseCommitish, headCommitish]); + const result = await simpleGit(cwd).diff(["--name-only", baseCommitish, headCommitish, ...paths]); const files = result.trim().split("\n"); - logger?.info("Changed Files:"); for (const file of files) { logger?.info(` ${file}`); @@ -36,16 +44,32 @@ export async function getChangedFiles(options = {}) { } /** + * Get a list of changed files in a git repository with statuses for additions, + * modifications, deletions, and renames. Warning: rename behavior can vary + * based on the git client's configuration of diff.renames. + * * @param {Object} [options] * @param {string} [options.baseCommitish] Default: "HEAD^". * @param {string} [options.cwd] Current working directory. Default: process.cwd(). * @param {string} [options.headCommitish] Default: "HEAD". * @param {import('./logger.js').ILogger} [options.logger] + * @param {string[]} [options.paths] Limits the diff to the named paths. If not set, includes all paths in repo. Default: [] * @returns {Promise<{additions: string[], modifications: string[], deletions: string[], renames: {from: string, to: string}[], total: number}>} */ export async function getChangedFilesStatuses(options = {}) { - const { baseCommitish = "HEAD^", cwd, headCommitish = "HEAD", logger } = options; - const result = await simpleGit(cwd).diff(["--name-status", baseCommitish, headCommitish]); + const { baseCommitish = "HEAD^", cwd, headCommitish = "HEAD", logger, paths = [] } = options; + + if (paths.length > 0) { + // Use "--" to separate paths from revisions + paths.unshift("--"); + } + + const result = await simpleGit(cwd).diff([ + "--name-status", + baseCommitish, + headCommitish, + ...paths, + ]); const categorizedFiles = { additions: /** @type {string[]} */ ([]), @@ -133,6 +157,7 @@ export async function getChangedFilesStatuses(options = {}) { } // Functions suitable for passing to string[].filter(), ordered roughly in order of increasing specificity +// Functions accept both relative and absolute paths, since paths are resolve()'d before searching (when needed) /** * @param {string} [file] @@ -152,22 +177,13 @@ export function readme(file) { return typeof file === "string" && file.toLowerCase().endsWith("readme.md"); } -/** - * @param {string} [file] - * @returns {boolean} - */ -export function specification(file) { - // Folder name "specification" should match case, since it already exists in repo - return typeof file === "string" && file.startsWith("specification/"); -} - /** * @param {string} [file] * @returns {boolean} */ export function dataPlane(file) { // Folder name "data-plane" should match case for consistency across specs - return typeof file === "string" && specification(file) && file.includes("/data-plane/"); + return typeof file === "string" && includesFolder(file, "data-plane"); } /** @@ -176,7 +192,7 @@ export function dataPlane(file) { */ export function resourceManager(file) { // Folder name "resource-manager" should match case for consistency across specs - return typeof file === "string" && specification(file) && file.includes("/resource-manager/"); + return typeof file === "string" && includesFolder(file, "resource-manager"); } /** @@ -185,11 +201,28 @@ export function resourceManager(file) { */ export function example(file) { // Folder name "examples" should match case for consistency across specs + return typeof file === "string" && json(file) && includesFolder(file, "examples"); +} + +/** + * @param {string} file + * @returns {boolean} + */ +export function typespec(file) { return ( - typeof file === "string" && json(file) && specification(file) && file.includes("/examples/") + typeof file === "string" && + (file.toLowerCase().endsWith(".tsp") || file.toLowerCase().endsWith("tspconfig.yaml")) ); } +/** + * @param {string} [file] + * @returns {boolean} + */ +export function quickstartTemplate(file) { + return typeof file === "string" && json(file) && file.includes("/quickstart-templates/"); +} + /** * @param {string} [file] * @returns {boolean} @@ -200,6 +233,7 @@ export function swagger(file) { json(file) && (dataPlane(file) || resourceManager(file)) && !example(file) && + !quickstartTemplate(file) && !scenario(file) ); } @@ -209,7 +243,5 @@ export function swagger(file) { * @returns {boolean} */ export function scenario(file) { - return ( - typeof file === "string" && json(file) && specification(file) && file.includes("/scenarios/") - ); + return typeof file === "string" && json(file) && includesFolder(file, "scenarios"); } diff --git a/.github/shared/src/doc-preview.js b/.github/shared/src/doc-preview.js new file mode 100644 index 000000000000..d6dde743cad6 --- /dev/null +++ b/.github/shared/src/doc-preview.js @@ -0,0 +1,177 @@ +// @ts-check +const DOCS_NAMESPACE = "_swagger_specs"; +const SPEC_FILE_REGEX = + "(specification/)+(.*)/(resourcemanager|resource-manager|dataplane|data-plane|control-plane)/(.*)/(preview|stable|privatepreview)/(.*?)/(example)?(.*)"; + +/** + * @typedef {Object} SwaggerFileMetadata + * @property {string} path + * @property {string} serviceName + * @property {string} serviceType + * @property {string} resourceProvider + * @property {string} releaseState + * @property {string} apiVersion + * @property {string} fileName + */ + +/** + * @typedef {Object} RepoJSONTemplate + * @property {Object[]} repo + * @property {string} repo[].url + * @property {string} repo[].prNumber + * @property {string} repo[].name + */ + +/** + * @typedef {Object} MappingJSONStructure + * @property {string} target_api_root_dir + * @property {boolean} enable_markdown_fragment + * @property {string} markdown_fragment_folder + * @property {boolean} use_yaml_toc + * @property {boolean} formalize_url + * @property {string[]} version_list + * @property {Object[]} organizations + * @property {string} organizations[].index + * @property {string} organizations[].default_toc_title + * @property {string} organizations[].version + * @property {Object[]} organizations[].services + * @property {string} organizations[].services[].toc_title + * @property {string} organizations[].services[].url_group + * @property {Object[]} organizations[].services[].swagger_files + * @property {string} organizations[].services[].swagger_files[].source + */ + +/** + * Extract swagger file metadata from path. + * @param {string} specPath + * @returns {SwaggerFileMetadata} + */ +export function parseSwaggerFilePath(specPath) { + const m = specPath.match(SPEC_FILE_REGEX); + if (!m) { + throw new Error(`Path "${specPath}" does not match expected swagger file pattern.`); + } + const [path, , serviceName, serviceType, resourceProvider, releaseState, apiVersion, , fileName] = + m; + return { + path, + serviceName, + serviceType, + resourceProvider, + releaseState, + apiVersion, + fileName, + }; +} + +/** + * @param {string} repoName + * @param {string} prNumber + * @returns {object} + */ +export function repoJSONTemplate(repoName, prNumber) { + return { + repo: [ + { + url: `https://github.com/${repoName}`, + prNumber: prNumber, + name: DOCS_NAMESPACE, + }, + ], + }; +} + +/** + * @param {string[]} files + * @returns {MappingJSONStructure} + */ +export function mappingJSONTemplate(files) { + return { + target_api_root_dir: "structured", + enable_markdown_fragment: true, + markdown_fragment_folder: "authored", + use_yaml_toc: true, + formalize_url: true, + version_list: ["default"], + organizations: [ + { + index: "index.md", + default_toc_title: "Getting Started", + version: "default", + services: [ + { + toc_title: "Documentation Preview", + url_group: "documentation-preview", + swagger_files: files.map((source) => ({ + source: `${DOCS_NAMESPACE}/${source}`, + })), + }, + ], + }, + ], + }; +} + +/** + * @param {string} buildId + * @param {string} repoName + * @param {string} prNumber + * @returns {string} + */ +export function indexMd(buildId, repoName, prNumber) { + return `# Documentation Preview for swagger pipeline build #${buildId} + +Welcome to documentation preview for ${repoName}/pull/${prNumber} +created via the swagger pipeline. + +Your documentation may be viewed in the menu on the left hand side. + +If you have issues around documentation generation, please feel free to contact +us in the [Docs Support Teams Channel](https://aka.ms/ci-fix/api-docs-help)`; +} + +/** + * Given a list of changed swagger files, select an API version and a list of + * swagger files in that API version to process. + * @param {string[]} swaggerFiles + **/ +export function getSwaggersToProcess(swaggerFiles) { + const swaggerFileObjs = []; + for (const file of swaggerFiles) { + try { + const parsed = parseSwaggerFilePath(file); + swaggerFileObjs.push(parsed); + } catch (error) { + console.log(`Skipping file "${file}" due to parsing error: ${error}`); + continue; + } + } + + const versions = swaggerFileObjs.map((obj) => obj.apiVersion).filter(Boolean); + if (versions.length === 0) { + console.log("No API versions found in eligible swagger files."); + return { selectedVersion: null, swaggersToProcess: [] }; + } + const uniqueVersions = Array.from(new Set(versions)); + + let selectedVersion; + if (uniqueVersions.length === 1) { + selectedVersion = uniqueVersions[0]; + console.log(`Single API version found: ${selectedVersion}`); + } else { + // This sorting logic is ported from the original code which sorts only the + // strings and doesn't attempt to parse versions for more semantically-aware + // sorting. + const sortedVersions = [...uniqueVersions].sort(); + selectedVersion = sortedVersions[sortedVersions.length - 1]; + console.log( + `Multiple API versions found: ${JSON.stringify(sortedVersions)}. Selected version: ${selectedVersion}`, + ); + } + + const swaggersToProcess = swaggerFileObjs + .filter((obj) => obj.apiVersion === selectedVersion) + .map((obj) => obj.path); + + return { selectedVersion, swaggersToProcess }; +} diff --git a/.github/shared/src/error-reporting.js b/.github/shared/src/error-reporting.js index 71a71a9eab9d..5eab9575c988 100644 --- a/.github/shared/src/error-reporting.js +++ b/.github/shared/src/error-reporting.js @@ -19,6 +19,28 @@ export function setSummary(content) { fs.writeFileSync(summaryFile, content); } +/** + * Set the output for a Github Actions step. The output is written to the GITHUB_OUTPUT environment variable. + * This is used to pass data between steps in a workflow. + * + * To access this output later, leverage: ${{ steps..outputs. }}. + * + * This function is the equivalent of using `core.setOutput(name, value)` in a GitHub Action, without the package dependency. + * @param {string} name - The name of the output variable. + * @param {string} value - The value to set for the output variable. + * @returns {void} + */ +export function setOutput(name, value) { + if (!process.env.GITHUB_OUTPUT) { + console.log(`GITHUB_OUTPUT is not set. Skipping ${name} update with value '${value}.'`); + return; + } + + if (process.env.GITHUB_OUTPUT) { + fs.appendFileSync(process.env.GITHUB_OUTPUT, `${name}=${value}\n`); + } +} + /** * This function is used to ask the github agent to annotate a file in a github PR with an error message. * @param {string} repoPath diff --git a/.github/shared/src/git.js b/.github/shared/src/git.js new file mode 100644 index 000000000000..9ab7cab1d2d6 --- /dev/null +++ b/.github/shared/src/git.js @@ -0,0 +1,11 @@ +// @ts-check + +/** + * Returns true if a string is a possible full git SHA (40 hex chars, case insensitive) + * + * @param {string} string + * @returns {boolean} + */ +export function isFullGitSha(string) { + return /^[0-9a-f]{40}$/i.test(string); +} diff --git a/.github/shared/src/github.js b/.github/shared/src/github.js new file mode 100644 index 000000000000..4c5dafaf3589 --- /dev/null +++ b/.github/shared/src/github.js @@ -0,0 +1,104 @@ +/* v8 ignore start */ + +// @ts-check + +export const PER_PAGE_MAX = 100; + +/** + * https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks#check-statuses-and-conclusions + * + * @readonly + * @enum {"completed" | "expected" | "failure" | "in_progress" | "pending" | "queued" | "requested" | "startup_failure" | "waiting" } + */ +export const CheckStatus = Object.freeze({ + /** + * @description The check run completed and has a conclusion. + */ + COMPLETED: "completed", + /** + * @description The check run is waiting for a status to be reported. + */ + EXPECTED: "expected", + /** + * @description The check run failed. + */ + FAILURE: "failure", + /** + * @description The check run is in progress. + */ + IN_PROGRESS: "in_progress", + /** + * @description The check run is at the front of the queue but the group-based concurrency limit has been reached. + */ + PENDING: "pending", + /** + * @description The check run has been queued. + */ + QUEUED: "queued", + /** + * @description The check run has been created but has not been queued. + */ + REQUESTED: "requested", + /** + * @description The check suite failed during startup. This status is not applicable to check runs. + */ + STARTUP_FAILURE: "startup_failure", + /** + * @description The check run is waiting for a deployment protection rule to be satisfied. + */ + WAITING: "waiting", +}); + +/** + * https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks#check-statuses-and-conclusions + * + * @readonly + * @enum {"action_required" | "cancelled" | "failure" | "neutral" | "skipped" | "stale" | "success" | "timed_out" } + */ +export const CheckConclusion = Object.freeze({ + /** + * @description The check run provided required actions upon its completion. For more information, see Using the REST API to interact with checks. + */ + ACTION_REQUIRED: "action_required", + /** + * @description The check run was cancelled before it completed. + */ + CANCELLED: "cancelled", + /** + * @description The check run failed. + */ + FAILURE: "failure", + /** + * @description The check run completed with a neutral result. This is treated as a success for dependent checks in GitHub Actions. + */ + NEUTRAL: "neutral", + /** + * @description The check run was skipped. This is treated as a success for dependent checks in GitHub Actions. + */ + SKIPPED: "skipped", + /** + * @description The check run was marked stale by GitHub because it took too long. + */ + STALE: "stale", + /** + * @description The check run completed successfully. + */ + SUCCESS: "success", + /** + * @description The check run timed out. + */ + TIMED_OUT: "timed_out", +}); + +/** + * https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#create-a-commit-status--parameters + * + * @readonly + * @enum {"error" | "failure" | "pending" | "success"} + */ +export const CommitStatusState = Object.freeze({ + ERROR: "error", + FAILURE: "failure", + PENDING: "pending", + SUCCESS: "success", +}); diff --git a/.github/shared/src/path.js b/.github/shared/src/path.js index 35382461bfad..fe28bf8afb7e 100644 --- a/.github/shared/src/path.js +++ b/.github/shared/src/path.js @@ -9,5 +9,5 @@ import { resolve, sep } from "path"; * @returns {boolean} True if path contains the named folder */ export function includesFolder(path, folder) { - return resolve(path).includes(sep + folder + sep); + return resolve(path).split(sep).includes(folder); } diff --git a/.github/shared/src/set.js b/.github/shared/src/set.js new file mode 100644 index 000000000000..6dd547dfa88e --- /dev/null +++ b/.github/shared/src/set.js @@ -0,0 +1,13 @@ +// @ts-check + +/** + * @template T + * @param {Set} a + * @param {Set} b + * @returns {Set} + */ +export function intersect(a, b) { + // Since set lookup is O(1), iterate over the smaller set for better perf: O(small) vs O(large) + const [small, large] = a.size < b.size ? [a, b] : [b, a]; + return new Set([...small].filter((value) => large.has(value))); +} diff --git a/.github/shared/src/simple-git.js b/.github/shared/src/simple-git.js new file mode 100644 index 000000000000..c8241c839e35 --- /dev/null +++ b/.github/shared/src/simple-git.js @@ -0,0 +1,13 @@ +import { resolve } from "path"; +import { simpleGit } from "simple-git"; + +/** + * + * @param {string} inputPath + * @returns {Promise} + */ +export async function getRootFolder(inputPath) { + // expecting users to handle the case where inputPath is not a git repo + const gitRoot = await simpleGit(inputPath).revparse("--show-toplevel"); + return resolve(gitRoot.trim()); +} diff --git a/.github/shared/src/spec-model.js b/.github/shared/src/spec-model.js index 53f3fb404e3d..f1fac09e7eb3 100644 --- a/.github/shared/src/spec-model.js +++ b/.github/shared/src/spec-model.js @@ -3,7 +3,12 @@ import { readdir } from "fs/promises"; import { resolve } from "path"; import { flatMapAsync, mapAsync } from "./array.js"; +import { readme } from "./changed-files.js"; import { Readme } from "./readme.js"; +import { SpecModelError } from "./spec-model-error.js"; + +/** @type {Map} */ +const specModelCache = new Map(); /** * @typedef {Object} ToJSONOptions @@ -16,6 +21,7 @@ import { Readme } from "./readme.js"; export class SpecModel { /** @type {string} absolute path */ + // @ts-expect-error Ignore error that value may not be set in ctor (since we may returned cached value) #folder; /** @type {import('./logger.js').ILogger | undefined} */ @@ -30,8 +36,17 @@ export class SpecModel { * @param {import('./logger.js').ILogger} [options.logger] */ constructor(folder, options) { - this.#folder = resolve(folder); + const resolvedFolder = resolve(folder); + + const cachedSpecModel = specModelCache.get(resolvedFolder); + if (cachedSpecModel !== undefined) { + return cachedSpecModel; + } + + this.#folder = resolvedFolder; this.#logger = options?.logger; + + specModelCache.set(resolvedFolder, this); } /** @@ -147,7 +162,12 @@ export class SpecModel { // The swagger file supplied does not exist in the given specModel if (affectedSwaggers.size === 0) { - throw new Error(`No affected swaggers found in specModel for ${swaggerPath}`); + throw new SpecModelError( + `Swagger file ${swaggerPath} not found in specModel.\n` + + `It must be referenced in the "input-file" section of a tag in a readme.md file ` + + `or in a swagger JSON file using $ref.`, + { source: swaggerPath }, + ); } return affectedSwaggers; @@ -187,7 +207,8 @@ export class SpecModel { const readmes = [...(await this.getReadmes()).values()]; const tags = await flatMapAsync(readmes, async (r) => [...(await r.getTags()).values()]); const swaggers = tags.flatMap((t) => [...t.inputFiles.values()]); - return swaggers; + const refs = await flatMapAsync(swaggers, async (s) => [...(await s.getRefs()).values()]); + return [...swaggers, ...refs]; } /** @@ -213,14 +234,3 @@ export class SpecModel { return `SpecModel(${this.#folder}, {logger: ${this.#logger}}})`; } } - -// TODO: Remove duplication with changed-files.js (which currently requires paths relative to repo root) - -/** - * @param {string} [file] - * @returns {boolean} - */ -function readme(file) { - // Filename "readme.md" with any case is a valid README file - return typeof file === "string" && file.toLowerCase().endsWith("readme.md"); -} diff --git a/.github/shared/src/swagger.js b/.github/shared/src/swagger.js index 8532c6cd9787..5a4bece6ccf9 100644 --- a/.github/shared/src/swagger.js +++ b/.github/shared/src/swagger.js @@ -4,6 +4,7 @@ import $RefParser, { ResolverError } from "@apidevtools/json-schema-ref-parser"; import { readFile } from "fs/promises"; import { dirname, relative, resolve } from "path"; import { mapAsync } from "./array.js"; +import { example } from "./changed-files.js"; import { includesFolder } from "./path.js"; import { SpecModelError } from "./spec-model-error.js"; @@ -191,7 +192,7 @@ export class Swagger { * @returns {string} version kind (stable or preview) */ get versionKind() { - return dirname(this.#path).includes("/preview/") + return includesFolder(this.#path, "preview") ? API_VERSION_LIFECYCLE_STAGES.PREVIEW : API_VERSION_LIFECYCLE_STAGES.STABLE; } @@ -222,26 +223,6 @@ export class Swagger { } } -// TODO: Remove duplication with changed-files.js (which currently requires paths relative to repo root) - -/** - * @param {string} [file] - * @returns {boolean} - */ -function example(file) { - // Folder name "examples" should match case for consistency across specs - return typeof file === "string" && json(file) && includesFolder(file, "examples"); -} - -/** - * @param {string} [file] - * @returns {boolean} - */ -function json(file) { - // Extension "json" with any case is a valid JSON file - return typeof file === "string" && file.toLowerCase().endsWith(".json"); -} - // API version lifecycle stages export const API_VERSION_LIFECYCLE_STAGES = Object.freeze({ PREVIEW: "preview", diff --git a/.github/shared/test/array.test.js b/.github/shared/test/array.test.js index 007449622882..6ef31a69906b 100644 --- a/.github/shared/test/array.test.js +++ b/.github/shared/test/array.test.js @@ -1,7 +1,7 @@ // @ts-check import { describe, expect, it } from "vitest"; -import { filterAsync, flatMapAsync, mapAsync } from "../src/array.js"; +import { filterAsync, flatMapAsync, includesEvery, includesNone, mapAsync } from "../src/array.js"; import { sleep } from "../src/sleep.js"; describe("array", () => { @@ -37,4 +37,22 @@ describe("array", () => { expect(result).toEqual([0, 2, 6]); }); + + it("includesEvery", () => { + const input = [1, 2, 3]; + const values = [1, 2]; + + expect(includesEvery(input, values)).toBe(true); + expect(includesEvery(input, [4])).toBe(false); + expect(includesEvery(input, [])).toBe(true); + }); + + it("includesNone", () => { + const input = [1, 2, 3]; + const values = [4, 5]; + + expect(includesNone(input, values)).toBe(true); + expect(includesNone(input, [2])).toBe(false); + expect(includesNone(input, [])).toBe(true); + }); }); diff --git a/.github/shared/test/changed-files.test.js b/.github/shared/test/changed-files.test.js index c4908df76df2..2af62f98b84b 100644 --- a/.github/shared/test/changed-files.test.js +++ b/.github/shared/test/changed-files.test.js @@ -8,6 +8,7 @@ vi.mock("simple-git", () => ({ }), })); +import { resolve } from "path"; import * as simpleGit from "simple-git"; import { dataPlane, @@ -15,11 +16,12 @@ import { getChangedFiles, getChangedFilesStatuses, json, + quickstartTemplate, readme, resourceManager, scenario, - specification, swagger, + typespec, } from "../src/changed-files.js"; import { debugLogger } from "../src/logger.js"; @@ -39,6 +41,20 @@ describe("changedFiles", () => { vi.mocked(simpleGit.simpleGit().diff).mockResolvedValue(files.join("\n")); await expect(getChangedFiles(options)).resolves.toEqual(files); + expect(simpleGit.simpleGit().diff).toHaveBeenCalledWith(["--name-only", "HEAD^", "HEAD"]); + + const specFiles = files.filter((f) => f.startsWith("specification")); + vi.mocked(simpleGit.simpleGit().diff).mockResolvedValue(specFiles.join("\n")); + await expect(getChangedFiles({ ...options, paths: ["specification"] })).resolves.toEqual( + specFiles, + ); + expect(simpleGit.simpleGit().diff).toHaveBeenCalledWith([ + "--name-only", + "HEAD^", + "HEAD", + "--", + "specification", + ]); }); const files = [ @@ -46,91 +62,125 @@ describe("changedFiles", () => { "cspell.yaml", "MixedCase.jSoN", "README.MD", + "not-spec/contosowidgetmanager/data-plane/readme.md", + "not-spec/contosowidgetmanager/resource-manager/readme.md", + "not-spec/contosowidgetmanager/Contoso.Management/main.tsp", + "not-spec/contosowidgetmanager/Contoso.Management/tspconfig.yaml", + "not-spec/contosowidgetmanager/Contoso.Management/examples/2021-11-01/Employees_Get.json", + "not-spec/contosowidgetmanager/Contoso.Management/scenarios/2021-11-01/Employees_Get.json", + "not-spec/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", "specification/contosowidgetmanager/data-plane/readme.md", "specification/contosowidgetmanager/Contoso.Management/main.tsp", + "specification/contosowidgetmanager/Contoso.Management/tspconfig.yaml", "specification/contosowidgetmanager/Contoso.Management/examples/2021-11-01/Employees_Get.json", "specification/contosowidgetmanager/resource-manager/readme.md", "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/examples/Employees_Get.json", "specification/contosowidgetmanager/Contoso.Management/scenarios/2021-11-01/Employees_Get.json", + "specification/compute/quickstart-templates/swagger.json", ]; + const filesResolved = files.map((f) => resolve(f)); + it("filter:json", () => { const expected = [ "cspell.json", "MixedCase.jSoN", + "not-spec/contosowidgetmanager/Contoso.Management/examples/2021-11-01/Employees_Get.json", + "not-spec/contosowidgetmanager/Contoso.Management/scenarios/2021-11-01/Employees_Get.json", + "not-spec/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", "specification/contosowidgetmanager/Contoso.Management/examples/2021-11-01/Employees_Get.json", "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/examples/Employees_Get.json", "specification/contosowidgetmanager/Contoso.Management/scenarios/2021-11-01/Employees_Get.json", + "specification/compute/quickstart-templates/swagger.json", ]; expect(files.filter(json)).toEqual(expected); + expect(filesResolved.filter(json)).toEqual(expected.map((f) => resolve(f))); }); it("filter:readme", () => { const expected = [ "README.MD", + "not-spec/contosowidgetmanager/data-plane/readme.md", + "not-spec/contosowidgetmanager/resource-manager/readme.md", "specification/contosowidgetmanager/data-plane/readme.md", "specification/contosowidgetmanager/resource-manager/readme.md", ]; expect(files.filter(readme)).toEqual(expected); + expect(filesResolved.filter(readme)).toEqual(expected.map((f) => resolve(f))); }); - it("filter:specification", () => { + it("filter:typespec", () => { const expected = [ - "specification/contosowidgetmanager/data-plane/readme.md", + "not-spec/contosowidgetmanager/Contoso.Management/main.tsp", + "not-spec/contosowidgetmanager/Contoso.Management/tspconfig.yaml", "specification/contosowidgetmanager/Contoso.Management/main.tsp", - "specification/contosowidgetmanager/Contoso.Management/examples/2021-11-01/Employees_Get.json", - "specification/contosowidgetmanager/resource-manager/readme.md", - "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", - "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/examples/Employees_Get.json", - "specification/contosowidgetmanager/Contoso.Management/scenarios/2021-11-01/Employees_Get.json", + "specification/contosowidgetmanager/Contoso.Management/tspconfig.yaml", ]; - - expect(files.filter(specification)).toEqual(expected); + expect(files.filter(typespec)).toEqual(expected); }); it("filter:data-plane", () => { - const expected = ["specification/contosowidgetmanager/data-plane/readme.md"]; + const expected = [ + "not-spec/contosowidgetmanager/data-plane/readme.md", + "specification/contosowidgetmanager/data-plane/readme.md", + ]; expect(files.filter(dataPlane)).toEqual(expected); + expect(filesResolved.filter(dataPlane)).toEqual(expected.map((f) => resolve(f))); }); it("filter:resource-manager", () => { const expected = [ + "not-spec/contosowidgetmanager/resource-manager/readme.md", + "not-spec/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", "specification/contosowidgetmanager/resource-manager/readme.md", "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/examples/Employees_Get.json", ]; expect(files.filter(resourceManager)).toEqual(expected); + expect(filesResolved.filter(resourceManager)).toEqual(expected.map((f) => resolve(f))); }); it("filter:example", () => { const expected = [ + "not-spec/contosowidgetmanager/Contoso.Management/examples/2021-11-01/Employees_Get.json", "specification/contosowidgetmanager/Contoso.Management/examples/2021-11-01/Employees_Get.json", "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/examples/Employees_Get.json", ]; expect(files.filter(example)).toEqual(expected); + expect(filesResolved.filter(example)).toEqual(expected.map((f) => resolve(f))); + }); + + it("filter:quickstartTemplate", () => { + const expected = ["specification/compute/quickstart-templates/swagger.json"]; + + expect(files.filter(quickstartTemplate)).toEqual(expected); }); it("filter:scenarios", () => { const expected = [ + "not-spec/contosowidgetmanager/Contoso.Management/scenarios/2021-11-01/Employees_Get.json", "specification/contosowidgetmanager/Contoso.Management/scenarios/2021-11-01/Employees_Get.json", ]; expect(files.filter(scenario)).toEqual(expected); + expect(filesResolved.filter(scenario)).toEqual(expected.map((f) => resolve(f))); }); it("filter:swagger", () => { const expected = [ + "not-spec/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", "specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", ]; expect(files.filter(swagger)).toEqual(expected); + expect(filesResolved.filter(swagger)).toEqual(expected.map((f) => resolve(f))); }); describe("getChangedFilesStatuses", () => { @@ -138,6 +188,7 @@ describe("changedFiles", () => { "should categorize files correctly with all types of changes (%o)", async (options) => { const gitOutput = [ + "M\t.github/src/changed-files.js", "A\tspecification/new-service/readme.md", "M\tspecification/existing-service/main.tsp", "D\tspecification/old-service/contoso.json", @@ -147,7 +198,31 @@ describe("changedFiles", () => { ].join("\n"); vi.mocked(simpleGit.simpleGit().diff).mockResolvedValue(gitOutput); - const result = await getChangedFilesStatuses(options); + let result = await getChangedFilesStatuses(options); + expect(result).toEqual({ + additions: ["specification/new-service/readme.md", "specification/service/derived.json"], + modifications: [ + ".github/src/changed-files.js", + "specification/existing-service/main.tsp", + "specification/service/type-changed.json", + ], + deletions: ["specification/old-service/contoso.json"], + renames: [ + { + from: "specification/service/old-name.json", + to: "specification/service/new-name.json", + }, + ], + total: 7, + }); + expect(simpleGit.simpleGit().diff).toHaveBeenCalledWith(["--name-status", "HEAD^", "HEAD"]); + + const specGitOutput = gitOutput + .split("\n") + .filter((f) => f.includes("specification/")) + .join("\n"); + vi.mocked(simpleGit.simpleGit().diff).mockResolvedValue(specGitOutput); + result = await getChangedFilesStatuses({ ...options, paths: ["specification"] }); expect(result).toEqual({ additions: ["specification/new-service/readme.md", "specification/service/derived.json"], modifications: [ @@ -163,6 +238,13 @@ describe("changedFiles", () => { ], total: 6, }); + expect(simpleGit.simpleGit().diff).toHaveBeenCalledWith([ + "--name-status", + "HEAD^", + "HEAD", + "--", + "specification", + ]); }, ); diff --git a/.github/shared/test/doc-preview.test.js b/.github/shared/test/doc-preview.test.js new file mode 100644 index 000000000000..315632be4d59 --- /dev/null +++ b/.github/shared/test/doc-preview.test.js @@ -0,0 +1,157 @@ +// @ts-check + +import { describe, expect, test } from "vitest"; +import { + getSwaggersToProcess, + indexMd, + mappingJSONTemplate, + parseSwaggerFilePath, + repoJSONTemplate, +} from "../src/doc-preview.js"; + +describe("parseSwaggerFilePath", () => { + test("throws null when given invalid path", () => { + expect(() => parseSwaggerFilePath("invalid/path/to/swagger.json")).toThrow(); + }); + + test("parses valid swagger file path", () => { + const path = + "specification/batch/data-plane/Azure.Batch/preview/2024-07-01.20.0/BatchService.json"; + const result = parseSwaggerFilePath(path); + + expect(result).toEqual({ + path: path, + serviceName: "batch", + serviceType: "data-plane", + resourceProvider: "Azure.Batch", + releaseState: "preview", + apiVersion: "2024-07-01.20.0", + fileName: "BatchService.json", + }); + }); +}); + +describe("getSwaggersToProcess", () => { + test("returns empty arrays when no files match the expected pattern", () => { + const files = ["invalid/path/to/swagger.json", "another/invalid/path/swagger.json"]; + const { selectedVersion, swaggersToProcess } = getSwaggersToProcess(files); + expect(selectedVersion).toBeNull(); + expect(swaggersToProcess).toEqual([]); + }); + + test("skips files that do not match the expected pattern", () => { + const files = [ + "specification/batch/data-plane/Azure.Batch/preview/2024-07-01.20.0/BatchService.json", + "invalid/path/to/swagger.json", + ]; + + const { selectedVersion, swaggersToProcess } = getSwaggersToProcess(files); + + expect(selectedVersion).toEqual("2024-07-01.20.0"); + expect(swaggersToProcess).toEqual([ + "specification/batch/data-plane/Azure.Batch/preview/2024-07-01.20.0/BatchService.json", + ]); + }); + + test("returns swaggers to process for valid files", () => { + const files = [ + "specification/batch/data-plane/Azure.Batch/preview/2024-07-01.20.0/BatchService.json", + ]; + + const { selectedVersion, swaggersToProcess } = getSwaggersToProcess(files); + + expect(selectedVersion).toEqual("2024-07-01.20.0"); + expect(swaggersToProcess).toEqual(files); + }); + + test("selects the latest version from multiple files with multiple versions", () => { + const files = [ + "specification/batch/data-plane/Azure.Batch/preview/2024-07-01.20.0/BatchService.json", + "specification/batch/data-plane/Azure.Batch/preview/2025-06-01/BatchService.json", + ]; + + const { selectedVersion, swaggersToProcess } = getSwaggersToProcess(files); + + expect(selectedVersion).toEqual("2025-06-01"); + expect(swaggersToProcess).toEqual([files[1]]); + }); +}); + +describe("repoJSONTemplate", () => { + test("matches snapshot", () => { + const actual = repoJSONTemplate("test-repo", "1234"); + + expect(actual).toMatchInlineSnapshot(` + { + "repo": [ + { + "name": "_swagger_specs", + "prNumber": "1234", + "url": "https://github.com/test-repo", + }, + ], + } + `); + }); +}); + +describe("mappingJSONTemplate", () => { + test("matches snapshot", () => { + const swaggers = [ + "specification/batch/data-plane/Azure.Batch/preview/2024-07-01.20.0/BatchService.json", + ]; + const actual = mappingJSONTemplate(swaggers); + + expect(actual).toMatchInlineSnapshot(` + { + "enable_markdown_fragment": true, + "formalize_url": true, + "markdown_fragment_folder": "authored", + "organizations": [ + { + "default_toc_title": "Getting Started", + "index": "index.md", + "services": [ + { + "swagger_files": [ + { + "source": "_swagger_specs/specification/batch/data-plane/Azure.Batch/preview/2024-07-01.20.0/BatchService.json", + }, + ], + "toc_title": "Documentation Preview", + "url_group": "documentation-preview", + }, + ], + "version": "default", + }, + ], + "target_api_root_dir": "structured", + "use_yaml_toc": true, + "version_list": [ + "default", + ], + } + `); + }); +}); + +describe("indexMd", () => { + test("matches snapshot", () => { + const buildId = "build-123"; + const repoName = "test-repo"; + const prNumber = "1234"; + const actual = indexMd(buildId, repoName, prNumber); + + expect(actual).toMatchInlineSnapshot(` + "# Documentation Preview for swagger pipeline build #build-123 + + Welcome to documentation preview for test-repo/pull/1234 + created via the swagger pipeline. + + Your documentation may be viewed in the menu on the left hand side. + + If you have issues around documentation generation, please feel free to contact + us in the [Docs Support Teams Channel](https://aka.ms/ci-fix/api-docs-help)" + `); + }); +}); diff --git a/.github/shared/test/equality.test.js b/.github/shared/test/equality.test.js index 1dbcc77b255a..b4ab178f4e6e 100644 --- a/.github/shared/test/equality.test.js +++ b/.github/shared/test/equality.test.js @@ -31,9 +31,9 @@ describe("equality", () => { [new Set([1, 2]), new Set([1, 2]), true], [new Set([1, 2, 3]), new Set([1, 2, 3]), true], ])("setEquals(%s, %s, %s)", (set1, set2, expected) => { - // @ts-ignore + // @ts-expect-error testing runtime behavior of invalid types expect(setEquals(set1, set2)).toBe(expected); - // @ts-ignore + // @ts-expect-error testing runtime behavior of invalid types expect(setEquals(set2, set1)).toBe(expected); }); }); diff --git a/.github/shared/test/error-reporting.test.js b/.github/shared/test/error-reporting.test.js index 107e2141de71..0fa3a2c64ea5 100644 --- a/.github/shared/test/error-reporting.test.js +++ b/.github/shared/test/error-reporting.test.js @@ -1,8 +1,8 @@ // @ts-check -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import { setSummary, annotateFileError } from "../src/error-reporting.js"; import fs from "fs/promises"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { annotateFileError, setOutput, setSummary } from "../src/error-reporting.js"; describe("ErrorReporting", () => { let logSpy; @@ -12,13 +12,21 @@ describe("ErrorReporting", () => { // ensure that on test runs GITHUB_STEP_SUMMARY is not set in my current env by default // this gives us a clean slate for each test delete process.env.GITHUB_STEP_SUMMARY; + delete process.env.GITHUB_OUTPUT; }); afterEach(() => { logSpy.mockRestore(); }); - it("should warn when GITHUB_STEP_SUMMARY is unset", () => { + it("should warn when calling setOutput when GITHUB_OUTPUT is unset", () => { + setOutput("test", "value"); + expect(logSpy).toHaveBeenCalledWith( + "GITHUB_OUTPUT is not set. Skipping test update with value 'value.'", + ); + }); + + it("should warn when calling setSummary when GITHUB_STEP_SUMMARY is unset", () => { setSummary("hello"); expect(logSpy).toHaveBeenCalledWith("GITHUB_STEP_SUMMARY is not set. Skipping summary update."); }); @@ -42,6 +50,22 @@ describe("ErrorReporting", () => { expect(content).toBe("# Title"); }); + it("should write an output when GITHUB_OUTPUT is set", async () => { + process.env.GITHUB_OUTPUT = `${__dirname}/tmp-output.txt`; + + setOutput("test", "value"); + + expect(logSpy).not.toHaveBeenCalledWith( + "GITHUB_OUTPUT is not set. Skipping test update with value 'value.'", + ); + + const content = await fs.readFile(process.env.GITHUB_OUTPUT, "utf-8"); + expect(content).toBe("test=value\n"); + + // cleanup after the test so nothing is left behind + await fs.unlink(process.env.GITHUB_OUTPUT); + }); + it("should emit a GitHub-style error annotation", () => { annotateFileError("src/foo.js", "Something broke", 42, 7); expect(logSpy).toHaveBeenCalledWith("::error file=src/foo.js,line=42,col=7::Something broke"); diff --git a/.github/shared/test/examples.js b/.github/shared/test/examples.js index 12336f56be2f..d8924c0183d7 100644 --- a/.github/shared/test/examples.js +++ b/.github/shared/test/examples.js @@ -1,5 +1,7 @@ // @ts-check +export const fullGitSha = "abc123abc123abc123abc123abc123abc123abc1"; + export const swaggerHandWritten = JSON.stringify("foo"); export const swaggerTypeSpecGenerated = JSON.stringify({ diff --git a/.github/shared/test/fixtures/swagger/specification/common-types/resource-management/v2/types.json b/.github/shared/test/fixtures/swagger/specification/common-types/resource-management/v2/types.json new file mode 100644 index 000000000000..31f4bcaf80de --- /dev/null +++ b/.github/shared/test/fixtures/swagger/specification/common-types/resource-management/v2/types.json @@ -0,0 +1,26 @@ +{ + "swagger": "2.0", + "info": { + "title": "Common Types", + "version": "v2" + }, + "definitions": { + "Resource": { + "type": "object", + "properties": { + "id": { + "type": "string", + "readOnly": true + }, + "name": { + "type": "string", + "readOnly": true + }, + "type": { + "type": "string", + "readOnly": true + } + } + } + } +} diff --git a/.github/shared/test/fixtures/swagger/specification/common-types/resource-management/v3/types.json b/.github/shared/test/fixtures/swagger/specification/common-types/resource-management/v3/types.json new file mode 100644 index 000000000000..6d1a88e373f2 --- /dev/null +++ b/.github/shared/test/fixtures/swagger/specification/common-types/resource-management/v3/types.json @@ -0,0 +1,69 @@ +{ + "swagger": "2.0", + "info": { + "title": "Common Types - Resource Management", + "version": "v3", + "description": "Common types for resource management APIs" + }, + "host": "management.azure.com", + "schemes": ["https"], + "consumes": ["application/json"], + "produces": ["application/json"], + "definitions": { + "Resource": { + "type": "object", + "description": "Common fields that are returned in the response for all Azure Resource Manager resources", + "properties": { + "id": { + "readOnly": true, + "type": "string", + "description": "Fully qualified resource ID for the resource. Ex - /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}" + }, + "name": { + "readOnly": true, + "type": "string", + "description": "The name of the resource" + }, + "type": { + "readOnly": true, + "type": "string", + "description": "The type of the resource. E.g. \"Microsoft.Compute/virtualMachines\" or \"Microsoft.Storage/storageAccounts\"" + } + } + }, + "ProxyResource": { + "type": "object", + "description": "The resource model definition for a Azure Resource Manager proxy resource.", + "allOf": [ + { + "$ref": "#/definitions/Resource" + } + ] + }, + "TrackedResource": { + "type": "object", + "description": "The resource model definition for an Azure Resource Manager tracked top level resource", + "allOf": [ + { + "$ref": "#/definitions/Resource" + } + ], + "properties": { + "tags": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Resource tags." + }, + "location": { + "type": "string", + "description": "The geo-location where the resource lives" + } + }, + "required": [ + "location" + ] + } + } +} diff --git a/.github/shared/test/git.test.js b/.github/shared/test/git.test.js new file mode 100644 index 000000000000..d5e08121e2a4 --- /dev/null +++ b/.github/shared/test/git.test.js @@ -0,0 +1,25 @@ +// @ts-check + +import { describe, expect, it } from "vitest"; +import { isFullGitSha } from "../src/git"; +import { fullGitSha } from "./examples"; + +describe("git", () => { + it.each([ + [undefined, false], + [null, false], + ["", false], + // Short SHAs of 7 chars are not valid + ["abc1234", false], + // Invalid hex chars + ["aBcDeG0189".repeat(4), false], + ["aBcDe 0189".repeat(4), false], + ["aBcDe_0189".repeat(4), false], + // Valid + ["aBcDeF0189".repeat(4), true], + [fullGitSha, true], + ])("isFullGitSha(%o) => %o", (string, result) => { + // @ts-expect-error Testing invalid input types + expect(isFullGitSha(string)).toBe(result); + }); +}); diff --git a/.github/shared/test/path.test.js b/.github/shared/test/path.test.js index 600e8a1fc91d..2f7e29aa040d 100644 --- a/.github/shared/test/path.test.js +++ b/.github/shared/test/path.test.js @@ -1,11 +1,11 @@ // @ts-check -import { describe, it, beforeEach, afterEach } from "vitest"; -import { includesFolder } from "../src/path.js"; import { strict as assert } from "assert"; -import { join, dirname } from "path"; +import { existsSync, mkdirSync, rmSync } from "fs"; +import { dirname, join } from "path"; import { fileURLToPath } from "url"; -import { mkdirSync, rmSync, existsSync } from "fs"; +import { afterEach, beforeEach, describe, it } from "vitest"; +import { includesFolder } from "../src/path.js"; // Get the directory of this test file const __filename = fileURLToPath(import.meta.url); @@ -36,6 +36,7 @@ describe("Path utilities", () => { describe("includesFolder", () => { it("should return true when path contains the specified folder", () => { assert.equal(includesFolder("/path/to/examples/file.json", "examples"), true); + assert.equal(includesFolder("/path/to/examples", "examples"), true); }); it("should return false when path does not contain the specified folder", () => { diff --git a/.github/shared/test/set.test.js b/.github/shared/test/set.test.js new file mode 100644 index 000000000000..97c18b13a2c3 --- /dev/null +++ b/.github/shared/test/set.test.js @@ -0,0 +1,42 @@ +// @ts-check + +import { describe, expect, it } from "vitest"; +import { intersect } from "../src/set"; + +describe("set", () => { + it.each([ + [[], [], []], + [[1, 2, 3], [], []], + [ + [1, 2, 3], + [2, 3, 4], + [2, 3], + ], + [[1, 2, 3], [4, 5, 6], []], + [ + [1, 2, 3], + [1, 2, 3], + [1, 2, 3], + ], + [ + ["a", "b", "c"], + ["b", "c", "d"], + ["b", "c"], + ], + ])( + "intersect(%o, %o, %o)", + async ( + /** @type {(string|number)[]} */ a, + /** @type {(string|number)[]} */ b, + /** @type {(string|number)[]} */ result, + ) => { + const setA = new Set(a); + const setB = new Set(b); + const setResult = new Set(result); + + // Check both orders, result should be same + expect(intersect(setA, setB)).toEqual(setResult); + expect(intersect(setB, setA)).toEqual(setResult); + }, + ); +}); diff --git a/.github/shared/test/simple-git.test.js b/.github/shared/test/simple-git.test.js new file mode 100644 index 000000000000..706ab6407ce8 --- /dev/null +++ b/.github/shared/test/simple-git.test.js @@ -0,0 +1,20 @@ +import { mkdtemp, rm } from "fs/promises"; +import os from "os"; +import path from "path"; +import { describe, expect, it } from "vitest"; +import { getRootFolder } from "../src/simple-git.js"; + +describe("getRootFolder", () => { + it("resolves to repo root from a nested folder", async () => { + const testDir = __dirname; + const calculatedRoot = await getRootFolder(testDir); + const expectedRoot = path.resolve(path.join(__dirname, "..", "..", "..")); + expect(calculatedRoot).toBe(expectedRoot); + }); + + it("throws when directory is not a git repository", async () => { + const tempDir = await mkdtemp(path.join(os.tmpdir(), "non-git-")); + await expect(getRootFolder(tempDir)).rejects.toThrow(); + await rm(tempDir, { recursive: true, force: true }); + }); +}); diff --git a/.github/shared/test/spec-model.test.js b/.github/shared/test/spec-model.test.js index 128b563d3dd9..3477937a1209 100644 --- a/.github/shared/test/spec-model.test.js +++ b/.github/shared/test/spec-model.test.js @@ -1,5 +1,6 @@ // @ts-check +import { randomUUID } from "crypto"; import { readdir } from "fs/promises"; import { dirname, isAbsolute, join, resolve } from "path"; import { describe, expect, it } from "vitest"; @@ -18,6 +19,15 @@ describe("SpecModel", () => { await expect(specModel.getReadmes()).rejects.toThrowError(/no such file or directory/i); }); + it("returns cached spec model", async () => { + const path = randomUUID(); + + const specModel1 = new SpecModel(path); + const specModel2 = new SpecModel(path); + + expect(specModel1).toBe(specModel2); + }); + it("returns spec model", async () => { const folder = resolve( __dirname, @@ -107,7 +117,7 @@ describe("SpecModel", () => { const tag = globalConfig["tag"]; - // @ts-ignore + // @ts-expect-error testing runtime behavior of invalid types expect(tag).not.toBeTypeOf(Date); expect(tag).toBeTypeOf("string"); @@ -222,7 +232,7 @@ describe("SpecModel", () => { const swaggerPath = resolve(folder, "data-plane/not-found.json"); await expect(specModel.getAffectedSwaggers(swaggerPath)).rejects.toThrowError( - /no affected swaggers/i, + /not found in specModel/i, ); }); @@ -431,22 +441,27 @@ describe("getSwaggers", () => { }); it("should work with swagger fixtures", async () => { - const folder = resolve( - __dirname, - "fixtures/swagger/specification/servicelinker/resource-manager", - ); + const folder = resolve(__dirname, "fixtures/swagger"); const specModel = new SpecModel(folder, options); const swaggers = await specModel.getSwaggers(); - // Should return an array (may be empty if no valid readmes in this fixture) - expect(swaggers.length).toBe(5); - // If swaggers are found, they should have the expected structure - for (const swagger of swaggers) { - expect(swagger.path).toBeDefined(); - expect(swagger.versionKind).toBeDefined(); - } - expect(swaggers[0].path).contains(folder); - expect(swaggers[0].versionKind).toBe("stable"); + // Expected paths as complete normalized paths + const expectedSwaggerPaths = [ + "specification/common-types/resource-management/v2/types.json", + "specification/common-types/resource-management/v3/types.json", + "specification/common-types/resource-management/v3/types.json", + "specification/common-types/resource-management/v3/types.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/preview/2023-04-01-preview/servicelinker.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/preview/2024-07-01-preview/servicelinker.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/stable/2022-05-01/servicelinker.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/stable/2024-04-01/servicelinker.json", + "specification/servicelinker/resource-manager/Microsoft.ServiceLinker/stable/2024-04-01/test.json", + ]; + + // Sort swaggers by path to ensure consistent order + expect(swaggers.sort((a, b) => a.path.localeCompare(b.path)).map((s) => s.path)).toEqual( + expectedSwaggerPaths.map((p) => resolve(folder, p)), + ); }); }); diff --git a/.github/shared/test/swagger.test.js b/.github/shared/test/swagger.test.js index 13bb7ca53a55..7704a5ba099a 100644 --- a/.github/shared/test/swagger.test.js +++ b/.github/shared/test/swagger.test.js @@ -1,14 +1,14 @@ // @ts-check -import { dirname, resolve, join } from "path"; +import { dirname, join, resolve } from "path"; import { describe, expect, it } from "vitest"; -import { Swagger } from "../src/swagger.js"; +import { API_VERSION_LIFECYCLE_STAGES, Swagger } from "../src/swagger.js"; import { fileURLToPath } from "url"; +import { ConsoleLogger } from "../src/logger.js"; import { Readme } from "../src/readme.js"; -import { Tag } from "../src/tag.js"; import { SpecModel } from "../src/spec-model.js"; -import { ConsoleLogger } from "../src/logger.js"; +import { Tag } from "../src/tag.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -51,6 +51,34 @@ describe("Swagger", () => { ); }); + it("returns examples", async () => { + const swagger = new Swagger(resolve(__dirname, "fixtures/swagger/ignoreExamples/swagger.json")); + const examples = await swagger.getExamples(); + + const expectedExamplePath = resolve( + __dirname, + "fixtures/swagger/ignoreExamples/examples/example.json", + ); + expect(examples).toMatchObject( + new Map([ + [ + expectedExamplePath, + expect.objectContaining({ + path: expect.stringContaining(expectedExamplePath), + }), + ], + ]), + ); + }); + + it("computes versionKind from path", () => { + let swagger = new Swagger(resolve("foo/preview/2025-01-01-preview/foo.json")); + expect(swagger.versionKind).toEqual(API_VERSION_LIFECYCLE_STAGES.PREVIEW); + + swagger = new Swagger(resolve("foo/stable/2025-01-01/foo.json")); + expect(swagger.versionKind).toEqual(API_VERSION_LIFECYCLE_STAGES.STABLE); + }); + describe("getOperations", () => { it("should return normal operations", async () => { const testFixturePath = join(__dirname, "fixtures", "swagger", "specification"); diff --git a/.github/workflows/SDK-Suppressions-Label.yaml b/.github/workflows/SDK-Suppressions-Label.yaml index 88f5511d1c6f..920b5bffe4cf 100644 --- a/.github/workflows/SDK-Suppressions-Label.yaml +++ b/.github/workflows/SDK-Suppressions-Label.yaml @@ -45,20 +45,20 @@ jobs: run: | node eng/tools/sdk-suppressions/cmd/sdk-suppressions-label.js HEAD^ HEAD "$GITHUB_PULL_REQUEST_LABELS" - OUTPUT=$(cat $OUTPUT_FILE) + OUTPUT=$(cat "$OUTPUT_FILE") echo "Script output labels: $OUTPUT" labelsToAdd=$(echo "$OUTPUT" | sed -n 's/.*"labelsToAdd":\[\([^]]*\)\].*/\1/p' | tr -d '" ') labelsToRemove=$(echo "$OUTPUT" | sed -n 's/.*"labelsToRemove":\[\([^]]*\)\].*/\1/p' | tr -d '" ') - for label in $(echo $labelsToAdd | tr ',' '\n'); do + for label in $(echo "$labelsToAdd" | tr ',' '\n'); do echo "Label to add: $label" - echo "$label=true" >> $GITHUB_OUTPUT + echo "$label=true" >> "$GITHUB_OUTPUT" done - for label in $(echo $labelsToRemove | tr ',' '\n'); do + for label in $(echo "$labelsToRemove" | tr ',' '\n'); do echo "Label to remove: $label" - echo "$label=false" >> $GITHUB_OUTPUT + echo "$label=false" >> "$GITHUB_OUTPUT" done # No Action or Add/Remove label ​​according to step run-suppressions-script output diff --git a/.github/workflows/_reusable-set-check-status.yaml b/.github/workflows/_reusable-set-check-status.yaml index 6589ccc1199d..1f4c1779f964 100644 --- a/.github/workflows/_reusable-set-check-status.yaml +++ b/.github/workflows/_reusable-set-check-status.yaml @@ -56,13 +56,28 @@ jobs: - name: "Set Status" uses: actions/github-script@v7 + id: set-status with: script: | const { default: setStatus } = await import('${{ github.workspace }}/.github/workflows/src/set-status.js'); return await setStatus( - { github, context, core }, + { github, context, core }, '${{ inputs.monitored_workflow_name }}', '${{ inputs.required_check_name }}', '${{ inputs.overriding_label }}' ); + + - if: ${{ always() && steps.set-status.outputs.head_sha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: head-sha + value: ${{ steps.set-status.outputs.head_sha }} + + - if: ${{ always() && steps.set-status.outputs.issue_number }} + name: Upload artifact with issue number + uses: ./.github/actions/add-empty-artifact + with: + name: issue-number + value: ${{ steps.set-status.outputs.issue_number }} diff --git a/.github/workflows/_reusable-verify-run-status.yaml b/.github/workflows/_reusable-verify-run-status.yaml index ab08c8788506..1c76ce45721f 100644 --- a/.github/workflows/_reusable-verify-run-status.yaml +++ b/.github/workflows/_reusable-verify-run-status.yaml @@ -16,8 +16,10 @@ on: type: string permissions: + actions: read checks: read contents: read + statuses: read jobs: check-run-status: diff --git a/.github/workflows/arm-auto-signoff.yaml b/.github/workflows/arm-auto-signoff.yaml index 0f3cfcbc1032..d4f46fdb1eb5 100644 --- a/.github/workflows/arm-auto-signoff.yaml +++ b/.github/workflows/arm-auto-signoff.yaml @@ -1,57 +1,40 @@ name: ARM Auto SignOff on: - issue_comment: - types: - - edited # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on # labels from bot accounts in fork PRs. pull_request_target is also more similar to the other - # triggers "issue_comment" and "workflow_run" -- they are all privileged# and run in the target - # branch and repo -- which simplifies implementation. + # trigger "workflow_run" -- they are both privileged and run in the target branch and repo -- + # which simplifies implementation. pull_request_target: types: # Depends on labels, so must re-evaluate whenever a relevant label is manually added or removed. - labeled - unlabeled workflow_run: - workflows: ["ARM Incremental TypeSpec"] + workflows: + ["ARM Incremental TypeSpec", "Swagger Avocado - Set Status", "Swagger LintDiff - Set Status"] types: [completed] - # For manual testing - workflow_dispatch: - inputs: - owner: - description: The account owner of the repository. The name is not case sensitive. - required: true - type: string - repo: - description: The name of the repository without the .git extension. The name is not case sensitive. - required: true - type: string - issue_number: - description: The number of the pull request. - required: true - type: string - head_sha: - description: The SHA of the commit. - required: true - type: string permissions: + # actions.listWorkflowRunsForRepo + # actions.listWorkflowRunArtifacts actions: read - checks: read + # default contents: read + # issues.listLabelsOnIssue issues: read + # issues.listLabelsOnIssue pull-requests: read + # repos.listCommitStatusesForRef + statuses: read jobs: arm-auto-signoff: name: ARM Auto SignOff + # workflow_run - already filtered by triggers above # pull_request_target:labeled - filter to only the input and output labels - # issue_comment:edited - filter to only PR comments containing "next steps to merge", - # a signal that checks like "Swagger LintDiff" or "Swagger Avocado" status may have changed if: | - github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run' || (github.event_name == 'pull_request_target' && (github.event.action == 'labeled' || @@ -61,10 +44,7 @@ jobs: github.event.label.name == 'ARMReview' || github.event.label.name == 'ARMSignedOff' || github.event.label.name == 'NotReadyForARMReview' || - github.event.label.name == 'SuppressionReviewRequired')) || - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - contains(github.event.comment.body, 'next steps to merge')) + github.event.label.name == 'SuppressionReviewRequired')) runs-on: ubuntu-24.04 @@ -96,11 +76,6 @@ jobs: const { default: getLabelAction } = await import('${{ github.workspace }}/.github/workflows/src/arm-auto-signoff.js'); return await getLabelAction({ github, context, core }); - env: - OWNER: ${{ inputs.owner }} - REPO: ${{ inputs.repo }} - ISSUE_NUMBER: ${{ inputs.issue_number }} - HEAD_SHA: ${{ inputs.head_sha }} - if: | fromJson(steps.get-label-action.outputs.result).labelAction == 'add' || @@ -122,15 +97,28 @@ jobs: # Convert "add/remove" to "true/false" value: "${{ fromJson(steps.get-label-action.outputs.result).labelAction == 'add' }}" + # Required for consumers to identify the head SHA associated with this workflow run. + # Output can be trusted, because it was uploaded from a workflow that is trusted, + # because "issue_comment", and "workflow_run" only trigger on workflows in the default branch. + # Consumers should verify the "event_name" before attempting to extract from the artifact name. + - if: | + always() && + (github.event_name == 'issue_comment' || github.event_name == 'workflow_run') && + fromJson(steps.get-label-action.outputs.result).headSha + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: "head-sha" + value: "${{ fromJson(steps.get-label-action.outputs.result).headSha }}" + # Required for consumers to identify the PR associated with this workflow run. # Output can be trusted, because it was uploaded from a workflow that is trusted, # because "issue_comment", and "workflow_run" only trigger on workflows in the default branch. # Consumers should verify the "event_name" before attempting to extract from the artifact name. - if: | - (github.event_name == 'issue_comment' || - github.event_name == 'workflow_run') && - (fromJson(steps.get-label-action.outputs.result).labelAction == 'add' || - fromJson(steps.get-label-action.outputs.result).labelAction == 'remove') + always() && + (github.event_name == 'issue_comment' || github.event_name == 'workflow_run') && + fromJson(steps.get-label-action.outputs.result).issueNumber > 0 name: Upload artifact with issue number uses: ./.github/actions/add-empty-artifact with: diff --git a/.github/workflows/avocado-code.yaml b/.github/workflows/avocado-code.yaml index 97d2dec14db9..b46dbf169349 100644 --- a/.github/workflows/avocado-code.yaml +++ b/.github/workflows/avocado-code.yaml @@ -23,9 +23,11 @@ jobs: - name: Run Avocado id: run-avocado run: | + set -x + AVOCADO_OUTPUT_FILE=$RUNNER_TEMP/avocado.ndjson - echo "output-file=$AVOCADO_OUTPUT_FILE" >> $GITHUB_OUTPUT + echo "output-file=$AVOCADO_OUTPUT_FILE" >> "$GITHUB_OUTPUT" npm exec --no -- avocado \ --excludePaths \ @@ -41,13 +43,13 @@ jobs: "data-plane" \ "resource-manager" \ --file \ - $AVOCADO_OUTPUT_FILE + "$AVOCADO_OUTPUT_FILE" TIME=$(node -p 'JSON.stringify(new Date())') # Avocado doesn't write any output if it was successful, so we add some to simplify later processing [[ -e $AVOCADO_OUTPUT_FILE ]] || \ - echo "{\"type\":\"Raw\",\"level\":\"Info\",\"message\":\"success\",\"time\":$TIME}" > $AVOCADO_OUTPUT_FILE + echo "{\"type\":\"Raw\",\"level\":\"Info\",\"message\":\"success\",\"time\":$TIME}" > "$AVOCADO_OUTPUT_FILE" env: # Tells Avocado to analyze the files changed between the PR head (default checkout) # and the PR base branch. diff --git a/.github/workflows/avocado-status.yaml b/.github/workflows/avocado-status.yaml index 80942c1c2544..eaf2316c30b6 100644 --- a/.github/workflows/avocado-status.yaml +++ b/.github/workflows/avocado-status.yaml @@ -15,7 +15,9 @@ on: - labeled - unlabeled workflow_run: - workflows: ["Swagger Avocado - Analyze Code"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + ["Swagger Avocado - Analyze Code", "\\[TEST-IGNORE\\] Swagger Avocado - Analyze Code"] types: [completed] permissions: diff --git a/.github/workflows/breaking-change-add-label-artifacts.yaml b/.github/workflows/breaking-change-add-label-artifacts.yaml new file mode 100644 index 000000000000..29b698d66f19 --- /dev/null +++ b/.github/workflows/breaking-change-add-label-artifacts.yaml @@ -0,0 +1,80 @@ +name: "Breaking Change - Add Label Artifacts" + +on: + workflow_run: + workflows: + - "Swagger BreakingChange - Analyze Code" + - "Breaking Change(Cross-Version) - Analyze Code" + types: [completed] + +permissions: + # Required for github.rest.actions.listWorkflowRunsForRepo() + actions: read + contents: read + # Required for github.rest.repos.listPullRequestsAssociatedWithCommit() + issues: read + pull-requests: read + +jobs: + breaking-change-add-label-artifacts: + name: "Breaking Change - Add Label Artifacts" + + runs-on: ubuntu-24.04 + + steps: + # *** IMPORTANT *** + # For workflows that are triggered by the pull_request_target event, the workflow runs in the + # context of the base of the pull request. You should make sure that you do not check out, + # build, or run untrusted code from the head of the pull request. + - uses: actions/checkout@v4 + with: + # Only needs .github folder for automation, not the files in the PR (analyzed in a + # separate workflow). + # + # Uses the .github folder from the PR base branch (pull_request_target trigger), + # or the repo default branch (other triggers). + sparse-checkout: | + .github + + - id: get-label-actions + name: Get Label Actions + uses: actions/github-script@v7 + with: + script: | + const { default: getLabelActions } = + await import('${{ github.workspace }}/.github/workflows/src/breaking-change-add-label-artifacts.js'); + return await getLabelActions({ github, context, core }); + + # Upload artifact for 'BreakingChangeReviewRequired' label + - if: | + always() && + (steps.get-label-actions.outputs.breakingChangeReviewLabelName != '') + name: Upload artifact with BreakingChangeReviewRequiredLabel label + uses: ./.github/actions/add-label-artifact + with: + name: "${{ steps.get-label-actions.outputs.breakingChangeReviewLabelName }}" + value: "${{ steps.get-label-actions.outputs.breakingChangeReviewLabelValue == 'true' }}" + + # Upload artifact for 'VersioningReviewRequired' label + - if: | + always() && + (steps.get-label-actions.outputs.versioningReviewLabelName != '') + name: Upload artifact with VersioningReviewRequiredLabel label + uses: ./.github/actions/add-label-artifact + with: + name: "${{ steps.get-label-actions.outputs.versioningReviewLabelName }}" + value: "${{ steps.get-label-actions.outputs.versioningReviewLabelValue == 'true' }}" + + - if: ${{ always() && steps.get-label-actions.outputs.head_sha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: head-sha + value: ${{ steps.get-label-actions.outputs.head_sha }} + + - if: ${{ always() && steps.get-label-actions.outputs.issue_number > 0 }} + name: Upload artifact with issue number + uses: ./.github/actions/add-empty-artifact + with: + name: issue-number + value: ${{ steps.get-label-actions.outputs.issue_number }} diff --git a/.github/workflows/breaking-change-code.yaml b/.github/workflows/breaking-change-code.yaml index d3fe1d9b5522..119882466912 100644 --- a/.github/workflows/breaking-change-code.yaml +++ b/.github/workflows/breaking-change-code.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code" +name: "Swagger BreakingChange - Analyze Code" on: pull_request @@ -7,7 +7,7 @@ permissions: jobs: validateBreakingChange: - name: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code" + name: "Swagger BreakingChange - Analyze Code" runs-on: ubuntu-24.04 steps: @@ -27,6 +27,7 @@ jobs: - name: Swagger Breaking Change - Analyze Code id: swagger-breaking-change-analyze-code run: | + echo "summary=$GITHUB_STEP_SUMMARY" >> "$GITHUB_OUTPUT" npm exec --no -- openapi-diff-runner \ --spec-repo-path "$GITHUB_WORKSPACE" \ --pr-source-branch "$GITHUB_HEAD_REF" \ @@ -40,8 +41,8 @@ jobs: - if: | always() && (steps.swagger-breaking-change-analyze-code.outputs.breakingChangeReviewLabelName != '') - name: Upload artifact with BreakingChangeReviewRequiredLabel label - uses: ./.github/actions/add-label-artifact + name: Upload artifact with BreakingChangeReviewRequired status + uses: ./.github/actions/add-empty-artifact with: name: "${{ steps.swagger-breaking-change-analyze-code.outputs.breakingChangeReviewLabelName }}" value: "${{ steps.swagger-breaking-change-analyze-code.outputs.breakingChangeReviewLabelValue == 'true' }}" @@ -50,21 +51,18 @@ jobs: - if: | always() && (steps.swagger-breaking-change-analyze-code.outputs.versioningReviewLabelName != '') - name: Upload artifact with VersioningReviewRequiredLabel label - uses: ./.github/actions/add-label-artifact + name: Upload artifact with VersioningReviewRequired status + uses: ./.github/actions/add-empty-artifact with: name: "${{ steps.swagger-breaking-change-analyze-code.outputs.versioningReviewLabelName }}" - # Convert "add/remove" to "true/false" value: "${{ steps.swagger-breaking-change-analyze-code.outputs.versioningReviewLabelValue == 'true' }}" - # Upload artifact with issue number if labels are present and PR number is valid - - if: | - always() && - (steps.swagger-breaking-change-analyze-code.outputs.breakingChangeReviewLabelName != '' || - steps.swagger-breaking-change-analyze-code.outputs.versioningReviewLabelName != '') && - github.event.pull_request.number > 0 - name: Upload artifact with issue number - uses: ./.github/actions/add-empty-artifact + # Used by other workflows like set-status + - name: Set job-summary artifact + if: ${{ always() && steps.swagger-breaking-change-analyze-code.outputs.summary }} + uses: actions/upload-artifact@v4 with: - name: "issue-number" - value: "${{ github.event.pull_request.number }}" + name: job-summary + path: ${{ steps.swagger-breaking-change-analyze-code.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/breaking-change-cross-version-code.yaml b/.github/workflows/breaking-change-cross-version-code.yaml index 3c44f1ad3184..1aa46a8584ea 100644 --- a/.github/workflows/breaking-change-cross-version-code.yaml +++ b/.github/workflows/breaking-change-cross-version-code.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Breaking Change(Cross-Version) - Analyze Code" +name: "Breaking Change(Cross-Version) - Analyze Code" on: pull_request @@ -7,7 +7,7 @@ permissions: jobs: validateBreakingChange: - name: "[TEST-IGNORE] Breaking Change(Cross-Version) - Analyze Code" + name: "Breaking Change(Cross-Version) - Analyze Code" runs-on: ubuntu-24.04 steps: @@ -27,6 +27,7 @@ jobs: - name: Breaking Change(Cross-Version) - Analyze Code id: breaking-change-cross-version-analyze-code run: | + echo "summary=$GITHUB_STEP_SUMMARY" >> "$GITHUB_OUTPUT" npm exec --no -- openapi-diff-runner \ --run-type "CrossVersion" \ --spec-repo-path "$GITHUB_WORKSPACE" \ @@ -41,8 +42,8 @@ jobs: - if: | always() && (steps.breaking-change-cross-version-analyze-code.outputs.breakingChangeReviewLabelName != '') - name: Upload artifact with BreakingChangeReviewRequiredLabel label - uses: ./.github/actions/add-label-artifact + name: Upload artifact with BreakingChangeReviewRequired status + uses: ./.github/actions/add-empty-artifact with: name: "${{ steps.breaking-change-cross-version-analyze-code.outputs.breakingChangeReviewLabelName }}" value: "${{ steps.breaking-change-cross-version-analyze-code.outputs.breakingChangeReviewLabelValue == 'true' }}" @@ -51,20 +52,18 @@ jobs: - if: | always() && (steps.breaking-change-cross-version-analyze-code.outputs.versioningReviewLabelName != '') - name: Upload artifact with VersioningReviewRequiredLabel label - uses: ./.github/actions/add-label-artifact + name: Upload artifact with VersioningReviewRequired status + uses: ./.github/actions/add-empty-artifact with: name: "${{ steps.breaking-change-cross-version-analyze-code.outputs.versioningReviewLabelName }}" value: "${{ steps.breaking-change-cross-version-analyze-code.outputs.versioningReviewLabelValue == 'true' }}" - # Upload artifact with issue number if labels are present and PR number is valid - - if: | - always() && - (steps.breaking-change-cross-version-analyze-code.outputs.breakingChangeReviewLabelName != '' || - steps.breaking-change-cross-version-analyze-code.outputs.versioningReviewLabelName != '') && - github.event.pull_request.number > 0 - name: Upload artifact with issue number - uses: ./.github/actions/add-empty-artifact + # Used by other workflows like set-status + - name: Set job-summary artifact + if: ${{ always() && steps.breaking-change-cross-version-analyze-code.outputs.summary }} + uses: actions/upload-artifact@v4 with: - name: "issue-number" - value: "${{ github.event.pull_request.number }}" + name: job-summary + path: ${{ steps.breaking-change-cross-version-analyze-code.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/breaking-change-cross-version-status.yaml b/.github/workflows/breaking-change-cross-version-status.yaml index fda112c1ef41..17a7b7b2d606 100644 --- a/.github/workflows/breaking-change-cross-version-status.yaml +++ b/.github/workflows/breaking-change-cross-version-status.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Breaking Change(Cross-Version) - Set Status" +name: "Breaking Change(Cross-Version) - Set Status" on: # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on @@ -15,7 +15,12 @@ on: - labeled - unlabeled workflow_run: - workflows: ["\\[TEST-IGNORE\\] Breaking Change(Cross-Version) - Analyze Code"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + [ + "Breaking Change(Cross-Version) - Analyze Code", + "\\[TEST-IGNORE\\] Breaking Change(Cross-Version) - Analyze Code", + ] types: [completed] permissions: @@ -30,6 +35,6 @@ jobs: name: Set Breaking Change(Cross-Version) Status uses: ./.github/workflows/_reusable-set-check-status.yaml with: - monitored_workflow_name: "[TEST-IGNORE] Breaking Change(Cross-Version) - Analyze Code" - required_check_name: "[TEST-IGNORE] Breaking Change(Cross-Version)" - overriding_label: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact,BreakingChange-Approved-BranchPolicyException,BreakingChange-Approved-Previously,BreakingChange-Approved-Security,Versioning-Approved-Benign,Versioning-Approved-BugFix,Versioning-Approved-PrivatePreview,Versioning-Approved-BranchPolicyException,Versioning-Approved-Previously,Versioning-Approved-Retired" + monitored_workflow_name: "Breaking Change(Cross-Version) - Analyze Code" + required_check_name: "Breaking Change(Cross-Version)" + overriding_label: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact,BreakingChange-Approved-BranchPolicyException,BreakingChange-Approved-Previously,BreakingChange-Approved-Security,BreakingChange-Approved-Retired,Versioning-Approved-Benign,Versioning-Approved-BugFix,Versioning-Approved-PrivatePreview,Versioning-Approved-BranchPolicyException,Versioning-Approved-Previously,Versioning-Approved-Retired,Versioning-Approved-NotReleased" diff --git a/.github/workflows/breaking-change-status.yaml b/.github/workflows/breaking-change-status.yaml index 3b3d77c4dcce..0ced53a9e937 100644 --- a/.github/workflows/breaking-change-status.yaml +++ b/.github/workflows/breaking-change-status.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger BreakingChange - Set Status" +name: "Swagger BreakingChange - Set Status" on: # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on @@ -15,7 +15,12 @@ on: - labeled - unlabeled workflow_run: - workflows: ["\\[TEST-IGNORE\\] Swagger BreakingChange - Analyze Code"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + [ + "Swagger BreakingChange - Analyze Code", + "\\[TEST-IGNORE\\] Swagger BreakingChange - Analyze Code", + ] types: [completed] permissions: @@ -30,6 +35,6 @@ jobs: name: Set BreakingChange Status uses: ./.github/workflows/_reusable-set-check-status.yaml with: - monitored_workflow_name: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code" - required_check_name: "[TEST-IGNORE] Swagger BreakingChange" - overriding_label: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact,BreakingChange-Approved-BranchPolicyException,BreakingChange-Approved-Previously,BreakingChange-Approved-Security,Versioning-Approved-Benign,Versioning-Approved-BugFix,Versioning-Approved-PrivatePreview,Versioning-Approved-BranchPolicyException,Versioning-Approved-Previously,Versioning-Approved-Retired" + monitored_workflow_name: "Swagger BreakingChange - Analyze Code" + required_check_name: "Swagger BreakingChange" + overriding_label: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact,BreakingChange-Approved-BranchPolicyException,BreakingChange-Approved-Previously,BreakingChange-Approved-Security,BreakingChange-Approved-Retired,Versioning-Approved-Benign,Versioning-Approved-BugFix,Versioning-Approved-PrivatePreview,Versioning-Approved-BranchPolicyException,Versioning-Approved-Previously,Versioning-Approved-Retired,Versioning-Approved-NotReleased" diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 840384194b83..97917165ce3f 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -22,3 +22,8 @@ jobs: client-id: "936c56f0-298b-467f-b702-3ad5bf4b15c1" tenant-id: "72f988bf-86f1-41af-91ab-2d7cd011db47" allow-no-subscriptions: true + + - name: Install azsdk mcp server + shell: pwsh + run: | + ./eng/common/mcp/azure-sdk-mcp.ps1 -InstallDirectory $HOME/bin diff --git a/.github/workflows/github-test.yaml b/.github/workflows/github-test.yaml index 846608423fc2..9cea7c2bc60a 100644 --- a/.github/workflows/github-test.yaml +++ b/.github/workflows/github-test.yaml @@ -34,6 +34,17 @@ jobs: sparse-checkout: | .github + # Content copied from https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/.github/actionlint-matcher.json + - if: ${{ matrix.folder == '.github' && matrix.os == 'ubuntu'}} + name: Add ActionLint Problem Matcher + run: echo "::add-matcher::.github/matchers/actionlint.json" + + - if: ${{ matrix.folder == '.github' && matrix.os == 'ubuntu'}} + name: Lint workflows + uses: docker://rhysd/actionlint:1.7.7 + with: + args: -color -verbose + - if: ${{ matrix.folder == '.github' }} name: Setup Node 20 and install runtime deps uses: ./.github/actions/setup-node-install-deps diff --git a/.github/workflows/lintdiff-code.yaml b/.github/workflows/lintdiff-code.yaml index 6457779ff02c..11e0e9c187df 100644 --- a/.github/workflows/lintdiff-code.yaml +++ b/.github/workflows/lintdiff-code.yaml @@ -42,7 +42,7 @@ jobs: const { join } = await import('path'); // TODO: Logger - const changedFiles = await getChangedFiles({ cwd: 'after'}); + const changedFiles = await getChangedFiles({ cwd: 'after', paths: ['specification'] }); console.log('Changed files:', changedFiles); const filePath = join(process.cwd(), 'changed-files.txt'); @@ -53,7 +53,9 @@ jobs: - name: Run LintDiff id: run-lintdiff run: | - echo "summary=$GITHUB_STEP_SUMMARY" >> $GITHUB_OUTPUT + set -x + + echo "summary=$GITHUB_STEP_SUMMARY" >> "$GITHUB_OUTPUT" npm exec --no -- lint-diff \ --before before \ @@ -61,7 +63,7 @@ jobs: --changed-files-path changed-files.txt \ --base-branch ${{ github.event.pull_request.base.ref }} \ --compare-sha ${{ github.event.pull_request.head.sha }} \ - --out-file $GITHUB_STEP_SUMMARY + --out-file "$GITHUB_STEP_SUMMARY" env: # Some LintDiff runs are memory intensive and require more than the # default. diff --git a/.github/workflows/lintdiff-status.yaml b/.github/workflows/lintdiff-status.yaml index a938b9c8d534..a769ffe49021 100644 --- a/.github/workflows/lintdiff-status.yaml +++ b/.github/workflows/lintdiff-status.yaml @@ -15,7 +15,9 @@ on: - labeled - unlabeled workflow_run: - workflows: ["Swagger LintDiff - Analyze Code"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + ["Swagger LintDiff - Analyze Code", "\\[TEST-IGNORE\\] Swagger LintDiff - Analyze Code"] types: [completed] permissions: diff --git a/.github/workflows/sdk-breaking-change-labels.yaml b/.github/workflows/sdk-breaking-change-labels.yaml index 013a43ee8dab..44e82c6b3abe 100644 --- a/.github/workflows/sdk-breaking-change-labels.yaml +++ b/.github/workflows/sdk-breaking-change-labels.yaml @@ -37,7 +37,7 @@ jobs: run: | # Get token for Azure DevOps resource ADO_TOKEN=$(az account get-access-token --resource "499b84ac-1321-427f-aa17-267ca6975798" --query "accessToken" -o tsv) - echo "ADO_TOKEN=$ADO_TOKEN" >> $GITHUB_ENV + echo "ADO_TOKEN=$ADO_TOKEN" >> "$GITHUB_ENV" - name: Get label and action id: get-label-and-action @@ -58,10 +58,14 @@ jobs: # Convert "add/remove" to "true/false" value: "${{ fromJson(steps.get-label-and-action.outputs.result).labelAction == 'add' }}" - - if: | - ((fromJson(steps.get-label-and-action.outputs.result).labelAction == 'add' || - fromJson(steps.get-label-and-action.outputs.result).labelAction == 'remove') && - fromJson(steps.get-label-and-action.outputs.result).issueNumber > 0) + - if: ${{ always() && fromJson(steps.get-label-and-action.outputs.result).headSha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: "head-sha" + value: "${{ fromJson(steps.get-label-and-action.outputs.result).headSha }}" + + - if: ${{ always() && fromJson(steps.get-label-and-action.outputs.result).issueNumber > 0 }} name: Upload artifact with issue number uses: ./.github/actions/add-empty-artifact with: diff --git a/.github/workflows/spec-gen-sdk-status.yml b/.github/workflows/spec-gen-sdk-status.yml index cc409df325e7..faaaf71bbbf7 100644 --- a/.github/workflows/spec-gen-sdk-status.yml +++ b/.github/workflows/spec-gen-sdk-status.yml @@ -37,7 +37,7 @@ jobs: run: | # Get token for Azure DevOps resource ADO_TOKEN=$(az account get-access-token --resource "499b84ac-1321-427f-aa17-267ca6975798" --query "accessToken" -o tsv) - echo "ADO_TOKEN=$ADO_TOKEN" >> $GITHUB_ENV + echo "ADO_TOKEN=$ADO_TOKEN" >> "$GITHUB_ENV" - name: "SDK Validation Set Status" id: sdk-validation-status @@ -47,3 +47,17 @@ jobs: const setStatus = (await import('${{ github.workspace }}/.github/workflows/src/spec-gen-sdk-status.js')).default; return await setStatus({ github, context, core }); + + - if: ${{ always() && steps.sdk-validation-status.outputs.head_sha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: head-sha + value: ${{ steps.sdk-validation-status.outputs.head_sha }} + + - if: ${{ always() && steps.sdk-validation-status.outputs.issue_number }} + name: Upload artifact with issue number + uses: ./.github/actions/add-empty-artifact + with: + name: issue-number + value: ${{ steps.sdk-validation-status.outputs.issue_number }} diff --git a/.github/workflows/src/arm-auto-signoff.js b/.github/workflows/src/arm-auto-signoff.js index a141093a3f06..99413ba623d1 100644 --- a/.github/workflows/src/arm-auto-signoff.js +++ b/.github/workflows/src/arm-auto-signoff.js @@ -1,8 +1,9 @@ // @ts-check import { setEquals } from "../../shared/src/equality.js"; +import { CommitStatusState, PER_PAGE_MAX } from "../../shared/src/github.js"; +import { byDate, invert } from "../../shared/src/sort.js"; import { extractInputs } from "./context.js"; -import { PER_PAGE_MAX } from "./github.js"; import { LabelAction } from "./label.js"; // TODO: Add tests @@ -12,18 +13,7 @@ import { LabelAction } from "./label.js"; * @returns {Promise<{labelAction: LabelAction, issueNumber: number}>} */ export default async function getLabelAction({ github, context, core }) { - let owner = process.env.OWNER || ""; - let repo = process.env.REPO || ""; - let issue_number = parseInt(process.env.ISSUE_NUMBER || ""); - let head_sha = process.env.HEAD_SHA || ""; - - if (!owner || !repo || !issue_number || !head_sha) { - let inputs = await extractInputs(github, context, core); - owner = owner || inputs.owner; - repo = repo || inputs.repo; - issue_number = issue_number || inputs.issue_number; - head_sha = head_sha || inputs.head_sha; - } + const { owner, repo, issue_number, head_sha } = await extractInputs(github, context, core); return await getLabelActionImpl({ owner, @@ -44,25 +34,29 @@ export default async function getLabelAction({ github, context, core }) { * @param {string} params.head_sha * @param {(import("@octokit/core").Octokit & import("@octokit/plugin-rest-endpoint-methods/dist-types/types.js").Api & { paginate: import("@octokit/plugin-paginate-rest").PaginateInterface; })} params.github * @param {typeof import("@actions/core")} params.core - * @returns {Promise<{labelAction: LabelAction, issueNumber: number}>} + * @returns {Promise<{labelAction: LabelAction, headSha: string, issueNumber: number}>} */ export async function getLabelActionImpl({ owner, repo, issue_number, head_sha, github, core }) { const labelActions = { [LabelAction.None]: { labelAction: LabelAction.None, + headSha: head_sha, issueNumber: issue_number, }, [LabelAction.Add]: { labelAction: LabelAction.Add, + headSha: head_sha, issueNumber: issue_number, }, [LabelAction.Remove]: { labelAction: LabelAction.Remove, + headSha: head_sha, issueNumber: issue_number, }, }; // TODO: Try to extract labels from context (when available) to avoid unnecessary API call + // permissions: { issues: read, pull-requests: read } const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, { owner: owner, repo: repo, @@ -79,6 +73,7 @@ export async function getLabelActionImpl({ owner, repo, issue_number, head_sha, core.info(`Labels: ${labelNames}`); + // permissions: { actions: read } const workflowRuns = await github.paginate(github.rest.actions.listWorkflowRunsForRepo, { owner, repo, @@ -113,6 +108,7 @@ export async function getLabelActionImpl({ owner, repo, issue_number, head_sha, return removeAction; } + // permissions: { actions: read } const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { owner, repo, @@ -150,52 +146,64 @@ export async function getLabelActionImpl({ owner, repo, issue_number, head_sha, return removeAction; } - const checkRuns = await github.paginate(github.rest.checks.listForRef, { + // permissions: { statuses: read } + const statuses = await github.paginate(github.rest.repos.listCommitStatusesForRef, { owner: owner, repo: repo, ref: head_sha, per_page: PER_PAGE_MAX, }); - const requiredCheckNames = ["Swagger LintDiff", "Swagger Avocado"]; + core.info("Statuses:"); + statuses.forEach((status) => { + core.info(`- ${status.context}: ${status.state}`); + }); + + const requiredStatusNames = ["Swagger LintDiff", "Swagger Avocado"]; /** - * @type {typeof checkRuns.check_runs} + * @type {typeof statuses} */ - let requiredCheckRuns = []; - - for (const checkName of requiredCheckNames) { - const matchingRuns = checkRuns.filter((run) => run.name === checkName); - - if (matchingRuns.length > 1) { - throw new Error(`Unexpected number of checks named '${checkName}': ${matchingRuns.length}`); - } - - const matchingRun = matchingRuns.length === 1 ? matchingRuns[0] : undefined; - - core.info( - `${checkName}: Status='${matchingRun?.status}', Conclusion='${matchingRun?.conclusion}'`, - ); - - if (matchingRun && matchingRun.status === "completed" && matchingRun.conclusion !== "success") { - core.info(`Check '${checkName}' did not succeed`); + let requiredStatuses = []; + + for (const statusName of requiredStatusNames) { + // The "statuses" array may contain multiple statuses with the same "context" (aka "name"), + // but different states and update times. We only care about the latest. + const matchingStatuses = statuses + .filter((status) => status.context.toLowerCase() === statusName.toLowerCase()) + .sort(invert(byDate((status) => status.updated_at))); + + // undefined if matchingStatuses.length === 0 (which is OK) + const matchingStatus = matchingStatuses[0]; + + core.info(`${statusName}: State='${matchingStatus?.state}'`); + + if ( + matchingStatus && + (matchingStatus.state === CommitStatusState.ERROR || + matchingStatus.state === CommitStatusState.FAILURE) + ) { + core.info(`Status '${matchingStatus}' did not succeed`); return removeAction; } - if (matchingRun) { - requiredCheckRuns.push(matchingRun); + if (matchingStatus) { + requiredStatuses.push(matchingStatus); } } if ( - setEquals(new Set(requiredCheckRuns.map((run) => run.name)), new Set(requiredCheckNames)) && - requiredCheckRuns.every((run) => run.status === "completed" && run.conclusion === "success") + setEquals( + new Set(requiredStatuses.map((status) => status.context)), + new Set(requiredStatusNames), + ) && + requiredStatuses.every((status) => status.state === CommitStatusState.SUCCESS) ) { core.info("All requirements met for auto-signoff"); return labelActions[LabelAction.Add]; } - // If any checks are missing or not completed, no-op to prevent frequent remove/add label as checks re-run - core.info("One or more checks are still in-progress"); + // If any statuses are missing or pending, no-op to prevent frequent remove/add label as checks re-run + core.info("One or more statuses are still pending"); return labelActions[LabelAction.None]; } diff --git a/.github/workflows/src/arm-incremental-typespec.js b/.github/workflows/src/arm-incremental-typespec.js index e71619e7d297..14cd9173090a 100644 --- a/.github/workflows/src/arm-incremental-typespec.js +++ b/.github/workflows/src/arm-incremental-typespec.js @@ -24,6 +24,7 @@ debug.enable("simple-git"); export default async function incrementalTypeSpec({ core }) { const options = { cwd: process.env.GITHUB_WORKSPACE, + paths: ["specification"], logger: new CoreLogger(core), }; diff --git a/.github/workflows/src/breaking-change-add-label-artifacts.js b/.github/workflows/src/breaking-change-add-label-artifacts.js new file mode 100644 index 000000000000..b057ce661d2c --- /dev/null +++ b/.github/workflows/src/breaking-change-add-label-artifacts.js @@ -0,0 +1,109 @@ +// @ts-check + +import { REVIEW_REQUIRED_LABELS } from "../../shared/src/breaking-change.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import { byDate, invert } from "../../shared/src/sort.js"; +import { extractInputs } from "./context.js"; + +export const SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME = "Swagger BreakingChange - Analyze Code"; +export const CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME = + "Breaking Change(Cross-Version) - Analyze Code"; + +/** + * @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments + * @returns {Promise} + */ +export default async function getLabelActions({ github, context, core }) { + const { owner, repo, head_sha, issue_number } = await extractInputs(github, context, core); + + core.setOutput("head_sha", head_sha); + core.setOutput("issue_number", issue_number); + + const workflowRuns = await github.paginate(github.rest.actions.listWorkflowRunsForRepo, { + owner, + repo, + event: "pull_request", + head_sha, + per_page: PER_PAGE_MAX, + }); + + core.info("Workflow Runs:"); + workflowRuns.forEach((wf) => { + core.info(`- ${wf.name}: ${wf.conclusion || wf.status}`); + }); + + const latestBreakingChangesRun = workflowRuns + .filter((wf) => wf.name === SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME) + // Sort by "updated_at" descending + .sort(invert(byDate((wf) => wf.updated_at)))[0]; + + const latestCrossVersionBreakingChangesRun = workflowRuns + .filter((wf) => wf.name === CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME) + // Sort by "updated_at" descending + .sort(invert(byDate((wf) => wf.updated_at)))[0]; + + // Add null checks before accessing artifacts + if (!latestBreakingChangesRun || latestBreakingChangesRun.status !== "completed") { + core.info("No completed breaking changes workflow run found"); + return; + } + + if ( + !latestCrossVersionBreakingChangesRun || + latestCrossVersionBreakingChangesRun.status !== "completed" + ) { + core.info("No completed cross-version breaking changes workflow run found"); + return; + } + + core.info(`breaking change workflow run: ${latestBreakingChangesRun.html_url}`); + core.info( + `cross-version breaking change workflow run: ${latestCrossVersionBreakingChangesRun.html_url}`, + ); + const breakingChangesArtifactNames = ( + await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { + owner: owner, + repo: repo, + run_id: latestBreakingChangesRun.id, + per_page: PER_PAGE_MAX, + }) + ).map((a) => a.name); + + const crossVersionBreakingChangesArtifactNames = ( + await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { + owner: owner, + repo: repo, + run_id: latestCrossVersionBreakingChangesRun.id, + per_page: PER_PAGE_MAX, + }) + ).map((a) => a.name); + + if ( + breakingChangesArtifactNames.length === 0 && + crossVersionBreakingChangesArtifactNames.length === 0 + ) { + core.info("No artifacts found for breaking changes or cross-version breaking changes"); + return; + } + const breakingChangeKey = `${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}=true`; + const versioningKey = `${REVIEW_REQUIRED_LABELS.VERSIONING}=true`; + + // Check for labels in both artifact collections + const hasBreakingChangeReviewLabel = + breakingChangesArtifactNames.includes(breakingChangeKey) || + crossVersionBreakingChangesArtifactNames.includes(breakingChangeKey); + + const hasVersioningReviewLabel = + breakingChangesArtifactNames.includes(versioningKey) || + crossVersionBreakingChangesArtifactNames.includes(versioningKey); + + // Apply precedence rule: breaking change takes precedence over versioning, only one label should be added + const breakingChangeReviewLabelValue = hasBreakingChangeReviewLabel; + const versioningReviewLabelValue = hasVersioningReviewLabel && !hasBreakingChangeReviewLabel; + core.info(`${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}: ${breakingChangeReviewLabelValue}`); + core.info(`${REVIEW_REQUIRED_LABELS.VERSIONING}: ${versioningReviewLabelValue}`); + core.setOutput("breakingChangeReviewLabelName", REVIEW_REQUIRED_LABELS.BREAKING_CHANGE); + core.setOutput("breakingChangeReviewLabelValue", breakingChangeReviewLabelValue); + core.setOutput("versioningReviewLabelName", REVIEW_REQUIRED_LABELS.VERSIONING); + core.setOutput("versioningReviewLabelValue", versioningReviewLabelValue); +} diff --git a/.github/workflows/src/comment.js b/.github/workflows/src/comment.js index e6f8ae63b73d..48d10f54539d 100644 --- a/.github/workflows/src/comment.js +++ b/.github/workflows/src/comment.js @@ -1,4 +1,4 @@ -import { PER_PAGE_MAX } from "./github.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; /** * @typedef {Object} IssueComment @@ -63,50 +63,38 @@ export async function commentOrUpdate( body, commentIdentifier, ) { - try { - // Get the authenticated user to know who we are - const { data: user } = await github.rest.users.getAuthenticated(); - const authenticatedUsername = user.login; - const computedBody = body + `\n`; + const computedBody = body + `\n`; - /** @type {IssueComment[]} */ - const comments = await github.paginate(github.rest.issues.listComments, { + /** @type {IssueComment[]} */ + const comments = await github.paginate(github.rest.issues.listComments, { + owner, + repo, + issue_number, + per_page: PER_PAGE_MAX, + }); + + const [commentId, commentBody] = parseExistingComments(comments, commentIdentifier); + + if (commentId) { + if (commentBody === computedBody) { + core.info(`No update needed for comment ${commentId}.`); + return; // No-op if the body is the same + } + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: commentId, + body: computedBody, + }); + core.info(`Updated existing comment ${commentId}.`); + } else { + // Create a new comment + const { data: newComment } = await github.rest.issues.createComment({ owner, repo, issue_number, - per_page: PER_PAGE_MAX, + body: computedBody, }); - - // only examine the comments from user in our current GITHUB_TOKEN context - const existingComments = comments.filter( - (comment) => comment.user?.login === authenticatedUsername, - ); - - const [commentId, commentBody] = parseExistingComments(existingComments, commentIdentifier); - - if (commentId) { - if (commentBody === computedBody) { - core.info(`No update needed for comment ${commentId} by ${authenticatedUsername}`); - return; // No-op if the body is the same - } - await github.rest.issues.updateComment({ - owner, - repo, - comment_id: commentId, - body: computedBody, - }); - core.info(`Updated existing comment ${commentId} by ${authenticatedUsername}`); - } else { - // Create a new comment - const { data: newComment } = await github.rest.issues.createComment({ - owner, - repo, - issue_number, - body: computedBody, - }); - core.info(`Created new comment #${newComment.id}`); - } - } catch (/** @type {any} */ error) { - core.error(`Failed to comment or update: ${error.message}`); + core.info(`Created new comment #${newComment.id}`); } } diff --git a/.github/workflows/src/context.js b/.github/workflows/src/context.js index 99e78b52eb56..f49e2bb7cfb0 100644 --- a/.github/workflows/src/context.js +++ b/.github/workflows/src/context.js @@ -1,6 +1,8 @@ // @ts-check -import { PER_PAGE_MAX } from "./github.js"; +import { isFullGitSha } from "../../shared/src/git.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import { rateLimitHook } from "./github.js"; import { getIssueNumber } from "./issues.js"; /** @@ -20,12 +22,16 @@ import { getIssueNumber } from "./issues.js"; export async function extractInputs(github, context, core) { core.info("extractInputs()"); core.info(` eventName: ${context.eventName}`); - core.info(` payload.action: ${context.eventName}`); + core.info(` payload.action: ${context.payload.action}`); core.info(` payload.workflow_run.event: ${context.payload.workflow_run?.event || "undefined"}`); // Log full context when debug is enabled. Most workflows should be idempotent and can be re-run // with debug enabled to replay the previous context. - core.isDebug() && core.debug(`context: ${JSON.stringify(context)}`); + if (core.isDebug()) { + core.debug(`context: ${JSON.stringify(context)}`); + } + + github.hook.after("request", rateLimitHook); /** @type {{ owner: string, repo: string, head_sha: string, issue_number: number, run_id: number, details_url?: string }} */ let inputs; @@ -39,7 +45,10 @@ export async function extractInputs(github, context, core) { context.payload.action === "synchronize" || context.payload.action === "reopened" || context.payload.action === "labeled" || - context.payload.action === "unlabeled")) + context.payload.action === "unlabeled" || + context.payload.action === "edited" || + context.payload.action === "ready_for_review" || + context.payload.action === "converted_to_draft")) ) { // Most properties on payload should be the same for both pull_request and pull_request_target @@ -93,11 +102,14 @@ export async function extractInputs(github, context, core) { ); let issue_number = NaN; + let head_sha = ""; if ( payload.workflow_run.event === "pull_request" || payload.workflow_run.event == "pull_request_target" ) { + head_sha = payload.workflow_run.head_sha; + // Other properties on payload.workflow_run should be the same for both pull_request and pull_request_target. // Extract the issue number from the payload itself, or by passing the head_sha to an API. @@ -121,7 +133,6 @@ export async function extractInputs(github, context, core) { // Owner and repo for the PR head (at least one should differ from base for fork PRs) const head_owner = payload.workflow_run.head_repository.owner.login; const head_repo = payload.workflow_run.head_repository.name; - const head_sha = payload.workflow_run.head_sha; /** @type {PullRequest[]} */ let pullRequests = []; @@ -168,7 +179,9 @@ export async function extractInputs(github, context, core) { issue_number = pullRequests[0].number; } else { throw new Error( - `Unexpected number of pull requests associated with commit '${head_sha}'. Expected: '1'. Actual: '${pullRequests.length}'.`, + `Unexpected number of pull requests associated with commit '${head_sha}'. ` + + `Expected: '1'. Actual: '${pullRequests.length}'. PRs:\n` + + pullRequests.map((pr) => pr.html_url).join("\n"), ); } if (!issue_number) { @@ -196,25 +209,44 @@ export async function extractInputs(github, context, core) { core.info(`artifactNames: ${JSON.stringify(artifactNames)}`); for (const artifactName of artifactNames) { - // If artifactName has format "issue-number=number", set issue_number - // Else, if artifactName has format "issue-number=other-string", throw an error - // Else, if artifactName does not start with "issue-number=", ignore it + // If artifactName has format "head-sha=valid-full-sha", set head_sha=value + // Else, if artifactName has format "head-sha=other-string", warn and set head_sha="" + // Else, if artifactName has format "issue-number=positive-integer", set issue_number=value + // Else, if artifactName has format "issue-number=other-string", warn and set issue_number=NaN + // - Workflows should probably only set "issue-number" to positive integers, but sometimes set it to "null" + // Else, if artifactName does not start with "head-sha=" or "issue-number=", ignore it const firstEquals = artifactName.indexOf("="); if (firstEquals !== -1) { const key = artifactName.substring(0, firstEquals); - const value = artifactName.substring(firstEquals + 1); - - if (key === "issue-number") { + if (key === "head-sha") { + const value = artifactName.substring(firstEquals + 1); + if (isFullGitSha(value)) { + head_sha = value; + } else { + // Producers must ensure they only set head-sha to valid full git SHA + throw new Error(`head-sha is not a valid full git SHA: '${value}'`); + } + continue; + } else if (key === "issue-number") { + const value = artifactName.substring(firstEquals + 1); const parsedValue = Number.parseInt(value); - if (parsedValue) { + if (parsedValue > 0) { issue_number = parsedValue; - continue; } else { - throw new Error(`Invalid issue-number: '${value}' parsed to '${parsedValue}'`); + // TODO: Consider throwing instead of warning. May need to handle `issue-number=null|undefined`, + // but invalid integers should throw. + core.info(`Invalid issue-number: '${value}' parsed to '${parsedValue}'`); + issue_number = NaN; } + continue; } } } + if (!head_sha) { + core.info( + `Could not find 'head-sha' artifact, which is required to associate the triggering workflow run with the head SHA of a PR`, + ); + } if (!issue_number) { core.info( `Could not find 'issue-number' artifact, which is required to associate the triggering workflow run with a PR`, @@ -229,8 +261,8 @@ export async function extractInputs(github, context, core) { inputs = { owner: payload.workflow_run.repository.owner.login, repo: payload.workflow_run.repository.name, - head_sha: payload.workflow_run.head_sha, - issue_number: issue_number, + head_sha, + issue_number, run_id: payload.workflow_run.id, }; } else if (context.eventName === "check_run") { diff --git a/.github/workflows/src/github.js b/.github/workflows/src/github.js index 503b2ce66d32..274341509649 100644 --- a/.github/workflows/src/github.js +++ b/.github/workflows/src/github.js @@ -1,5 +1,6 @@ // @ts-check +import { PER_PAGE_MAX } from "../../shared/src/github.js"; import { byDate, invert } from "../../shared/src/sort.js"; /** @@ -9,127 +10,6 @@ import { byDate, invert } from "../../shared/src/sort.js"; * @typedef {RestEndpointMethodTypes["repos"]["listCommitStatusesForRef"]["response"]["data"]} CommitStatuses */ -export const PER_PAGE_MAX = 100; - -/** - * https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks#check-statuses-and-conclusions - */ -export const CheckStatus = { - /** - * @type {"completed"} - * @description The check run completed and has a conclusion. - */ - COMPLETED: "completed", - /** - * @type {"expected"} - * @description The check run is waiting for a status to be reported. - */ - EXPECTED: "expected", - /** - * @type {"failure"} - * @description The check run failed. - */ - FAILURE: "failure", - /** - * @type {"in_progress"} - * @description The check run is in progress. - */ - IN_PROGRESS: "in_progress", - /** - * @type {"pending"} - * @description The check run is at the front of the queue but the group-based concurrency limit has been reached. - */ - PENDING: "pending", - /** - * @type {"queued"} - * @description The check run has been queued. - */ - QUEUED: "queued", - /** - * @type {"requested"} - * @description The check run has been created but has not been queued. - */ - REQUESTED: "requested", - /** - * @type {"startup_failure"} - * @description The check suite failed during startup. This status is not applicable to check runs. - */ - STARTUP_FAILURE: "startup_failure", - /** - * @type {"waiting"} - * @description The check run is waiting for a deployment protection rule to be satisfied. - */ - WAITING: "waiting", -}; - -/** - * https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/about-status-checks#check-statuses-and-conclusions - */ -export const CheckConclusion = { - /** - * @type {"action_required"} - * @description The check run provided required actions upon its completion. For more information, see Using the REST API to interact with checks. - */ - ACTION_REQUIRED: "action_required", - /** - * @type {"cancelled"} - * @description The check run was cancelled before it completed. - */ - CANCELLED: "cancelled", - /** - * @type {"failure"} - * @description The check run failed. - */ - FAILURE: "failure", - /** - * @type {"neutral"} - * @description The check run completed with a neutral result. This is treated as a success for dependent checks in GitHub Actions. - */ - NEUTRAL: "neutral", - /** - * @type {"skipped"} - * @description The check run was skipped. This is treated as a success for dependent checks in GitHub Actions. - */ - SKIPPED: "skipped", - /** - * @type {"stale"} - * @description The check run was marked stale by GitHub because it took too long. - */ - STALE: "stale", - /** - * @type {"success"} - * @description The check run completed successfully. - */ - SUCCESS: "success", - /** - * @type {"timed_out"} - * @description The check run timed out. - */ - TIMED_OUT: "timed_out", -}; - -/** - * https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#create-a-commit-status--parameters - */ -export const CommitStatusState = { - /** - * @type {"error"} - */ - ERROR: "error", - /** - * @type {"failure"} - */ - FAILURE: "failure", - /** - * @type {"pending"} - */ - PENDING: "pending", - /** - * @type {"success"} - */ - SUCCESS: "success", -}; - /** * Writes content to the GitHub Actions summary * @param {string} content - Markdown content to add to the summary @@ -220,3 +100,19 @@ export async function getWorkflowRuns(github, context, workflowName, ref) { .filter((run) => run.name === workflowName) .sort(invert(byDate((run) => run.updated_at))); } + +/** + * @param {import("@octokit/types").OctokitResponse} response + * @param {import("@octokit/types").RequestParameters & {url: string, method: string}} options + */ +export function rateLimitHook(response, options) { + const limits = { + method: options.method.toUpperCase(), + url: options.url, + limit: response.headers["x-ratelimit-limit"], + remaining: response.headers["x-ratelimit-remaining"], + reset: new Date(parseInt(response.headers["x-ratelimit-reset"] || "") * 1000), + }; + + console.log(`rate-limits: ${JSON.stringify(limits)}`); +} diff --git a/.github/workflows/src/label.js b/.github/workflows/src/label.js index cb8a6a2b189d..3d1d29dc3f8a 100644 --- a/.github/workflows/src/label.js +++ b/.github/workflows/src/label.js @@ -9,10 +9,3 @@ export const LabelAction = Object.freeze({ Add: "add", Remove: "remove", }); - -export const Label = { - /** - * @type {"Approved-Avocado"} - */ - APPROVED_AVOCADO: "Approved-Avocado", -}; diff --git a/.github/workflows/src/sdk-breaking-change-labels.js b/.github/workflows/src/sdk-breaking-change-labels.js index f62b79b3d634..35dffaae6356 100644 --- a/.github/workflows/src/sdk-breaking-change-labels.js +++ b/.github/workflows/src/sdk-breaking-change-labels.js @@ -39,12 +39,13 @@ export async function getLabelAndAction({ github, context, core }) { * @param {string} params.details_url * @param {typeof import("@actions/core")} params.core * @param {import('./retries.js').RetryOptions} [params.retryOptions] - * @returns {Promise<{labelName: string | undefined, labelAction: LabelAction, issueNumber: number}>} + * @returns {Promise<{labelName: string | undefined, labelAction: LabelAction, headSha: string, issueNumber: number}>} */ export async function getLabelAndActionImpl({ details_url, core, retryOptions = {} }) { // Override default logger from console.log to core.info retryOptions = { logger: core.info, ...retryOptions }; + let head_sha = ""; let issue_number = NaN; let labelAction; /** @type {String | undefined} */ @@ -74,6 +75,9 @@ export async function getLabelAndActionImpl({ details_url, core, retryOptions = // Parse the JSON data const specGenSdkArtifactInfo = JSON.parse(result.artifactData); const labelActionText = specGenSdkArtifactInfo.labelAction; + + head_sha = specGenSdkArtifactInfo.headSha; + issue_number = parseInt(specGenSdkArtifactInfo.prNumber, 10); if (!issue_number) { core.warning( @@ -95,10 +99,10 @@ export async function getLabelAndActionImpl({ details_url, core, retryOptions = } } - if (!labelAction) { - core.info("No label action found, defaulting to None"); + if (!labelAction || !labelName) { + core.info("No label action or name found, defaulting to None"); labelAction = LabelAction.None; } - return { labelName, labelAction, issueNumber: issue_number }; + return { labelName, labelAction, headSha: head_sha, issueNumber: issue_number }; } diff --git a/.github/workflows/src/set-status.js b/.github/workflows/src/set-status.js index d499e1730c3b..c49f9399d365 100644 --- a/.github/workflows/src/set-status.js +++ b/.github/workflows/src/set-status.js @@ -1,7 +1,14 @@ // @ts-check +import { isFullGitSha } from "../../shared/src/git.js"; +import { + CheckConclusion, + CheckStatus, + CommitStatusState, + PER_PAGE_MAX, +} from "../../shared/src/github.js"; +import { byDate, invert } from "../../shared/src/sort.js"; import { extractInputs } from "./context.js"; -import { CheckConclusion, CheckStatus, CommitStatusState, PER_PAGE_MAX } from "./github.js"; // TODO: Add tests /* v8 ignore start */ @@ -66,6 +73,16 @@ export async function setStatusImpl({ requiredStatusName, overridingLabel, }) { + if (!isFullGitSha(head_sha)) { + throw new Error(`head_sha is not a valid full git SHA: '${head_sha}'`); + } + core.setOutput("head_sha", head_sha); + + if (!Number.isInteger(issue_number) || issue_number <= 0) { + throw new Error(`issue_number must be a positive integer: ${issue_number}`); + } + core.setOutput("issue_number", issue_number); + // TODO: Try to extract labels from context (when available) to avoid unnecessary API call const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, { owner: owner, @@ -122,9 +139,13 @@ export async function setStatusImpl({ }); const targetRuns = workflowRuns - .filter((wf) => wf.name == monitoredWorkflowName) + .filter( + (wf) => + // Match both old and new names, to handle the rename more gracefully + wf.name === monitoredWorkflowName || wf.name === `[TEST-IGNORE] ${monitoredWorkflowName}`, + ) // Sort by "updated_at" descending - .sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()); + .sort(invert(byDate((r) => r.updated_at))); // Sorted by "updated_at" descending, so most recent run is at index 0. // If "targetRuns.length === 0", run will be "undefined", which the following @@ -132,7 +153,9 @@ export async function setStatusImpl({ const run = targetRuns[0]; if (!run) { - console.log(`No workflow runs found for '${monitoredWorkflowName}'.`); + console.log( + `No workflow runs found for '${monitoredWorkflowName}' or '[TEST-IGNORE] ${monitoredWorkflowName}'.`, + ); } if (run) { @@ -203,7 +226,8 @@ export async function setStatusImpl({ }); } else { core.info( - `No workflow runs found for '${monitoredWorkflowName}'. Setting status to ${CommitStatusState.PENDING} for required status: ${requiredStatusName}.`, + `No workflow runs found for '${monitoredWorkflowName}' or '[TEST-IGNORE] ${monitoredWorkflowName}'.` + + ` Setting status to ${CommitStatusState.PENDING} for required status: ${requiredStatusName}.`, ); // Run was not found (not started), or not completed await github.rest.repos.createCommitStatus({ diff --git a/.github/workflows/src/spec-gen-sdk-status.js b/.github/workflows/src/spec-gen-sdk-status.js index ceee5a79c96d..3a176e57bef9 100644 --- a/.github/workflows/src/spec-gen-sdk-status.js +++ b/.github/workflows/src/spec-gen-sdk-status.js @@ -1,7 +1,8 @@ // @ts-check +import { CheckStatus, CommitStatusState, PER_PAGE_MAX } from "../../shared/src/github.js"; import { getAdoBuildInfoFromUrl, getAzurePipelineArtifact } from "./artifacts.js"; import { extractInputs } from "./context.js"; -import { CheckStatus, CommitStatusState, PER_PAGE_MAX, writeToActionsSummary } from "./github.js"; +import { writeToActionsSummary } from "./github.js"; /** * @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments @@ -11,6 +12,7 @@ export default async function setSpecGenSdkStatus({ github, context, core }) { const inputs = await extractInputs(github, context, core); const head_sha = inputs.head_sha; const details_url = inputs.details_url; + const issue_number = inputs.issue_number; if (!details_url || !head_sha) { throw new Error( `Required inputs are not valid: details_url:${details_url}, head_sha:${head_sha}`, @@ -30,6 +32,7 @@ export default async function setSpecGenSdkStatus({ github, context, core }) { target_url, github, core, + issue_number, }); } @@ -39,18 +42,30 @@ export default async function setSpecGenSdkStatus({ github, context, core }) { * @param {string} params.repo * @param {string} params.head_sha * @param {string} params.target_url + * @param {number} params.issue_number * @param {(import("@octokit/core").Octokit & import("@octokit/plugin-rest-endpoint-methods/dist-types/types.js").Api & { paginate: import("@octokit/plugin-paginate-rest").PaginateInterface; })} params.github * @param {typeof import("@actions/core")} params.core * @returns {Promise} */ -export async function setSpecGenSdkStatusImpl({ owner, repo, head_sha, target_url, github, core }) { +export async function setSpecGenSdkStatusImpl({ + owner, + repo, + head_sha, + target_url, + github, + core, + issue_number, +}) { const statusName = "SDK Validation Status"; + core.setOutput("head_sha", head_sha); + core.setOutput("issue_number", issue_number); const checks = await github.paginate(github.rest.checks.listForRef, { owner, repo, ref: head_sha, per_page: PER_PAGE_MAX, }); + // Filter sdk generation check runs const specGenSdkChecks = checks.filter( (check) => check.app?.name === "Azure Pipelines" && check.name.includes("SDK Validation"), @@ -110,10 +125,10 @@ export async function setSpecGenSdkStatusImpl({ owner, repo, head_sha, target_ur * @param {Object} params * @param {Array} params.checkRuns * @param {typeof import("@actions/core")} params.core - * @returns {Promise<{state: import("./github.js").CommitStatusState, description: string}>} + * @returns {Promise<{state: CommitStatusState, description: string}>} */ async function processResult({ checkRuns, core }) { - /** @type {import("./github.js").CommitStatusState} */ + /** @type {CommitStatusState} */ let state = CommitStatusState.SUCCESS; let specGenSdkFailedRequiredLanguages = ""; let description = "SDK Validation CI checks succeeded"; @@ -186,7 +201,9 @@ async function processResult({ checkRuns, core }) { if (state === CommitStatusState.FAILURE) { summaryContent += "\n### Next Steps\n\n" + - `Please fix any issues in the the SDK Validation CI checks for languages: ${specGenSdkFailedRequiredLanguages}.`; + `Address the issues reported in the the SDK Validation CI checks for language(s): ${specGenSdkFailedRequiredLanguages}.` + + `\n### More Information\n\n` + + `Refer to the [SDK Validation Wiki](https://github.com/Azure/azure-rest-api-specs/wiki/SDK-Validation).`; } // Write to the summary page diff --git a/.github/workflows/src/summarize-checks/dump-trigger-metadata.js b/.github/workflows/src/summarize-checks/dump-trigger-metadata.js new file mode 100644 index 000000000000..efb2e006af57 --- /dev/null +++ b/.github/workflows/src/summarize-checks/dump-trigger-metadata.js @@ -0,0 +1,55 @@ +// @ts-check + +/** + * @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments + * @returns {Promise} + */ +export default async function dumpTriggerMetadata({ context, core }) { + core.info(`Event name: ${context.eventName}`); + core.info(`Action: ${context.payload.action}`); + + if ( + context.eventName === "workflow_run" && + context.payload.action === "completed" && + context.payload.workflow_run + ) { + const payload = /** @type {import("@octokit/webhooks-types").WorkflowRunCompletedEvent} */ ( + context.payload + ); + + const run = payload.workflow_run; + core.info(`Triggering workflow: ${run.name}`); + core.info(`Triggering workflow ID: ${run.id}`); + core.info(`Triggering run ID: ${run.run_number}`); + core.info(`Triggering event: ${run.event}`); + core.info(`Triggering status: ${run.status}`); + core.info(`Triggering conclusion: ${run.conclusion}`); + core.info(`Triggering branch: ${run.head_branch}`); + core.info(`Triggering SHA: ${run.head_sha}`); + core.info(`Triggering actor: ${run.actor?.login || "unknown"}`); + + // Create clickable URL to the triggering workflow run + const triggeringRunUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${run.id}`; + core.info(`🔗 Triggering workflow run URL: ${triggeringRunUrl}`); + + // If there's a pull request associated, show that too + if (run.pull_requests && run.pull_requests.length > 0) { + run.pull_requests.forEach((pr) => { + const prUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/pull/${pr.number}`; + core.info(`🔗 Associated PR: ${prUrl}`); + }); + } + } else if (context.eventName === "pull_request_target" && context.payload.pull_request) { + const payload = /** @type {import("@octokit/webhooks-types").PullRequestEvent} */ ( + context.payload + ); + + const pr = payload.pull_request; + core.info(`PR number: ${pr.number}`); + core.info(`PR title: ${pr.title}`); + core.info(`PR state: ${pr.state}`); + core.info(`PR head SHA: ${pr.head.sha}`); + core.info(`PR base branch: ${pr.base.ref}`); + core.info(`🔗 PR URL: ${pr.html_url}`); + } +} diff --git a/.github/workflows/src/summarize-checks/label-rules.js b/.github/workflows/src/summarize-checks/labelling.js similarity index 59% rename from .github/workflows/src/summarize-checks/label-rules.js rename to .github/workflows/src/summarize-checks/labelling.js index a21205c8eed2..3a1050885255 100644 --- a/.github/workflows/src/summarize-checks/label-rules.js +++ b/.github/workflows/src/summarize-checks/labelling.js @@ -1,10 +1,10 @@ /* - This file determines what labels are required for a given PR. It uses this context to obtain two key - pieces of information: - 1. The labels that are currently present on the PR. - 2. The target branch of the PR. + This file covers two areas of enforcement: + 1. It calculates what set of label rules has been violated by the current PR, for the purposes of updating next steps to merge. + 2. It calculates what set of labels should be added or removed from the PR. */ +import { includesEvery, includesNone } from "../../../shared/src/array.js"; import { brchTsg, diagramTsg, @@ -13,6 +13,54 @@ import { wrapInArmReviewMessage, } from "./tsgs.js"; +// #region typedefs +/** + * The LabelContext is used by the updateLabels() to determine which labels to add or remove to the PR. + * + * The "present" set represents the set of labels that are currently present on the PR and should be populated + * ONCE at the beginning of the summarize-checks action script. + * + * The "toAdd" set is the set of labels to be added to the PR at the end of invocation of updateLabels(). + * This is to be done by calling GitHub Octokit API to add the labels. + * + * The "toRemove" set is analogous to "toAdd" set, but instead it is the set of labels to be removed. + * + * The general pattern used in the code to populate "toAdd" or "toRemove" sets to be ready for + * Octokit invocation is as follows: + * + * - the summary() function passes the context through its invocation chain. + * - given function responsible for given label, like e.g. for label "ARMReview", + * creates a new instance of Label: const armReviewLabel = new Label("ARMReview", labelContext.present) + * - the function then processes the label to determine if armReviewLabel.shouldBePresent is to be set to true or false. + * - the function at the end of its invocation calls armReviewLabel.applyStateChanges(labelContext.toAdd, labelContext.toRemove) + * to update the sets. + * - the function may optionally return { armReviewLabel.shouldBePresent } to allow the caller to pass this value + * further to downstream business logic that depends on it. + * - at the end of invocation summary() calls Octokit passing it as input labelContext.toAdd and labelContext.toRemove. + */ +/** + * @typedef {Object} LabelContext + * @property {Set} present - The current set of labels + * @property {Set} toAdd - The set of labels to add + * @property {Set} toRemove - The set of labels to remove + */ + +/** + * @typedef {Object} ImpactAssessment + * @property {boolean} resourceManagerRequired - Whether a resource manager review is required. + * @property {boolean} dataPlaneRequired - Whether a data plane review is required. + * @property {boolean} suppressionReviewRequired - Whether a suppression review is required. + * @property {boolean} isNewApiVersion - Whether this PR introduces a new API version. + * @property {boolean} rpaasExceptionRequired - Whether an RPaaS exception is required. + * @property {boolean} rpaasRpNotInPrivateRepo - Whether the RPaaS RP is not present in the private repo. + * @property {boolean} rpaasChange - Whether this PR includes RPaaS changes. + * @property {boolean} newRP - Whether this PR introduces a new resource provider. + * @property {boolean} rpaasRPMissing - Whether the RPaaS RP label is missing. + * @property {boolean} typeSpecChanged - Whether a TypeSpec file has changed. + * @property {boolean} isDraft - Whether the PR is a draft. + * @property {string} targetBranch - The name of the target branch for the PR. + */ + /** * This file is the single source of truth for the labels used by the SDK generation tooling * in the Azure/azure-rest-api-specs and Azure/azure-rest-api-specs-pr repositories. @@ -23,6 +71,230 @@ import { * - https://microsoftapc-my.sharepoint.com/:w:/g/personal/raychen_microsoft_com/EbOAA9SkhQhGlgxtf7mc0kUB-25bFue0EFbXKXS3TFLTQA */ +/** + * RequiredLabelRule: + * IF ((any of the anyPrerequisiteLabels is present + * OR all of the allPrerequisiteLabels are present) + * AND none of the allPrerequisiteAbsentLabels is present) + * THEN any of the anyRequiredLabels is required. + * + * IF any of the anyRequiredLabels is required + * THEN display the troubleshootingGuide in "Next Steps to Merge" comment. + * + * @typedef {Object} RequiredLabelRule + * @property {number} precedence - If multiple RequiredLabelRules are violated, the one with lowest + * precedence should be displayed to the user first. If multiple rules have + * the same precedence, and one of them should be displayed, + * then all of them should be displayed. + * + * Note this independent of the CheckMetadata.precedence. That is, + * if there are failing checks, and failing required label rules, + * both of them will be shown, both taking appropriate lowest precedence. + * @property {string[]} [branches] - Branches, in format "repo/branch", e.g. "azure-rest-api-specs/main", + * to which this required label rule applies. + * + * To be exact: + * - If there is at least one branch defined, and the evaluated PR is + * not targeting any of the branches defined, then the rule is not applicable (it implicitly passes). + * - If "branches" is empty or undefined, then the rule applies to all branches in all repos. + * @property {string[]} [anyPrerequisiteLabels] - If any of anyPrerequisiteLabels is present, the requiredLabel is required. + * This condition is ORed with allPrerequisiteLabels. + * + * If the anyRequiredLabels collection is empty or undefined, anyPrerequisiteLabels must have exactly one entry. + * @property {string[]} [allPrerequisiteLabels] - If all of allPrerequisiteLabels are present, the requiredLabel is required. + * This condition is ORed with anyPrerequisiteLabels. + * + * If the anyRequiredLabels collection is empty or undefined, allPrerequisiteLabels must be empty or undefined. + * @property {string[]} [allPrerequisiteAbsentLabels] - If any of the allPrerequisiteAbsentLabels is present, + * the requiredLabel is not required. + * @property {string[]} anyRequiredLabels - If any of the labels in anyRequiredLabels is present, + * then the rule prerequisites, as expressed by anyPrerequisiteLabels and allPrerequisiteLabels, are met. + * Conversely, if none of anyRequiredLabels are present, then the rule is violated. + * + * For the purposes of determining which label to display as required in the 'automated merging requirements met' check and + * 'Next Steps to Merge" comments, the first label in anyRequiredLabels is used. + * The assumption here is that anyRequiredLabels[0] is the 'current' label, + * while the remaining required label options are 'legacy' labels. + * + * If given required label string ends with an asterisk, it is treated as a prefix substring match. + * For example, requiredLabel of 'Foo-Approved-*' means that any label with prefix 'Foo-Approved-' will satisfy the rule. + * For example, 'Foo-Approved-Bar' or 'Foo-Approved-Qux', but not 'Foo-Approved' nor 'Foo-Approved-'. + * + * If anyRequiredLabels is an empty array, then if the rule is violated, there is no way to meet the rule prerequisites. + * @property {string} troubleshootingGuide - The doc to display to the user if the required label is required, but missing. + */ + +/** + * This file is the single source of truth for types used by the OpenAPI specification breaking change checks + * in the Azure/azure-rest-api-specs and Azure/azure-rest-api-specs-pr repositories. + * + * For additional context, see: + * + * - "Deep-dive into breaking changes on spec PRs" + * https://aka.ms/azsdk/pr-brch-deep + * + * - "[Breaking Change][PR Workflow] Use more granular labels for Breaking Changes approvals" + * https://github.com/Azure/azure-sdk-tools/issues/6374 + */ +/** + * @typedef {"SameVersion" | "CrossVersion"} BreakingChangesCheckType + * @typedef {"VersioningReviewRequired" | "BreakingChangeReviewRequired"} ReviewRequiredLabel + * @typedef {"Versioning-Approved-*" | "BreakingChange-Approved-*"} ReviewApprovalPrefixLabel + * @typedef {"Versioning-Approved-Benign" | "Versioning-Approved-BugFix" | "Versioning-Approved-PrivatePreview" | "Versioning-Approved-BranchPolicyException" | "Versioning-Approved-Previously" | "Versioning-Approved-Retired"} ValidVersioningApproval + * @typedef {"BreakingChange-Approved-Benign" | "BreakingChange-Approved-BugFix" | "BreakingChange-Approved-UserImpact" | "BreakingChange-Approved-BranchPolicyException" | "BreakingChange-Approved-Previously" | "BreakingChange-Approved-Security"} ValidBreakingChangeApproval + * @typedef {ReviewRequiredLabel | ReviewApprovalPrefixLabel | ValidBreakingChangeApproval | ValidVersioningApproval} SpecsBreakingChangesLabel + */ +/** + * @typedef {Object} BreakingChangesCheckConfig + * @property {ReviewRequiredLabel} reviewRequiredLabel + * @property {ReviewApprovalPrefixLabel} approvalPrefixLabel + * @property {(ValidVersioningApproval | ValidBreakingChangeApproval)[]} approvalLabels + * @property {string} [deprecatedReviewRequiredLabel] + */ + +/** + * @typedef {"SwaggerFile" | "TypeSpecFile" | "ExampleFile" | "ReadmeFile"} FileTypes + */ + +/** + * @typedef {"Addition" | "Deletion" | "Update"} ChangeTypes + */ + +/** + * @typedef {Object} PRChange + * @property {FileTypes} fileType + * @property {ChangeTypes} changeType + * @property {string} filePath + * @property {any} [additionalInfo] + */ + +/** + * @typedef {Object} ChangeHandler + * @property {function(PRChange): void} [SwaggerFile] + * @property {function(PRChange): void} [TypeSpecFile] + * @property {function(PRChange): void} [ExampleFile] + * @property {function(PRChange): void} [ReadmeFile] + */ + +/** + * @typedef {"resource-manager" | "data-plane"} PRType + */ + +/** + * Represents a GitHub label. + * Currently used in the context of processing + * labels related to the review workflow of PRs submitted to + * Azure/azure-rest-api-specs and Azure/azure-rest-api-specs-pr repositories. + * This processing happens in the prSummary.ts / summary() function. + * + * See also: https://aka.ms/SpecPRReviewARMInvariants + */ +// todo: inject `core` for access to logging +export class Label { + /** + * @param {string} name + * @param {Set} [presentLabels] + */ + constructor(name, presentLabels) { + /** @type {string} */ + this.name = name; + + /** + * Is the label currently present on the pull request? + * This is determined at the time of construction of this object. + * @type {boolean | undefined} + */ + this.present = presentLabels?.has(this.name) ?? undefined; + + /** + * Should this label be present on the pull request? + * Must be defined before applyStateChange is called. + * Not set at the construction time to facilitate determining desired presence + * of multiple labels in single code block, without intermixing it with + * label construction logic. + * @type {boolean | undefined} + */ + this.shouldBePresent = undefined; + } + + /** + * If the label should be added, add its name to labelsToAdd. + * If the label should be removed, add its name to labelsToRemove. + * Otherwise, do nothing. + * + * Precondition: this.shouldBePresent has been defined. + * @param {Set} labelsToAdd + * @param {Set} labelsToRemove + */ + applyStateChange(labelsToAdd, labelsToRemove) { + if (this.shouldBePresent === undefined) { + console.warn( + "ASSERTION VIOLATION! " + + `Cannot applyStateChange for label '${this.name}' ` + + "as its desired presence hasn't been defined. Returning early.", + ); + throw new Error( + `Label '${this.name}' has not been properly initialized with shouldBePresent before being applied.`, + ); + } + + if (!this.present && this.shouldBePresent) { + if (!labelsToAdd.has(this.name)) { + console.log( + `Label.applyStateChange: '${this.name}' was not present and should be present. Scheduling addition.`, + ); + labelsToAdd.add(this.name); + } else { + console.log( + `Label.applyStateChange: '${this.name}' was not present and should be present. It is already scheduled for addition.`, + ); + } + } else if (this.present && !this.shouldBePresent) { + if (!labelsToRemove.has(this.name)) { + console.log( + `Label.applyStateChange: '${this.name}' was present and should not be present. Scheduling removal.`, + ); + labelsToRemove.add(this.name); + } else { + console.log( + `Label.applyStateChange: '${this.name}' was present and should not be present. It is already scheduled for removal.`, + ); + } + } else if (this.present === this.shouldBePresent) { + console.log( + `Label.applyStateChange: '${this.name}' is ${this.present ? "present" : "not present"}. This is the desired state.`, + ); + } else { + console.warn( + "ASSERTION VIOLATION! " + + `Label.applyStateChange: '${this.name}' is ${this.present ? "present" : "not present"} while it should be ${this.shouldBePresent ? "present" : "not present"}. ` + + `At this point of execution this should not happen.`, + ); + } + } + + /** + * @param {string} label + * @returns {boolean} + */ + isEqualToOrPrefixOf(label) { + return this.name.endsWith("*") ? label.startsWith(this.name.slice(0, -1)) : this.name === label; + } + + /** + * @returns {string} + */ + logString() { + return ( + `Label: name: ${this.name}, ` + + `present: ${this.present}, ` + + `shouldBePresent: ${this.shouldBePresent}. ` + ); + } +} + +// #endregion typedefs +// #region constants export const sdkLabels = { "azure-cli-extensions": { breakingChange: undefined, @@ -99,38 +371,10 @@ export const sdkLabels = { }, }; -/** - * This file is the single source of truth for types used by the OpenAPI specification breaking change checks - * in the Azure/azure-rest-api-specs and Azure/azure-rest-api-specs-pr repositories. - * - * For additional context, see: - * - * - "Deep-dive into breaking changes on spec PRs" - * https://aka.ms/azsdk/pr-brch-deep - * - * - "[Breaking Change][PR Workflow] Use more granular labels for Breaking Changes approvals" - * https://github.com/Azure/azure-sdk-tools/issues/6374 - */ -/** - * @typedef {"SameVersion" | "CrossVersion"} BreakingChangesCheckType - * @typedef {"VersioningReviewRequired" | "BreakingChangeReviewRequired"} ReviewRequiredLabel - * @typedef {"Versioning-Approved-*" | "BreakingChange-Approved-*"} ReviewApprovalPrefixLabel - * @typedef {"Versioning-Approved-Benign" | "Versioning-Approved-BugFix" | "Versioning-Approved-PrivatePreview" | "Versioning-Approved-BranchPolicyException" | "Versioning-Approved-Previously" | "Versioning-Approved-Retired"} ValidVersioningApproval - * @typedef {"BreakingChange-Approved-Benign" | "BreakingChange-Approved-BugFix" | "BreakingChange-Approved-UserImpact" | "BreakingChange-Approved-BranchPolicyException" | "BreakingChange-Approved-Previously" | "BreakingChange-Approved-Security"} ValidBreakingChangeApproval - * @typedef {ReviewRequiredLabel | ReviewApprovalPrefixLabel | ValidBreakingChangeApproval | ValidVersioningApproval} SpecsBreakingChangesLabel - */ -/** - * @typedef {Object} BreakingChangesCheckConfig - * @property {ReviewRequiredLabel} reviewRequiredLabel - * @property {ReviewApprovalPrefixLabel} approvalPrefixLabel - * @property {(ValidVersioningApproval | ValidBreakingChangeApproval)[]} approvalLabels - * @property {string} [deprecatedReviewRequiredLabel] - */ - /** * @type {Record} */ -// todo: pull this from eng/tools/openapi-diff-runner/src/types/breaking-change.ts +// todo: pull values from eng/tools/openapi-diff-runner/src/types/breaking-change.ts export const breakingChangesCheckType = { SameVersion: { reviewRequiredLabel: "VersioningReviewRequired", @@ -158,6 +402,352 @@ export const breakingChangesCheckType = { }, }; +// #endregion constants +// #region Required Labels + +/** + * @param {LabelContext} context + * @param {string[]} existingLabels + * @returns {void} + */ +export function processArmReviewLabels(context, existingLabels) { + // only kick this off if the ARMReview label is present and NotReadyForARMReview is not present + if (existingLabels.includes("ARMReview") && !existingLabels.includes("NotReadyForARMReview")) { + // the important part about how this will work depends how the users use it + // EG: if they add the "ARMSignedOff" label, we will remove the "ARMChangesRequested" and "WaitForARMFeedback" labels. + // if they add the "ARMChangesRequested" label, we will remove the "WaitForARMFeedback" label. + // if they remove the "ARMChangesRequested" label, we will add the "WaitForARMFeedback" label. + // so if the user or ARM team actually unlabels `ARMChangesRequested`, then we're actually ok + // if we are signed off, we should remove the "ARMChangesRequested" and "WaitForARMFeedback" labels + if (includesEvery(existingLabels, ["ARMSignedOff"])) { + if (existingLabels.includes("ARMChangesRequested")) { + context.toRemove.add("ARMChangesRequested"); + } + if (existingLabels.includes("WaitForARMFeedback")) { + context.toRemove.add("WaitForARMFeedback"); + } + } + // if there are ARM changes requested, we should remove the "WaitForARMFeedback" label as the presence indicates that ARM has reviewed + else if ( + includesEvery(existingLabels, ["ARMChangesRequested"]) && + includesNone(existingLabels, ["ARMSignedOff"]) + ) { + if (existingLabels.includes("WaitForARMFeedback")) { + context.toRemove.add("WaitForARMFeedback"); + } + } + // finally, if ARMChangesRequested are not present, and we've gotten here by lac;k of signoff, we should add the "WaitForARMFeedback" label + else if (includesNone(existingLabels, ["ARMChangesRequested"])) { + if (!existingLabels.includes("WaitForARMFeedback")) { + context.toAdd.add("WaitForARMFeedback"); + } + } + } +} + +/** +This function determines which labels of the ARM review should +be applied to given PR. It adds and removes the labels as appropriate. It used to be called +ProcessARMReview, but was renamed to processImpactAssessment to better reflect its purpose given that is +merely evaluating the impact assessment of the PR and returning a final set of labels to be applied/removed +from the PR. + +This function does the following, **among other things**: + +- Adds the "ARMReview" label if all of the following conditions hold: + - The processed PR "isReleaseBranch" or "isShiftLeftPRWithRPSaaSDev" + - The PR is not a draft, as determined by "isDraftPR" + - The PR is labelled with "resource-manager" label, meaning it pertains + to ARM, as previously determined by the "isManagementPR" function, + called from the "getPRType" function. + +- Calls the "processARMReviewWorkflowLabels" function if "ARMReview" label applies. +*/ +/** + * @param {LabelContext} labelContext + * @param {ImpactAssessment} impactAssessment + * @returns {Promise<{armReviewLabelShouldBePresent: boolean}>} + */ +export async function processImpactAssessment(labelContext, impactAssessment) { + console.log("ENTER definition processARMReview"); + + // By default these two should not be present. We may determine later in this function that they should be present after all. + const armReviewLabel = new Label("ARMReview", labelContext.present); + armReviewLabel.shouldBePresent = false; + const newApiVersionLabel = new Label("new-api-version", labelContext.present); + newApiVersionLabel.shouldBePresent = false; + + const resourceManagerLabel = new Label("resource-manager", labelContext.present); + resourceManagerLabel.shouldBePresent = impactAssessment.resourceManagerRequired || false; + + const dataplaneLabel = new Label("data-plane", labelContext.present); + dataplaneLabel.shouldBePresent = impactAssessment.dataPlaneRequired || false; + + const typeSpecLabel = new Label("TypeSpec", labelContext.present); + typeSpecLabel.shouldBePresent = impactAssessment.typeSpecChanged || false; + + const suppressionReviewRequiredLabel = new Label( + "SuppressionReviewRequired", + labelContext.present, + ); + suppressionReviewRequiredLabel.shouldBePresent = + impactAssessment.suppressionReviewRequired || false; + + const rpassReviewRequiredLabel = new Label("RPaaS", labelContext.present); + rpassReviewRequiredLabel.shouldBePresent = impactAssessment.rpaasChange || false; + + const newRPNamespaceLabel = new Label("new-rp-namespace", labelContext.present); + newRPNamespaceLabel.shouldBePresent = impactAssessment.newRP || false; + + const ciNewRPNamespaceWithoutRpaaSLabel = new Label( + "CI-NewRPNamespaceWithoutRPaaS", + labelContext.present, + ); + ciNewRPNamespaceWithoutRpaaSLabel.shouldBePresent = impactAssessment.rpaasRPMissing || false; + + const rpaasExceptionLabel = new Label("RPaaSException", labelContext.present); + rpaasExceptionLabel.shouldBePresent = impactAssessment.rpaasExceptionRequired || false; + + const ciRpaasRPNotInPrivateRepoLabel = new Label( + "CI-RpaaSRPNotInPrivateRepo", + labelContext.present, + ); + ciRpaasRPNotInPrivateRepoLabel.shouldBePresent = + impactAssessment.rpaasRpNotInPrivateRepo || false; + + const branch = impactAssessment.targetBranch; + const isReleaseBranchVal = isReleaseBranch(branch); + + // we used to also calculate if the branch name was from ShiftLeft, in which case we would or that + // with isReleaseBranchVal to see if it's in scope of ARM review. ShiftLeft is not supported anymore, + // so we only check if the branch is a release branch. + // const prTitle = await getPrTitle(owner, repo, prNumber) + // const isShiftLeftPRWithRPSaaSDevVal = isShiftLeftPRWithRPSaaSDev(prTitle, branch) + const isBranchInScopeOfSpecReview = isReleaseBranchVal; + + // 'specReviewApplies' means that either ARM or data-plane review applies. Downstream logic + // determines which kind of review exactly we need. + let specReviewApplies = !impactAssessment.isDraft && isBranchInScopeOfSpecReview; + if (specReviewApplies) { + if (impactAssessment.isNewApiVersion) { + // Note that in case of data-plane PRs, the addition of this label will result + // in API stewardship board review being required. + // See requiredLabelsRules.ts. + newApiVersionLabel.shouldBePresent = true; + } + + armReviewLabel.shouldBePresent = impactAssessment.resourceManagerRequired; + await processARMReviewWorkflowLabels( + labelContext, + armReviewLabel.shouldBePresent, + impactAssessment.rpaasRPMissing, + impactAssessment.rpaasExceptionRequired, + impactAssessment.rpaasRpNotInPrivateRepo, + ); + } + + dataplaneLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + resourceManagerLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + newApiVersionLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + armReviewLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + typeSpecLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + suppressionReviewRequiredLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + rpassReviewRequiredLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + newRPNamespaceLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + ciNewRPNamespaceWithoutRpaaSLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + rpaasExceptionLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + ciRpaasRPNotInPrivateRepoLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + + // this is the only labelling that was part of original pipelinebot logic, it handles the rotation of + // ARMChangesRequested, WaitForArmFeedback, and ARMSignedOff labels. The thing is, we only want to make + // these changes if the review is actually an ARM review. So we're going to place this after processing an impactAssessment + // which will tell us if a the ARMReview label should be present or not. + processArmReviewLabels(labelContext, [...labelContext.present, ...labelContext.toAdd]); + + console.log( + `RETURN definition processARMReview. ` + + `isReleaseBranch: ${isReleaseBranchVal}, ` + + `isArmReview: ${armReviewLabel.shouldBePresent}, ` + + `isBranchInScopeOfArmReview: ${isBranchInScopeOfSpecReview}, ` + + `isNewApiVersion: ${impactAssessment.isNewApiVersion}, ` + + `isDraft: ${impactAssessment.isDraft}, ` + + `newApiVersionLabel.shouldBePresent: ${newApiVersionLabel.shouldBePresent}, ` + + `armReviewLabel.shouldBePresent: ${armReviewLabel.shouldBePresent}.`, + ); + + return { armReviewLabelShouldBePresent: armReviewLabel.shouldBePresent }; +} + +/** + * @param {string} branchName + * @returns {boolean} + */ +function isReleaseBranch(branchName) { + const branchRegex = [/main/, /RPSaaSMaster/, /release*/, /ARMCoreRPDev/]; + return branchRegex.some((b) => b.test(branchName)); +} + +/** +CODESYNC: +- requiredLabelsRules.ts / requiredLabelsRules +- https://github.com/Azure/azure-rest-api-specs/blob/main/.github.amrom.workers.devment.yml + +This function determines which label from the ARM review workflow labels +should be present on the PR. It adds and removes the labels as appropriate. + +In other words, this function captures the +ARM review workflow label processing logic. + +To be exact, this function executes if and only if the PR in question +has been determined to have the "ARMReview" label, denoting given PR +is in scope for ARM review. + +The implementation of this function is the source of truth specifying the +desired behavior. + +To understand this implementation, the most important constraint to keep in mind +is that if "ARMReview" label is present, then exactly one of the following +labels must be present: + +- NotReadyForARMReview +- WaitForARMFeedback +- ARMChangesRequested +- ARMSignedOff + +Note that another important place in this codebase where ARM review workflow +labels are being removed or added to a PR is pipelineBotOnPRLabelEvent.ts. +*/ +/** + * @param {LabelContext} labelContext + * @param {boolean} armReviewLabelShouldBePresent + * @param {boolean} ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent + * @param {boolean} rpaasExceptionLabelShouldBePresent + * @param {boolean} ciRpaasRPNotInPrivateRepoLabelShouldBePresent + * @returns {Promise} + */ +async function processARMReviewWorkflowLabels( + labelContext, + armReviewLabelShouldBePresent, + ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent, + rpaasExceptionLabelShouldBePresent, + ciRpaasRPNotInPrivateRepoLabelShouldBePresent, +) { + console.log("ENTER definition processARMReviewWorkflowLabels"); + + const notReadyForArmReviewLabel = new Label("NotReadyForARMReview", labelContext.present); + + const waitForArmFeedbackLabel = new Label("WaitForARMFeedback", labelContext.present); + + const armChangesRequestedLabel = new Label("ARMChangesRequested", labelContext.present); + + const armSignedOffLabel = new Label("ARMSignedOff", labelContext.present); + + const blockedOnVersioningPolicy = getBlockedOnVersioningPolicy(labelContext); + + const blockedOnRpaas = getBlockedOnRpaas( + ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent, + rpaasExceptionLabelShouldBePresent, + ciRpaasRPNotInPrivateRepoLabelShouldBePresent, + ); + + const blocked = blockedOnRpaas || blockedOnVersioningPolicy; + + // If given PR is in scope of ARM review and it is blocked for any reason, + // the "NotReadyForARMReview" label should be present, to the exclusion + // of all other ARM review workflow labels. + notReadyForArmReviewLabel.shouldBePresent = armReviewLabelShouldBePresent && blocked; + + // If given PR is in scope of ARM review and the review is not blocked, + // then "ARMSignedOff" label should remain present on the PR if it was + // already present. This means that labels "ARMChangesRequested" + // and "WaitForARMFeedback" are invalid and will be removed by automation + // in presence of "ARMSignedOff". + armSignedOffLabel.shouldBePresent = + armReviewLabelShouldBePresent && !blocked && armSignedOffLabel.present; + + // If given PR is in scope of ARM review and the review is not blocked and + // not signed-off, then the label "ARMChangesRequested" should remain present + // if it was already present. This means that labels "WaitForARMFeedback" + // is invalid and will be removed by automation in presence of + // "WaitForARMFeedback". + armChangesRequestedLabel.shouldBePresent = + armReviewLabelShouldBePresent && + !blocked && + !armSignedOffLabel.shouldBePresent && + armChangesRequestedLabel.present; + + // If given PR is in scope of ARM review and the review is not blocked and + // not signed-off, and ARM reviewer didn't request any changes, + // then the label "WaitForARMFeedback" should be present on the PR, whether + // it was present before or not. + waitForArmFeedbackLabel.shouldBePresent = + armReviewLabelShouldBePresent && + !blocked && + !armSignedOffLabel.shouldBePresent && + !armChangesRequestedLabel.shouldBePresent && + (waitForArmFeedbackLabel.present || true); + + const exactlyOneArmReviewWorkflowLabelShouldBePresent = + Number(notReadyForArmReviewLabel.shouldBePresent) + + Number(armSignedOffLabel.shouldBePresent) + + Number(armChangesRequestedLabel.shouldBePresent) + + Number(waitForArmFeedbackLabel.shouldBePresent) === + 1 || !armReviewLabelShouldBePresent; + + if (!exactlyOneArmReviewWorkflowLabelShouldBePresent) { + console.warn("ASSERTION VIOLATION! exactlyOneArmReviewWorkflowLabelShouldBePresent is false"); + } + + notReadyForArmReviewLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + armSignedOffLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + armChangesRequestedLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + waitForArmFeedbackLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + + console.log( + `RETURN definition processARMReviewWorkflowLabels. ` + + `presentLabels: ${[...labelContext.present].join(",")}, ` + + `blockedOnRpaas: ${blockedOnRpaas}. ` + + `exactlyOneArmReviewWorkflowLabelShouldBePresent: ${exactlyOneArmReviewWorkflowLabelShouldBePresent}. `, + ); + return; +} + +/** + * @param {LabelContext} labelContext + * @returns {boolean} + */ +function getBlockedOnVersioningPolicy(labelContext) { + const pendingVersioningReview = + labelContext.present.has("VersioningReviewRequired") && + !anyApprovalLabelPresent("SameVersion", [...labelContext.present]); + + const pendingBreakingChangeReview = + labelContext.present.has("BreakingChangeReviewRequired") && + !anyApprovalLabelPresent("CrossVersion", [...labelContext.present]); + + const blockedOnVersioningPolicy = pendingVersioningReview || pendingBreakingChangeReview; + return blockedOnVersioningPolicy; +} + +/** + * @param {boolean} ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent + * @param {boolean} rpaasExceptionLabelShouldBePresent + * @param {boolean} ciRpaasRPNotInPrivateRepoLabelShouldBePresent + * @returns {boolean} + */ +function getBlockedOnRpaas( + ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent, + rpaasExceptionLabelShouldBePresent, + ciRpaasRPNotInPrivateRepoLabelShouldBePresent, +) { + return ( + (ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent && !rpaasExceptionLabelShouldBePresent) || + ciRpaasRPNotInPrivateRepoLabelShouldBePresent + ); +} + +// #endregion +// #region LabelRules /** * @param {BreakingChangesCheckType} approvalType * @param {string[]} labels @@ -169,59 +759,6 @@ export function anyApprovalLabelPresent(approvalType, labels) { return anyLabelMatches(labelsToMatchAgainst, labels); } -/** - * RequiredLabelRule: - * IF ((any of the anyPrerequisiteLabels is present - * OR all of the allPrerequisiteLabels are present) - * AND none of the allPrerequisiteAbsentLabels is present) - * THEN any of the anyRequiredLabels is required. - * - * IF any of the anyRequiredLabels is required - * THEN display the troubleshootingGuide in "Next Steps to Merge" comment. - * - * @typedef {Object} RequiredLabelRule - * @property {number} precedence - If multiple RequiredLabelRules are violated, the one with lowest - * precedence should be displayed to the user first. If multiple rules have - * the same precedence, and one of them should be displayed, - * then all of them should be displayed. - * - * Note this independent of the CheckMetadata.precedence. That is, - * if there are failing checks, and failing required label rules, - * both of them will be shown, both taking appropriate lowest precedence. - * @property {string[]} [branches] - Branches, in format "repo/branch", e.g. "azure-rest-api-specs/main", - * to which this required label rule applies. - * - * To be exact: - * - If there is at least one branch defined, and the evaluated PR is - * not targeting any of the branches defined, then the rule is not applicable (it implicitly passes). - * - If "branches" is empty or undefined, then the rule applies to all branches in all repos. - * @property {string[]} [anyPrerequisiteLabels] - If any of anyPrerequisiteLabels is present, the requiredLabel is required. - * This condition is ORed with allPrerequisiteLabels. - * - * If the anyRequiredLabels collection is empty or undefined, anyPrerequisiteLabels must have exactly one entry. - * @property {string[]} [allPrerequisiteLabels] - If all of allPrerequisiteLabels are present, the requiredLabel is required. - * This condition is ORed with anyPrerequisiteLabels. - * - * If the anyRequiredLabels collection is empty or undefined, allPrerequisiteLabels must be empty or undefined. - * @property {string[]} [allPrerequisiteAbsentLabels] - If any of the allPrerequisiteAbsentLabels is present, - * the requiredLabel is not required. - * @property {string[]} anyRequiredLabels - If any of the labels in anyRequiredLabels is present, - * then the rule prerequisites, as expressed by anyPrerequisiteLabels and allPrerequisiteLabels, are met. - * Conversely, if none of anyRequiredLabels are present, then the rule is violated. - * - * For the purposes of determining which label to display as required in the 'automated merging requirements met' check and - * 'Next Steps to Merge" comments, the first label in anyRequiredLabels is used. - * The assumption here is that anyRequiredLabels[0] is the 'current' label, - * while the remaining required label options are 'legacy' labels. - * - * If given required label string ends with an asterisk, it is treated as a prefix substring match. - * For example, requiredLabel of 'Foo-Approved-*' means that any label with prefix 'Foo-Approved-' will satisfy the rule. - * For example, 'Foo-Approved-Bar' or 'Foo-Approved-Qux', but not 'Foo-Approved' nor 'Foo-Approved-'. - * - * If anyRequiredLabels is an empty array, then if the rule is violated, there is no way to meet the rule prerequisites. - * @property {string} troubleshootingGuide - The doc to display to the user if the required label is required, but missing. - */ - /** * @param {RequiredLabelRule} rule * @returns {boolean} @@ -772,3 +1309,4 @@ export function isEqualToOrPrefixOf(inputstring, label) { ? label.startsWith(inputstring.slice(0, -1)) : inputstring === label; } +// #endregion diff --git a/.github/workflows/src/summarize-checks/summarize-checks.js b/.github/workflows/src/summarize-checks/summarize-checks.js index 07b0051c67eb..d4c98567e726 100644 --- a/.github/workflows/src/summarize-checks/summarize-checks.js +++ b/.github/workflows/src/summarize-checks/summarize-checks.js @@ -19,22 +19,33 @@ */ // #region imports/constants -import { extractInputs } from "../context.js"; -// eslint-disable-next-line no-unused-vars +import { execFile } from "../../../shared/src/exec.js"; +import { CheckConclusion, PER_PAGE_MAX } from "../../../shared/src/github.js"; +import { intersect } from "../../../shared/src/set.js"; +import { byDate, invert } from "../../../shared/src/sort.js"; import { commentOrUpdate } from "../comment.js"; -import { PER_PAGE_MAX } from "../github.js"; -import { verRevApproval, brChRevApproval, getViolatedRequiredLabelsRules } from "./label-rules.js"; +import { extractInputs } from "../context.js"; +import { + brChRevApproval, + getViolatedRequiredLabelsRules, + processImpactAssessment, + verRevApproval, +} from "./labelling.js"; import { brchTsg, - diagramTsg, checkAndDiagramTsg, defaultTsg, + diagramTsg, reqMetCheckTsg, typeSpecRequirementArmTsg, typeSpecRequirementDataPlaneTsg, } from "./tsgs.js"; +import fs from "fs/promises"; +import os from "os"; +import path from "path"; + /** * @typedef {Object} CheckMetadata * @property {number} precedence @@ -47,12 +58,80 @@ import { * @typedef {Object} CheckRunData * @property {string} name * @property {string} status - * @property {string} conclusion + * @property {string | null} conclusion * @property {CheckMetadata} checkInfo */ /** - * @typedef {import("./label-rules.js").RequiredLabelRule} RequiredLabelRule + * @typedef {Object} WorkflowRunArtifact + * @property {string} name + * @property {number} id + * @property {string} url + * @property {string} archive_download_url + */ + +/** + * @typedef {Object} WorkflowRunInfo + * @property {string} name + * @property {number} id + * @property {number} databaseId + * @property {string} url + * @property {number} workflowId + * @property {string} status + * @property {string} conclusion + * @property {string} createdAt + * @property {string} updatedAt + */ + +/** + * @typedef {Object} GraphQLCheckRun + * @property {string} name + * @property {string} status + * @property {string} conclusion + * @property {boolean} isRequired + */ + +/** + * @typedef {Object} GraphQLCheckSuite + * @property {GraphQLCheckRun[]} nodes + */ + +/** + * @typedef {Object} GraphQLCheckSuites + * @property {GraphQLCheckSuite[]} nodes + */ + +/** + * @typedef {Object} GraphQLCommit + * @property {GraphQLCheckSuites} checkSuites + */ + +/** + * @typedef {Object} GraphQLResource + * @property {GraphQLCheckSuites} checkSuites + */ + +/** + * @typedef {Object} GraphQLResponse + * @property {GraphQLResource} resource + * @property {Object} rateLimit + * @property {number} rateLimit.limit + * @property {number} rateLimit.cost + * @property {number} rateLimit.used + * @property {number} rateLimit.remaining + * @property {string} rateLimit.resetAt + */ + +/** + * @typedef {import("./labelling.js").RequiredLabelRule} RequiredLabelRule + */ + +/** + * @typedef {Object} CheckRunResult + * @property {string} name + * @property {string} summary + * @property {"pending" | keyof typeof CheckConclusion} result + * @property {string} [target_url] */ // Placing these configuration items here until we decide another way to pull them in. @@ -63,6 +142,7 @@ const FYI_CHECK_NAMES = [ "Swagger PrettierCheck", ]; const AUTOMATED_CHECK_NAME = "Automated merging requirements met"; +const IMPACT_CHECK_NAME = "Summarize PR Impact"; const NEXT_STEPS_COMMENT_ID = "NextStepsToMerge"; /** @type {CheckMetadata[]} */ @@ -209,14 +289,21 @@ const EXCLUDED_CHECK_NAMES = []; * @returns {Promise} */ export default async function summarizeChecks({ github, context, core }) { - logGitHubRateLimitInfo(github, core); let { owner, repo, issue_number, head_sha } = await extractInputs(github, context, core); + + if (!issue_number) { + core.warning(`No issue number found for this event. Exiting summarize-checks.js early.`); + return; + } + const targetBranch = context.payload.pull_request?.base?.ref; core.info(`PR target branch: ${targetBranch}`); + // Default target is this run itself + const target_url = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + await summarizeChecksImpl( github, - context, core, owner, repo, @@ -224,12 +311,34 @@ export default async function summarizeChecks({ github, context, core }) { head_sha, context.eventName, targetBranch, + target_url, ); } +/** + * @param {typeof import("@actions/core")} core + * @param {CheckRunData[]} requiredCheckRuns + * @param {CheckRunData[]} fyiCheckRuns + */ +export function outputRunDetails(core, requiredCheckRuns, fyiCheckRuns) { + core.info( + `Observed ${requiredCheckRuns.length} required check runs ${requiredCheckRuns.length > 0 ? ":" : "."}`, + ); + requiredCheckRuns.forEach((x) => { + core.info( + `Required check "${x.name}" with status "${x.status}" and conclusion "${x.conclusion}"`, + ); + }); + core.info( + `Observed ${fyiCheckRuns.length} FYI check runs ${fyiCheckRuns.length > 0 ? ":" : "."}`, + ); + fyiCheckRuns.forEach((x) => { + core.info(`FYI check "${x.name}" with status "${x.status}" and conclusion "${x.conclusion}"`); + }); +} + /** * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github - * @param {import('@actions/github').context } context * @param {typeof import("@actions/core")} core * @param {string} owner * @param {string} repo @@ -237,11 +346,11 @@ export default async function summarizeChecks({ github, context, core }) { * @param {string} head_sha * @param {string} event_name * @param {string} targetBranch + * @param {string} target_url * @returns {Promise} */ export async function summarizeChecksImpl( github, - context, core, owner, repo, @@ -249,48 +358,20 @@ export async function summarizeChecksImpl( head_sha, event_name, targetBranch, + target_url, ) { - core.info( - `Handling ${event_name} event for PR #${issue_number} in ${owner}/${repo} with targeted branch ${targetBranch}`, - ); - - const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, { - owner: owner, - repo: repo, - issue_number: issue_number, - per_page: PER_PAGE_MAX, - }); + core.info(`Handling ${event_name} event for PR #${issue_number} in ${owner}/${repo}.`); - /** @type {string[]} */ - let labelNames = labels.map((/** @type {{ name: string; }} */ label) => label.name); - - // handle our label trigger first, we may bail out early if it's a label action we're reacting to - // this also implies that if a label action is performed before any workflows complete, we shouldn't - // accidentally update the next steps to merge with the results of the workflows that haven't completed yet. - if (event_name in ["labeled", "unlabeled"]) { - // if anything goes wrong with label actions, the invocation will end within handleLabeledEvent due to localized error handling - const [labelsToAdd, labelsToRemove] = await handleLabeledEvent( - github, - context, - core, - owner, - repo, - issue_number, - event_name, - labelNames, - ); + const prUrl = `https://github.com/${owner}/${repo}/pull/${issue_number}`; + core.summary.addRaw("PR: "); + core.summary.addLink(prUrl, prUrl); + core.summary.write(); + core.setOutput("summary", process.env.GITHUB_STEP_SUMMARY); - // adjust labelNames based on labelsToAdd/labelsToRemove - labelNames = labelNames.filter((name) => !labelsToRemove.includes(name)); - for (const label of labelsToAdd) { - if (!labelNames.includes(label)) { - labelNames.push(label); - } - } - } + let labelNames = await getExistingLabels(github, owner, repo, issue_number); - /** @type {[CheckRunData[], CheckRunData[]]} */ - const [requiredCheckRuns, fyiCheckRuns] = await getCheckRunTuple( + /** @type {[CheckRunData[], CheckRunData[], import("./labelling.js").ImpactAssessment | undefined]} */ + const [requiredCheckRuns, fyiCheckRuns, impactAssessment] = await getCheckRunTuple( github, core, owner, @@ -300,186 +381,225 @@ export async function summarizeChecksImpl( EXCLUDED_CHECK_NAMES, ); - const commentBody = await createNextStepsComment( + outputRunDetails(core, requiredCheckRuns, fyiCheckRuns); + + if (impactAssessment) { + core.info(`ImpactAssessment: ${JSON.stringify(impactAssessment)}`); + targetBranch = impactAssessment.targetBranch; + } else { + core.info( + `No impact assessment found for ${owner}/${repo}#${issue_number}. ` + + `No labels will be added or removed in this run, and only "pending" status check will be set.`, + ); + } + + let labelContext = await updateLabels(labelNames, impactAssessment); + + core.info( + `Summarize checks label actions against ${owner}/${repo}#${issue_number}: \n` + + `The following labels were present: [${Array.from(labelContext.present).join(", ")}]` + + `Removing labels [${Array.from(labelContext.toRemove).join(", ")}] then \n` + + `Adding labels [${Array.from(labelContext.toAdd).join(", ")}]`, + ); + + for (const label of labelContext.toRemove) { + core.info(`Removing label: ${label} from ${owner}/${repo}#${issue_number}.`); + await github.rest.issues.removeLabel({ + owner: owner, + repo: repo, + issue_number: issue_number, + name: label, + }); + } + + if (labelContext.toAdd.size > 0) { + core.info( + `Adding labels: ${Array.from(labelContext.toAdd).join(", ")} to ${owner}/${repo}#${issue_number}.`, + ); + await github.rest.issues.addLabels({ + owner: owner, + repo: repo, + issue_number: issue_number, + labels: Array.from(labelContext.toAdd), + }); + } + + // adjust labelNames based on labelsToAdd/labelsToRemove + labelNames = labelNames.filter((name) => !labelContext.toRemove.has(name)); + for (const label of labelContext.toAdd) { + if (!labelNames.includes(label)) { + labelNames.push(label); + } + } + + const [commentBody, automatedChecksMet] = await createNextStepsComment( core, repo, labelNames, targetBranch, requiredCheckRuns, fyiCheckRuns, + impactAssessment !== undefined, + target_url, ); + automatedChecksMet.target_url = target_url; + core.info( `Updating comment '${NEXT_STEPS_COMMENT_ID}' on ${owner}/${repo}#${issue_number} with body: ${commentBody}`, ); + core.summary.addRaw(`\n${commentBody}\n\n`); + core.summary.write(); + // this will remain commented until we're comfortable with the change. - // await commentOrUpdate( - // { github, context, core }, - // owner, - // repo, - // issue_number, - // commentName, - // commentBody - // ) + await commentOrUpdate( + github, + core, + owner, + repo, + issue_number, + commentBody, + NEXT_STEPS_COMMENT_ID, + ); + + // finally, update the "Automated merging requirements met" commit status + await updateCommitStatus(github, core, owner, repo, head_sha, automatedChecksMet); + + core.info( + `Summarize checks has identified that status of "${AUTOMATED_CHECK_NAME}" commit status should be updated to: ${JSON.stringify(automatedChecksMet)}.`, + ); + core.summary.addHeading("Automated Checks Met", 2); + core.summary.addCodeBlock(JSON.stringify(automatedChecksMet, null, 2)); + core.summary.write(); } /** + * Updates or creates a commit status with the given status * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github - * @param {import('@actions/github-script').AsyncFunctionArguments['core']} core + * @param {typeof import("@actions/core")} core + * @param {string} owner + * @param {string} repo + * @param {string} head_sha + * @param {CheckRunResult} checkResult * @returns {Promise} */ -export async function logGitHubRateLimitInfo(github, core) { - try { - const { data: rateLimit } = await github.rest.rateLimit.get(); - const { data: user } = await github.rest.users.getAuthenticated(); - core.info(`GitHub RateLimit Info for user ${user.login}: ${JSON.stringify(rateLimit)}`); - } catch (e) { - core.error(`GitHub RateLimit Info: error emitting. Exception: ${e}`); +export async function updateCommitStatus(github, core, owner, repo, head_sha, checkResult) { + // Map CheckRunResult status to commit status state + /** @type {"pending" | "success" | "failure" | "error"} */ + let state; + + const validStates = [CheckConclusion.SUCCESS, CheckConclusion.FAILURE, "pending"]; + if (validStates.includes(checkResult.result.toLowerCase())) { + state = /** @type {"pending" | "success" | "failure"} */ (checkResult.result.toLowerCase()); + } else { + state = "error"; // fallback for unexpected values } + + // Create commit status instead of check run + await github.rest.repos.createCommitStatus({ + owner, + repo, + sha: head_sha, + state: state, + description: + checkResult.summary.length > 140 + ? checkResult.summary.substring(0, 137) + "..." + : checkResult.summary, + context: checkResult.name, + target_url: checkResult.target_url, + }); + + core.info( + `Created commit status for ${checkResult.name} with state: ${state} and description: ${checkResult.summary}`, + ); } /** - * A GraphQL query to GitHub API that returns all check runs for given commit, with "isRequired" field for given PR. - * - * If you want to see example response, copy the query body into this: - * https://docs.github.com/en/graphql/overview/explorer - * Example inputs: - * resourceUrl: "https://github.com/test-repo-billy/azure-rest-api-specs/commit/c2789c5bde1b3f4fa34f76a8eeaaed479df23c4d" - * prNumber: 2996 - * - * Reference: - * https://docs.github.com/en/graphql/reference/queries#resource - * https://docs.github.com/en/graphql/guides/using-global-node-ids#3-do-a-direct-node-lookup-in-graphql - * https://docs.github.com/en/graphql/reference/objects#checkrun - * Rate limit: - * https://docs.github.com/en/graphql/overview/resource-limitations#rate-limit - * https://docs.github.com/en/graphql/reference/objects#ratelimit - * - * Note: here, for "checkRuns(first: ..)", maybe we should add a filter that filters to LATEST, per: - * https://docs.github.com/en/graphql/reference/input-objects#checkrunfilter - * https://docs.github.com/en/graphql/reference/enums#checkruntype - **/ -/** - * Returns a GraphQL query string for the given resource URL and PR number. - * - * @param {string} owner - The URL of the GitHub resource (commit). - * @param {string} repo - The URL of the GitHub resource (commit). - * @param {string} sha - targeted commit. context.pr!.headInfo.sha - * @param {number} prNumber - The pull request number. - * @returns {string} The GraphQL query string. + * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github + * @param {string} owner + * @param {string} repo + * @param {number} issue_number + * @return {Promise} */ -function getGraphQLQuery(owner, repo, sha, prNumber) { - const resourceUrl = `https://github.com/${owner}/${repo}/commit/${sha}`; - - return ` - { - resource(url: "${resourceUrl}") { - ... on Commit { - checkSuites(first: 20) { - nodes { - checkRuns(first: 30) { - nodes { - name - status - conclusion - isRequired(pullRequestNumber: ${prNumber}) - } - } - } - } - } - } - rateLimit { - limit - cost - used - remaining - resetAt - } - } - `; +export async function getExistingLabels(github, owner, repo, issue_number) { + const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, { + owner, + repo, + issue_number: issue_number, + per_page: PER_PAGE_MAX, + }); + return labels.map((label) => label.name); } // #endregion // #region label update /** - * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github - * @param {import('@actions/github').context } context - * @param {typeof import("@actions/core")} core - * @param {string} owner - * @param {string} repo - * @param {number} issue_number - * @param {string} event_name - * @param {string[]} known_labels - * @returns {Promise<[string[], string[]]>} + * @param {Set} labelsToAdd + * @param {Set} labelsToRemove */ -// @ts-ignore: 'github' is currently unused but will be used after necessary changes -export async function handleLabeledEvent( - github, - context, - core, - owner, - repo, - issue_number, - event_name, - known_labels, -) { - // logic for this event is based on code directly ripped from pipelinebot: - // private/openapi-kebab/src/bots/pipeline/pipelineBotOnPRLabelEvent.ts - // todo: further enhance with labelling actions from `PR Summary` check. - const changedLabel = context.payload.label?.name; - const labelsToAdd = new Set(); - const labelsToRemove = new Set(); - - if (event_name === "labeled") { - if (changedLabel == "ARMChangesRequested") { - if (known_labels.indexOf("WaitForARMFeedback") !== -1) { - labelsToRemove.add("WaitForARMFeedback"); - } - } - if (changedLabel == "ARMSignedOff") { - if (known_labels.indexOf("WaitForARMFeedback") !== -1) { - labelsToRemove.add("WaitForARMFeedback"); - } - if (known_labels.indexOf("ARMChangesRequested") !== -1) { - labelsToRemove.add("ARMChangesRequested"); - } - } +function warnIfLabelSetsIntersect(labelsToAdd, labelsToRemove) { + const intersection = [...intersect(labelsToAdd, labelsToRemove)]; + if (intersection.length > 0) { + console.warn( + "ASSERTION VIOLATION! The intersection of labelsToRemove and labelsToAdd is non-empty! " + + `labelsToAdd: [${[...labelsToAdd].join(", ")}]. ` + + `labelsToRemove: [${[...labelsToRemove].join(", ")}]. ` + + `intersection: [${intersection.join(", ")}].`, + ); + } +} - for (const label of labelsToRemove) { - core.info(`Removing label: ${label} from ${owner}/${repo}#${issue_number}.`); - // await github.rest.issues.removeLabel({ - // owner: owner, - // repo: repo, - // issue_number: issue_number, - // name: label, - // }); - } - } else if (event_name === "unlabeled") { - if (changedLabel == "ARMChangesRequested") { - if (known_labels.indexOf("WaitForARMFeedback") !== -1) { - labelsToAdd.add("WaitForARMFeedback"); - } - } +// * @param {string} eventName +// * @param {string | undefined } changedLabel +/** + * @param {string[]} existingLabels + * @param {import("./labelling.js").ImpactAssessment | undefined} impactAssessment + * @returns {import("./labelling.js").LabelContext} + */ +export function updateLabels(existingLabels, impactAssessment) { + // logic for this function originally present in: + // - private/openapi-kebab/src/bots/pipeline/pipelineBotOnPRLabelEvent.ts + // - public/rest-api-specs-scripts/src/prSummary.ts + // it has since been simplified and moved here to handle all label addition and subtraction given a PR context - if (labelsToAdd.size > 0) { - core.info( - `Adding labels: ${Array.from(labelsToAdd).join(", ")} to ${owner}/${repo}#${issue_number}.`, - ); - // await github.rest.issues.addLabels({ - // owner: owner, - // repo: repo, - // issue_number: issue_number, - // labels: Array.from(labelsToAdd), - // }); - } + /** @type {import("./labelling.js").LabelContext} */ + const labelContext = { + present: new Set(existingLabels), + toAdd: new Set(), + toRemove: new Set(), + }; + + if (impactAssessment) { + // will further update the label context if necessary + processImpactAssessment(labelContext, impactAssessment); } - return [Array.from(labelsToAdd), Array.from(labelsToRemove)]; + warnIfLabelSetsIntersect(labelContext.toAdd, labelContext.toRemove); + return labelContext; } // #endregion // #region checks +/** + * Extracts required status check context names from GitHub branch rules response. + * @param {import('@octokit/rest').RestEndpointMethodTypes['repos']['getBranchRules']['response']} checkResponseObj - The GitHub branch rules API response object + * @returns {string[]} Array of required status check context names (e.g., ["license/cla", "Swagger LintDiff"]) + */ +export function getRequiredChecksFromBranchRuleOutput(checkResponseObj) { + const requiredChecks = []; + + // Look through all rules for required_status_checks type + for (const rule of checkResponseObj.data) { + if (rule.type === "required_status_checks" && rule.parameters?.required_status_checks) { + for (const statusCheck of rule.parameters.required_status_checks) { + requiredChecks.push(statusCheck.context); + } + } + } + + return requiredChecks; +} + /** * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github * @param {typeof import("@actions/core")} core @@ -488,7 +608,7 @@ export async function handleLabeledEvent( * @param {string} head_sha - The commit SHA to check. * @param {number} prNumber - The pull request number. * @param {string[]} excludedCheckNames - * @returns {Promise<[CheckRunData[], CheckRunData[]]>} + * @returns {Promise<[CheckRunData[], CheckRunData[], import("./labelling.js").ImpactAssessment | undefined]>} */ export async function getCheckRunTuple( github, @@ -501,34 +621,202 @@ export async function getCheckRunTuple( ) { // This function was originally a version of getRequiredAndFyiAndAutomatedMergingRequirementsMetCheckRuns // but has been simplified for clarity and purpose. - /** @type {CheckRunData[]} */ - let reqCheckRuns = []; - /** @type {CheckRunData[]} */ - let fyiCheckRuns = []; + /** @type {string[]} */ + let requiredCheckNames = []; - const response = await github.graphql(getGraphQLQuery(owner, repo, head_sha, prNumber)); - core.info(`GraphQL Rate Limit Information: ${JSON.stringify(response.rateLimit)}`); + /** @type {number | undefined} */ + let impactAssessmentWorkflowRun = undefined; - [reqCheckRuns, fyiCheckRuns] = extractRunsFromGraphQLResponse(response); + /** @type { import("./labelling.js").ImpactAssessment | undefined } */ + let impactAssessment = undefined; + + const allCheckRuns = await github.paginate(github.rest.checks.listForRef, { + owner: owner, + repo: repo, + ref: head_sha, + per_page: PER_PAGE_MAX, + }); + + const allCommitStatuses = await github.paginate(github.rest.repos.listCommitStatusesForRef, { + owner: owner, + repo: repo, + ref: head_sha, + per_page: PER_PAGE_MAX, + }); + + // Process allCheckRuns and allCommitStatuses into unified CheckRunData array + // all checks will be considered as "FYI" until we have an impact assessment, so we can + // determine the target branch, and from there pull branch protect rulesets to ensure we + // are marking the required checks correctly. + /** @type {Array} */ + const allChecks = []; + + allCheckRuns.forEach((checkRun) => { + allChecks.push({ + name: checkRun.name, + status: checkRun.status, + conclusion: checkRun.conclusion || null, + checkInfo: getCheckInfo(checkRun.name), + // Store original object for date sorting + _originalData: checkRun, + _source: "checkRun", + }); + }); + + allCommitStatuses.forEach((status) => { + // Map commit status state to check run conclusion + let conclusion = null; + let checkStatus = "completed"; + + switch (status.state) { + case "success": + conclusion = "success"; + break; + case "failure": + conclusion = "failure"; + break; + case "error": + conclusion = "failure"; + break; + case "pending": + checkStatus = "in_progress"; + conclusion = null; + break; + } + + allChecks.push({ + name: status.context, + status: checkStatus, + conclusion: conclusion, + checkInfo: getCheckInfo(status.context), + // Store original object for date sorting and data access + _originalData: status, + _source: "commitStatus", + }); + }); + + // Group by name and take the latest for each + const checksByName = new Map(); + + allChecks.forEach((check) => { + const name = check.name; + if (!checksByName.has(name)) { + checksByName.set(name, []); + } + checksByName.get(name).push(check); + }); + + // For each group, sort by date (newest first) and take the first one + const unifiedCheckRuns = []; + for (const [, checks] of checksByName) { + // Sort by date - newest first using invert(byDate(...)) + const sortedChecks = checks.sort( + invert( + byDate((check) => { + if (check._source === "checkRun") { + // Check runs have started_at, completed_at, etc. Use the most recent available date + return ( + check._originalData.completed_at || + check._originalData.started_at || + check._originalData.created_at + ); + } else { + // Commit statuses have created_at and updated_at + return check._originalData.updated_at || check._originalData.created_at; + } + }), + ), + ); + + const latestCheck = sortedChecks[0]; + + if ( + latestCheck.name === IMPACT_CHECK_NAME && + latestCheck.status === "completed" && + latestCheck.conclusion === "success" + ) { + const workflowRuns = await github.paginate(github.rest.actions.listWorkflowRunsForRepo, { + owner, + repo, + head_sha: head_sha, + check_suite_id: latestCheck._originalData.check_suite.id, + per_page: PER_PAGE_MAX, + }); + + if (workflowRuns.length === 0) { + core.warning( + `No workflow runs found for check suite ID: ${latestCheck._originalData.check_suite.id}`, + ); + } else { + // Sort by updated_at to get the most recent run + const sortedRuns = workflowRuns.sort( + (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), + ); + impactAssessmentWorkflowRun = sortedRuns[0].id; + + if (workflowRuns.length > 1) { + core.info( + `Found ${workflowRuns.length} workflow runs for check suite ID: ${latestCheck._originalData.check_suite.id}, using most recent: ${sortedRuns[0].id}`, + ); + } + } + } + + // Create clean CheckRunData without temporary properties + unifiedCheckRuns.push({ + name: latestCheck.name, + status: latestCheck.status, + conclusion: latestCheck.conclusion, + checkInfo: latestCheck.checkInfo, + }); + } core.info( - `RequiredCheckRuns: ${JSON.stringify(reqCheckRuns)}, ` + - `FyiCheckRuns: ${JSON.stringify(fyiCheckRuns)}`, + `Processed ${allCheckRuns.length} check runs and ${allCommitStatuses.length} commit statuses into ${unifiedCheckRuns.length} unified checks`, ); - const filteredReqCheckRuns = reqCheckRuns.filter( - /** - * @param {CheckRunData} checkRun - */ - (checkRun) => !excludedCheckNames.includes(checkRun.name), + + if (impactAssessmentWorkflowRun) { + core.info( + `Impact Assessment Workflow Run ID is present: ${impactAssessmentWorkflowRun}. Downloading job summary artifact`, + ); + impactAssessment = await getImpactAssessment( + github, + core, + owner, + repo, + impactAssessmentWorkflowRun, + ); + + const branchRules = await github.rest.repos.getBranchRules({ + owner: owner, + repo: repo, + branch: impactAssessment.targetBranch, + }); + + if (branchRules) { + requiredCheckNames = getRequiredChecksFromBranchRuleOutput(branchRules).filter( + // "Automated merging requirements met" may be required in repo settings, to ensure PRs cannot be merged unless + // it's passing. However, it must be excluded from our list of requiredCheckNames, since it's status is set + // by our own workflow. If this check isn't excluded, it creates a deadlock where it can never be set. + (checkName) => checkName !== AUTOMATED_CHECK_NAME, + ); + } + } else { + requiredCheckNames = [IMPACT_CHECK_NAME]; + } + + const filteredReqCheckRuns = unifiedCheckRuns.filter( + (checkRun) => + !excludedCheckNames.includes(checkRun.name) && requiredCheckNames.includes(checkRun.name), ); - const filteredFyiCheckRuns = fyiCheckRuns.filter( - /** - * @param {CheckRunData} checkRun - */ - (checkRun) => !excludedCheckNames.includes(checkRun.name), + const filteredFyiCheckRuns = unifiedCheckRuns.filter( + (checkRun) => + !excludedCheckNames.includes(checkRun.name) && + !requiredCheckNames.includes(checkRun.name) && + FYI_CHECK_NAMES.includes(checkRun.name), ); - return [filteredReqCheckRuns, filteredFyiCheckRuns]; + return [filteredReqCheckRuns, filteredFyiCheckRuns, impactAssessment]; } /** @@ -553,61 +841,22 @@ export function checkRunIsSuccessful(checkRun) { } /** - * @param {any} response - GraphQL response data - * @returns {[CheckRunData[], CheckRunData[]]} + * Get metadata for a specific check from our index. + * @param {string} checkName + * @returns {CheckMetadata} */ -function extractRunsFromGraphQLResponse(response) { - /** @type {CheckRunData[]} */ - const reqCheckRuns = []; - /** @type {CheckRunData[]} */ - const fyiCheckRuns = []; - - // Define the automated merging requirements check name - - if (response.resource?.checkSuites?.nodes) { - response.resource.checkSuites.nodes.forEach( - /** @param {{ checkRuns?: { nodes?: any[] } }} checkSuiteNode */ - (checkSuiteNode) => { - if (checkSuiteNode.checkRuns?.nodes) { - checkSuiteNode.checkRuns.nodes.forEach((checkRunNode) => { - // We have some specific guidance for some of the required checks. - const checkInfo = - CHECK_METADATA.find((metadata) => metadata.name === checkRunNode.name) || - /** @type {CheckMetadata} */ ({ - precedence: 1000, - name: checkRunNode.name, - suppressionLabels: [], - troubleshootingGuide: defaultTsg, - }); - - if (checkRunNode.isRequired) { - reqCheckRuns.push({ - name: checkRunNode.name, - status: checkRunNode.status, - conclusion: checkRunNode.conclusion, - checkInfo: checkInfo, - }); - } - // Note the "else" here. It means that: - // A GH check will be bucketed into "failing FYI check run" if: - // - It is failing - // - AND is is NOT marked as 'required' in GitHub branch policy - // - AND it is marked as 'FYI' in this file's FYI_CHECK_NAMES array - else if (FYI_CHECK_NAMES.includes(checkRunNode.name)) { - fyiCheckRuns.push({ - name: checkRunNode.name, - status: checkRunNode.status, - conclusion: checkRunNode.conclusion, - checkInfo: checkInfo, - }); - } - }); - } - }, - ); - } - return [reqCheckRuns, fyiCheckRuns]; +export function getCheckInfo(checkName) { + return ( + CHECK_METADATA.find((metadata) => metadata.name === checkName) || + /** @type {CheckMetadata} */ ({ + precedence: 1000, + name: checkName, + suppressionLabels: [], + troubleshootingGuide: defaultTsg, + }) + ); } + // #endregion // #region next steps /** @@ -618,7 +867,9 @@ function extractRunsFromGraphQLResponse(response) { * @param {string} targetBranch * @param {CheckRunData[]} requiredRuns * @param {CheckRunData[]} fyiRuns - * @returns {Promise} + * @param {boolean} assessmentCompleted + * @param {string} target_url + * @returns {Promise<[string, CheckRunResult]>} */ export async function createNextStepsComment( core, @@ -627,29 +878,40 @@ export async function createNextStepsComment( targetBranch, requiredRuns, fyiRuns, + assessmentCompleted, + target_url, ) { // select just the metadata that we need about the runs. - const requiredCheckInfos = requiredRuns + const failingCheckInfos = requiredRuns .filter((run) => checkRunIsSuccessful(run) === false) .map((run) => run.checkInfo); - const requiredCheckInfosPresent = requiredRuns.some((run) => { + + // determine if required runs have any in-progress or queued runs + // if there are any, we consider the requirements not met. + // if there are NO required runs, we also consider this to be a "requirements met" situation. + // there is a possibility that this will be a false positive, but it is better than + // assuming that the requirements are not met when they actually are. + const requiredCheckInfosPresent = requiredRuns.every((run) => { const status = run.status.toLowerCase(); - return status !== "queued" && status !== "in_progress"; + return status === "completed"; }); + const fyiCheckInfos = fyiRuns .filter((run) => checkRunIsSuccessful(run) === false) .map((run) => run.checkInfo); - const commentBody = await buildNextStepsToMergeCommentBody( + const [commentBody, automatedChecksMet] = await buildNextStepsToMergeCommentBody( core, labels, `${repo}/${targetBranch}`, requiredCheckInfosPresent, - requiredCheckInfos, + failingCheckInfos, fyiCheckInfos, + assessmentCompleted, + target_url, ); - return commentBody; + return [commentBody, automatedChecksMet]; } /** @@ -659,7 +921,9 @@ export async function createNextStepsComment( * @param {boolean} requiredCheckInfosPresent * @param {CheckMetadata[]} failingReqChecksInfo * @param {CheckMetadata[]} failingFyiChecksInfo - * @returns {Promise} + * @param {boolean} assessmentCompleted + * @param {string} target_url + * @returns {Promise<[string, CheckRunResult]>} */ async function buildNextStepsToMergeCommentBody( core, @@ -668,33 +932,37 @@ async function buildNextStepsToMergeCommentBody( requiredCheckInfosPresent, failingReqChecksInfo, failingFyiChecksInfo, + assessmentCompleted, + target_url, ) { // Build the comment header const commentTitle = `

Next Steps to Merge

`; const violatedReqLabelsRules = await getViolatedRequiredLabelsRules(core, labels, targetBranch); - // this is the first place of adjusted logic. I am treating `requirementsMet` as `no failed required checks`. - // I do this because the `automatedMergingRequirementsMetCheckRun` WILL NOT BE PRESENT in the new world. - // The new world we will simply pull all the required checks and if any are failing then we are blocked. If there are - // no failed checks we can't yet say that everything is met, because a check MIGHT run in the future. To prevent - // this "no checks run" accidentally evaluating as success, we need to ensure that we have at least one failing check - // in the required checks to consider the requirements met + // we are "blocked" if we have any violated labelling rules OR if we have any failing required checks const anyBlockerPresent = failingReqChecksInfo.length > 0 || violatedReqLabelsRules.length > 0; const anyFyiPresent = failingFyiChecksInfo.length > 0; - const requirementsMet = !anyBlockerPresent && requiredCheckInfosPresent; + // we consider requirements met if there are: + // - no blockers (which includes violated labelling rules in its definition) (anyBlockerPresent) + // - that none of the required checks are in_progress or queued (requiredCheckInfosPresent) + // - and that the assessment is completed. If it is not, we assume we are still evaluating the requirements. Not having + // the assessment completed is a blocker, as we may end up having violated labelling rules that would be detected only after + // it is completed. + const requirementsMet = !anyBlockerPresent && requiredCheckInfosPresent && assessmentCompleted; // Compose the body based on the current state - const commentBody = getCommentBody( + const [commentBody, automatedChecksMet] = getCommentBody( requirementsMet, anyBlockerPresent, anyFyiPresent, failingReqChecksInfo, failingFyiChecksInfo, violatedReqLabelsRules, + target_url, ); - return commentTitle + commentBody; + return [commentTitle + commentBody, automatedChecksMet]; } /** @@ -705,7 +973,8 @@ async function buildNextStepsToMergeCommentBody( * @param {CheckMetadata[]} failingReqChecksInfo - Failing required checks info * @param {CheckMetadata[]} failingFyiChecksInfo - Failing FYI checks info * @param {RequiredLabelRule[]} violatedRequiredLabelsRules - Violated required label rules - * @returns {string} The body content HTML + * @param {string} target_url - The target URL for the automated checks met run which will be set at the outset of summarize-checks + * @returns {[string, CheckRunResult]} The body content HTML and the CheckRunResult that automated checks met should be set to. */ function getCommentBody( requirementsMet, @@ -714,12 +983,21 @@ function getCommentBody( failingReqChecksInfo, failingFyiChecksInfo, violatedRequiredLabelsRules, + target_url, ) { + /** @type {"pending" | keyof typeof CheckConclusion} */ + let status = "pending"; + let summaryData = "The requirements for merging this PR are still being evaluated. Please wait."; + + // Generate the comment body using the original logic for backwards compatibility let bodyProper = ""; if (anyBlockerPresent || anyFyiPresent) { if (anyBlockerPresent) { bodyProper += getBlockerPresentBody(failingReqChecksInfo, violatedRequiredLabelsRules); + summaryData = + "❌ This PR cannot be merged because some requirements are not met. See the details."; + status = "FAILURE"; } if (anyBlockerPresent && anyFyiPresent) { @@ -728,19 +1006,43 @@ function getCommentBody( if (anyFyiPresent) { bodyProper += getFyiPresentBody(failingFyiChecksInfo); - if (!anyBlockerPresent) { + if (!anyBlockerPresent && requirementsMet) { bodyProper += `If you still want to proceed merging this PR without addressing the above failures, ${diagramTsg(4, false)}.`; + summaryData = + `⚠️ Some important automated merging requirements have failed. As of today you can still merge this PR, ` + + `but soon these requirements will be blocking.` + + `
See Next Steps to merge comment on this PR for details on how to address them.` + + `
If you want to proceed with merging this PR without fixing them, refer to ` + + `aka.ms/azsdk/specreview/merge.`; + status = "SUCCESS"; } } } else if (requirementsMet) { bodyProper = `✅ All automated merging requirements have been met! ` + `To get your PR merged, see aka.ms/azsdk/specreview/merge.`; + summaryData = + `✅ All automated merging requirements have been met.` + + `
To merge this PR, refer to ` + + `aka.ms/azsdk/specreview/merge.` + + "
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help)."; + status = "SUCCESS"; } else { bodyProper = "⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛"; + // dont need to update the status of the check, as pending is the default state. } - return bodyProper; + + bodyProper += `

Comment generated by summarize-checks workflow run.`; + + /** @type {CheckRunResult} */ + const automatedChecksMet = { + name: AUTOMATED_CHECK_NAME, + summary: summaryData, + result: status, + }; + + return [bodyProper, automatedChecksMet]; } /** @@ -825,3 +1127,60 @@ function buildViolatedLabelRulesNextStepsText(violatedRequiredLabelsRules) { return violatedReqLabelsNextStepsText; } // #endregion + +// #region artifact downloading +/** + * Downloads the job-summary artifact for a given workflow run. + * @param {import('@actions/github-script').AsyncFunctionArguments['github']} github + * @param {typeof import("@actions/core")} core + * @param {string} owner + * @param {string} repo + * @param {number} runId - The workflow run databaseId + * @returns {Promise} The parsed job summary data + */ +export async function getImpactAssessment(github, core, owner, repo, runId) { + // List artifacts for provided workflow run + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner, + repo, + run_id: runId, + }); + + // Find the job-summary artifact + const jobSummaryArtifact = artifacts.data.artifacts.find( + (artifact) => artifact.name === "job-summary", + ); + + if (!jobSummaryArtifact) { + throw new Error( + `Unable to find job-summary artifact for run ID: ${runId}. This should never happen, as this section of code should only run with a valid runId.`, + ); + } + + // Download the artifact as a zip archive + const download = await github.rest.actions.downloadArtifact({ + owner, + repo, + artifact_id: jobSummaryArtifact.id, + archive_format: "zip", + }); + + core.info(`Successfully downloaded job-summary artifact ID: ${jobSummaryArtifact.id}`); + + // Write zip buffer to temp file and extract JSON + const tmpZip = path.join(process.env.RUNNER_TEMP || os.tmpdir(), `job-summary-${runId}.zip`); + // Convert ArrayBuffer to Buffer + // Convert ArrayBuffer (download.data) to Node Buffer + const arrayBuffer = /** @type {ArrayBuffer} */ (download.data); + const zipBuffer = Buffer.from(new Uint8Array(arrayBuffer)); + await fs.writeFile(tmpZip, zipBuffer); + // Extract JSON content from zip archive + const { stdout: jsonContent } = await execFile("unzip", ["-p", tmpZip]); + await fs.unlink(tmpZip); + + /** @type {import("./labelling.js").ImpactAssessment} */ + // todo: we need to zod this to ensure the structure is correct, however we do not have zod installed at time of run + const impact = JSON.parse(jsonContent); + return impact; +} +// #endregion diff --git a/.github/workflows/src/update-labels.js b/.github/workflows/src/update-labels.js index 7a1a550dae16..05b450cea962 100644 --- a/.github/workflows/src/update-labels.js +++ b/.github/workflows/src/update-labels.js @@ -1,38 +1,52 @@ // @ts-check +import { isFullGitSha } from "../../shared/src/git.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; import { extractInputs } from "../src/context.js"; -import { PER_PAGE_MAX } from "./github.js"; /** * @param {import('@actions/github-script').AsyncFunctionArguments} AsyncFunctionArguments */ export default async function updateLabels({ github, context, core }) { - let owner = process.env.OWNER; - let repo = process.env.REPO; - let issue_number = parseInt(process.env.ISSUE_NUMBER || ""); - let run_id = parseInt(process.env.RUN_ID || ""); - - if (!owner || !repo || !(issue_number || run_id)) { - let inputs = await extractInputs(github, context, core); - owner = owner || inputs.owner; - repo = repo || inputs.repo; - issue_number = issue_number || inputs.issue_number; - run_id = run_id || inputs.run_id; - } - - await updateLabelsImpl({ owner, repo, issue_number, run_id, github, core }); + const { owner, repo, head_sha, issue_number, run_id } = await extractInputs( + github, + context, + core, + ); + await updateLabelsImpl({ owner, repo, head_sha, issue_number, run_id, github, core }); } /** * @param {Object} params * @param {string} params.owner * @param {string} params.repo + * @param {string} params.head_sha * @param {number} params.issue_number * @param {number} params.run_id * @param {(import("@octokit/core").Octokit & import("@octokit/plugin-rest-endpoint-methods/dist-types/types.js").Api & { paginate: import("@octokit/plugin-paginate-rest").PaginateInterface; })} params.github * @param {typeof import("@actions/core")} params.core */ -export async function updateLabelsImpl({ owner, repo, issue_number, run_id, github, core }) { +export async function updateLabelsImpl({ + owner, + repo, + head_sha, + issue_number, + run_id, + github, + core, +}) { + if (isFullGitSha(head_sha)) { + core.setOutput("head_sha", head_sha); + } else { + core.info(`head_sha is not a valid full git SHA: '${head_sha}'`); + } + + if (Number.isInteger(issue_number) && issue_number > 0) { + core.setOutput("issue_number", issue_number); + } else { + core.info(`issue_number must be a positive integer: ${issue_number}`); + } + /** @type {string[]} */ let artifactNames = []; @@ -71,6 +85,11 @@ export async function updateLabelsImpl({ owner, repo, issue_number, run_id, gith if (key.startsWith("label-")) { const name = key.substring("label-".length); + + if (!name) { + throw new Error(`Invalid value for label name: '${name}'`); + } + if (value === "true") { labelsToAdd.push(name); } else if (value === "false") { diff --git a/.github/workflows/summarize-checks.yaml b/.github/workflows/summarize-checks.yaml index 1cffd04eb3cf..42b418ae45b1 100644 --- a/.github/workflows/summarize-checks.yaml +++ b/.github/workflows/summarize-checks.yaml @@ -1,14 +1,15 @@ -name: "[TEST-IGNORE] Summarize Checks" +name: "Summarize Checks" on: workflow_run: - # todo, any others need to be added here? workflows: - - "\\[TEST-IGNORE\\] Swagger SemanticValidation - Set Status" - - "\\[TEST-IGNORE\\] Swagger ModelValidation - Set Status" + - "Swagger SemanticValidation - Set Status" + - "Swagger ModelValidation - Set Status" + - "Summarize PR Impact" - "Swagger Avocado - Set Status" - "Swagger LintDiff - Set Status" - "SDK Validation Status" + - "Update Labels" types: - completed pull_request_target: # when a PR is labeled. NOT pull_request, because that would run on the PR branch, not the base branch. @@ -18,18 +19,22 @@ on: - opened - synchronize - reopened + - edited + - ready_for_review + - converted_to_draft permissions: actions: read # to inspect workflow_run metadata contents: read # for actions/checkout checks: read # for octokit.rest.checks.listForRef - statuses: read # for octokit.rest.repos.getCombinedStatusForRef - issues: write # to post comments via the Issues API I'm not certain if I need this one or pull-requests only. we'll find out! + statuses: write # for octokit.rest.repos.getCombinedStatusForRef and octokit.rest.repos.createCommitStatus + issues: write # to post comments via the Issues API pull-requests: write # to post comments via the Pull Requests API jobs: run-summarize-checks: - name: "[TEST-IGNORE] Summarize Checks" + if: ${{ github.event_name == 'pull_request_target' || github.event.workflow_run.conclusion != 'skipped' }} + name: "Summarize Checks" runs-on: ubuntu-24.04 steps: @@ -47,6 +52,17 @@ jobs: sparse-checkout: | .github + # todo: add the npm install ci here for the zod usage in summarize-checks (not currently there) + + - id: dump-trigger-metadata + name: Dump Trigger Metadata + uses: actions/github-script@v7 + with: + script: | + const { default: dumpTriggerMetadata } = + await import('${{ github.workspace }}/.github/workflows/src/summarize-checks/dump-trigger-metadata.js'); + return await dumpTriggerMetadata({ context, core }); + - id: summarize-checks name: Summarize Checks uses: actions/github-script@v7 @@ -55,3 +71,13 @@ jobs: const { default: summarizeChecks } = await import('${{ github.workspace }}/.github/workflows/src/summarize-checks/summarize-checks.js'); return await summarizeChecks({ github, context, core }); + + # May be used by downstream workflows + - name: Set job-summary artifact + if: ${{ always() && steps.summarize-checks.outputs.summary }} + uses: actions/upload-artifact@v4 + with: + name: job-summary + path: ${{ steps.summarize-checks.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/summarize-impact-test.yaml b/.github/workflows/summarize-impact-test.yaml new file mode 100644 index 000000000000..ba274bf6cbf6 --- /dev/null +++ b/.github/workflows/summarize-impact-test.yaml @@ -0,0 +1,28 @@ +name: Summarize Impact - Test + +on: + push: + branches: + - main + pull_request: + paths: + - package-lock.json + - package.json + - tsconfig.json + - .github/shared + - .github/workflows/_reusable-eng-tools-test.yaml + - .github/workflows/summarize-impact-test.yaml + - eng/tools/package.json + - eng/tools/tsconfig.json + - eng/tools/summarize-impact/** + workflow_dispatch: + +permissions: + contents: read + +jobs: + summarize-impact: + uses: ./.github/workflows/_reusable-eng-tools-test.yaml + with: + package: summarize-impact + lint: false diff --git a/.github/workflows/summarize-impact.yaml b/.github/workflows/summarize-impact.yaml new file mode 100644 index 000000000000..6bafe8e84454 --- /dev/null +++ b/.github/workflows/summarize-impact.yaml @@ -0,0 +1,65 @@ +name: "Summarize PR Impact" + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - edited + - ready_for_review + - converted_to_draft + +permissions: + contents: read + pull-requests: read + +jobs: + impact: + name: "Summarize PR Impact" + runs-on: ubuntu-24.04 + + steps: + - name: Checkout 'after' state + uses: actions/checkout@v4 + with: + fetch-depth: 2 + path: after + + - name: Checkout 'before' state + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.base.sha }} + path: before + + - name: Setup Node and install deps + uses: ./after/.github/actions/setup-node-install-deps + with: + working-directory: after + + - name: Run Summarize Impact + id: summarize-impact + working-directory: after + run: | + npm exec --no -- summarize-impact --sourceDirectory . --targetDirectory ../before \ + --number "${{ github.event.pull_request.number }}" \ + --sourceBranch "$HEAD_REF" \ + --targetBranch "${{ github.event.pull_request.base.ref }}" \ + --sha "${{ github.event.pull_request.head.sha }}" \ + --repo "${{ github.event.repository.name }}" \ + --owner "${{ github.repository_owner }}" \ + ${{ github.event.pull_request.draft && '--isDraft' || '' }} + env: + # We absolutely need to avoid OOM errors due to certain inherited types from openapi-alps + NODE_OPTIONS: "--max-old-space-size=8192" + HEAD_REF: ${{ github.event.pull_request.head.ref }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Set job-summary artifact + if: ${{ always() && steps.summarize-impact.outputs.summary }} + uses: actions/upload-artifact@v4 + with: + name: job-summary + path: ${{ steps.summarize-impact.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/swagger-modelvalidation-code.yaml b/.github/workflows/swagger-modelvalidation-code.yaml index 7ee3c19b8571..5406e0b124dd 100644 --- a/.github/workflows/swagger-modelvalidation-code.yaml +++ b/.github/workflows/swagger-modelvalidation-code.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger ModelValidation" +name: "Swagger ModelValidation - Analyze Code" on: pull_request @@ -7,7 +7,7 @@ permissions: jobs: oav: - name: "[TEST-IGNORE] Swagger ModelValidation" + name: "Swagger ModelValidation - Analyze Code" runs-on: ubuntu-24.04 steps: @@ -20,5 +20,17 @@ jobs: uses: ./.github/actions/setup-node-install-deps - name: Swagger Model Validation + id: swagger-model-validation run: | + echo "summary=$GITHUB_STEP_SUMMARY" >> "$GITHUB_OUTPUT" npm exec --no -- oav-runner examples + + # Used by other workflows like set-status + - name: Set job-summary artifact + if: ${{ always() && steps.swagger-model-validation.outputs.summary }} + uses: actions/upload-artifact@v4 + with: + name: job-summary + path: ${{ steps.swagger-model-validation.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/swagger-modelvalidation-status.yaml b/.github/workflows/swagger-modelvalidation-status.yaml index 5dc54b4caa8c..ac178380a2a9 100644 --- a/.github/workflows/swagger-modelvalidation-status.yaml +++ b/.github/workflows/swagger-modelvalidation-status.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger ModelValidation - Set Status" +name: "Swagger ModelValidation - Set Status" on: # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on @@ -15,7 +15,12 @@ on: - labeled - unlabeled workflow_run: - workflows: ["\\[TEST-IGNORE\\] Swagger ModelValidation"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + [ + "Swagger ModelValidation - Analyze Code", + "\\[TEST-IGNORE\\] Swagger ModelValidation - Analyze Code", + ] types: [completed] permissions: @@ -30,6 +35,6 @@ jobs: name: Set ModelValidation Status uses: ./.github/workflows/_reusable-set-check-status.yaml with: - monitored_workflow_name: "[TEST-IGNORE] Swagger ModelValidation" - required_check_name: "[TEST-IGNORE] Swagger ModelValidation" + monitored_workflow_name: "Swagger ModelValidation - Analyze Code" + required_check_name: "Swagger ModelValidation" overriding_label: "Approved-ModelValidation" diff --git a/.github/workflows/swagger-semanticvalidation-code.yaml b/.github/workflows/swagger-semanticvalidation-code.yaml index 532de1554d88..63665dbed1f7 100644 --- a/.github/workflows/swagger-semanticvalidation-code.yaml +++ b/.github/workflows/swagger-semanticvalidation-code.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger SemanticValidation" +name: "Swagger SemanticValidation - Analyze Code" on: pull_request @@ -7,7 +7,7 @@ permissions: jobs: oav: - name: "[TEST-IGNORE] Swagger SemanticValidation" + name: "Swagger SemanticValidation - Analyze Code" runs-on: ubuntu-24.04 steps: @@ -20,5 +20,17 @@ jobs: uses: ./.github/actions/setup-node-install-deps - name: Swagger Semantic Validation + id: swagger-semantic-validation run: | + echo "summary=$GITHUB_STEP_SUMMARY" >> "$GITHUB_OUTPUT" npm exec --no -- oav-runner specs + + # Used by other workflows like set-status + - name: Set job-summary artifact + if: ${{ always() && steps.swagger-semantic-validation.outputs.summary }} + uses: actions/upload-artifact@v4 + with: + name: job-summary + path: ${{ steps.swagger-semantic-validation.outputs.summary }} + # If the file doesn't exist, just don't add the artifact + if-no-files-found: ignore diff --git a/.github/workflows/swagger-semanticvalidation-status.yaml b/.github/workflows/swagger-semanticvalidation-status.yaml index 637866d22004..930533c6d4ad 100644 --- a/.github/workflows/swagger-semanticvalidation-status.yaml +++ b/.github/workflows/swagger-semanticvalidation-status.yaml @@ -1,4 +1,4 @@ -name: "[TEST-IGNORE] Swagger SemanticValidation - Set Status" +name: "Swagger SemanticValidation - Set Status" on: # Must run on pull_request_target instead of pull_request, since the latter cannot trigger on @@ -15,7 +15,12 @@ on: - labeled - unlabeled workflow_run: - workflows: ["\\[TEST-IGNORE\\] Swagger SemanticValidation"] + # Trigger on both old and new names, to handle the rename more gracefully + workflows: + [ + "Swagger SemanticValidation - Analyze Code", + "\\[TEST-IGNORE\\] Swagger SemanticValidation - Analyze Code", + ] types: [completed] permissions: @@ -30,6 +35,6 @@ jobs: name: Set SemanticValidation Status uses: ./.github/workflows/_reusable-set-check-status.yaml with: - monitored_workflow_name: "[TEST-IGNORE] Swagger SemanticValidation" - required_check_name: "[TEST-IGNORE] Swagger SemanticValidation" + monitored_workflow_name: "Swagger SemanticValidation - Analyze Code" + required_check_name: "Swagger SemanticValidation" overriding_label: "Approved-SemanticValidation" diff --git a/.github/workflows/test/arm-auto-signoff.test.js b/.github/workflows/test/arm-auto-signoff.test.js index c27801369f36..d354f08b54bf 100644 --- a/.github/workflows/test/arm-auto-signoff.test.js +++ b/.github/workflows/test/arm-auto-signoff.test.js @@ -1,4 +1,5 @@ import { describe, expect, it } from "vitest"; +import { CommitStatusState } from "../../shared/src/github.js"; import { getLabelActionImpl } from "../src/arm-auto-signoff.js"; import { LabelAction } from "../src/label.js"; import { createMockCore, createMockGithub as createMockGithubBase } from "./mocks.js"; @@ -78,7 +79,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.None, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.None, headSha: "abc123", issueNumber: 123 }); }); it("removes label if not incremental typespec", async () => { @@ -96,7 +97,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }); it("no-ops if incremental typespec in progress", async () => { @@ -123,7 +124,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.None, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.None, headSha: "abc123", issueNumber: 123 }); }); it("removes label if no runs of incremental typespec", async () => { @@ -146,7 +147,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }); it("uses latest run of incremental typespec", async () => { @@ -184,7 +185,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }); it.each([ @@ -207,7 +208,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }); it.each(["Swagger Avocado", "Swagger LintDiff"])( @@ -218,16 +219,13 @@ describe("getLabelActionImpl", () => { github.rest.issues.listLabelsOnIssue.mockResolvedValue({ data: [{ name: "ARMAutoSignedOff" }, { name: "ARMReview" }], }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [ - { - name: check, - status: "completed", - conclusion: "failure", - }, - ], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [ + { + context: check, + state: CommitStatusState.FAILURE, + }, + ], }); await expect( @@ -239,33 +237,58 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Remove, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Remove, headSha: "abc123", issueNumber: 123 }); }, ); - it("thows if multiple runs for same check", async () => { + it.each([ + [CommitStatusState.ERROR, ["ARMReview", "ARMAutoSignedOff"]], + [CommitStatusState.FAILURE, ["ARMReview", "ARMAutoSignedOff"]], + [CommitStatusState.PENDING, ["ARMReview"]], + [CommitStatusState.SUCCESS, ["ARMReview"]], + ])("uses latest status if multiple (%o)", async (state, labels) => { const github = createMockGithub({ incrementalTypeSpec: true }); github.rest.issues.listLabelsOnIssue.mockResolvedValue({ - data: [{ name: "ARMReview" }], + data: labels.map((l) => ({ + name: l, + })), }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [ - { - name: "Swagger LintDiff", - status: "in_progress", - conclusion: null, - }, - { - name: "Swagger LintDiff", - status: "in_progress", - conclusion: null, - }, - ], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [ + { + context: "Swagger Avocado", + state: CommitStatusState.SUCCESS, + updated_at: "2025-01-01", + }, + { + context: "Swagger LintDiff", + state: CommitStatusState.PENDING, + updated_at: "2025-01-01", + }, + { + context: "Swagger LintDiff", + state, + updated_at: "2025-01-02", + }, + ], }); + + let expectedAction; + switch (state) { + case CommitStatusState.ERROR: + case CommitStatusState.FAILURE: + expectedAction = LabelAction.Remove; + break; + case CommitStatusState.SUCCESS: + expectedAction = LabelAction.Add; + break; + case CommitStatusState.PENDING: + expectedAction = LabelAction.None; + break; + } + await expect( getLabelActionImpl({ owner: "TestOwner", @@ -275,7 +298,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).rejects.toThrow(); + ).resolves.toEqual({ labelAction: expectedAction, headSha: "abc123", issueNumber: 123 }); }); it("no-ops if check not found or not completed", async () => { @@ -285,10 +308,8 @@ describe("getLabelActionImpl", () => { data: [{ name: "ARMReview" }], }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [], }); await expect( getLabelActionImpl({ @@ -299,18 +320,10 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.None, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.None, headSha: "abc123", issueNumber: 123 }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [ - { - name: "Swagger LintDiff", - status: "in_progress", - conclusion: null, - }, - ], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [{ context: "Swagger LintDiff", state: CommitStatusState.PENDING }], }); await expect( getLabelActionImpl({ @@ -321,7 +334,7 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.None, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.None, headSha: "abc123", issueNumber: 123 }); }); it("adds label if incremental tsp, labels match, and check succeeded", async () => { @@ -330,21 +343,17 @@ describe("getLabelActionImpl", () => { github.rest.issues.listLabelsOnIssue.mockResolvedValue({ data: [{ name: "ARMReview" }], }); - github.rest.checks.listForRef.mockResolvedValue({ - data: { - check_runs: [ - { - name: "Swagger LintDiff", - status: "completed", - conclusion: "success", - }, - { - name: "Swagger Avocado", - status: "completed", - conclusion: "success", - }, - ], - }, + github.rest.repos.listCommitStatusesForRef.mockResolvedValue({ + data: [ + { + context: "Swagger LintDiff", + state: CommitStatusState.SUCCESS, + }, + { + context: "Swagger Avocado", + state: CommitStatusState.SUCCESS, + }, + ], }); await expect( @@ -356,6 +365,6 @@ describe("getLabelActionImpl", () => { github: github, core: core, }), - ).resolves.toEqual({ labelAction: LabelAction.Add, issueNumber: 123 }); + ).resolves.toEqual({ labelAction: LabelAction.Add, headSha: "abc123", issueNumber: 123 }); }); }); diff --git a/.github/workflows/test/artifacts.test.js b/.github/workflows/test/artifacts.test.js index 42cac2507539..ac5810967cf1 100644 --- a/.github/workflows/test/artifacts.test.js +++ b/.github/workflows/test/artifacts.test.js @@ -1,8 +1,8 @@ -import { describe, expect, it, vi, beforeEach } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { - getAzurePipelineArtifact, - getAdoBuildInfoFromUrl, fetchFailedArtifact, + getAdoBuildInfoFromUrl, + getAzurePipelineArtifact, } from "../src/artifacts.js"; import { createMockCore } from "./mocks.js"; diff --git a/.github/workflows/test/breaking-change-add-label-artifacts.test.js b/.github/workflows/test/breaking-change-add-label-artifacts.test.js new file mode 100644 index 000000000000..a36b9fc4b56c --- /dev/null +++ b/.github/workflows/test/breaking-change-add-label-artifacts.test.js @@ -0,0 +1,474 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { REVIEW_REQUIRED_LABELS } from "../../shared/src/breaking-change.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import getLabelActions, { + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, +} from "../src/breaking-change-add-label-artifacts.js"; +import { createMockContext, createMockCore, createMockGithub } from "./mocks.js"; + +// Mock dependencies +vi.mock("../src/context.js", () => ({ + extractInputs: vi.fn(), +})); + +describe("breaking-change-add-label-artifacts", () => { + let mockGithub; + let mockContext; + let mockCore; + + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + + // Create fresh mock instances for each test + mockGithub = createMockGithub(); + mockContext = createMockContext(); + mockCore = createMockCore(); + }); + + const mockInputs = { + owner: "Azure", + repo: "azure-rest-api-specs", + head_sha: "abc123def456", + issue_number: 12345, + }; + + const createMockWorkflowRun = ( + name, + status = "completed", + conclusion = "success", + id = 1, + updated_at = "2024-01-01T12:00:00Z", + ) => ({ + id, + name, + status, + conclusion, + updated_at, + }); + + const createMockArtifact = (name) => ({ name }); + + // Shared setup helpers + const setupMockInputs = async () => { + const { extractInputs } = await import("../src/context.js"); + extractInputs.mockResolvedValue(mockInputs); + }; + + const setupWorkflowRunsMock = (workflowRuns) => { + mockGithub.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({ + data: { workflow_runs: workflowRuns }, + }); + }; + + const setupArtifactsMock = (breakingChangeArtifacts, crossVersionArtifacts) => { + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: breakingChangeArtifacts } }) + .mockResolvedValueOnce({ data: { artifacts: crossVersionArtifacts } }); + }; + + const createStandardWorkflowRuns = () => [ + createMockWorkflowRun(SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, "completed", "success", 1), + createMockWorkflowRun(CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, "completed", "success", 2), + ]; + + const expectRequiredOutputs = () => { + expect(mockCore.setOutput).toHaveBeenCalledWith("head_sha", mockInputs.head_sha); + expect(mockCore.setOutput).toHaveBeenCalledWith("issue_number", mockInputs.issue_number); + }; + + const expectStandardOutputs = (breakingChangeValue, versioningValue) => { + expectRequiredOutputs(); + + expect(mockCore.setOutput).toHaveBeenCalledWith( + "breakingChangeReviewLabelName", + REVIEW_REQUIRED_LABELS.BREAKING_CHANGE, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "breakingChangeReviewLabelValue", + breakingChangeValue, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "versioningReviewLabelName", + REVIEW_REQUIRED_LABELS.VERSIONING, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", versioningValue); + }; + + const expectEarlyReturn = (infoMessage) => { + expectRequiredOutputs(); + + // Ensure setOutput was *only* called with the two required outputs + expect(mockCore.setOutput).toHaveBeenCalledTimes(2); + + expect(mockCore.info).toHaveBeenCalledWith(infoMessage); + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).not.toHaveBeenCalled(); + }; + + describe("successful execution with both workflows completed", () => { + it("should process breaking change and versioning labels correctly", async () => { + // Setup mocks + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun( + SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "success", + 1, + "2024-01-01T12:00:00Z", + ), + createMockWorkflowRun( + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "success", + 2, + "2024-01-01T11:30:00Z", + ), + createMockWorkflowRun("Other Workflow", "completed", "success", 3, "2024-01-01T11:00:00Z"), + ]; + + const breakingChangeArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}=true`), + createMockArtifact("other-artifact"), + ]; + + const crossVersionArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.VERSIONING}=true`), + createMockArtifact("another-artifact"), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + setupArtifactsMock(breakingChangeArtifacts, crossVersionArtifacts); + + // Execute the function + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Verify API calls + expect(mockGithub.rest.actions.listWorkflowRunsForRepo).toHaveBeenCalledWith({ + owner: "Azure", + repo: "azure-rest-api-specs", + event: "pull_request", + head_sha: "abc123def456", + per_page: PER_PAGE_MAX, + }); + + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).toHaveBeenCalledTimes(2); + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).toHaveBeenNthCalledWith(1, { + owner: "Azure", + repo: "azure-rest-api-specs", + run_id: 1, + per_page: PER_PAGE_MAX, + }); + + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).toHaveBeenNthCalledWith(2, { + owner: "Azure", + repo: "azure-rest-api-specs", + run_id: 2, + per_page: PER_PAGE_MAX, + }); + + // Verify outputs - breaking change takes precedence over versioning + expectStandardOutputs(true, false); // breaking change takes precedence + }); + + it("should set versioning label when only versioning artifacts are present", async () => { + await setupMockInputs(); + + const breakingChangeArtifacts = [createMockArtifact("other-artifact")]; + const crossVersionArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.VERSIONING}=true`), + ]; + + setupWorkflowRunsMock(createStandardWorkflowRuns()); + setupArtifactsMock(breakingChangeArtifacts, crossVersionArtifacts); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expectStandardOutputs(false, true); // only versioning label should be true + }); + + it("should set no labels when no relevant artifacts are present", async () => { + await setupMockInputs(); + + const breakingChangeArtifacts = [createMockArtifact("other-artifact")]; + const crossVersionArtifacts = [createMockArtifact("another-artifact")]; + + setupWorkflowRunsMock(createStandardWorkflowRuns()); + setupArtifactsMock(breakingChangeArtifacts, crossVersionArtifacts); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expectStandardOutputs(false, false); // no labels should be true + }); + }); + + describe("workflow run status handling", () => { + it("should return early when breaking changes workflow is not completed", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun(SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, "in_progress", null, 1), + createMockWorkflowRun( + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "success", + 2, + ), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expectEarlyReturn("No completed breaking changes workflow run found"); + }); + + it("should return early when cross-version workflow is not completed", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun(SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, "completed", "success", 1), + createMockWorkflowRun(CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, "queued", null, 2), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expectEarlyReturn("No completed cross-version breaking changes workflow run found"); + }); + + it("should return early when breaking changes workflow is not found", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun( + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "success", + 2, + ), + createMockWorkflowRun("Other Workflow", "completed", "success", 3), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expect(mockCore.info).toHaveBeenCalledWith( + "No completed breaking changes workflow run found", + ); + }); + + it("should return early when cross-version workflow is not found", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + createMockWorkflowRun(SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, "completed", "success", 1), + createMockWorkflowRun("Other Workflow", "completed", "success", 3), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expect(mockCore.info).toHaveBeenCalledWith( + "No completed cross-version breaking changes workflow run found", + ); + }); + }); + + describe("artifact processing", () => { + it("should find labels in cross-version artifacts when not in breaking change artifacts", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + const breakingChangeArtifacts = [createMockArtifact("other-artifact")]; + const crossVersionArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}=true`), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: breakingChangeArtifacts } }) + .mockResolvedValueOnce({ data: { artifacts: crossVersionArtifacts } }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expect(mockCore.setOutput).toHaveBeenCalledWith("breakingChangeReviewLabelValue", true); + expect(mockCore.setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", false); + }); + + it("should handle both labels present in different artifact collections", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + const breakingChangeArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.VERSIONING}=true`), + ]; + const crossVersionArtifacts = [ + createMockArtifact(`${REVIEW_REQUIRED_LABELS.BREAKING_CHANGE}=true`), + ]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: breakingChangeArtifacts } }) + .mockResolvedValueOnce({ data: { artifacts: crossVersionArtifacts } }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Breaking change should take precedence + expect(mockCore.setOutput).toHaveBeenCalledWith("breakingChangeReviewLabelValue", true); + expect(mockCore.setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", false); + }); + }); + + describe("edge cases", () => { + it("should handle empty workflow runs list", async () => { + await setupMockInputs(); + + mockGithub.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({ + data: { workflow_runs: [] }, + }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + expect(mockCore.info).toHaveBeenCalledWith( + "No completed breaking changes workflow run found", + ); + expect(mockGithub.rest.actions.listWorkflowRunArtifacts).not.toHaveBeenCalled(); + }); + + it("should return early when both artifact lists are empty", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: [] } }) // Empty breaking change artifacts + .mockResolvedValueOnce({ data: { artifacts: [] } }); // Empty cross-version artifacts + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Should log the info message about no artifacts found + expect(mockCore.info).toHaveBeenCalledWith( + "No artifacts found for breaking changes or cross-version breaking changes", + ); + + // Should only set issue_number output, but not the label outputs since it returns early + expect(mockCore.setOutput).toHaveBeenCalledWith("issue_number", mockInputs.issue_number); + expect(mockCore.setOutput).not.toHaveBeenCalledWith( + "breakingChangeReviewLabelValue", + expect.anything(), + ); + expect(mockCore.setOutput).not.toHaveBeenCalledWith( + "versioningReviewLabelValue", + expect.anything(), + ); + }); + + it("should return early when one workflow has empty artifacts and other has only non-label artifacts", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts: [] } }) // Empty breaking change artifacts + .mockResolvedValueOnce({ + data: { artifacts: [createMockArtifact("some-other-artifact")] }, + }); // Non-label artifacts + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Should NOT return early because there are artifacts (even though they're not label artifacts) + // The function should continue and set outputs with false values + expect(mockCore.info).not.toHaveBeenCalledWith( + "No artifacts found for breaking changes or cross-version breaking changes", + ); + + // Should set all outputs including the label values + expect(mockCore.setOutput).toHaveBeenCalledWith("issue_number", mockInputs.issue_number); + expect(mockCore.setOutput).toHaveBeenCalledWith("breakingChangeReviewLabelValue", false); + expect(mockCore.setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", false); + }); + + it("should handle workflow runs with status conclusion pairs", async () => { + await setupMockInputs(); + + const mockWorkflowRuns = [ + { + ...createMockWorkflowRun( + SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME, + "completed", + "failure", + 1, + ), + }, + { + ...createMockWorkflowRun( + CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME, + "in_progress", + null, + 2, + ), + }, + ]; + + mockGithub.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({ + data: { workflow_runs: mockWorkflowRuns }, + }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Should log status for in_progress and conclusion for completed + expect(mockCore.info).toHaveBeenCalledWith( + `- ${SWAGGER_BREAKING_CHANGE_WORKFLOW_NAME}: failure`, + ); + expect(mockCore.info).toHaveBeenCalledWith( + `- ${CROSS_VERSION_BREAKING_CHANGE_WORKFLOW_NAME}: in_progress`, + ); + }); + }); + + describe("output validation", () => { + it("should always set all required outputs", async () => { + await setupMockInputs(); + const mockWorkflowRuns = createStandardWorkflowRuns(); + + const artifacts = [createMockArtifact("test-artifact")]; + + setupWorkflowRunsMock(mockWorkflowRuns); + + mockGithub.rest.actions.listWorkflowRunArtifacts + .mockResolvedValueOnce({ data: { artifacts } }) + .mockResolvedValueOnce({ data: { artifacts } }); + + await getLabelActions({ github: mockGithub, context: mockContext, core: mockCore }); + + // Verify all required outputs are set + expect(mockCore.setOutput).toHaveBeenCalledWith("issue_number", mockInputs.issue_number); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "breakingChangeReviewLabelName", + REVIEW_REQUIRED_LABELS.BREAKING_CHANGE, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "breakingChangeReviewLabelValue", + expect.any(Boolean), + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "versioningReviewLabelName", + REVIEW_REQUIRED_LABELS.VERSIONING, + ); + expect(mockCore.setOutput).toHaveBeenCalledWith( + "versioningReviewLabelValue", + expect.any(Boolean), + ); + }); + }); +}); diff --git a/.github/workflows/test/context.test.js b/.github/workflows/test/context.test.js index baedbdbcfa8b..eb3fd8d7836b 100644 --- a/.github/workflows/test/context.test.js +++ b/.github/workflows/test/context.test.js @@ -1,6 +1,7 @@ import { describe, expect, it } from "vitest"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import { fullGitSha } from "../../shared/test/examples.js"; import { extractInputs } from "../src/context.js"; -import { PER_PAGE_MAX } from "../src/github.js"; import { createMockCore, createMockGithub } from "./mocks.js"; describe("extractInputs", () => { @@ -34,7 +35,7 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual({ + await expect(extractInputs(createMockGithub(), context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", head_sha: "abc123", @@ -44,6 +45,8 @@ describe("extractInputs", () => { }); it("pull_request_target", async () => { + const github = createMockGithub(); + const context = { eventName: "pull_request_target", payload: { @@ -71,23 +74,32 @@ describe("extractInputs", () => { run_id: NaN, }; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); context.payload.action = "unlabeled"; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); context.payload.action = "opened"; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); context.payload.action = "synchronize"; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); context.payload.action = "reopened"; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual(expected); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); + + context.payload.action = "ready_for_review"; + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); + + context.payload.action = "edited"; + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); + + context.payload.action = "converted_to_draft"; + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual(expected); // Action not yet supported context.payload.action = "assigned"; - await expect(extractInputs(null, context, createMockCore())).rejects.toThrow(); + await expect(extractInputs(github, context, createMockCore())).rejects.toThrow(); }); it("issue_comment:edited", async () => { @@ -140,7 +152,7 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual({ + await expect(extractInputs(createMockGithub(), context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", head_sha: "", @@ -157,7 +169,7 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).rejects.toThrow(); + await expect(extractInputs(createMockGithub(), context, createMockCore())).rejects.toThrow(); }); it("workflow_run:completed:pull_request (same repo)", async () => { @@ -180,7 +192,7 @@ describe("extractInputs", () => { }, }; - await expect(extractInputs(null, context, createMockCore())).resolves.toEqual({ + await expect(extractInputs(createMockGithub(), context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", head_sha: "abc123", @@ -273,6 +285,32 @@ describe("extractInputs", () => { }); expect(github.rest.search.issuesAndPullRequests).toHaveBeenCalled(); + + // Simulate REST API throwing error, which should be handled, log a warning, and then + // treat like any other scenario with no pull requests. + github.rest.repos.listPullRequestsAssociatedWithCommit.mockRejectedValue( + new Error("test-error"), + ); + + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ + owner: "TestRepoOwnerLogin", + repo: "TestRepoName", + head_sha: "abc123", + issue_number: 789, + run_id: 456, + }); + + // Simulate REST API throwing object, which should be handled, log a warning, and then + // treat like any other scenario with no pull requests. + github.rest.repos.listPullRequestsAssociatedWithCommit.mockRejectedValue("test-error"); + + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ + owner: "TestRepoOwnerLogin", + repo: "TestRepoName", + head_sha: "abc123", + issue_number: 789, + run_id: 456, + }); } else if (numPullRequests === 1 || numPullRequests === 2) { // Second PR is to a different repo, so expect same behavior with or without it await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ @@ -295,13 +333,17 @@ describe("extractInputs", () => { commit_sha: "abc123", per_page: PER_PAGE_MAX, }); + + // For pull_request, do NOT attempt to extract the issue number from an artifact, since this could be modified + // in a fork PR. + expect(github.rest.actions.listWorkflowRunArtifacts).toHaveBeenCalledTimes(0); }, ); it("workflow_run:completed:workflow_run", async () => { const github = createMockGithub(); github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { artifacts: [{ name: "issue-number=123" }] }, + data: { artifacts: [{ name: "issue-number=123" }, { name: `head-sha=${fullGitSha}` }] }, }); const context = { @@ -310,7 +352,7 @@ describe("extractInputs", () => { action: "completed", workflow_run: { event: "workflow_run", - head_sha: "abc123", + head_sha: "def456", id: 456, repository: { name: "TestRepoName", @@ -325,25 +367,49 @@ describe("extractInputs", () => { await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", - head_sha: "abc123", + head_sha: fullGitSha, issue_number: 123, run_id: 456, }); github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { artifacts: [{ name: "issue-number=not-a-number" }] }, + data: { artifacts: [{ name: "head-sha=not-full-git-sha" }] }, }); - await expect(extractInputs(github, context, createMockCore())).rejects.toThrow( - /invalid issue-number/i, + await expect( + extractInputs(github, context, createMockCore()), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: head-sha is not a valid full git SHA: 'not-full-git-sha']`, ); + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ + data: { artifacts: [{ name: "issue-number=not-a-number" }] }, + }); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ + owner: "TestRepoOwnerLogin", + repo: "TestRepoName", + head_sha: "", + issue_number: NaN, + run_id: 456, + }); + + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ + data: { artifacts: [{ name: "issue-number=null" }] }, + }); + await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ + owner: "TestRepoOwnerLogin", + repo: "TestRepoName", + head_sha: "", + issue_number: NaN, + run_id: 456, + }); + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ data: { artifacts: [] }, }); await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", - head_sha: "abc123", + head_sha: "", issue_number: NaN, run_id: 456, }); @@ -366,7 +432,7 @@ describe("extractInputs", () => { it("workflow_run:completed:check_run", async () => { const github = createMockGithub(); github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { artifacts: [] }, + data: { artifacts: [{ name: `head-sha=${fullGitSha}` }] }, }); const context = { @@ -375,7 +441,7 @@ describe("extractInputs", () => { action: "completed", workflow_run: { event: "check_run", - head_sha: "abc123", + head_sha: "def456", id: 456, repository: { name: "TestRepoName", @@ -387,13 +453,10 @@ describe("extractInputs", () => { }, }; - github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { artifacts: [] }, - }); await expect(extractInputs(github, context, createMockCore())).resolves.toEqual({ owner: "TestRepoOwnerLogin", repo: "TestRepoName", - head_sha: "abc123", + head_sha: fullGitSha, issue_number: NaN, run_id: 456, }); diff --git a/.github/workflows/test/github.test.js b/.github/workflows/test/github.test.js index 9dfcef9dbd0a..3cc5770e4e60 100644 --- a/.github/workflows/test/github.test.js +++ b/.github/workflows/test/github.test.js @@ -191,7 +191,7 @@ describe("writeToActionsSummary function", () => { const result = await writeToActionsSummary("Test content", mockCore); // Verify result - expect(result).undefined; + expect(result).toBeUndefined(); // Verify summary methods were called expect(mockCore.summary.addRaw).toHaveBeenCalledWith("Test content"); diff --git a/.github/workflows/test/issues.test.js b/.github/workflows/test/issues.test.js index e1bb5048ef3d..03facfee03ea 100644 --- a/.github/workflows/test/issues.test.js +++ b/.github/workflows/test/issues.test.js @@ -1,6 +1,6 @@ -import { describe, expect, it, vi, beforeEach } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { getIssueNumber } from "../src/issues.js"; -import { createMockGithub, createMockCore } from "./mocks.js"; +import { createMockCore, createMockGithub } from "./mocks.js"; const mockGithub = createMockGithub(); const mockCore = createMockCore(); diff --git a/.github/workflows/test/mocks.js b/.github/workflows/test/mocks.js index d6fdfafc66de..2260f5675e44 100644 --- a/.github/workflows/test/mocks.js +++ b/.github/workflows/test/mocks.js @@ -4,6 +4,9 @@ import { vi } from "vitest"; // Partial mock of `github` parameter passed into github-script actions export function createMockGithub() { return { + hook: { + after: vi.fn(), + }, paginate: async (func, params) => { // Assume all test data fits in single page const data = (await func(params)).data; @@ -30,6 +33,7 @@ export function createMockGithub() { }, repos: { createCommitStatus: vi.fn(), + listCommitStatusesForRef: vi.fn().mockResolvedValue({ data: [] }), listPullRequestsAssociatedWithCommit: vi.fn().mockResolvedValue({ data: [], }), @@ -53,7 +57,7 @@ export function createMockCore() { setOutput: vi.fn((name, value) => console.log(`setOutput('${name}', '${value}')`)), setFailed: vi.fn((msg) => console.log(`setFailed('${msg}')`)), summary: { - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars addRaw: vi.fn(function (content) { return this; // Return 'this' for method chaining }), diff --git a/.github/workflows/test/retries.test.js b/.github/workflows/test/retries.test.js index ba7f2a8bd5c5..49895789f26b 100644 --- a/.github/workflows/test/retries.test.js +++ b/.github/workflows/test/retries.test.js @@ -1,5 +1,5 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; -import { retry, fetchWithRetry } from "../src/retries.js"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { fetchWithRetry, retry } from "../src/retries.js"; // Mock console.log to avoid cluttering test output const originalConsoleLog = console.log; diff --git a/.github/workflows/test/sdk-breaking-change-labels.test.js b/.github/workflows/test/sdk-breaking-change-labels.test.js index 244eb4dd6b1f..a2dca7492c61 100644 --- a/.github/workflows/test/sdk-breaking-change-labels.test.js +++ b/.github/workflows/test/sdk-breaking-change-labels.test.js @@ -138,6 +138,62 @@ describe("sdk-breaking-change-labels", () => { issueNumber: 123, }); }); + it("should correctly set labelAction to none when label name is empty", async () => { + // Setup inputs + const inputs = { + details_url: "https://dev.azure.com/project/_build/results?buildId=12345", + head_sha: "abc123", + }; + + // Setup mock for extractInputs + const { extractInputs } = await import("../src/context.js"); + extractInputs.mockResolvedValue(inputs); + + // Mock artifact responses with 'remove' action + const mockArtifactResponse = { + ok: true, + json: vi.fn().mockResolvedValue({ + resource: { + downloadUrl: "https://dev.azure.com/download?format=zip", + }, + }), + }; + + const language = "azure-sdk-for-java"; + const mockContentResponse = { + ok: true, + text: vi.fn().mockResolvedValue( + JSON.stringify({ + labelAction: false, + language, + prNumber: "123", + }), + ), + }; + + // Setup fetch to return different responses for each call + global.fetch.mockImplementation((url) => { + if (url.includes("artifacts?artifactName=")) { + return mockArtifactResponse; + } else { + return mockContentResponse; + } + }); + + // Call function + const result = await getLabelAndAction({ + github: mockGithub, + context: mockContext, + core: mockCore, + }); + + // Verify result has none action + expect(result).toEqual({ + labelName: sdkLabels[language].breakingChange, + labelAction: LabelAction.None, + issueNumber: 123, + }); + }); it("should throw error with invalid inputs", async () => { // Setup inputs const inputs = { @@ -188,6 +244,7 @@ describe("sdk-breaking-change-labels", () => { expect(result).toEqual({ labelName: "", labelAction: LabelAction.None, + headSha: "", issueNumber: NaN, }); @@ -258,6 +315,7 @@ describe("sdk-breaking-change-labels", () => { expect(result).toEqual({ labelName: "", labelAction: LabelAction.None, + headSha: "", issueNumber: NaN, }); }); diff --git a/.github/workflows/test/set-status.test.js b/.github/workflows/test/set-status.test.js index b2d22575ae54..f1d0c6341f76 100644 --- a/.github/workflows/test/set-status.test.js +++ b/.github/workflows/test/set-status.test.js @@ -1,9 +1,9 @@ // @ts-check import { beforeEach, describe, expect, it } from "vitest"; +import { CheckConclusion, CheckStatus, CommitStatusState } from "../../shared/src/github.js"; +import { fullGitSha } from "../../shared/test/examples.js"; import { setStatusImpl } from "../src/set-status.js"; - -import { CheckConclusion, CheckStatus, CommitStatusState } from "../src/github.js"; import { createMockCore, createMockGithub } from "./mocks.js"; describe("setStatusImpl", () => { @@ -16,7 +16,46 @@ describe("setStatusImpl", () => { }); it("throws if inputs null", async () => { - await expect(setStatusImpl({})).rejects.toThrow(); + // @ts-expect-error Testing invalid input type + await expect(setStatusImpl({})).rejects.toMatchInlineSnapshot( + `[Error: head_sha is not a valid full git SHA: 'undefined']`, + ); + }); + + it.each([null, undefined, "", "abc123"])("throws when head_sha is %o", async (head_sha) => { + await expect( + setStatusImpl({ + owner: "test-owner", + repo: "test-repo", + // @ts-expect-error - Testing invalid input + head_sha, + issue_number: 123, + target_url: "https://test.com/set_status_url", + github, + core, + monitoredWorkflowName: "test-workflow", + requiredStatusName: "test-status", + overridingLabel: "test-label", + }), + ).rejects.toThrow("head_sha is not a valid full git SHA"); + }); + + it.each([null, undefined, NaN, 0, -1])("throws when issue_number is %o", async (issue_number) => { + await expect( + setStatusImpl({ + owner: "test-owner", + repo: "test-repo", + head_sha: fullGitSha, + // @ts-expect-error - Testing invalid input + issue_number, + target_url: "https://test.com/set_status_url", + github, + core, + monitoredWorkflowName: "test-workflow", + requiredStatusName: "test-status", + overridingLabel: "test-label", + }), + ).rejects.toThrow("issue_number must be a positive integer"); }); it("sets success if approved by label", async () => { @@ -28,13 +67,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger Avocado - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger Avocado", + monitoredWorkflowName: "Swagger Avocado - Analyze Code", + requiredStatusName: "Swagger Avocado", overridingLabel: "Approved-Avocado", }), ).resolves.toBeUndefined(); @@ -42,9 +81,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.SUCCESS, - context: "[TEST-IGNORE] Swagger Avocado", + context: "Swagger Avocado", description: "Found label 'Approved-Avocado'", target_url: "https://test.com/set_status_url", }); @@ -59,13 +98,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact", }), @@ -74,12 +113,15 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.SUCCESS, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", description: "Found label 'BreakingChange-Approved-Benign'", target_url: "https://test.com/set_status_url", }); + + expect(core.setOutput).toBeCalledWith("head_sha", fullGitSha); + expect(core.setOutput).toBeCalledWith("issue_number", 123); }); it("handles comma-separated labels with whitespace", async () => { @@ -91,13 +133,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "BreakingChange-Approved-Benign, BreakingChange-Approved-BugFix , BreakingChange-Approved-UserImpact", }), @@ -106,9 +148,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.SUCCESS, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", description: "Found label 'BreakingChange-Approved-UserImpact'", target_url: "https://test.com/set_status_url", }); @@ -123,13 +165,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "BreakingChange-Approved-Benign,,BreakingChange-Approved-Security,", }), ).resolves.toBeUndefined(); @@ -137,9 +179,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.SUCCESS, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", description: "Found label 'BreakingChange-Approved-Security'", target_url: "https://test.com/set_status_url", }); @@ -158,13 +200,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "BreakingChange-Approved-Benign,BreakingChange-Approved-BugFix,BreakingChange-Approved-UserImpact", }), @@ -173,9 +215,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.PENDING, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", target_url: "https://test.com/set_status_url", }); }); @@ -193,13 +235,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger BreakingChange - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger BreakingChange", + monitoredWorkflowName: "Swagger BreakingChange - Analyze Code", + requiredStatusName: "Swagger BreakingChange", overridingLabel: "", }), ).resolves.toBeUndefined(); @@ -207,9 +249,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: CommitStatusState.PENDING, - context: "[TEST-IGNORE] Swagger BreakingChange", + context: "Swagger BreakingChange", target_url: "https://test.com/set_status_url", }); }); @@ -252,7 +294,7 @@ describe("setStatusImpl", () => { github.rest.actions.listWorkflowRunsForRepo.mockResolvedValue({ data: [ { - name: "[TEST-IGNORE] Swagger Avocado - Analyze Code", + name: "Swagger Avocado - Analyze Code", status: checkStatus, conclusion: checkConclusion, updated_at: "2025-01-01", @@ -286,13 +328,13 @@ describe("setStatusImpl", () => { setStatusImpl({ owner: "test-owner", repo: "test-repo", - head_sha: "test-head-sha", + head_sha: fullGitSha, issue_number: 123, target_url: "https://test.com/set_status_url", github, core, - monitoredWorkflowName: "[TEST-IGNORE] Swagger Avocado - Analyze Code", - requiredStatusName: "[TEST-IGNORE] Swagger Avocado", + monitoredWorkflowName: "Swagger Avocado - Analyze Code", + requiredStatusName: "Swagger Avocado", overridingLabel: "Approved-Avocado", }), ).resolves.toBeUndefined(); @@ -300,9 +342,9 @@ describe("setStatusImpl", () => { expect(github.rest.repos.createCommitStatus).toBeCalledWith({ owner: "test-owner", repo: "test-repo", - sha: "test-head-sha", + sha: fullGitSha, state: commitStatusState, - context: "[TEST-IGNORE] Swagger Avocado", + context: "Swagger Avocado", target_url: targetUrl, }); }, diff --git a/.github/workflows/test/spec-gen-sdk-status.test.js b/.github/workflows/test/spec-gen-sdk-status.test.js index 9c88e28cfc3b..edb3f10be70e 100644 --- a/.github/workflows/test/spec-gen-sdk-status.test.js +++ b/.github/workflows/test/spec-gen-sdk-status.test.js @@ -1,11 +1,10 @@ -/* eslint-disable no-unused-vars */ // @ts-check -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import { setSpecGenSdkStatusImpl } from "../src/spec-gen-sdk-status.js"; +import fs from "fs"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import * as artifacts from "../src/artifacts.js"; import * as github from "../src/github.js"; -import { createMockGithub, createMockCore } from "./mocks.js"; -import fs from "fs"; +import { setSpecGenSdkStatusImpl } from "../src/spec-gen-sdk-status.js"; +import { createMockCore, createMockGithub } from "./mocks.js"; describe("spec-gen-sdk-status", () => { let mockGithub; @@ -22,6 +21,7 @@ describe("spec-gen-sdk-status", () => { // Setup specific mocks getAzurePipelineArtifactMock = vi .spyOn(artifacts, "getAzurePipelineArtifact") + // eslint-disable-next-line @typescript-eslint/no-unused-vars .mockImplementation(async ({ ado_build_id, ado_project_url, artifactName }) => { return { artifactData: JSON.stringify({ @@ -34,6 +34,7 @@ describe("spec-gen-sdk-status", () => { writeToActionsSummaryMock = vi .spyOn(github, "writeToActionsSummary") + // eslint-disable-next-line @typescript-eslint/no-unused-vars .mockImplementation(async (content, core) => { // Implementation that just returns return; @@ -78,6 +79,7 @@ describe("spec-gen-sdk-status", () => { target_url: "https://example.com", github: mockGithub, core: mockCore, + issue_number: 123, }); // Verify the right status was set @@ -89,6 +91,9 @@ describe("spec-gen-sdk-status", () => { state: "pending", }), ); + + expect(mockCore.setOutput).toBeCalledWith("head_sha", "testSha"); + expect(mockCore.setOutput).toBeCalledWith("issue_number", 123); }); it("should set success status when all checks are completed successfully", async () => { diff --git a/.github/workflows/test/summarize-checks.test.js b/.github/workflows/test/summarize-checks.test.js deleted file mode 100644 index dee9ce4f8379..000000000000 --- a/.github/workflows/test/summarize-checks.test.js +++ /dev/null @@ -1,514 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - createNextStepsComment, - summarizeChecksImpl, -} from "../src/summarize-checks/summarize-checks.js"; -import { createMockCore } from "./mocks.js"; -import { Octokit } from "@octokit/rest"; - -const mockCore = createMockCore(); - -describe("summarizeChecksImpl", () => { - describe("next steps comment rendering", () => { - it("Should generate summary for a mockdata PR scenario", async () => { - const repo = "azure-rest-api-specs"; - const targetBranch = "main"; - const labelNames = [ - "Cognitive Services", - "data-plane", - "TypeSpec", - "VersioningReviewRequired", - ]; - const fyiCheckRuns = []; - const expectedOutput = - "

Next Steps to Merge

Next steps that must be taken to merge this PR:
    " + - "
  • ❌ This PR targets either the main branch of the public specs repo or the RPSaaSMaster branch of the private specs repo. " + - "These branches are not intended for iterative development. Therefore, you must acknowledge you understand that after this PR is merged, the APIs are considered " + - "shipped to Azure customers. Any further attempts at in-place modifications to the APIs will be subject to Azure's versioning " + - "and breaking change policies. Additionally, for control plane APIs, you must acknowledge that you are following all " + - 'the best practices documented by ARM at aka.ms/armapibestpractices. ' + - "If you do intend to release the APIs to your customers by merging this PR, add the PublishToCustomers label " + - "to your PR in acknowledgement of the above. Otherwise, retarget this PR onto a feature branch, i.e. with prefix release- " + - '(see aka.ms/azsdk/api-versions#release--branches).
  • ' + - "
  • ❌ This PR has at least one change violating Azure versioning policy (label: VersioningReviewRequired).
    To unblock this PR, either a) " + - 'introduce a new API version with these changes instead of modifying an existing API version, or b) follow the process at aka.ms/brch.' + - "
  • ❌ The required check named TypeSpec Validation has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult " + - 'the aka.ms/ci-fix guide
'; - - const requiredCheckRuns = [ - { - name: "SpellCheck", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "SpellCheck", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "TypeSpec Requirement", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "TypeSpec Requirement", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Protected Files", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "Protected Files", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "TypeSpec Validation", - status: "COMPLETED", - conclusion: "FAILURE", - checkInfo: { - precedence: 0, - name: "TypeSpec Validation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger BreakingChange", - status: "COMPLETED", - conclusion: "FAILURE", - checkInfo: { - precedence: 4, - name: "Swagger BreakingChange", - suppressionLabels: ["Versioning-Approved-*", "BreakingChange-Approved-*"], - troubleshootingGuide: - 'To unblock this PR, follow the process at aka.ms/brch.', - }, - }, - { - name: "Breaking Change(Cross-Version)", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 4, - name: "Breaking Change(Cross-Version)", - suppressionLabels: ["Versioning-Approved-*", "BreakingChange-Approved-*"], - troubleshootingGuide: - 'To unblock this PR, follow the process at aka.ms/brch.', - }, - }, - { - name: "Swagger Avocado", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1, - name: "Swagger Avocado", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger ModelValidation", - status: "COMPLETED", - conclusion: "FAILURE", - checkInfo: { - precedence: 3, - name: "Swagger ModelValidation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger SemanticValidation", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 2, - name: "Swagger SemanticValidation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger Lint(RPaaS)", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 5, - name: "Swagger Lint(RPaaS)", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Automated merging requirements met", - status: "COMPLETED", - conclusion: "FAILURE", - checkInfo: { - precedence: 10, - name: "Automated merging requirements met", - suppressionLabels: [], - troubleshootingGuide: - 'This is the final check that must pass. Refer to the check in the PR\'s \'Checks\' tab for details on how to fix it and consult the aka.ms/ci-fix guide. In addition, refer to step 4 in the PR workflow diagram', - }, - }, - { - name: "license/cla", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 0, - name: "license/cla", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger PrettierCheck", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1, - name: "Swagger PrettierCheck", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - ]; - - const output = await createNextStepsComment( - mockCore, - repo, - labelNames, - targetBranch, - requiredCheckRuns, - fyiCheckRuns, - ); - - expect(output).toEqual(expectedOutput); - }); - - it("should generate pending summary for no matched check suites", async () => { - const repo = "azure-rest-api-specs"; - const targetBranch = "main"; - const labelNames = []; - const fyiCheckRuns = []; - const requiredCheckRuns = []; - const expectedOutput = - "

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛"; - - const output = await createNextStepsComment( - mockCore, - repo, - labelNames, - targetBranch, - requiredCheckRuns, - fyiCheckRuns, - ); - - expect(output).toEqual(expectedOutput); - }); - - it("should generate success summary for all completed check suites", async () => { - const repo = "azure-rest-api-specs"; - const targetBranch = "main"; - const labelNames = []; - const fyiCheckRuns = []; - const expectedOutput = - '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.'; - - const requiredCheckRuns = [ - { - name: "SpellCheck", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "SpellCheck", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "TypeSpec Requirement", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "TypeSpec Requirement", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Protected Files", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1000, - name: "Protected Files", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "TypeSpec Validation", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 0, - name: "TypeSpec Validation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger BreakingChange", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 4, - name: "Swagger BreakingChange", - suppressionLabels: ["Versioning-Approved-*", "BreakingChange-Approved-*"], - troubleshootingGuide: - 'To unblock this PR, follow the process at aka.ms/brch.', - }, - }, - { - name: "Breaking Change(Cross-Version)", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 4, - name: "Breaking Change(Cross-Version)", - suppressionLabels: ["Versioning-Approved-*", "BreakingChange-Approved-*"], - troubleshootingGuide: - 'To unblock this PR, follow the process at aka.ms/brch.', - }, - }, - { - name: "Swagger Avocado", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1, - name: "Swagger Avocado", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger ModelValidation", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 3, - name: "Swagger ModelValidation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger SemanticValidation", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 2, - name: "Swagger SemanticValidation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger Lint(RPaaS)", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 5, - name: "Swagger Lint(RPaaS)", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Automated merging requirements met", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 10, - name: "Automated merging requirements met", - suppressionLabels: [], - troubleshootingGuide: - 'This is the final check that must pass. Refer to the check in the PR\'s \'Checks\' tab for details on how to fix it and consult the aka.ms/ci-fix guide. In addition, refer to step 4 in the PR workflow diagram', - }, - }, - { - name: "license/cla", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 0, - name: "license/cla", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger PrettierCheck", - status: "COMPLETED", - conclusion: "SUCCESS", - checkInfo: { - precedence: 1, - name: "Swagger PrettierCheck", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - ]; - - const output = await createNextStepsComment( - mockCore, - repo, - labelNames, - targetBranch, - requiredCheckRuns, - fyiCheckRuns, - ); - - expect(output).toEqual(expectedOutput); - }); - - it("should generate pending summary when checks are in progress", async () => { - const repo = "azure-rest-api-specs"; - const targetBranch = "main"; - const labelNames = []; - const fyiCheckRuns = []; - const expectedOutput = - "

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛"; - - const requiredCheckRuns = [ - { - name: "TypeSpec Validation", - status: "IN_PROGRESS", - conclusion: null, - checkInfo: { - precedence: 0, - name: "TypeSpec Validation", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "Swagger Avocado", - status: "QUEUED", - conclusion: null, - checkInfo: { - precedence: 1, - name: "Swagger Avocado", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - { - name: "license/cla", - status: "IN_PROGRESS", - conclusion: null, - checkInfo: { - precedence: 0, - name: "license/cla", - suppressionLabels: [], - troubleshootingGuide: - "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", - }, - }, - ]; - - const output = await createNextStepsComment( - mockCore, - repo, - labelNames, - targetBranch, - requiredCheckRuns, - fyiCheckRuns, - ); - - expect(output).toEqual(expectedOutput); - }); - - it.skipIf(!process.env.GITHUB_TOKEN || !process.env.INTEGRATION_TEST)( - "Should fetch real PR data when GITHUB_TOKEN is available and when integration testing is enabled.", - async () => { - const github = new Octokit({ - auth: process.env.GITHUB_TOKEN, - }); - - const owner = "Azure"; - const repo = "azure-rest-api-specs"; - const issue_number = 35629; - const head_sha = "c12f0191c34212c4e6be88121d132ccb0a7f560c"; - const event_name = "pull_request"; - const mockContext = { - repo: { - owner: owner, - repo: repo, - }, - payload: { - action: "opened", - pull_request: { - number: issue_number, - head: { - sha: head_sha, - }, - }, - }, - eventName: event_name, - }; - - await expect( - summarizeChecksImpl( - github, - mockContext, - mockCore, - owner, - repo, - issue_number, - head_sha, - event_name, - "main", - ), - ).resolves.not.toThrow(); - }, - 600000, - ); - }); -}); diff --git a/.github/workflows/test/summarize-checks/labelling.test.js b/.github/workflows/test/summarize-checks/labelling.test.js new file mode 100644 index 000000000000..3e81694bfed7 --- /dev/null +++ b/.github/workflows/test/summarize-checks/labelling.test.js @@ -0,0 +1,308 @@ +import { describe, expect, it } from "vitest"; +import { processArmReviewLabels } from "../../src/summarize-checks/labelling.js"; +import { updateLabels } from "../../src/summarize-checks/summarize-checks.js"; + +describe("update labels", () => { + const testCases = [ + { + description: "Add ARMReview and resource-manager labels when existing labels are empty", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["ARMReview", "resource-manager", "TypeSpec", "WaitForARMFeedback"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Shouldn't add ARM review if resourceManagerRequired is false", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["TypeSpec"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: false, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Shouldn't add ARM review if not targeting a release branch", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["TypeSpec", "resource-manager"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "a-test-branch", + }, + }, + { + description: "Shouldn't add add new-api-version if not targeting a release branch", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["TypeSpec", "resource-manager"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "a-test-branch", + }, + }, + { + description: "Should add new-api-version correctly", + existingLabels: ["other-label"], + expectedLabelsToAdd: [ + "TypeSpec", + "resource-manager", + "new-api-version", + "ARMReview", + "WaitForARMFeedback", + ], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should add CI-NewRPNamespaceWithoutRPaaS label properly", + existingLabels: ["other-label"], + expectedLabelsToAdd: ["TypeSpec", "resource-manager", "CI-NewRPNamespaceWithoutRPaaS"], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: true, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "a-test-branch", + }, + }, + { + description: "Shouldn't add any ARM process updates when not ready for ARM review", + existingLabels: ["other-label"], + expectedLabelsToAdd: [ + "TypeSpec", + "resource-manager", + "CI-NewRPNamespaceWithoutRPaaS", + "NotReadyForARMReview", + "ARMReview", + "new-api-version", + ], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: true, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: + "Shouldn't remove NotReadyForARMReview label when not ready for ARM review due to being blocked on versioning or breaking change review.", + existingLabels: [ + "ARMReview", + "NotReadyForARMReview", + "PipelineBotTrigger", + "PublishToCustomers", + "resource-manager", + "RPaaS", + "TypeSpec", + "VersioningReviewRequired", + ], + expectedLabelsToAdd: [], + expectedLabelsToRemove: [], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: true, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "RPSaaSMaster", + }, + }, + { + description: + "Should remove NotReadyForARMReview and add WaitForARMFeedback when VersioningReviewRequired is approved.", + existingLabels: [ + "ARMReview", + "NotReadyForARMReview", + "PipelineBotTrigger", + "PublishToCustomers", + "resource-manager", + "RPaaS", + "TypeSpec", + "VersioningReviewRequired", + "Versioning-Approved-Benign", + ], + expectedLabelsToAdd: ["WaitForARMFeedback"], + expectedLabelsToRemove: ["NotReadyForARMReview"], + impactAssessment: { + suppressionReviewRequired: false, + rpaasChange: true, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "RPSaaSMaster", + }, + }, + ]; + it.each(testCases)( + "$description", + async ({ existingLabels, expectedLabelsToAdd, expectedLabelsToRemove, impactAssessment }) => { + const labelContext = await updateLabels(existingLabels, impactAssessment); + + expect([...labelContext.toAdd].sort()).toEqual(expectedLabelsToAdd.sort()); + expect([...labelContext.toRemove].sort()).toEqual(expectedLabelsToRemove.sort()); + }, + ); +}); + +describe("ARM review process labelling", () => { + const testCases = [ + { + existingLabels: ["WaitForARMFeedback", "ARMChangesRequested", "other-label", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback"], + }, + { + existingLabels: ["other-label", "ARMChangesRequested"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: [], + }, + { + existingLabels: [ + "WaitForARMFeedback", + "ARMSignedOff", + "ARMChangesRequested", + "other-label", + "ARMReview", + ], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback", "ARMChangesRequested"], + }, + { + existingLabels: ["WaitForARMFeedback", "ARMSignedOff", "other-label", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback"], + }, + { + existingLabels: ["ARMChangesRequested", "ARMSignedOff", "other-label", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["ARMChangesRequested"], + }, + { + existingLabels: ["other-label", "ARMSignedOff"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: [], + }, + { + existingLabels: ["WaitForARMFeedback", "other-label", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: [], + }, + { + existingLabels: ["other-label", "ARMReview"], + expectedLabelsToAdd: ["WaitForARMFeedback"], + expectedLabelsToRemove: [], + }, + { + existingLabels: ["WaitForARMFeedback", "ARMChangesRequested", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback"], + }, + { + existingLabels: ["WaitForARMFeedback", "ARMChangesRequested", "ARMReview"], + expectedLabelsToAdd: [], + expectedLabelsToRemove: ["WaitForARMFeedback"], + }, + ]; + + it.each(testCases)( + "$description", + async ({ existingLabels, expectedLabelsToAdd, expectedLabelsToRemove }) => { + /** @type {import("./labelling.js").LabelContext} */ + const labelContext = { + present: new Set(), + toAdd: new Set(), + toRemove: new Set(), + }; + await processArmReviewLabels(labelContext, existingLabels); + + expect([...labelContext.toAdd].sort()).toEqual(expectedLabelsToAdd.sort()); + expect([...labelContext.toRemove].sort()).toEqual(expectedLabelsToRemove.sort()); + }, + ); +}); diff --git a/.github/workflows/test/summarize-checks/summarize-checks.test.js b/.github/workflows/test/summarize-checks/summarize-checks.test.js new file mode 100644 index 000000000000..31cf84e215da --- /dev/null +++ b/.github/workflows/test/summarize-checks/summarize-checks.test.js @@ -0,0 +1,1150 @@ +import { Octokit } from "@octokit/rest"; +import { describe, expect, it } from "vitest"; +import { + createNextStepsComment, + getCheckInfo, + getCheckRunTuple, + getExistingLabels, + updateLabels, +} from "../../src/summarize-checks/summarize-checks.js"; +import { createMockCore } from "../mocks.js"; + +const mockCore = createMockCore(); +const WORKFLOW_URL = "http://github.com/a/fake/workflowrun/url"; + +/** + * Find and extract the "Next Steps to Merge" existing comment on a PR. + * Used in the integration test. + */ +async function getNextStepsComment(github, owner, repo, prNumber) { + try { + const { data: comments } = await github.rest.issues.listComments({ + owner: owner, + repo: repo, + issue_number: prNumber, + }); + + // Find comment containing "Next Steps to Merge" + const nextStepsComment = comments.find((comment) => + comment.body.includes("

Next Steps to Merge

"), + ); + + return nextStepsComment ? nextStepsComment.body : null; + } catch (error) { + console.error(`Error getting comments for PR #${prNumber}:`, error.message); + return null; + } +} + +describe("Summarize Checks Integration Tests", () => { + describe("Repro a PR summarize-checks invocation", () => { + it.skipIf(!process.env.GITHUB_TOKEN || !process.env.INTEGRATION_TEST)( + "Should fetch real pr data and check the next steps to merge and final labels against what is actually there.", + async () => { + const issue_number = 24021; + const owner = "Azure"; + const repo = "azure-rest-api-specs-pr"; + + const ignorableLabels = [ + "VersioningReviewRequired", + "BreakingChangeReviewRequired", + "customer-reported", + "dependencies", + "javascript", + "Monitor", + ]; + + const github = new Octokit({ + auth: process.env.GITHUB_TOKEN, + }); + + const { data: pr } = await github.rest.pulls.get({ + owner: owner, + repo: repo, + pull_number: issue_number, + }); + + // Get current PR labels and Next Steps comment + const expectedNextStepsComment = await getNextStepsComment( + github, + owner, + repo, + issue_number, + ); + + const head_sha = "961faf0dd048e0b846026bc84fdd795f4b46e9e8"; + const expectedLabels = await getExistingLabels(github, owner, repo, issue_number); + + const [requiredCheckRuns, fyiCheckRuns, impactAssessment] = await getCheckRunTuple( + github, + mockCore, + owner, + repo, + head_sha, + issue_number, + [], + ); + + let adjustedStartLabels = expectedLabels.filter((x) => ignorableLabels.includes(x)); + let labelContext = await updateLabels(adjustedStartLabels, impactAssessment); + + adjustedStartLabels = adjustedStartLabels.filter( + (name) => !labelContext.toRemove.has(name), + ); + for (const label of labelContext.toAdd) { + if (!adjustedStartLabels.includes(label)) { + adjustedStartLabels.push(label); + } + } + + const [commentBody, automatedChecksMet] = await createNextStepsComment( + mockCore, + repo, + adjustedStartLabels, + pr.base.ref, + requiredCheckRuns, + fyiCheckRuns, + impactAssessment !== undefined, + WORKFLOW_URL, + ); + + const actualLabels = [...labelContext.toAdd, ...labelContext.present]; + expect(actualLabels.sort()).toEqual(expectedLabels.sort()); + expect(commentBody).toEqual(expectedNextStepsComment); + expect(automatedChecksMet).toBeTruthy(); + }, + 600000, + ); + }); +}); + +describe("Summarize Checks Unit Tests", () => { + describe("check result processing", () => { + it("should generate success summary for no matched check suites (completed impactAssessment)", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const requiredCheckRuns = []; + const expectedOutput = [ + '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.

Comment generated by summarize-checks workflow run.', + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `✅ All automated merging requirements have been met.
To merge this PR, refer to aka.ms/azsdk/specreview/merge.
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help).`, + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate success summary for all completed check suites", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.

Comment generated by summarize-checks workflow run.', + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `✅ All automated merging requirements have been met.
To merge this PR, refer to aka.ms/azsdk/specreview/merge.
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help).`, + }, + ]; + + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + { + name: "Protected Files", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Protected Files"), + }, + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Breaking Change(Cross-Version)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Breaking Change(Cross-Version)"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "Swagger ModelValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger ModelValidation"), + }, + { + name: "Swagger SemanticValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger SemanticValidation"), + }, + { + name: "Swagger Lint(RPaaS)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Lint(RPaaS)"), + }, + { + name: "Automated merging requirements met", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Automated merging requirements met"), + }, + { + name: "license/cla", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("license/cla"), + }, + { + name: "Swagger PrettierCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger PrettierCheck"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate success summary with completed required checks but in-progress FYI", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedOutput = [ + '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.

Comment generated by summarize-checks workflow run.', + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `✅ All automated merging requirements have been met.
To merge this PR, refer to aka.ms/azsdk/specreview/merge.
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help).`, + }, + ]; + + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + ]; + + const fyiCheckRuns = [ + { + name: "Swagger Avocado", + status: "QUEUED", + conclusion: null, + checkInfo: { + precedence: 1, + name: "Swagger Avocado", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: { + precedence: 0, + name: "license/cla", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + // this case should NEVER occur in practice, due to Summarize PR Impact being made a "required" check, but in cases + // where the user is targeting a branch that is not the main branch, we may have no required checks + // but still have FYI checks in progress. This is a regression test to ensure we handle this case correctly. + it("should generate success summary with 0 required checks but in-progress FYI", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedOutput = [ + '

Next Steps to Merge

✅ All automated merging requirements have been met! To get your PR merged, see aka.ms/azsdk/specreview/merge.

Comment generated by summarize-checks workflow run.', + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `✅ All automated merging requirements have been met.
To merge this PR, refer to aka.ms/azsdk/specreview/merge.
For help, consult comments on this PR and see [aka.ms/azsdk/pr-getting-help](https://aka.ms/azsdk/pr-getting-help).`, + }, + ]; + + const requiredCheckRuns = []; + + const fyiCheckRuns = [ + { + name: "Swagger Avocado", + status: "QUEUED", + conclusion: null, + checkInfo: { + precedence: 1, + name: "Swagger Avocado", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: { + precedence: 0, + name: "license/cla", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate success with warning for error FYI", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedOutput = [ + `

Next Steps to Merge

Important checks have failed. As of today they are not blocking this PR, but in near future they may.
Addressing the following failures is highly recommended:
  • ⚠️ The check named license/cla has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide
If you still want to proceed merging this PR without addressing the above failures, refer to step 4 in the PR workflow diagram.

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "SUCCESS", + summary: `⚠️ Some important automated merging requirements have failed. As of today you can still merge this PR, but soon these requirements will be blocking.
See Next Steps to merge comment on this PR for details on how to address them.
If you want to proceed with merging this PR without fixing them, refer to aka.ms/azsdk/specreview/merge.`, + }, + ]; + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + ]; + + const fyiCheckRuns = [ + { + name: "license/cla", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("license/cla"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when checks are partially in progress", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + `

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "TypeSpec Validation", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("license/cla"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when checks are in progress", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + `

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "TypeSpec Validation", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger Avocado", + status: "QUEUED", + conclusion: null, + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("license/cla"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when impact assessment is not completed", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + `

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + { + name: "Protected Files", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Protected Files"), + }, + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Breaking Change(Cross-Version)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Breaking Change(Cross-Version)"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "Swagger ModelValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger ModelValidation"), + }, + { + name: "Swagger SemanticValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger SemanticValidation"), + }, + { + name: "Swagger Lint(RPaaS)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Lint(RPaaS)"), + }, + { + name: "Automated merging requirements met", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Automated merging requirements met"), + }, + { + name: "license/cla", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("license/cla"), + }, + { + name: "Swagger PrettierCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger PrettierCheck"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + false, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when checks are in progress", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedOutput = [ + `

Next Steps to Merge

⌛ Please wait. Next steps to merge this PR are being evaluated by automation. ⌛

Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "TypeSpec Validation", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: { + precedence: 0, + name: "TypeSpec Validation", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + { + name: "Swagger Avocado", + status: "QUEUED", + conclusion: null, + checkInfo: { + precedence: 1, + name: "Swagger Avocado", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + { + name: "license/cla", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: { + precedence: 0, + name: "license/cla", + suppressionLabels: [], + troubleshootingGuide: + "Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide", + }, + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate pending summary when checks there are no required checks blocking or completed, but successful FYI checks", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = [ + "ARMReview", + "ARMAutoSignedOff", + "resource-manager", + "TypeSpec", + "RPaaS", + "ARMSignedOff", + "PublishToCustomers", + ]; + const expectedOutput = [ + `

Next Steps to Merge

Important checks have failed. As of today they are not blocking this PR, but in near future they may.
Addressing the following failures is highly recommended:
  • ⚠️ The check named Swagger LintDiff has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide


Comment generated by summarize-checks workflow run.`, + { + name: "Automated merging requirements met", + result: "pending", + summary: "The requirements for merging this PR are still being evaluated. Please wait.", + }, + ]; + + const fyiCheckRuns = [ + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Swagger LintDiff", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger LintDiff"), + }, + ]; + + const requiredCheckRuns = [ + { + name: "Summarize PR Impact", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("Summarize PR Impact"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + false, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("Should generate error summary for a PR scenario where NotReadyForARMReview is present", async () => { + const repo = "azure-rest-api-specs-pr"; + const targetBranch = "RPSaaSMaster"; + const labelNames = [ + "ARMReview", + "NotReadyForARMReview", + "PipelineBotTrigger", + "PublishToCustomers", + "resource-manager", + "RPaaS", + "TypeSpec", + "VersioningReviewRequired", + ]; + const fyiCheckRuns = []; + const expectedComment = + `

Next Steps to Merge

Next steps that must be taken to merge this PR:
  • ❌ ` + + `This PR is in purview of the ARM review (label: ARMReview). This PR must get ARMSignedOff label ` + + `from an ARM reviewer.
    This PR is not ready for ARM review (label: NotReadyForARMReview). This PR will not ` + + `be reviewed by ARM until relevant problems are fixed. Consult the rest of this Next Steps to Merge comment ` + + `for details.
    Once the blocking problems are addressed, add to the PR a comment with contents /azp run. ` + + `Automation will re-evaluate this PR and if everything looks good, it will add WaitForARMFeedback label which ` + + `will put this PR on the ARM review queue.
    For details of the ARM review, see ` + + `aka.ms/azsdk/pr-arm-review
  • ❌ This PR is NotReadyForARMReview because it has the VersioningReviewRequired ` + + `label.
  • ❌ This PR has at least one change violating Azure versioning policy ` + + `(label: VersioningReviewRequired).
    To unblock this PR, either a) introduce a new API version with these ` + + `changes instead of modifying an existing API version, or b) follow the process at aka.ms/brch.
  • ` + + `
  • ❌ The required check named Swagger BreakingChange has failed. To unblock this PR, follow the process at ` + + `aka.ms/brch.


Comment generated by summarize-checks workflow run.`; + + const expectedOutput = [ + expectedComment, + { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "license/cla", + status: "QUEUED", + conclusion: null, + checkInfo: getCheckInfo("license/cla"), + }, + { + name: "Swagger PrettierCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger PrettierCheck"), + }, + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger SemanticValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger SemanticValidation"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + { + name: "Protected Files", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Protected Files"), + }, + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "Swagger ModelValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger ModelValidation"), + }, + { + name: "SDK Validation Status", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SDK Validation Status"), + }, + { + name: "Breaking Change(Cross-Version)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Breaking Change(Cross-Version)"), + }, + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "Swagger LintDiff", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger LintDiff"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("Should generate error summary for a PR scenario with labeling issues", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = [ + "Cognitive Services", + "data-plane", + "TypeSpec", + "VersioningReviewRequired", + ]; + const fyiCheckRuns = []; + const expectedComment = + "

Next Steps to Merge

Next steps that must be taken to merge this PR:
    " + + "
  • ❌ This PR targets either the main branch of the public specs repo or the RPSaaSMaster branch of the private specs repo. " + + "These branches are not intended for iterative development. Therefore, you must acknowledge you understand that after this PR is merged, the APIs are considered " + + "shipped to Azure customers. Any further attempts at in-place modifications to the APIs will be subject to Azure's versioning " + + "and breaking change policies. Additionally, for control plane APIs, you must acknowledge that you are following all " + + 'the best practices documented by ARM at aka.ms/armapibestpractices. ' + + "If you do intend to release the APIs to your customers by merging this PR, add the PublishToCustomers label " + + "to your PR in acknowledgement of the above. Otherwise, retarget this PR onto a feature branch, i.e. with prefix release- " + + '(see aka.ms/azsdk/api-versions#release--branches).
  • ' + + "
  • ❌ This PR has at least one change violating Azure versioning policy (label: VersioningReviewRequired).
    To unblock this PR, either a) " + + 'introduce a new API version with these changes instead of modifying an existing API version, or b) follow the process at aka.ms/brch.' + + "
  • ❌ The required check named TypeSpec Validation has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult " + + 'the aka.ms/ci-fix guide


Comment generated by summarize-checks workflow run.'; + const expectedOutput = [ + expectedComment, + { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }, + ]; + + const requiredCheckRuns = [ + { + name: "SpellCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("SpellCheck"), + }, + { + name: "TypeSpec Requirement", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("TypeSpec Requirement"), + }, + { + name: "Protected Files", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Protected Files"), + }, + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + { + name: "Breaking Change(Cross-Version)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Breaking Change(Cross-Version)"), + }, + { + name: "Swagger Avocado", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Avocado"), + }, + { + name: "Swagger ModelValidation", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger ModelValidation"), + }, + { + name: "Swagger SemanticValidation", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger SemanticValidation"), + }, + { + name: "Swagger Lint(RPaaS)", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger Lint(RPaaS)"), + }, + { + name: "Automated merging requirements met", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Automated merging requirements met"), + }, + { + name: "license/cla", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("license/cla"), + }, + { + name: "Swagger PrettierCheck", + status: "COMPLETED", + conclusion: "SUCCESS", + checkInfo: getCheckInfo("Swagger PrettierCheck"), + }, + ]; + + const output = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(output).toEqual(expectedOutput); + }); + + it("should generate error summary with a failed required check, and failed FYI checks", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedCheckOutput = { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }; + const expectedCommentOutput = `

Next Steps to Merge

Next steps that must be taken to merge this PR:
  • ❌ The required check named Swagger BreakingChange has failed. To unblock this PR, follow the process at aka.ms/brch.

Important checks have failed. As of today they are not blocking this PR, but in near future they may.
Addressing the following failures is highly recommended:
  • ⚠️ The check named TypeSpec Validation has failed. Refer to the check in the PR's 'Checks' tab for details on how to fix it and consult the aka.ms/ci-fix guide


Comment generated by summarize-checks workflow run.`; + + const fyiCheckRuns = [ + { + name: "TypeSpec Validation", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + ]; + + const requiredCheckRuns = [ + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + ]; + + const [commentOutput, automatedCheckOutput] = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + WORKFLOW_URL, + ); + + expect(automatedCheckOutput).toEqual(expectedCheckOutput); + expect(commentOutput).toEqual(expectedCommentOutput); + }); + + it("should generate error summary with a failed required check, and in-progress FYI checks", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const expectedCheckOutput = { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }; + + const fyiCheckRuns = [ + { + name: "TypeSpec Validation", + status: "IN_PROGRESS", + conclusion: null, + checkInfo: getCheckInfo("TypeSpec Validation"), + }, + ]; + + const requiredCheckRuns = [ + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + ]; + + const [, automatedCheckOutput] = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + ); + + expect(automatedCheckOutput).toEqual(expectedCheckOutput); + }); + + it("should generate error summary when checks are in error state", async () => { + const repo = "azure-rest-api-specs"; + const targetBranch = "main"; + const labelNames = []; + const fyiCheckRuns = []; + const expectedCheckOutput = { + name: "Automated merging requirements met", + result: "FAILURE", + summary: + "❌ This PR cannot be merged because some requirements are not met. See the details.", + }; + + const requiredCheckRuns = [ + { + name: "Swagger BreakingChange", + status: "COMPLETED", + conclusion: "FAILURE", + checkInfo: getCheckInfo("Swagger BreakingChange"), + }, + ]; + + const [, automatedCheckOutput] = await createNextStepsComment( + mockCore, + repo, + labelNames, + targetBranch, + requiredCheckRuns, + fyiCheckRuns, + true, // assessmentCompleted + ); + + expect(automatedCheckOutput).toEqual(expectedCheckOutput); + }); + }); +}); diff --git a/.github/workflows/test/update-labels.test.js b/.github/workflows/test/update-labels.test.js index 196c65cfaf13..e9cfdd97c869 100644 --- a/.github/workflows/test/update-labels.test.js +++ b/.github/workflows/test/update-labels.test.js @@ -1,53 +1,13 @@ import { describe, expect, it } from "vitest"; -import { PER_PAGE_MAX } from "../src/github.js"; +import { PER_PAGE_MAX } from "../../shared/src/github.js"; +import { fullGitSha } from "../../shared/test/examples.js"; import updateLabels, { updateLabelsImpl } from "../src/update-labels.js"; import { createMockCore, createMockGithub, createMockRequestError } from "./mocks.js"; describe("updateLabels", () => { - it("loads inputs from env", async () => { - const github = createMockGithub(); - github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { - artifacts: [{ name: "label-foo=true" }], - }, - }); - - try { - process.env.OWNER = "TestRepoOwnerLoginEnv"; - process.env.REPO = "TestRepoNameEnv"; - process.env.ISSUE_NUMBER = "123"; - process.env.RUN_ID = "456"; - - await expect( - updateLabels({ - github: github, - context: null, - core: createMockCore(), - }), - ).resolves.toBeUndefined(); - } finally { - delete process.env.OWNER; - delete process.env.REPO; - delete process.env.ISSUE_NUMBER; - delete process.env.RUN_ID; - } - - expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({ - owner: "TestRepoOwnerLoginEnv", - repo: "TestRepoNameEnv", - run_id: 456, - per_page: PER_PAGE_MAX, - }); - expect(github.rest.issues.addLabels).toBeCalledWith({ - owner: "TestRepoOwnerLoginEnv", - repo: "TestRepoNameEnv", - issue_number: 123, - labels: ["foo"], - }); - expect(github.rest.issues.removeLabel).toBeCalledTimes(0); - }); - it("loads inputs from context", async () => { + const core = createMockCore(); + const github = createMockGithub(); github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ data: { @@ -61,7 +21,7 @@ describe("updateLabels", () => { action: "completed", workflow_run: { event: "pull_request", - head_sha: "abc123", + head_sha: fullGitSha, id: 456, repository: { name: "TestRepoName", @@ -74,13 +34,10 @@ describe("updateLabels", () => { }, }; - await expect( - updateLabels({ - github: github, - context: context, - core: createMockCore(), - }), - ).resolves.toBeUndefined(); + await expect(updateLabels({ github, context, core })).resolves.toBeUndefined(); + + expect(core.setOutput).toBeCalledWith("head_sha", fullGitSha); + expect(core.setOutput).toBeCalledWith("issue_number", 123); expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({ owner: "TestRepoOwnerLogin", @@ -96,64 +53,6 @@ describe("updateLabels", () => { }); expect(github.rest.issues.removeLabel).toBeCalledTimes(0); }); - - it("loads inputs from env and context", async () => { - const github = createMockGithub(); - github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ - data: { - artifacts: [{ name: "label-foo=true" }], - }, - }); - - const context = { - eventName: "workflow_run", - payload: { - action: "completed", - workflow_run: { - event: "pull_request", - head_sha: "abc123", - id: 456, - repository: { - name: "TestRepoName", - owner: { - login: "TestRepoOwnerLogin", - }, - }, - pull_requests: [{ number: 123 }], - }, - }, - }; - - try { - process.env.OWNER = "TestRepoOwnerLoginEnv"; - process.env.REPO = "TestRepoNameEnv"; - - await expect( - updateLabels({ - github: github, - context: context, - core: createMockCore(), - }), - ).resolves.toBeUndefined(); - } finally { - delete process.env.OWNER; - delete process.env.REPO; - } - - expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({ - owner: "TestRepoOwnerLoginEnv", - repo: "TestRepoNameEnv", - run_id: 456, - per_page: PER_PAGE_MAX, - }); - expect(github.rest.issues.addLabels).toBeCalledWith({ - owner: "TestRepoOwnerLoginEnv", - repo: "TestRepoNameEnv", - issue_number: 123, - labels: ["foo"], - }); - expect(github.rest.issues.removeLabel).toBeCalledTimes(0); - }); }); describe("updateLabelsImpl", () => { @@ -169,7 +68,9 @@ describe("updateLabelsImpl", () => { github: github, core: createMockCore(), }), - ).rejects.toThrow(); + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Required input 'run_id' not found in env or context]`, + ); expect(github.rest.issues.addLabels).toBeCalledTimes(0); expect(github.rest.issues.removeLabel).toBeCalledTimes(0); @@ -293,7 +194,44 @@ describe("updateLabelsImpl", () => { github: github, core: createMockCore(), }), - ).rejects.toThrow(); + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Invalid value for label 'baz': invalid. Expected "true" or "false".]`, + ); + + expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({ + owner: "owner", + repo: "repo", + run_id: 456, + per_page: PER_PAGE_MAX, + }); + + // Ensure no labels are added or removed if any are invalid + expect(github.rest.issues.addLabels).toBeCalledTimes(0); + expect(github.rest.issues.removeLabel).toBeCalledTimes(0); + }); + + it("throws for invalid label name", async () => { + const github = createMockGithub(); + github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({ + data: { + artifacts: [ + { name: "label-foo=true" }, + { name: "label-bar=false" }, + { name: "label-=true" }, + ], + }, + }); + + await expect( + updateLabelsImpl({ + owner: "owner", + repo: "repo", + issue_number: 123, + run_id: 456, + github: github, + core: createMockCore(), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Invalid value for label name: '']`); expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({ owner: "owner", diff --git a/.github/workflows/test/verify-run-status.test.js b/.github/workflows/test/verify-run-status.test.js index 28da7ef61cc7..7a6280e9d26a 100644 --- a/.github/workflows/test/verify-run-status.test.js +++ b/.github/workflows/test/verify-run-status.test.js @@ -1,4 +1,4 @@ -import { describe, expect, it, vi, beforeEach } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { verifyRunStatusImpl } from "../src/verify-run-status.js"; import { createMockCore, createMockGithub } from "./mocks.js"; diff --git a/.github/workflows/typespec-migration-validation.yaml b/.github/workflows/typespec-migration-validation.yaml index 2536989fe082..fa40fc303303 100644 --- a/.github/workflows/typespec-migration-validation.yaml +++ b/.github/workflows/typespec-migration-validation.yaml @@ -10,7 +10,7 @@ permissions: jobs: typespec-migration-validation: name: TypeSpec Migration Validation - if: contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w1') || contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w2') + if: contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w1') || contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w2') || contains(github.event.pull_request.labels.*.name, 'typespec-conversion-w3') runs-on: ubuntu-24.04 steps: @@ -23,5 +23,5 @@ jobs: - name: Run TypeSpec Migration Validation run: | - ./eng/tools/typespec-migration-validation/scripts/download-main.ps1 -Verbose -callValidation $true + ./eng/tools/typespec-migration-validation/scripts/download-main.ps1 -Verbose -reportFile $env:GITHUB_STEP_SUMMARY shell: pwsh diff --git a/.github/workflows/update-labels.yaml b/.github/workflows/update-labels.yaml index beaa0c4aa9db..e9fc28c9cc99 100644 --- a/.github/workflows/update-labels.yaml +++ b/.github/workflows/update-labels.yaml @@ -7,28 +7,14 @@ on: # If an upstream workflow if completed, get only the artifacts from that workflow, and update labels workflow_run: workflows: - ["ARM Auto SignOff", "SDK Breaking Change Labels", "SDK Suppressions", "TypeSpec Requirement"] + [ + "ARM Auto SignOff", + "SDK Breaking Change Labels", + "SDK Suppressions", + "TypeSpec Requirement", + "Breaking Change - Add Label Artifacts", + ] types: [completed] - workflow_dispatch: - inputs: - owner: - description: The account owner of the repository. The name is not case sensitive. - required: true - type: string - repo: - description: The name of the repository without the .git extension. The name is not case sensitive. - required: true - type: string - # simulate pull_request trigger - issue_number: - description: The number that identifies the issue. - required: false - type: number - # simulate workflow_run trigger - run_id: - description: The unique identifier of the workflow run. - required: false - type: number permissions: actions: read @@ -39,9 +25,6 @@ jobs: update-labels: name: Update Labels - # Only process labels from successful workflows - if: ${{ github.event.workflow_run.conclusion == 'success' }} - runs-on: ubuntu-24.04 steps: @@ -51,14 +34,24 @@ jobs: .github - name: Update Labels + id: update-labels uses: actions/github-script@v7 - env: - OWNER: ${{ inputs.owner }} - REPO: ${{ inputs.repo }} - ISSUE_NUMBER: ${{ inputs.issue_number }} - RUN_ID: ${{ inputs.run_id }} with: script: | const { default: updateLabels } = await import('${{ github.workspace }}/.github/workflows/src/update-labels.js'); await updateLabels({ github, context, core }); + + - if: ${{ always() && steps.update-labels.outputs.head_sha }} + name: Upload artifact with head SHA + uses: ./.github/actions/add-empty-artifact + with: + name: head-sha + value: ${{ steps.update-labels.outputs.head_sha }} + + - if: ${{ always() && steps.update-labels.outputs.issue_number }} + name: Upload artifact with issue number + uses: ./.github/actions/add-empty-artifact + with: + name: issue-number + value: ${{ steps.update-labels.outputs.issue_number }} diff --git a/.github/workflows/watch-breakingchange-crossversion.yaml b/.github/workflows/watch-breakingchange-crossversion.yaml deleted file mode 100644 index 69e4c50eacc0..000000000000 --- a/.github/workflows/watch-breakingchange-crossversion.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Use ~ to sort the workflow to the bottom of the list -name: "~Watch - Breaking Change(Cross-Version)" - -on: - # check_suite is preferred over check_run to avoid triggering on all check - # runs. In some cases, check_run must be used in testing environments. - check_suite: - types: completed - - workflow_run: - types: completed - workflows: - - "\\[TEST-IGNORE\\] Breaking Change(Cross-Version) - Set Status" - -permissions: - checks: read - contents: read - -jobs: - BreakingChangeCrossVersion: - name: Watch Breaking Change(Cross-Version) - uses: ./.github/workflows/_reusable-verify-run-status.yaml - with: - check_run_name: "Breaking Change(Cross-Version)" - commit_status_name: "[TEST-IGNORE] Breaking Change(Cross-Version)" diff --git a/.github/workflows/watch-breakingchange.yaml b/.github/workflows/watch-breakingchange.yaml deleted file mode 100644 index 9c14cbe57cb2..000000000000 --- a/.github/workflows/watch-breakingchange.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Use ~ to sort the workflow to the bottom of the list -name: "~Watch - BreakingChange" - -on: - # check_suite is preferred over check_run to avoid triggering on all check - # runs. In some cases, check_run must be used in testing environments. - check_suite: - types: completed - - workflow_run: - types: completed - workflows: - - "\\[TEST-IGNORE\\] Swagger BreakingChange - Set Status" - -permissions: - checks: read - contents: read - -jobs: - BreakingChange: - name: Watch BreakingChange - uses: ./.github/workflows/_reusable-verify-run-status.yaml - with: - check_run_name: "Swagger BreakingChange" - commit_status_name: "[TEST-IGNORE] Swagger BreakingChange" diff --git a/.github/workflows/watch-modelvalidation.yaml b/.github/workflows/watch-modelvalidation.yaml deleted file mode 100644 index a5d13ce232c6..000000000000 --- a/.github/workflows/watch-modelvalidation.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Use ~ to sort the workflow to the bottom of the list -name: "~Watch - Swagger ModelValidation" - -on: - # check_suite is preferred over check_run to avoid triggering on all check - # runs. In some cases, check_run must be used in testing environments. - check_suite: - types: completed - - workflow_run: - types: completed - workflows: - - "\\[TEST-IGNORE\\] Swagger ModelValidation" - -permissions: - checks: read - contents: read - -jobs: - ModelValidationWatch: - name: Watch ModelValidation - uses: ./.github/workflows/_reusable-verify-run-status.yaml - with: - check_run_name: "Swagger ModelValidation" - workflow_name: "[TEST-IGNORE] Swagger ModelValidation" diff --git a/.github/workflows/watch-semanticvalidation.yaml b/.github/workflows/watch-semanticvalidation.yaml deleted file mode 100644 index cba8cce1ead1..000000000000 --- a/.github/workflows/watch-semanticvalidation.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Use ~ to sort the workflow to the bottom of the list -name: "~Watch - Swagger SemanticValidation" - -on: - # check_suite is preferred over check_run to avoid triggering on all check - # runs. In some cases, check_run must be used in testing environments. - check_suite: - types: completed - - workflow_run: - types: completed - workflows: - - "\\[TEST-IGNORE\\] Swagger SemanticValidation" - -permissions: - checks: read - contents: read - -jobs: - SemanticValidationWatch: - name: Watch SemanticValidation - uses: ./.github/workflows/_reusable-verify-run-status.yaml - with: - check_run_name: "Swagger SemanticValidation" - workflow_name: "[TEST-IGNORE] Swagger SemanticValidation" diff --git a/dev/cognitiveservices/data-plane/Language/analyzeconversations-authoring.json b/dev/cognitiveservices/data-plane/Language/analyzeconversations-authoring.json deleted file mode 100644 index 78906d295c9a..000000000000 --- a/dev/cognitiveservices/data-plane/Language/analyzeconversations-authoring.json +++ /dev/null @@ -1,3267 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Microsoft Cognitive Language Service - Analyze Conversations Authoring", - "version": "2023-04-01", - "description": "The language service API is a suite of natural language processing (NLP) skills built with best-in-class Microsoft machine learning algorithms. The API can be used to analyze unstructured text for tasks such as sentiment analysis, key phrase extraction, language detection and question answering. Further documentation can be found in https://docs.microsoft.com/en-us/azure/cognitive-services/language-service/overview." - }, - "securityDefinitions": { - "AADToken": { - "type": "oauth2", - "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", - "flow": "implicit", - "description": "These are the [Azure Active Directory OAuth2](https://docs.microsoft.com/azure/active-directory/develop/v1-overview) Flows. When paired with [Azure role-based access](https://docs.microsoft.com/azure/role-based-access-control/overview) control it can be used to control access to Azure Maps REST APIs. Azure role-based access controls are used to designate access to one or more Azure Maps resource account or sub-resources. Any user, group, or service principal can be granted access via a built-in role or a custom role composed of one or more permissions to Azure Maps REST APIs.\n\nTo implement scenarios, we recommend viewing [authentication concepts](https://aka.ms/amauth). In summary, this security definition provides a solution for modeling application(s) via objects capable of access control on specific APIs and scopes.\n\n#### Notes\n* This security definition **requires** the use of the `x-ms-client-id` header to indicate which Azure Maps resource the application is requesting access to. This can be acquired from the [Maps management API](https://aka.ms/amauthdetails).\n* \nThe `Authorization URL` is specific to the Azure public cloud instance. Sovereign clouds have unique Authorization URLs and Azure Active directory configurations. \n* \nThe Azure role-based access control is configured from the [Azure management plane](https://aka.ms/amrbac) via Azure portal, PowerShell, CLI, Azure SDKs, or REST APIs.\n* \nUsage of the [Azure Maps Web SDK](https://aka.ms/amaadmc) allows for configuration based setup of an application for multiple use cases.\n* Currently, Azure Active Directory [v1.0 or v2.0](https://docs.microsoft.com/azure/active-directory/develop/azure-ad-endpoint-comparison) supports Work, School, and Guests but does not support Personal accounts.", - "scopes": { - "https://cognitiveservices.azure.com/.default": "https://cognitiveservices.azure.com/.default" - } - }, - "apim_key": { - "type": "apiKey", - "description": "A subscription key for a Language service resource.", - "name": "Ocp-Apim-Subscription-Key", - "in": "header" - } - }, - "security": [ - { - "AADToken": [ - "https://cognitiveservices.azure.com/.default" - ] - }, - { - "apim_key": [] - } - ], - "x-ms-parameterized-host": { - "hostTemplate": "{Endpoint}/language", - "useSchemePrefix": false, - "parameters": [ - { - "$ref": "common.json#/parameters/Endpoint" - } - ] - }, - "paths": { - "/authoring/analyze-conversations/projects": { - "get": { - "description": "Lists the existing projects.", - "operationId": "ConversationalAnalysisAuthoring_ListProjects", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The metadata of projects.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectsMetadata" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful List Projects": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulListProjects.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}": { - "patch": { - "description": "Creates a new project or updates an existing one.", - "operationId": "ConversationalAnalysisAuthoring_CreateProject", - "consumes": [ - "application/merge-patch+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "in": "body", - "name": "body", - "description": "The project parameters.", - "required": true, - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringCreateProjectOptions" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The metadata of the updated project, if it already exists.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectMetadata" - } - }, - "201": { - "description": "The metadata of the created project.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectMetadata" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Create Project": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulCreateProject.json" - } - } - }, - "get": { - "description": "Gets the details of a project.", - "operationId": "ConversationalAnalysisAuthoring_GetProject", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The metadata of the project.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectMetadata" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Project": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetProject.json" - } - } - }, - "delete": { - "description": "Deletes a project.", - "operationId": "ConversationalAnalysisAuthoring_DeleteProject", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Delete Project": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulDeleteProject.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-conversations/projects/{projectName}/:export": { - "post": { - "description": "Triggers a job to export a project's data.", - "operationId": "ConversationalAnalysisAuthoring_Export", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringFormatQueryOptionalParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringStringIndexTypeQueryParameter" - }, - { - "in": "query", - "name": "assetKind", - "description": "Kind of asset to export.", - "type": "string", - "x-ms-parameter-location": "method" - }, - { - "in": "query", - "name": "trainedModelLabel", - "description": "Trained model label to export. If the trainedModelLabel is null, the default behavior is to export the current working copy.", - "type": "string", - "x-ms-parameter-location": "method" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Export Project": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulExportProject.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-conversations/projects/{projectName}/:import": { - "post": { - "description": "Triggers a job to import a project. If a project with the same name already exists, the data of that project is replaced.", - "operationId": "ConversationalAnalysisAuthoring_Import", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringFormatQueryOptionalParameter" - }, - { - "in": "body", - "name": "body", - "description": "The project data to import.", - "required": true, - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedProject" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Import Project": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulImportProject.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-conversations/projects/{projectName}/:train": { - "post": { - "description": "Triggers a training job for a project.", - "operationId": "ConversationalAnalysisAuthoring_Train", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "in": "body", - "name": "body", - "description": "The training input parameters.", - "required": true, - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringTrainingJobOptions" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Train Project": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulTrainProject.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-conversations/projects/{projectName}/deployments": { - "get": { - "description": "Lists the deployments belonging to a project.", - "operationId": "ConversationalAnalysisAuthoring_ListDeployments", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of all deployments.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectDeployments" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful List Deployments": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulListDeployments.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/deployments/:swap": { - "post": { - "description": "Swaps two existing deployments with each other.", - "operationId": "ConversationalAnalysisAuthoring_SwapDeployments", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "in": "body", - "name": "body", - "description": "The job object to swap two deployments.", - "required": true, - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringSwapDeploymentsOptions" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Swap Deployments": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulSwapDeployments.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-conversations/projects/{projectName}/deployments/{deploymentName}": { - "get": { - "description": "Gets the details of a deployment.", - "operationId": "ConversationalAnalysisAuthoring_GetDeployment", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The Deployment info.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectDeployment" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Deployment": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetDeployment.json" - } - } - }, - "put": { - "description": "Creates a new deployment or replaces an existing one.", - "operationId": "ConversationalAnalysisAuthoring_DeployProject", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "in": "body", - "name": "body", - "description": "The new deployment info.", - "required": true, - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringCreateDeploymentOptions" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Deploy Project": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulDeployProject.json" - } - }, - "x-ms-long-running-operation": true - }, - "delete": { - "description": "Deletes a project deployment.", - "operationId": "ConversationalAnalysisAuthoring_DeleteDeployment", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Delete Deployment": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulDeleteDeployment.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-conversations/projects/{projectName}/deployments/{deploymentName}/jobs/{jobId}": { - "get": { - "description": "Gets the status of an existing deployment job.", - "operationId": "ConversationalAnalysisAuthoring_GetDeploymentStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The deployment job result.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringDeploymentJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Deployment Status": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetDeploymentStatus.json" - } - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/deployments/swap/jobs/{jobId}": { - "get": { - "description": "Gets the status of an existing swap deployment job.", - "operationId": "ConversationalAnalysisAuthoring_GetSwapDeploymentsStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The swap deployment job result.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringDeploymentJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Swap Deployments Status": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetSwapDeploymentsStatus.json" - } - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/export/jobs/{jobId}": { - "get": { - "description": "Gets the status of an export job. Once job completes, returns the project metadata, and assets.", - "operationId": "ConversationalAnalysisAuthoring_GetExportStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The status of the long running operation.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportProjectJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Export Status": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetExportStatus.json" - } - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/import/jobs/{jobId}": { - "get": { - "description": "Gets the status for an import.", - "operationId": "ConversationalAnalysisAuthoring_GetImportStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The details of the long running operation.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringImportProjectJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Import Status": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetImportStatus.json" - } - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/models": { - "get": { - "description": "Lists the trained models belonging to a project.", - "operationId": "ConversationalAnalysisAuthoring_ListTrainedModels", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of all trained models.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectTrainedModels" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful List Models": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulListModels.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/models/{trainedModelLabel}": { - "get": { - "description": "Gets the details of a trained model.", - "operationId": "ConversationalAnalysisAuthoring_GetTrainedModel", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "Trained model info", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectTrainedModel" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Model": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetModel.json" - } - } - }, - "delete": { - "description": "Deletes an existing trained model.", - "operationId": "ConversationalAnalysisAuthoring_DeleteTrainedModel", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "204": { - "description": "Deleted successfully." - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Delete Model": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulDeleteModel.json" - } - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/models/{trainedModelLabel}/:load-snapshot": { - "post": { - "description": "Restores the snapshot of this trained model to be the current working directory of the project.", - "operationId": "ConversationalAnalysisAuthoring_LoadSnapshot", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results in loading the working directory with the snapshot of the model provided.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Load Snapshot": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulLoadSnapshot.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-conversations/projects/{projectName}/models/{trainedModelLabel}/evaluation/result": { - "get": { - "description": "Gets the detailed results of the evaluation for a trained model. This includes the raw inference results for the data included in the evaluation process.", - "operationId": "ConversationalAnalysisAuthoring_GetModelEvaluationResults", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringStringIndexTypeQueryParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of the evaluation predictions.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringEvaluationResults" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Model Evaluation": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetModelEvaluation.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/models/{trainedModelLabel}/evaluation/summary-result": { - "get": { - "description": "Gets the evaluation summary of a trained model. The summary includes high level performance measurements of the model e.g., F1, Precision, Recall, etc.", - "operationId": "ConversationalAnalysisAuthoring_GetModelEvaluationSummary", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of all evaluation results.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringEvaluationSummary" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Model Evaluation Summary": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetModelEvaluationSummary.json" - } - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/models/{trainedModelLabel}/load-snapshot/jobs/{jobId}": { - "get": { - "description": "Gets the status for loading a snapshot.", - "operationId": "ConversationalAnalysisAuthoring_GetLoadSnapshotStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The details of the long running operation.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringLoadSnapshotJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Load Snapshot Status": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetLoadSnapshotStatus.json" - } - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/train/jobs": { - "get": { - "description": "Lists the non-expired training jobs created for a project.", - "operationId": "ConversationalAnalysisAuthoring_ListTrainingJobs", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of the training jobs.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringTrainingJobs" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful List Training Jobs": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulListTrainingJobs.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/train/jobs/{jobId}": { - "get": { - "description": "Gets the status for a training job.", - "operationId": "ConversationalAnalysisAuthoring_GetTrainingStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The training job result.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringTrainingJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Train Status": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetTrainStatus.json" - } - } - } - }, - "/authoring/analyze-conversations/projects/{projectName}/train/jobs/{jobId}/:cancel": { - "post": { - "description": "Triggers a cancellation for a running training job.", - "operationId": "ConversationalAnalysisAuthoring_CancelTrainingJob", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the job cancellation.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Cancel Training Job": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulCancelTrainingJob.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-conversations/projects/global/deletion-jobs/{jobId}": { - "get": { - "description": "Gets the status for a project deletion job.", - "operationId": "ConversationalAnalysisAuthoring_GetProjectDeletionStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The project deletion job result.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectDeletionJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Project Deletion Status": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetProjectDeletionStatus.json" - } - } - } - }, - "/authoring/analyze-conversations/projects/global/languages": { - "get": { - "description": "Lists the supported languages for the given project type.", - "operationId": "ConversationalAnalysisAuthoring_GetSupportedLanguages", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringProjectKindQueryParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The list of supported languages.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringSupportedLanguages" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Supported Languages": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetSupportedLanguages.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-conversations/projects/global/prebuilt-entities": { - "get": { - "description": "Lists the supported prebuilt entities that can be used while creating composed entities.", - "operationId": "ConversationalAnalysisAuthoring_GetSupportedPrebuiltEntities", - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "query", - "name": "language", - "description": "The language to get supported prebuilt entities for. Required if multilingual is false. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string", - "x-ms-parameter-location": "method" - }, - { - "in": "query", - "name": "multilingual", - "description": "Whether to get the support prebuilt entities for multilingual or monolingual projects. If true, the language parameter is ignored.", - "type": "string", - "x-ms-parameter-location": "method" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The list of supported prebuilt entities.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringPrebuiltEntities" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Supported Prebuilt Entities": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetSupportedPrebuiltEntities.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-conversations/projects/global/training-config-versions": { - "get": { - "description": "Lists the support training config version for a given project type.", - "operationId": "ConversationalAnalysisAuthoring_ListTrainingConfigVersions", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "#/parameters/ConversationalAnalysisAuthoringProjectKindQueryParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of config versions.", - "schema": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringTrainingConfigVersions" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Supported Training Config Versions": { - "$ref": "./examples/analyzeconversations-authoring/SuccessfulGetSupportedTrainingConfigVersions.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - } - }, - "definitions": { - "ConversationalAnalysisAuthoringCompositionSetting": { - "enum": [ - "returnLongestOverlap", - "requireExactOverlap", - "separateComponents", - "combineComponents" - ], - "type": "string", - "x-ms-enum": { - "name": "CompositionSetting", - "modelAsString": true, - "values": [ - { - "value": "returnLongestOverlap", - "description": "When two or more components are found in the text and overlap, the component with the longest set of characters is returned." - }, - { - "value": "requireExactOverlap", - "description": "All components must overlap at the exact same characters in the text for the entity to return. If one of the defined components is not matched or predicted, the entity will not return." - }, - { - "value": "separateComponents", - "description": "Every component's match or prediction is returned as a separate instance of the entity." - }, - { - "value": "combineComponents", - "description": "When two or more components are found in the text and overlap, the components' spans are merged together into one span combining all of them." - } - ] - }, - "x-ms-client-name": "CompositionSetting" - }, - "ConversationalAnalysisAuthoringConfusionMatrix": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringConfusionMatrixRow" - }, - "x-ms-client-name": "ConfusionMatrix" - }, - "ConversationalAnalysisAuthoringConfusionMatrixCell": { - "description": "Represents a cell in a confusion matrix.", - "required": [ - "normalizedValue", - "rawValue" - ], - "type": "object", - "properties": { - "normalizedValue": { - "format": "float", - "description": "Represents normalized value in percentages.", - "type": "number" - }, - "rawValue": { - "format": "float", - "description": "Represents raw value.", - "type": "number" - } - }, - "x-ms-client-name": "ConfusionMatrixCell" - }, - "ConversationalAnalysisAuthoringConfusionMatrixRow": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringConfusionMatrixCell" - }, - "x-ms-client-name": "ConfusionMatrixRow" - }, - "ConversationalAnalysisAuthoringConversationExportedEntity": { - "description": "Represents an entity with its components.", - "required": [ - "category" - ], - "type": "object", - "properties": { - "category": { - "description": "The category of the entity.", - "type": "string" - }, - "compositionSetting": { - "description": "The behavior to follow when the entity's components overlap with each other.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringCompositionSetting" - }, - "list": { - "description": "The list component of the entity.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedEntityList" - }, - "prebuilts": { - "description": "The prebuilt entities components.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedPrebuiltEntity" - } - }, - "regex": { - "description": "The regex component of the entity.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedEntityRegex" - }, - "requiredComponents": { - "description": "The required components. Allowed values are 'learned', 'list', 'prebuilts' and 'regex'.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "x-ms-client-name": "ConversationExportedEntity" - }, - "ConversationalAnalysisAuthoringConversationExportedIntent": { - "description": "Represents an exported intent of a conversational project.", - "required": [ - "category" - ], - "type": "object", - "properties": { - "category": { - "description": "The intent category.", - "type": "string" - } - }, - "x-ms-client-name": "ConversationExportedIntent" - }, - "ConversationalAnalysisAuthoringConversationExportedProjectAssets": { - "description": "Represents the exported assets of a conversational project.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedProjectAssets" - } - ], - "properties": { - "intents": { - "description": "The intents defined in the project.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringConversationExportedIntent" - } - }, - "entities": { - "description": "The entities defined in the project.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringConversationExportedEntity" - } - }, - "utterances": { - "description": "The utterances defined in the project.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringConversationExportedUtterance" - } - } - }, - "x-ms-discriminator-value": "Conversation", - "x-ms-client-name": "ConversationExportedProjectAssets" - }, - "ConversationalAnalysisAuthoringConversationExportedUtterance": { - "description": "Represents an exported utterance for a conversational project.", - "required": [ - "text", - "intent" - ], - "type": "object", - "properties": { - "entities": { - "description": "Represents the entity labels of the utterance.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedUtteranceEntityLabel" - } - }, - "text": { - "description": "The utterance text.", - "type": "string" - }, - "language": { - "description": "Represents the utterance's language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - }, - "intent": { - "description": "The intent of the utterance.", - "type": "string" - }, - "dataset": { - "description": "The dataset for this utterance. Allowed values are 'Train' and 'Test'.", - "type": "string" - } - }, - "x-ms-client-name": "ConversationExportedUtterance" - }, - "ConversationalAnalysisAuthoringCreateDeploymentOptions": { - "description": "Represents the options for creating or updating a project deployment.", - "required": [ - "trainedModelLabel" - ], - "type": "object", - "properties": { - "trainedModelLabel": { - "description": "Represents the trained model label.", - "type": "string" - } - }, - "x-ms-client-name": "CreateDeploymentOptions" - }, - "ConversationalAnalysisAuthoringCreateProjectOptions": { - "description": "Represents the options used to create or update a project.", - "required": [ - "projectKind", - "projectName", - "language" - ], - "type": "object", - "properties": { - "projectKind": { - "description": "Represents the project kind.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectKind" - }, - "settings": { - "description": "The project settings.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectSettings" - }, - "projectName": { - "description": "The new project name.", - "type": "string" - }, - "multilingual": { - "description": "Whether the project would be used for multiple languages or not.", - "type": "boolean" - }, - "description": { - "description": "The project description.", - "type": "string" - }, - "language": { - "description": "The project language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - } - }, - "x-ms-client-name": "CreateProjectOptions" - }, - "ConversationalAnalysisAuthoringDeploymentJobState": { - "description": "Represents the state of a deployment job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringJobState" - } - ], - "x-ms-client-name": "DeploymentJobState" - }, - "ConversationalAnalysisAuthoringEntitiesEvaluationSummary": { - "description": "Represents the evaluation result summary for the project's entities.", - "required": [ - "confusionMatrix", - "entities", - "microF1", - "microPrecision", - "microRecall", - "macroF1", - "macroPrecision", - "macroRecall" - ], - "type": "object", - "properties": { - "confusionMatrix": { - "description": "Represents the confusion matrix between two entities (the two entities can be the same). The matrix is between the entity that was labelled and the entity that was predicted.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringConfusionMatrix" - }, - "entities": { - "description": "Represents the entities evaluation summary.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringEntityEvaluationSummary" - } - }, - "microF1": { - "format": "float", - "description": "Represents the micro F1", - "type": "number" - }, - "microPrecision": { - "format": "float", - "description": "Represents the micro precision", - "type": "number" - }, - "microRecall": { - "format": "float", - "description": "Represents the micro recall", - "type": "number" - }, - "macroF1": { - "format": "float", - "description": "Represents the macro F1", - "type": "number" - }, - "macroPrecision": { - "format": "float", - "description": "Represents the macro precision", - "type": "number" - }, - "macroRecall": { - "format": "float", - "description": "Represents the macro recall", - "type": "number" - } - }, - "x-ms-client-name": "EntitiesEvaluationSummary" - }, - "ConversationalAnalysisAuthoringEntityEvaluationSummary": { - "description": "Represents the evaluation result for an entity.", - "required": [ - "f1", - "precision", - "recall", - "truePositiveCount", - "trueNegativeCount", - "falsePositiveCount", - "falseNegativeCount" - ], - "type": "object", - "properties": { - "f1": { - "format": "double", - "description": "Represents the model precision", - "type": "number" - }, - "precision": { - "format": "double", - "description": "Represents the model recall", - "type": "number" - }, - "recall": { - "format": "double", - "description": "Represents the model F1 score", - "type": "number" - }, - "truePositiveCount": { - "format": "int32", - "description": "Represents the count of true positive", - "type": "integer" - }, - "trueNegativeCount": { - "format": "int32", - "description": "Represents the count of true negative", - "type": "integer" - }, - "falsePositiveCount": { - "format": "int32", - "description": "Represents the count of false positive", - "type": "integer" - }, - "falseNegativeCount": { - "format": "int32", - "description": "Represents the count of false negative", - "type": "integer" - } - }, - "x-ms-client-name": "EntityEvaluationSummary" - }, - "ConversationalAnalysisAuthoringEvaluationKind": { - "enum": [ - "percentage", - "manual" - ], - "type": "string", - "x-ms-enum": { - "name": "EvaluationKind", - "modelAsString": true, - "values": [ - { - "value": "percentage", - "description": "Split the data into training and test sets according to user-defined percentages." - }, - { - "value": "manual", - "description": "Split the data according to the chosen dataset for every example in the data." - } - ] - }, - "x-ms-client-name": "EvaluationKind" - }, - "ConversationalAnalysisAuthoringEvaluationOptions": { - "description": "Represents the options used running the evaluation.", - "type": "object", - "properties": { - "kind": { - "description": "Represents the evaluation kind. By default, the evaluation kind is set to percentage.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringEvaluationKind" - }, - "trainingSplitPercentage": { - "format": "int32", - "description": "Represents the training dataset split percentage. Only needed in case the evaluation kind is percentage.", - "type": "integer" - }, - "testingSplitPercentage": { - "format": "int32", - "description": "Represents the testing dataset split percentage. Only needed in case the evaluation kind is percentage.", - "type": "integer" - } - }, - "x-ms-client-name": "EvaluationOptions" - }, - "ConversationalAnalysisAuthoringEvaluationResults": { - "description": "Represent a list of utterances' evaluation results.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The utterances evaluation results.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringUtteranceEvaluationResult" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "EvaluationResults" - }, - "ConversationalAnalysisAuthoringEvaluationSummary": { - "description": "Represents the evaluation result summary.", - "required": [ - "entitiesEvaluation", - "intentsEvaluation" - ], - "type": "object", - "properties": { - "entitiesEvaluation": { - "description": "Contains the data related to entities evaluation.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringEntitiesEvaluationSummary" - }, - "intentsEvaluation": { - "description": "Contains the data related to intents evaluation.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringIntentsEvaluationSummary" - }, - "evaluationOptions": { - "description": "The options that were used while running the evaluation.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringEvaluationOptions" - } - }, - "x-ms-client-name": "EvaluationSummary" - }, - "ConversationalAnalysisAuthoringExportedConversationOrchestration": { - "description": "Defines the orchestration details for a Conversational project target.", - "required": [ - "projectName", - "deploymentName" - ], - "type": "object", - "properties": { - "projectName": { - "description": "The name of the targeted project.", - "type": "string" - }, - "deploymentName": { - "description": "The name of the targeted deployment.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedConversationOrchestration" - }, - "ConversationalAnalysisAuthoringExportedConversationOrchestrationOptions": { - "description": "Represents the orchestration options for a Conversational project target.", - "required": [ - "conversationOrchestration" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedOrchestrationOptions" - } - ], - "properties": { - "conversationOrchestration": { - "description": "The Conversational project target details.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedConversationOrchestration" - } - }, - "x-ms-discriminator-value": "Conversation", - "x-ms-client-name": "ExportedConversationOrchestrationOptions" - }, - "ConversationalAnalysisAuthoringExportedEntityList": { - "description": "Represents a list component of an entity.", - "type": "object", - "properties": { - "sublists": { - "description": "The sub-lists of the list component.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedEntitySublist" - } - } - }, - "x-ms-client-name": "ExportedEntityList" - }, - "ConversationalAnalysisAuthoringExportedEntityListSynonym": { - "description": "Represents a list of synonyms inside a list component", - "type": "object", - "properties": { - "language": { - "description": "Represents the language of the synonyms. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - }, - "values": { - "description": "The list of synonyms.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "x-ms-client-name": "ExportedEntityListSynonym" - }, - "ConversationalAnalysisAuthoringExportedEntityRegex": { - "description": "Represents a regex component of an entity.", - "type": "object", - "properties": { - "expressions": { - "description": "The regex expressions of the regex component. These expressions follow the .NET regex syntax. For reference, check here: https://learn.microsoft.com/dotnet/standard/base-types/regular-expressions", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedEntityRegexExpression" - } - } - }, - "x-ms-client-name": "ExportedEntityRegex" - }, - "ConversationalAnalysisAuthoringExportedEntityRegexExpression": { - "description": "Represents a regex expression inside a regex component. This expression follows the .NET regex syntax. For reference, check here: https://learn.microsoft.com/dotnet/standard/base-types/regular-expressions", - "type": "object", - "properties": { - "regexKey": { - "description": "The key of the regex expression.", - "type": "string" - }, - "language": { - "description": "Represents the language of the regex expression. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - }, - "regexPattern": { - "description": "The regex pattern.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedEntityRegexExpression" - }, - "ConversationalAnalysisAuthoringExportedEntitySublist": { - "description": "Represents a sub-list inside a list component.", - "type": "object", - "properties": { - "listKey": { - "description": "The key of the sub-list.", - "type": "string" - }, - "synonyms": { - "description": "The phrases of that correspond to the sub-list.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedEntityListSynonym" - } - } - }, - "x-ms-client-name": "ExportedEntitySublist" - }, - "ConversationalAnalysisAuthoringExportedLuisOrchestration": { - "description": "Defines the orchestration details for a LUIS application target.", - "required": [ - "appId" - ], - "type": "object", - "properties": { - "appId": { - "format": "uuid", - "description": "The LUIS application ID.", - "type": "string", - "example": "00000000-0000-0000-0000-000000000000" - }, - "appVersion": { - "description": "The targeted version Id.", - "type": "string" - }, - "slotName": { - "description": "The targeted slot name.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedLuisOrchestration" - }, - "ConversationalAnalysisAuthoringExportedLuisOrchestrationOptions": { - "description": "Represents the orchestration options for a LUIS application target.", - "required": [ - "luisOrchestration" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedOrchestrationOptions" - } - ], - "properties": { - "luisOrchestration": { - "description": "The LUIS application target details.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedLuisOrchestration" - } - }, - "x-ms-discriminator-value": "Luis", - "x-ms-client-name": "ExportedLuisOrchestrationOptions" - }, - "ConversationalAnalysisAuthoringExportedOrchestrationOptions": { - "description": "Represents the options used to define the orchestration behavior of an intent.", - "required": [ - "targetProjectKind" - ], - "type": "object", - "properties": { - "targetProjectKind": { - "description": "The kind of the target used in the orchestration flow.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringOrchestrationTargetProjectKind" - } - }, - "discriminator": "targetProjectKind", - "x-ms-client-name": "ExportedOrchestrationOptions" - }, - "ConversationalAnalysisAuthoringExportedPrebuiltEntity": { - "description": "Represents an exported prebuilt entity component", - "required": [ - "category" - ], - "type": "object", - "properties": { - "category": { - "description": "The prebuilt entity category.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedPrebuiltEntity" - }, - "ConversationalAnalysisAuthoringExportedProject": { - "description": "Represents an exported project.", - "required": [ - "projectFileVersion", - "stringIndexType", - "metadata" - ], - "type": "object", - "properties": { - "projectFileVersion": { - "description": "The version of the exported file.", - "type": "string" - }, - "stringIndexType": { - "description": "Specifies the method used to interpret string offsets. For additional information see https://aka.ms/text-analytics-offsets.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringStringIndexType" - }, - "metadata": { - "description": "Represents the project metadata.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringCreateProjectOptions" - }, - "assets": { - "description": "Represents the project assets.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedProjectAssets" - } - }, - "x-ms-client-name": "ExportedProject" - }, - "ConversationalAnalysisAuthoringExportedProjectAssets": { - "description": "Represents the assets of an exported project.", - "required": [ - "projectKind" - ], - "type": "object", - "properties": { - "projectKind": { - "description": "The type of project containing the assets.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectKind" - } - }, - "discriminator": "projectKind", - "x-ms-client-name": "ExportedProjectAssets" - }, - "ConversationalAnalysisAuthoringExportedProjectFormat": { - "enum": [ - "Conversation", - "Luis" - ], - "type": "string", - "x-ms-enum": { - "name": "ExportedProjectFormat", - "modelAsString": true, - "values": [ - { - "value": "Conversation", - "description": "Specifies the format for a conversational project." - }, - { - "value": "Luis", - "description": "Specifies the format for an application that was exported from LUIS." - } - ] - }, - "x-ms-client-name": "ExportedProjectFormat" - }, - "ConversationalAnalysisAuthoringExportedQuestionAnsweringOrchestration": { - "description": "Defines the orchestration details for a Question Answering project target.", - "required": [ - "projectName" - ], - "type": "object", - "properties": { - "projectName": { - "description": "The name of the target project.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedQuestionAnsweringOrchestration" - }, - "ConversationalAnalysisAuthoringExportedQuestionAnsweringOrchestrationOptions": { - "description": "Represents the orchestration options for a Question Answering project target.", - "required": [ - "questionAnsweringOrchestration" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedOrchestrationOptions" - } - ], - "properties": { - "questionAnsweringOrchestration": { - "description": "The Question Answering project details.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedQuestionAnsweringOrchestration" - } - }, - "x-ms-discriminator-value": "QuestionAnswering", - "x-ms-client-name": "ExportedQuestionAnsweringOrchestrationOptions" - }, - "ConversationalAnalysisAuthoringExportedUtteranceEntityLabel": { - "description": "Represents an entity label for an utterance.", - "required": [ - "category", - "offset", - "length" - ], - "type": "object", - "properties": { - "category": { - "description": "The category of the entity label.", - "type": "string" - }, - "offset": { - "format": "int32", - "description": "Start position for the entity text.", - "type": "integer" - }, - "length": { - "format": "int32", - "description": "Length for the entity text.", - "type": "integer" - } - }, - "x-ms-client-name": "ExportedUtteranceEntityLabel" - }, - "ConversationalAnalysisAuthoringExportProjectJobState": { - "description": "Represents the state of an export job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringJobState" - } - ], - "properties": { - "resultUrl": { - "description": "The URL to use in order to download the exported project.", - "type": "string" - } - }, - "x-ms-client-name": "ExportProjectJobState" - }, - "ConversationalAnalysisAuthoringImportProjectJobState": { - "description": "Represents the state of an import job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringJobState" - } - ], - "x-ms-client-name": "ImportProjectJobState" - }, - "ConversationalAnalysisAuthoringIntentEvaluationSummary": { - "description": "The evaluation summary for an intent.", - "required": [ - "f1", - "precision", - "recall", - "truePositiveCount", - "trueNegativeCount", - "falsePositiveCount", - "falseNegativeCount" - ], - "type": "object", - "properties": { - "f1": { - "format": "double", - "description": "Represents the model precision", - "type": "number" - }, - "precision": { - "format": "double", - "description": "Represents the model recall", - "type": "number" - }, - "recall": { - "format": "double", - "description": "Represents the model F1 score", - "type": "number" - }, - "truePositiveCount": { - "format": "int32", - "description": "Represents the count of true positive", - "type": "integer" - }, - "trueNegativeCount": { - "format": "int32", - "description": "Represents the count of true negative", - "type": "integer" - }, - "falsePositiveCount": { - "format": "int32", - "description": "Represents the count of false positive", - "type": "integer" - }, - "falseNegativeCount": { - "format": "int32", - "description": "Represents the count of false negative", - "type": "integer" - } - }, - "x-ms-client-name": "IntentEvaluationSummary" - }, - "ConversationalAnalysisAuthoringIntentsEvaluationSummary": { - "description": "Represents the evaluation summary for the project's intents.", - "required": [ - "confusionMatrix", - "intents", - "microF1", - "microPrecision", - "microRecall", - "macroF1", - "macroPrecision", - "macroRecall" - ], - "type": "object", - "properties": { - "confusionMatrix": { - "description": "Represents the confusion matrix between two intents (the two intents can be the same). The matrix is between the intent that was labelled and the intent that was predicted.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringConfusionMatrix" - }, - "intents": { - "description": "Represents the intents evaluation summary.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringIntentEvaluationSummary" - } - }, - "microF1": { - "format": "float", - "description": "Represents the micro F1", - "type": "number" - }, - "microPrecision": { - "format": "float", - "description": "Represents the micro precision", - "type": "number" - }, - "microRecall": { - "format": "float", - "description": "Represents the micro recall", - "type": "number" - }, - "macroF1": { - "format": "float", - "description": "Represents the macro F1", - "type": "number" - }, - "macroPrecision": { - "format": "float", - "description": "Represents the macro precision", - "type": "number" - }, - "macroRecall": { - "format": "float", - "description": "Represents the macro recall", - "type": "number" - } - }, - "x-ms-client-name": "IntentsEvaluationSummary" - }, - "ConversationalAnalysisAuthoringJobState": { - "description": "Represents a job's state.", - "required": [ - "jobId", - "createdDateTime", - "lastUpdatedDateTime", - "status" - ], - "type": "object", - "properties": { - "jobId": { - "description": "The job ID.", - "type": "string" - }, - "createdDateTime": { - "format": "date-time", - "description": "The creation date time of the job.", - "type": "string" - }, - "lastUpdatedDateTime": { - "format": "date-time", - "description": "The last date time the job was updated.", - "type": "string" - }, - "expirationDateTime": { - "format": "date-time", - "description": "The expiration date time of the job.", - "type": "string" - }, - "status": { - "description": "The job status.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringJobStatus" - }, - "warnings": { - "description": "The warnings that were encountered while executing the job.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringWarning" - } - }, - "errors": { - "description": "The errors encountered while executing the job.", - "type": "array", - "items": { - "$ref": "common.json#/definitions/Error" - } - } - }, - "x-ms-client-name": "JobState" - }, - "ConversationalAnalysisAuthoringJobStatus": { - "enum": [ - "notStarted", - "running", - "succeeded", - "failed", - "cancelled", - "cancelling", - "partiallyCompleted" - ], - "type": "string", - "x-ms-enum": { - "name": "JobStatus", - "modelAsString": true - }, - "x-ms-client-name": "JobStatus" - }, - "ConversationalAnalysisAuthoringLoadSnapshotJobState": { - "description": "Represents the state of loading a snapshot job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringJobState" - } - ], - "x-ms-client-name": "LoadSnapshotJobState" - }, - "ConversationalAnalysisAuthoringOrchestrationExportedIntent": { - "description": "Represents an exported intent for an orchestration project.", - "required": [ - "category" - ], - "type": "object", - "properties": { - "orchestration": { - "description": "Specifies the behavior of this intent in the orchestration flow.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedOrchestrationOptions" - }, - "category": { - "description": "The intent category.", - "type": "string" - } - }, - "x-ms-client-name": "OrchestrationExportedIntent" - }, - "ConversationalAnalysisAuthoringOrchestrationExportedProjectAssets": { - "description": "Represents the assets of an orchestration project.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringExportedProjectAssets" - } - ], - "properties": { - "intents": { - "description": "Represents the intents of the project.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringOrchestrationExportedIntent" - } - }, - "utterances": { - "description": "Represents the utterances of the project.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringOrchestrationExportedUtterance" - } - } - }, - "x-ms-discriminator-value": "Orchestration", - "x-ms-client-name": "OrchestrationExportedProjectAssets" - }, - "ConversationalAnalysisAuthoringOrchestrationExportedUtterance": { - "description": "Represents an utterance of an orchestration project.", - "required": [ - "text", - "intent" - ], - "type": "object", - "properties": { - "text": { - "description": "The utterance text.", - "type": "string" - }, - "language": { - "description": "Represents the utterance's language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - }, - "intent": { - "description": "The intent of the utterance.", - "type": "string" - }, - "dataset": { - "description": "The dataset for this utterance. Allowed values are 'Train' and 'Test'.", - "type": "string" - } - }, - "x-ms-client-name": "OrchestrationExportedUtterance" - }, - "ConversationalAnalysisAuthoringOrchestrationTargetProjectKind": { - "enum": [ - "Luis", - "Conversation", - "QuestionAnswering" - ], - "type": "string", - "x-ms-enum": { - "name": "OrchestrationTargetProjectKind", - "modelAsString": true - }, - "x-ms-client-name": "OrchestrationTargetProjectKind" - }, - "ConversationalAnalysisAuthoringPrebuiltEntities": { - "description": "Represents a list of the retrieved supported prebuilt entities.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The supported prebuilt entities.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringPrebuiltEntity" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "PrebuiltEntities" - }, - "ConversationalAnalysisAuthoringPrebuiltEntity": { - "description": "Represents a supported prebuilt entity.", - "required": [ - "category", - "description", - "examples" - ], - "type": "object", - "properties": { - "category": { - "description": "The prebuilt entity category.", - "type": "string" - }, - "description": { - "description": "The description.", - "type": "string" - }, - "examples": { - "description": "English examples for the entity.", - "type": "string" - } - }, - "x-ms-client-name": "PrebuiltEntity" - }, - "ConversationalAnalysisAuthoringProjectDeletionJobState": { - "description": "Represents the state of a project deletion job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringJobState" - } - ], - "x-ms-client-name": "ProjectDeletionJobState" - }, - "ConversationalAnalysisAuthoringProjectDeployment": { - "description": "Represents a project deployment.", - "required": [ - "deploymentName", - "modelId", - "lastTrainedDateTime", - "lastDeployedDateTime", - "deploymentExpirationDate", - "modelTrainingConfigVersion" - ], - "type": "object", - "properties": { - "deploymentName": { - "description": "Represents deployment name.", - "type": "string" - }, - "modelId": { - "description": "Represents deployment modelId.", - "type": "string" - }, - "lastTrainedDateTime": { - "format": "date-time", - "description": "Represents deployment last trained time.", - "type": "string" - }, - "lastDeployedDateTime": { - "format": "date-time", - "description": "Represents deployment last deployed time.", - "type": "string" - }, - "deploymentExpirationDate": { - "format": "date", - "description": "Represents deployment expiration date in the runtime.", - "type": "string" - }, - "modelTrainingConfigVersion": { - "description": "Represents model training config version.", - "type": "string" - } - }, - "x-ms-client-name": "ProjectDeployment" - }, - "ConversationalAnalysisAuthoringProjectDeployments": { - "description": "Represents a list of retrieved deployments.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of retrieved deployments.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectDeployment" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "ProjectDeployments" - }, - "ConversationalAnalysisAuthoringProjectKind": { - "enum": [ - "Conversation", - "Orchestration" - ], - "type": "string", - "x-ms-enum": { - "name": "ProjectKind", - "modelAsString": true, - "values": [ - { - "value": "Conversation", - "description": "A project to build natural language into apps, bots, and IoT devices." - }, - { - "value": "Orchestration", - "description": "A project to connect and orchestrate Conversation, Custom question answering and LUIS projects together in one single project." - } - ] - }, - "x-ms-client-name": "ProjectKind" - }, - "ConversationalAnalysisAuthoringProjectMetadata": { - "description": "Represents the metadata of a project.", - "required": [ - "createdDateTime", - "lastModifiedDateTime", - "projectKind", - "projectName", - "language" - ], - "type": "object", - "properties": { - "createdDateTime": { - "format": "date-time", - "description": "Represents the project creation datetime.", - "type": "string" - }, - "lastModifiedDateTime": { - "format": "date-time", - "description": "Represents the project creation datetime.", - "type": "string" - }, - "lastTrainedDateTime": { - "format": "date-time", - "description": "Represents the project last trained datetime.", - "type": "string" - }, - "lastDeployedDateTime": { - "format": "date-time", - "description": "Represents the project last deployed datetime.", - "type": "string" - }, - "projectKind": { - "description": "Represents the project kind.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectKind" - }, - "settings": { - "description": "The project settings.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectSettings" - }, - "projectName": { - "description": "The new project name.", - "type": "string" - }, - "multilingual": { - "description": "Whether the project would be used for multiple languages or not.", - "type": "boolean" - }, - "description": { - "description": "The project description.", - "type": "string" - }, - "language": { - "description": "The project language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - } - }, - "x-ms-client-name": "ProjectMetadata" - }, - "ConversationalAnalysisAuthoringProjectSettings": { - "description": "Represents the settings used to define the project behavior.", - "required": [ - "confidenceThreshold" - ], - "type": "object", - "properties": { - "confidenceThreshold": { - "format": "float", - "description": "The threshold of the intent with the highest confidence, at which the prediction will automatically be changed to \"None\". The value of the threshold should be between 0 and 1 inclusive.", - "type": "number" - } - }, - "x-ms-client-name": "ProjectSettings" - }, - "ConversationalAnalysisAuthoringProjectsMetadata": { - "description": "Represents a list of retrieved projects' metadata.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of projects.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectMetadata" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "ProjectsMetadata" - }, - "ConversationalAnalysisAuthoringProjectTrainedModel": { - "description": "Represents a trained model.", - "required": [ - "label", - "modelId", - "lastTrainedDateTime", - "lastTrainingDurationInSeconds", - "modelExpirationDate", - "modelTrainingConfigVersion", - "hasSnapshot" - ], - "type": "object", - "properties": { - "label": { - "description": "The trained model label.", - "type": "string" - }, - "modelId": { - "description": "The model ID.", - "type": "string" - }, - "lastTrainedDateTime": { - "format": "date-time", - "description": "The last trained date time of the model.", - "type": "string" - }, - "lastTrainingDurationInSeconds": { - "format": "int32", - "description": "The duration of the model's last training request in seconds.", - "type": "integer" - }, - "modelExpirationDate": { - "format": "date", - "description": "The model expiration date.", - "type": "string" - }, - "modelTrainingConfigVersion": { - "description": "The model training config version.", - "type": "string" - }, - "hasSnapshot": { - "description": "The flag to indicate if the trained model has a snapshot ready.", - "type": "boolean" - } - }, - "x-ms-client-name": "ProjectTrainedModel" - }, - "ConversationalAnalysisAuthoringProjectTrainedModels": { - "description": "Represents a list of retrieved trained models.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of retrieved jobs.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringProjectTrainedModel" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "ProjectTrainedModels" - }, - "ConversationalAnalysisAuthoringStringIndexType": { - "enum": [ - "Utf16CodeUnit" - ], - "type": "string", - "x-ms-enum": { - "name": "StringIndexType", - "modelAsString": true, - "values": [ - { - "value": "Utf16CodeUnit", - "description": "The offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." - } - ] - }, - "x-ms-client-name": "StringIndexType" - }, - "ConversationalAnalysisAuthoringSubTrainingJobState": { - "description": "Represents the detailed state of a training sub-operation.", - "required": [ - "percentComplete", - "status" - ], - "type": "object", - "properties": { - "percentComplete": { - "format": "int32", - "description": "Represents progress percentage.", - "type": "integer" - }, - "startDateTime": { - "format": "date-time", - "description": "Represents the start date time.", - "type": "string" - }, - "endDateTime": { - "format": "date-time", - "description": "Represents the end date time.", - "type": "string" - }, - "status": { - "description": "Represents the status of the sub-operation.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringJobStatus" - } - }, - "x-ms-client-name": "SubTrainingJobState" - }, - "ConversationalAnalysisAuthoringSupportedLanguage": { - "description": "Represents a supported language.", - "required": [ - "languageName", - "languageCode" - ], - "type": "object", - "properties": { - "languageName": { - "description": "The language name.", - "type": "string" - }, - "languageCode": { - "description": "The language code. This is BCP-47 representation of a language. For example, \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - } - }, - "x-ms-client-name": "SupportedLanguage" - }, - "ConversationalAnalysisAuthoringSupportedLanguages": { - "description": "Represents a list of retrieved languages.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of the languages.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringSupportedLanguage" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "SupportedLanguages" - }, - "ConversationalAnalysisAuthoringSwapDeploymentsOptions": { - "description": "Represents the options for swapping two deployments together.", - "required": [ - "firstDeploymentName", - "secondDeploymentName" - ], - "type": "object", - "properties": { - "firstDeploymentName": { - "description": "Represents the first deployment name.", - "type": "string" - }, - "secondDeploymentName": { - "description": "Represents the second deployment name.", - "type": "string" - } - }, - "x-ms-client-name": "SwapDeploymentsOptions" - }, - "ConversationalAnalysisAuthoringTrainingConfigVersion": { - "description": "Represents a training config version.", - "required": [ - "trainingConfigVersion", - "modelExpirationDate" - ], - "type": "object", - "properties": { - "trainingConfigVersion": { - "description": "Represents the version of the config.", - "type": "string" - }, - "modelExpirationDate": { - "format": "date", - "description": "Represents the training config version expiration date.", - "type": "string" - } - }, - "x-ms-client-name": "TrainingConfigVersion" - }, - "ConversationalAnalysisAuthoringTrainingConfigVersions": { - "description": "Represents a list of training config versions.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of the training config versions.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringTrainingConfigVersion" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "TrainingConfigVersions" - }, - "ConversationalAnalysisAuthoringTrainingJobOptions": { - "description": "Represents the options for starting a new training job.", - "required": [ - "modelLabel", - "trainingMode" - ], - "type": "object", - "properties": { - "modelLabel": { - "description": "Represents the output model label.", - "type": "string" - }, - "trainingConfigVersion": { - "description": "Represents training config version. By default, \"latest\" value is used which uses the latest released training config version.", - "type": "string" - }, - "trainingMode": { - "description": "Represents the mode of the training operation.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringTrainingMode" - }, - "evaluationOptions": { - "description": "Represents the evaluation options. By default, the evaluation kind is percentage, with training split percentage as 80, and testing split percentage as 20.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringEvaluationOptions" - } - }, - "x-ms-client-name": "TrainingJobOptions" - }, - "ConversationalAnalysisAuthoringTrainingJobResult": { - "description": "Represents the output of a training job.", - "required": [ - "modelLabel", - "trainingConfigVersion", - "trainingStatus" - ], - "type": "object", - "properties": { - "modelLabel": { - "description": "Represents trained model label.", - "type": "string" - }, - "trainingConfigVersion": { - "description": "Represents training config version.", - "type": "string" - }, - "trainingMode": { - "description": "Represents the mode of the training operation.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringTrainingMode" - }, - "trainingStatus": { - "description": "Represents the model training status.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringSubTrainingJobState" - }, - "evaluationStatus": { - "description": "Represents model evaluation status.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringSubTrainingJobState" - }, - "estimatedEndDateTime": { - "format": "date-time", - "description": "Represents the estimated end date time for training and evaluation.", - "type": "string" - } - }, - "x-ms-client-name": "TrainingJobResult" - }, - "ConversationalAnalysisAuthoringTrainingJobs": { - "description": "Represents a list of retrieved training jobs.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of jobs.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringTrainingJobState" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "TrainingJobs" - }, - "ConversationalAnalysisAuthoringTrainingJobState": { - "description": "Represents the state of a training job.", - "required": [ - "result" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/ConversationalAnalysisAuthoringJobState" - } - ], - "properties": { - "result": { - "description": "Represents training tasks detailed result.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringTrainingJobResult" - } - }, - "x-ms-client-name": "TrainingJobState" - }, - "ConversationalAnalysisAuthoringTrainingMode": { - "enum": [ - "advanced", - "standard" - ], - "type": "string", - "x-ms-enum": { - "name": "TrainingMode", - "modelAsString": true, - "values": [ - { - "value": "advanced", - "description": "Trains using fine-tuned neural network transformer models. Can train multilingual projects." - }, - { - "value": "standard", - "description": "Faster training times for quicker iterations." - } - ] - }, - "x-ms-client-name": "TrainingMode" - }, - "ConversationalAnalysisAuthoringUtteranceEntitiesEvaluationResult": { - "description": "Represents the comparison between the expected and predicted entities for an utterance.", - "required": [ - "expectedEntities", - "predictedEntities" - ], - "type": "object", - "properties": { - "expectedEntities": { - "description": "Represents the expected entity labels.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringUtteranceEntityEvaluationResult" - } - }, - "predictedEntities": { - "description": "Represents the predicted entity labels.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationalAnalysisAuthoringUtteranceEntityEvaluationResult" - } - } - }, - "x-ms-client-name": "UtteranceEntitiesEvaluationResult" - }, - "ConversationalAnalysisAuthoringUtteranceEntityEvaluationResult": { - "description": "Represents the evaluation output of an entity label or prediction.", - "required": [ - "category", - "offset", - "length" - ], - "type": "object", - "properties": { - "category": { - "description": "Represents the entity category.", - "type": "string" - }, - "offset": { - "format": "int32", - "description": "Represents the entity offset index relative to the original text.", - "type": "integer" - }, - "length": { - "format": "int32", - "description": "Represents the entity length.", - "type": "integer" - } - }, - "x-ms-client-name": "UtteranceEntityEvaluationResult" - }, - "ConversationalAnalysisAuthoringUtteranceEvaluationResult": { - "description": "Represents the evaluation result for an utterance.", - "required": [ - "text", - "language", - "entitiesResult", - "intentsResult" - ], - "type": "object", - "properties": { - "text": { - "description": "Represents the utterance text.", - "type": "string" - }, - "language": { - "description": "Represents the utterance language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - }, - "entitiesResult": { - "description": "Represents the entities results for the utterance.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringUtteranceEntitiesEvaluationResult" - }, - "intentsResult": { - "description": "Represents the intents results for the utterance.", - "$ref": "#/definitions/ConversationalAnalysisAuthoringUtteranceIntentsEvaluationResult" - } - }, - "x-ms-client-name": "UtteranceEvaluationResult" - }, - "ConversationalAnalysisAuthoringUtteranceIntentsEvaluationResult": { - "description": "Represents the comparison between the expected and the predicted intent for an utterance.", - "required": [ - "expectedIntent", - "predictedIntent" - ], - "type": "object", - "properties": { - "expectedIntent": { - "description": "Represents the utterance's expected intent.", - "type": "string" - }, - "predictedIntent": { - "description": "Represents the utterance's predicted intent.", - "type": "string" - } - }, - "x-ms-client-name": "UtteranceIntentsEvaluationResult" - }, - "ConversationalAnalysisAuthoringWarning": { - "description": "Represents a warning that was encountered while executing the request.", - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "description": "The warning code.", - "type": "string" - }, - "message": { - "description": "The warning message.", - "type": "string" - } - }, - "x-ms-client-name": "Warning" - } - }, - "parameters": { - "ConversationalAnalysisAuthoringFormatQueryOptionalParameter": { - "in": "query", - "name": "format", - "description": "The format of the exported project file to use.", - "type": "string", - "enum": [ - "Conversation", - "Luis" - ], - "x-ms-enum": { - "name": "ExportedProjectFormat", - "modelAsString": true, - "values": [ - { - "value": "Conversation", - "description": "Specifies the format for a conversational project." - }, - { - "value": "Luis", - "description": "Specifies the format for an application that was exported from LUIS." - } - ] - }, - "x-ms-parameter-location": "method" - }, - "ConversationalAnalysisAuthoringStringIndexTypeQueryParameter": { - "in": "query", - "name": "stringIndexType", - "description": "Specifies the method used to interpret string offsets. For additional information see https://aka.ms/text-analytics-offsets.", - "required": true, - "type": "string", - "enum": [ - "Utf16CodeUnit" - ], - "x-ms-enum": { - "name": "StringIndexType", - "modelAsString": true, - "values": [ - { - "value": "Utf16CodeUnit", - "description": "The offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." - } - ] - }, - "x-ms-parameter-location": "method" - }, - "ConversationalAnalysisAuthoringJobIdPathParameter": { - "in": "path", - "name": "jobId", - "description": "The job ID.", - "required": true, - "type": "string", - "x-ms-parameter-location": "method" - }, - "ConversationalAnalysisAuthoringTrainedModelLabelPathParameter": { - "in": "path", - "name": "trainedModelLabel", - "description": "The trained model label.", - "required": true, - "type": "string", - "x-ms-parameter-location": "method" - }, - "ConversationalAnalysisAuthoringProjectKindQueryParameter": { - "in": "query", - "name": "projectKind", - "description": "The project kind.", - "required": true, - "type": "string", - "enum": [ - "Conversation", - "Orchestration" - ], - "x-ms-enum": { - "name": "ProjectKind", - "modelAsString": true, - "values": [ - { - "value": "Conversation", - "description": "A project to build natural language into apps, bots, and IoT devices." - }, - { - "value": "Orchestration", - "description": "A project to connect and orchestrate Conversation, Custom question answering and LUIS projects together in one single project." - } - ] - }, - "x-ms-parameter-location": "method" - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/analyzeconversations.json b/dev/cognitiveservices/data-plane/Language/analyzeconversations.json deleted file mode 100644 index bce20f237502..000000000000 --- a/dev/cognitiveservices/data-plane/Language/analyzeconversations.json +++ /dev/null @@ -1,2215 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Microsoft Cognitive Language Service - Analyze Conversations", - "description": "The language service conversations API is a suite of natural language processing (NLP) skills that can be used to analyze structured conversations (textual or spoken). The synchronous API in this suite accepts a request and mediates among multiple language projects, such as LUIS Generally Available, Question Answering, Conversational Language Understanding, and then calls the best candidate service to handle the request. At last, it returns a response with the candidate service's response as a payload.\n\n In some cases, this API needs to forward requests and responses between the caller and an upstream service. The asynchronous APIs in this suite enable tasks like Conversation Summarization and Conversational PII detection.", - "version": "2023-04-01" - }, - "securityDefinitions": { - "AADToken": { - "type": "oauth2", - "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", - "flow": "implicit", - "description": "These are the [Azure Active Directory OAuth2](https://docs.microsoft.com/azure/active-directory/develop/v1-overview) Flows. When paired with [Azure role-based access](https://docs.microsoft.com/azure/role-based-access-control/overview) control it can be used to control access to Azure Maps REST APIs. Azure role-based access controls are used to designate access to one or more Azure Maps resource account or sub-resources. Any user, group, or service principal can be granted access via a built-in role or a custom role composed of one or more permissions to Azure Maps REST APIs.\n\nTo implement scenarios, we recommend viewing [authentication concepts](https://aka.ms/amauth). In summary, this security definition provides a solution for modeling application(s) via objects capable of access control on specific APIs and scopes.\n\n#### Notes\n* This security definition **requires** the use of the `x-ms-client-id` header to indicate which Azure Maps resource the application is requesting access to. This can be acquired from the [Maps management API](https://aka.ms/amauthdetails).\n* \nThe `Authorization URL` is specific to the Azure public cloud instance. Sovereign clouds have unique Authorization URLs and Azure Active directory configurations. \n* \nThe Azure role-based access control is configured from the [Azure management plane](https://aka.ms/amrbac) via Azure portal, PowerShell, CLI, Azure SDKs, or REST APIs.\n* \nUsage of the [Azure Maps Web SDK](https://aka.ms/amaadmc) allows for configuration based setup of an application for multiple use cases.\n* Currently, Azure Active Directory [v1.0 or v2.0](https://docs.microsoft.com/azure/active-directory/develop/azure-ad-endpoint-comparison) supports Work, School, and Guests but does not support Personal accounts.", - "scopes": { - "https://cognitiveservices.azure.com/.default": "https://cognitiveservices.azure.com/.default" - } - }, - "apim_key": { - "type": "apiKey", - "description": "A subscription key for a Language service resource.", - "name": "Ocp-Apim-Subscription-Key", - "in": "header" - } - }, - "security": [ - { - "AADToken": [ - "https://cognitiveservices.azure.com/.default" - ] - }, - { - "apim_key": [] - } - ], - "x-ms-parameterized-host": { - "hostTemplate": "{Endpoint}/language", - "useSchemePrefix": false, - "parameters": [ - { - "$ref": "common.json#/parameters/Endpoint" - } - ] - }, - "paths": { - "/:analyze-conversations": { - "post": { - "operationId": "ConversationAnalysis_AnalyzeConversation", - "description": "Analyzes the input conversation utterance.", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "description": "A single conversational task to execute.", - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/AnalyzeConversationTask" - }, - "required": true - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "The conversation analysis response.", - "schema": { - "$ref": "#/definitions/AnalyzeConversationTaskResult" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - }, - "headers": { - "x-ms-error-code": { - "description": "The error code for specific error that occurred.", - "type": "string" - } - } - } - }, - "x-ms-examples": { - "Conversation project result": { - "$ref": "./examples/conversations/SuccessfulAnalyzeConversations.json" - }, - "Arbitration result": { - "$ref": "./examples/conversations/SuccessfulAnalyzeConversationsArbitration.json" - }, - "Orchestrator direct target result": { - "$ref": "./examples/conversations/SuccessfulAnalyzeConversationsArbitrationDirectTarget.json" - } - } - } - }, - "/analyze-conversations/jobs": { - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "description": "Submit a collection of conversations for analysis. Specify one or more unique tasks to be executed.", - "operationId": "AnalyzeConversation_SubmitJob", - "summary": "Submit analysis job for conversations", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "description": "Collection of conversations to analyze and one or more tasks to execute.", - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/AnalyzeConversationJobsInput" - }, - "required": true - } - ], - "responses": { - "202": { - "description": "A successful call results in an Operation-Location header that's used to check the status of the analysis job.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Conversation Summarization Analysis Job Request": { - "$ref": "./examples/conversations/SuccessfulConversationSummarySubmit.json" - }, - "Successful Conversation Summarization Task Submit": { - "$ref": "./examples/conversations/SuccessfulConversationSummarizationTaskSubmit.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/analyze-conversations/jobs/{jobId}": { - "get": { - "produces": [ - "application/json" - ], - "description": "Get the status of an analysis job. A job can consist of one or more tasks. After all tasks succeed, the job transitions to the succeeded state and results are available for each task.", - "operationId": "AnalyzeConversation_JobStatus", - "summary": "Get analysis status and results", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "common.json#/parameters/JobId" - }, - { - "$ref": "common.json#/parameters/ShowStats" - } - ], - "responses": { - "200": { - "description": "Analysis job status and metadata.", - "schema": { - "$ref": "#/definitions/AnalyzeConversationJobState" - } - }, - "default": { - "description": "Error encountered.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Text Conversation Analysis Job Status Request": { - "$ref": "./examples/conversations/SuccessfulConversationSummarizationTaskStatusRequest.json" - }, - "Successful Get Conversation Summarization Result": { - "$ref": "./examples/conversations/SuccessfulConversationSummarizationTaskResult.json" - } - } - } - }, - "/analyze-conversations/jobs/{jobId}:cancel": { - "post": { - "produces": [ - "application/json" - ], - "description": "Cancel a long-running job for text analysis of conversations.", - "operationId": "AnalyzeConversation_CancelJob", - "summary": "Cancel a long-running Text Analysis conversations job", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "common.json#/parameters/JobId" - } - ], - "responses": { - "202": { - "description": "The request to cancel a job has been received.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Unexpected error.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Job Delete Request": { - "$ref": "./examples/conversations/SuccessfulAnalyzeConversationsJobsCancelRequest.json" - } - }, - "x-ms-long-running-operation": true - } - } - }, - "definitions": { - "AnalyzeConversationTaskKind": { - "type": "string", - "description": "Enumeration of supported Conversation tasks.", - "enum": [ - "Conversation" - ], - "x-ms-enum": { - "name": "AnalyzeConversationTaskKind", - "modelAsString": true - } - }, - "AnalyzeConversationTaskResultsKind": { - "type": "string", - "description": "Enumeration of supported conversational task results", - "enum": [ - "ConversationResult" - ], - "x-ms-enum": { - "name": "AnalyzeConversationTaskResultsKind", - "modelAsString": true - } - }, - "AnalyzeConversationTask": { - "type": "object", - "description": "The base class of a conversation input task.", - "discriminator": "kind", - "required": [ - "kind" - ], - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeConversationTaskKind" - } - } - }, - "AnalyzeConversationTaskResult": { - "type": "object", - "description": "The base class of a conversation task result.", - "discriminator": "kind", - "required": [ - "kind" - ], - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeConversationTaskResultsKind" - } - } - }, - "ConversationalTask": { - "type": "object", - "description": "The input for a custom conversation task.", - "allOf": [ - { - "$ref": "#/definitions/AnalyzeConversationTask" - } - ], - "properties": { - "analysisInput": { - "$ref": "#/definitions/ConversationAnalysisOptions" - }, - "parameters": { - "$ref": "#/definitions/ConversationTaskParameters" - } - }, - "x-ms-discriminator-value": "Conversation", - "required": [ - "analysisInput", - "parameters" - ] - }, - "ConversationTaskParameters": { - "type": "object", - "description": "Input parameters necessary for a Conversation task.", - "properties": { - "projectName": { - "type": "string", - "description": "The name of the project to use." - }, - "deploymentName": { - "type": "string", - "description": "The name of the deployment to use." - }, - "verbose": { - "type": "boolean", - "description": "If true, the service will return more detailed information in the response." - }, - "isLoggingEnabled": { - "type": "boolean", - "description": "If true, the service will keep the query for further review." - }, - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - }, - "directTarget": { - "type": "string", - "description": "The name of a target project to forward the request to." - }, - "targetProjectParameters": { - "type": "object", - "description": "A dictionary representing the parameters for each target project.", - "additionalProperties": { - "$ref": "#/definitions/AnalysisParameters" - } - } - }, - "required": [ - "projectName", - "deploymentName" - ] - }, - "ConversationalTaskResult": { - "description": "The results of a Conversation task.", - "allOf": [ - { - "$ref": "#/definitions/AnalyzeConversationTaskResult" - } - ], - "type": "object", - "properties": { - "result": { - "$ref": "#/definitions/AnalyzeConversationResult" - } - }, - "required": [ - "result" - ], - "x-ms-discriminator-value": "ConversationResult" - }, - "ConversationItemBase": { - "type": "object", - "description": "The abstract base for a user input formatted conversation (e.g., Text, Transcript).", - "properties": { - "id": { - "description": "The ID of a conversation item.", - "type": "string" - }, - "participantId": { - "description": "The participant ID of a conversation item.", - "type": "string" - }, - "language": { - "description": "The override language of a conversation item in BCP 47 language representation.", - "type": "string" - }, - "modality": { - "$ref": "#/definitions/Modality" - }, - "role": { - "type": "string", - "description": "Role of the participant.", - "enum": [ - "agent", - "customer", - "generic" - ], - "x-ms-enum": { - "name": "role", - "modelAsString": true - } - } - }, - "required": [ - "participantId", - "id" - ], - "additionalProperties": true - }, - "TextConversationItem": { - "type": "object", - "description": "The text modality of an input conversation.", - "allOf": [ - { - "$ref": "#/definitions/ConversationItemBase" - } - ], - "properties": { - "text": { - "description": "The text input", - "type": "string" - } - }, - "required": [ - "text" - ] - }, - "ConversationAnalysisOptions": { - "type": "object", - "description": "The input ConversationItem and its optional parameters", - "required": [ - "conversationItem" - ], - "properties": { - "conversationItem": { - "$ref": "#/definitions/ConversationItemBase" - } - } - }, - "AnalysisParameters": { - "type": "object", - "description": "This is the parameter set of either the Orchestration project itself or one of the target services.", - "required": [ - "targetProjectKind" - ], - "discriminator": "targetProjectKind", - "properties": { - "targetProjectKind": { - "type": "string", - "description": "The type of a target service.", - "enum": [ - "Luis", - "Conversation", - "QuestionAnswering", - "NonLinked" - ], - "x-ms-enum": { - "name": "targetProjectKind", - "modelAsString": true - } - }, - "apiVersion": { - "type": "string", - "description": "The API version to use when call a specific target service." - } - } - }, - "NoneLinkedTargetIntentResult": { - "type": "object", - "description": "A wrap up of non-linked intent response.", - "x-ms-discriminator-value": "NonLinked", - "allOf": [ - { - "$ref": "#/definitions/TargetIntentResult" - } - ], - "properties": { - "result": { - "$ref": "#/definitions/ConversationResult", - "description": "The actual response from a Conversation project." - } - } - }, - "LuisParameters": { - "description": "This is a set of request parameters for LUIS Generally Available projects.", - "x-ms-discriminator-value": "Luis", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/AnalysisParameters" - } - ], - "properties": { - "query": { - "type": "string", - "maxLength": 500, - "description": "The utterance to predict." - }, - "callingOptions": { - "type": "object", - "description": "This customizes how the service calls LUIS Generally Available projects.", - "$ref": "#/definitions/LuisCallingOptions" - } - }, - "additionalProperties": true - }, - "LuisCallingOptions": { - "description": "This customizes how the service calls LUIS Generally Available projects.", - "type": "object", - "properties": { - "verbose": { - "description": "Enable verbose response.", - "type": "boolean" - }, - "log": { - "description": "Save log to add in training utterances later.", - "type": "boolean" - }, - "show-all-intents": { - "description": "Set true to show all intents.", - "type": "boolean" - }, - "timezoneOffset": { - "type": "number", - "description": "The timezone offset for the location of the request." - }, - "spellCheck": { - "type": "boolean", - "description": "Enable spell checking." - }, - "bing-spell-check-subscription-key": { - "description": "The subscription key to use when enabling Bing spell check", - "type": "string" - } - } - }, - "ConversationParameters": { - "type": "object", - "description": "This is a set of request parameters for Customized Conversation projects.", - "x-ms-discriminator-value": "Conversation", - "allOf": [ - { - "$ref": "#/definitions/AnalysisParameters" - } - ], - "properties": { - "callingOptions": { - "type": "object", - "$ref": "#/definitions/ConversationCallingOptions" - } - } - }, - "ConversationCallingOptions": { - "type": "object", - "description": "The option to set to call a Conversation project.", - "properties": { - "language": { - "description": "The language of the query in BCP 47 language representation..", - "type": "string" - }, - "verbose": { - "description": "If true, the service will return more detailed information.", - "type": "boolean" - }, - "isLoggingEnabled": { - "description": "If true, the query will be saved for customers to further review in authoring, to improve the model quality.", - "type": "boolean" - } - } - }, - "QuestionAnsweringParameters": { - "type": "object", - "description": "This is a set of request parameters for Question Answering knowledge bases.", - "x-ms-discriminator-value": "QuestionAnswering", - "allOf": [ - { - "$ref": "#/definitions/AnalysisParameters" - } - ], - "properties": { - "callingOptions": { - "description": "The options sent to a Question Answering KB.", - "$ref": "common.json#/definitions/AnswersOptions" - } - } - }, - "AnalyzeConversationResult": { - "type": "object", - "description": "Represents a conversation analysis response.", - "required": [ - "query", - "prediction" - ], - "properties": { - "query": { - "type": "string", - "description": "The conversation utterance given by the caller." - }, - "detectedLanguage": { - "type": "string", - "description": "The system detected language for the query in BCP 47 language representation.." - }, - "prediction": { - "description": "The prediction result of a conversation project.", - "$ref": "#/definitions/BasePrediction" - } - } - }, - "BasePrediction": { - "type": "object", - "description": "This is the base class of prediction", - "required": [ - "projectKind" - ], - "discriminator": "projectKind", - "properties": { - "projectKind": { - "type": "string", - "description": "The type of the project.", - "enum": [ - "Conversation", - "Orchestration" - ], - "x-ms-enum": { - "name": "projectKind", - "modelAsString": true - } - }, - "topIntent": { - "type": "string", - "description": "The intent with the highest score." - } - } - }, - "OrchestrationPrediction": { - "type": "object", - "description": "This represents the prediction result of an Orchestration project.", - "x-ms-discriminator-value": "Orchestration", - "required": [ - "intents" - ], - "allOf": [ - { - "$ref": "#/definitions/BasePrediction" - } - ], - "properties": { - "intents": { - "description": "A dictionary that contains all intents. A key is an intent name and a value is its confidence score and target type. The top intent's value also contains the actual response from the target project.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/TargetIntentResult" - } - } - } - }, - "TargetIntentResult": { - "type": "object", - "description": "This is the base class of an intent prediction", - "discriminator": "targetProjectKind", - "required": [ - "confidenceScore", - "targetProjectKind" - ], - "properties": { - "targetProjectKind": { - "type": "string", - "description": "This discriminator property specifies the type of the target project that returns the response.", - "enum": [ - "Luis", - "Conversation", - "QuestionAnswering", - "NonLinked" - ], - "x-ms-enum": { - "name": "targetProjectKind", - "modelAsString": true - } - }, - "apiVersion": { - "type": "string", - "description": "The API version used to call a target service." - }, - "confidenceScore": { - "type": "number", - "format": "double", - "x-ms-client-name": "confidence", - "description": "The prediction score and it ranges from 0.0 to 1.0.", - "minimum": 0, - "maximum": 1 - } - } - }, - "ConversationTargetIntentResult": { - "type": "object", - "description": "A wrap up of Conversation project response.", - "x-ms-discriminator-value": "Conversation", - "allOf": [ - { - "$ref": "#/definitions/TargetIntentResult" - } - ], - "properties": { - "result": { - "type": "object", - "$ref": "#/definitions/ConversationResult", - "description": "The actual response from a Conversation project." - } - } - }, - "ConversationResult": { - "type": "object", - "description": "The response returned by a Conversation project.", - "required": [ - "query" - ], - "properties": { - "query": { - "description": "The same query given in request.", - "type": "string" - }, - "detectedLanguage": { - "description": "The detected language from the query in BCP 47 language representation..", - "type": "string" - }, - "prediction": { - "description": "The predicted result for the query.", - "$ref": "#/definitions/ConversationPrediction" - } - } - }, - "ConversationPrediction": { - "type": "object", - "description": "Represents the prediction section of a Conversation project.", - "x-ms-discriminator-value": "Conversation", - "required": [ - "intents", - "entities" - ], - "allOf": [ - { - "$ref": "#/definitions/BasePrediction" - } - ], - "properties": { - "intents": { - "description": "The intent classification results.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationIntent" - } - }, - "entities": { - "description": "The entity extraction results.", - "type": "array", - "items": { - "$ref": "#/definitions/ConversationEntity" - } - } - } - }, - "ConversationIntent": { - "type": "object", - "description": "The intent classification result of a Conversation project.", - "required": [ - "category", - "confidenceScore" - ], - "properties": { - "category": { - "description": "A predicted class.", - "type": "string" - }, - "confidenceScore": { - "format": "float", - "x-ms-client-name": "confidence", - "description": "The confidence score of the class from 0.0 to 1.0.", - "type": "number", - "minimum": 0, - "maximum": 1 - } - } - }, - "ConversationEntity": { - "type": "object", - "description": "The entity extraction result of a Conversation project.", - "required": [ - "category", - "text", - "offset", - "length", - "confidenceScore" - ], - "properties": { - "category": { - "description": "The entity category.", - "type": "string" - }, - "text": { - "description": "The predicted entity text.", - "type": "string" - }, - "offset": { - "format": "int32", - "description": "The starting index of this entity in the query.", - "type": "integer" - }, - "length": { - "format": "int32", - "description": "The length of the text.", - "type": "integer" - }, - "confidenceScore": { - "format": "float", - "x-ms-client-name": "confidence", - "description": "The entity confidence score.", - "type": "number" - }, - "resolutions": { - "description": "The collection of entity resolution objects.", - "type": "array", - "items": { - "$ref": "#/definitions/BaseResolution" - } - }, - "extraInformation": { - "description": "The collection of entity extra information objects.", - "type": "array", - "items": { - "$ref": "#/definitions/BaseExtraInformation" - } - } - } - }, - "BaseResolution": { - "description": "The abstract base class for entity resolutions.", - "type": "object", - "discriminator": "resolutionKind", - "properties": { - "resolutionKind": { - "description": "The entity resolution object kind.", - "type": "string", - "enum": [ - "BooleanResolution", - "DateTimeResolution", - "NumberResolution", - "OrdinalResolution", - "SpeedResolution", - "WeightResolution", - "LengthResolution", - "VolumeResolution", - "AreaResolution", - "AgeResolution", - "InformationResolution", - "TemperatureResolution", - "CurrencyResolution", - "NumericRangeResolution", - "TemporalSpanResolution" - ], - "x-ms-enum": { - "name": "ResolutionKind", - "modelAsString": true - } - } - }, - "required": [ - "resolutionKind" - ] - }, - "QuantityResolution": { - "description": "Represents resolutions for quantities.", - "type": "object", - "properties": { - "value": { - "type": "number", - "format": "double", - "description": "The numeric value that the extracted text denotes." - } - }, - "required": [ - "value" - ] - }, - "AgeResolution": { - "description": "Represents the Age entity resolution model.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - }, - { - "$ref": "#/definitions/QuantityResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "AgeResolution", - "properties": { - "unit": { - "type": "string", - "enum": [ - "Unspecified", - "Year", - "Month", - "Week", - "Day" - ], - "x-ms-enum": { - "name": "AgeUnit", - "modelAsString": true - }, - "description": "The Age Unit of measurement" - } - }, - "required": [ - "unit" - ] - }, - "VolumeResolution": { - "description": "Represents the volume entity resolution model.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - }, - { - "$ref": "#/definitions/QuantityResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "VolumeResolution", - "properties": { - "unit": { - "type": "string", - "enum": [ - "Unspecified", - "CubicMeter", - "CubicCentimeter", - "CubicMillimeter", - "Hectoliter", - "Decaliter", - "Liter", - "Centiliter", - "Milliliter", - "CubicYard", - "CubicInch", - "CubicFoot", - "CubicMile", - "FluidOunce", - "Teaspoon", - "Tablespoon", - "Pint", - "Quart", - "Cup", - "Gill", - "Pinch", - "FluidDram", - "Barrel", - "Minim", - "Cord", - "Peck", - "Bushel", - "Hogshead" - ], - "x-ms-enum": { - "name": "VolumeUnit", - "modelAsString": true - }, - "description": "The Volume Unit of measurement" - } - }, - "required": [ - "unit" - ] - }, - "SpeedResolution": { - "description": "Represents the speed entity resolution model.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - }, - { - "$ref": "#/definitions/QuantityResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "SpeedResolution", - "properties": { - "unit": { - "type": "string", - "enum": [ - "Unspecified", - "MetersPerSecond", - "KilometersPerHour", - "KilometersPerMinute", - "KilometersPerSecond", - "MilesPerHour", - "Knot", - "FootPerSecond", - "FootPerMinute", - "YardsPerMinute", - "YardsPerSecond", - "MetersPerMillisecond", - "CentimetersPerMillisecond", - "KilometersPerMillisecond" - ], - "x-ms-enum": { - "name": "SpeedUnit", - "modelAsString": true - }, - "description": "The speed Unit of measurement" - } - }, - "required": [ - "unit" - ] - }, - "AreaResolution": { - "description": "Represents the area entity resolution model.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - }, - { - "$ref": "#/definitions/QuantityResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "AreaResolution", - "properties": { - "unit": { - "type": "string", - "enum": [ - "Unspecified", - "SquareKilometer", - "SquareHectometer", - "SquareDecameter", - "SquareDecimeter", - "SquareMeter", - "SquareCentimeter", - "SquareMillimeter", - "SquareInch", - "SquareFoot", - "SquareMile", - "SquareYard", - "Acre" - ], - "x-ms-enum": { - "name": "AreaUnit", - "modelAsString": true - }, - "description": "The area Unit of measurement" - } - }, - "required": [ - "unit" - ] - }, - "LengthResolution": { - "description": "Represents the length entity resolution model.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - }, - { - "$ref": "#/definitions/QuantityResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "LengthResolution", - "properties": { - "unit": { - "type": "string", - "enum": [ - "Unspecified", - "Kilometer", - "Hectometer", - "Decameter", - "Meter", - "Decimeter", - "Centimeter", - "Millimeter", - "Micrometer", - "Nanometer", - "Picometer", - "Mile", - "Yard", - "Inch", - "Foot", - "LightYear", - "Pt" - ], - "x-ms-enum": { - "name": "LengthUnit", - "modelAsString": true - }, - "description": "The length Unit of measurement" - } - }, - "required": [ - "unit" - ] - }, - "InformationResolution": { - "description": "Represents the information (data) entity resolution model.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - }, - { - "$ref": "#/definitions/QuantityResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "InformationResolution", - "properties": { - "unit": { - "type": "string", - "enum": [ - "Unspecified", - "Bit", - "Kilobit", - "Megabit", - "Gigabit", - "Terabit", - "Petabit", - "Byte", - "Kilobyte", - "Megabyte", - "Gigabyte", - "Terabyte", - "Petabyte" - ], - "x-ms-enum": { - "name": "InformationUnit", - "modelAsString": true - }, - "description": "The information (data) Unit of measurement." - } - }, - "required": [ - "unit" - ] - }, - "TemperatureResolution": { - "description": "Represents the temperature entity resolution model.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - }, - { - "$ref": "#/definitions/QuantityResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "TemperatureResolution", - "properties": { - "unit": { - "type": "string", - "enum": [ - "Unspecified", - "Fahrenheit", - "Kelvin", - "Rankine", - "Celsius" - ], - "x-ms-enum": { - "name": "TemperatureUnit", - "modelAsString": true - }, - "description": "The temperature Unit of measurement." - } - }, - "required": [ - "unit" - ] - }, - "WeightResolution": { - "description": "Represents the weight entity resolution model.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - }, - { - "$ref": "#/definitions/QuantityResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "WeightResolution", - "properties": { - "unit": { - "type": "string", - "enum": [ - "Unspecified", - "Kilogram", - "Gram", - "Milligram", - "Gallon", - "MetricTon", - "Ton", - "Pound", - "Ounce", - "Grain", - "PennyWeight", - "LongTonBritish", - "ShortTonUS", - "ShortHundredWeightUS", - "Stone", - "Dram" - ], - "x-ms-enum": { - "name": "WeightUnit", - "modelAsString": true - }, - "description": "The weight Unit of measurement." - } - }, - "required": [ - "unit" - ] - }, - "CurrencyResolution": { - "description": "Represents the currency entity resolution model.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - }, - { - "$ref": "#/definitions/QuantityResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "CurrencyResolution", - "properties": { - "ISO4217": { - "type": "string", - "description": "The alphabetic code based on another ISO standard, ISO 3166, which lists the codes for country names. The first two letters of the ISO 4217 three-letter code are the same as the code for the country name, and, where possible, the third letter corresponds to the first letter of the currency name." - }, - "value": { - "type": "number", - "format": "double", - "description": "The money amount captured in the extracted entity" - }, - "unit": { - "type": "string", - "description": "The unit of the amount captured in the extracted entity" - } - }, - "required": [ - "value", - "unit" - ] - }, - "BooleanResolution": { - "description": "A resolution for boolean expressions", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "BooleanResolution", - "properties": { - "value": { - "type": "boolean" - } - }, - "required": [ - "value" - ] - }, - "DateTimeResolution": { - "description": "A resolution for datetime entity instances.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "DateTimeResolution", - "properties": { - "timex": { - "$ref": "#/definitions/TimeExpression" - }, - "dateTimeSubKind": { - "type": "string", - "enum": [ - "Time", - "Date", - "DateTime", - "Duration", - "Set" - ], - "x-ms-enum": { - "name": "DateTimeSubKind", - "modelAsString": true - }, - "description": "The DateTime SubKind" - }, - "value": { - "type": "string", - "description": "The actual time that the extracted text denote." - }, - "modifier": { - "$ref": "#/definitions/TemporalModifier" - } - }, - "required": [ - "timex", - "dateTimeSubKind", - "value" - ] - }, - "NumberResolution": { - "description": "A resolution for numeric entity instances.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "NumberResolution", - "properties": { - "numberKind": { - "type": "string", - "enum": [ - "Integer", - "Decimal", - "Power", - "Fraction", - "Percent", - "Unspecified" - ], - "x-ms-enum": { - "name": "NumberKind", - "modelAsString": true - }, - "description": "The type of the extracted number entity." - }, - "value": { - "type": "number", - "format": "double", - "description": "A numeric representation of what the extracted text denotes." - } - }, - "required": [ - "numberKind", - "value" - ] - }, - "OrdinalResolution": { - "description": "A resolution for ordinal numbers entity instances.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "OrdinalResolution", - "properties": { - "offset": { - "type": "string", - "description": "The offset With respect to the reference (e.g., offset = -1 in \"show me the second to last\"" - }, - "relativeTo": { - "type": "string", - "enum": [ - "Current", - "End", - "Start" - ], - "x-ms-enum": { - "name": "RelativeTo", - "modelAsString": true - }, - "description": "The reference point that the ordinal number denotes." - }, - "value": { - "type": "string", - "description": "A simple arithmetic expression that the ordinal denotes." - } - }, - "required": [ - "offset", - "relativeTo", - "value" - ] - }, - "TemporalSpanResolution": { - "description": "represents the resolution of a date and/or time span.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "TemporalSpanResolution", - "properties": { - "begin": { - "$ref": "#/definitions/TimeExpression" - }, - "end": { - "$ref": "#/definitions/TimeExpression" - }, - "duration": { - "type": "string", - "description": "An optional duration value formatted based on the ISO 8601 (https://en.wikipedia.org/wiki/ISO_8601#Durations)" - }, - "modifier": { - "$ref": "#/definitions/TemporalModifier" - }, - "timex": { - "type": "string", - "description": "An optional triplet containing the beginning, the end, and the duration all stated as ISO 8601 formatted strings." - } - } - }, - "NumericRangeResolution": { - "description": "represents the resolution of numeric intervals.", - "allOf": [ - { - "$ref": "#/definitions/BaseResolution" - } - ], - "type": "object", - "x-ms-discriminator-value": "NumericRangeResolution", - "properties": { - "rangeKind": { - "type": "string", - "enum": [ - "Number", - "Speed", - "Weight", - "Length", - "Volume", - "Area", - "Age", - "Information", - "Temperature", - "Currency" - ], - "x-ms-enum": { - "name": "RangeKind", - "modelAsString": true - }, - "description": "The kind of range that the resolution object represents." - }, - "minimum": { - "type": "number", - "format": "double", - "description": "The beginning value of the interval." - }, - "maximum": { - "type": "number", - "format": "double", - "description": "The ending value of the interval." - } - }, - "required": [ - "rangeKind", - "minimum", - "maximum" - ] - }, - "TemporalModifier": { - "type": "string", - "description": "An optional modifier of a date/time instance.", - "enum": [ - "AfterApprox", - "Before", - "BeforeStart", - "Approx", - "ReferenceUndefined", - "SinceEnd", - "AfterMid", - "Start", - "After", - "BeforeEnd", - "Until", - "End", - "Less", - "Since", - "AfterStart", - "BeforeApprox", - "Mid", - "More" - ], - "x-ms-enum": { - "name": "TemporalModifier", - "modelAsString": true - } - }, - "TimeExpression": { - "type": "string", - "description": "An extended ISO 8601 date/time representation as described in (https://github.com/Microsoft/Recognizers-Text/blob/master/Patterns/English/English-DateTime.yaml)" - }, - "BaseExtraInformation": { - "description": "The abstract base object for entity extra information.", - "type": "object", - "discriminator": "extraInformationKind", - "properties": { - "extraInformationKind": { - "description": "The extra information object kind.", - "type": "string", - "enum": [ - "EntitySubtype", - "ListKey", - "RegexKey" - ], - "x-ms-enum": { - "name": "ExtraInformationKind", - "modelAsString": true - } - } - }, - "required": [ - "extraInformationKind" - ] - }, - "EntitySubtype": { - "description": "The concrete entity Subtype model of extra information.", - "allOf": [ - { - "$ref": "#/definitions/BaseExtraInformation" - } - ], - "type": "object", - "x-ms-discriminator-value": "EntitySubtype", - "properties": { - "value": { - "type": "string", - "description": "The Subtype of an extracted entity type." - } - } - }, - "ListKey": { - "description": "The list key extra data kind.", - "allOf": [ - { - "$ref": "#/definitions/BaseExtraInformation" - } - ], - "type": "object", - "x-ms-discriminator-value": "ListKey", - "properties": { - "key": { - "type": "string", - "description": "The canonical form of the extracted entity." - } - } - }, - "RegexKey": { - "description": "The regex key extra data kind.", - "allOf": [ - { - "$ref": "#/definitions/BaseExtraInformation" - } - ], - "type": "object", - "x-ms-discriminator-value": "RegexKey", - "properties": { - "key": { - "type": "string", - "description": "The key of the regex pattern used in extracting the entity." - }, - "regexPattern": { - "type": "string", - "description": "The .NET regex pattern used in extracting the entity. Please visit https://docs.microsoft.com/dotnet/standard/base-types/regular-expressions for more information about .NET regular expressions." - } - } - }, - "LuisTargetIntentResult": { - "type": "object", - "description": "It is a wrap up of LUIS Generally Available response.", - "x-ms-discriminator-value": "Luis", - "allOf": [ - { - "$ref": "#/definitions/TargetIntentResult" - } - ], - "properties": { - "result": { - "type": "object", - "description": "The actual response from a LUIS Generally Available application." - } - } - }, - "QuestionAnsweringTargetIntentResult": { - "type": "object", - "description": "It is a wrap up a Question Answering KB response.", - "x-ms-discriminator-value": "QuestionAnswering", - "allOf": [ - { - "$ref": "#/definitions/TargetIntentResult" - } - ], - "properties": { - "result": { - "description": "The generated answer by a Question Answering KB.", - "$ref": "common.json#/definitions/AnswersResult" - } - } - }, - "AnalyzeConversationJobsInput": { - "type": "object", - "properties": { - "displayName": { - "description": "Display name for the analysis job.", - "type": "string" - }, - "analysisInput": { - "$ref": "#/definitions/MultiLanguageConversationAnalysisInput" - }, - "tasks": { - "description": "Set of tasks to execute on the input conversation.", - "type": "array", - "items": { - "$ref": "#/definitions/AnalyzeConversationLROTask" - } - } - }, - "required": [ - "analysisInput", - "tasks" - ] - }, - "AnalyzeConversationLROTask": { - "type": "object", - "description": "Base class for a long-running conversation input task.", - "discriminator": "kind", - "required": [ - "kind" - ], - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeConversationLROTaskKind" - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/TaskIdentifier" - } - ] - }, - "AnalyzeConversationLROTaskKind": { - "type": "string", - "description": "Enumeration of supported analysis tasks on a collection of conversations.", - "enum": [ - "ConversationalSummarizationTask" - ], - "x-ms-enum": { - "name": "AnalyzeConversationLROTaskKind", - "modelAsString": true - } - }, - "AnalyzeConversationResultsKind": { - "type": "string", - "description": "Enumeration of results for supported conversation analysis tasks.", - "enum": [ - "ConversationalSummarizationResults" - ], - "x-ms-enum": { - "name": "AnalyzeConversationResultsKind", - "modelAsString": true - } - }, - "AnalyzeConversationJobState": { - "description": "Contains the status of the submitted job for analyzing a conversation, along with related statistics.", - "allOf": [ - { - "$ref": "common.json#/definitions/JobState" - }, - { - "$ref": "#/definitions/ConversationTasksState" - }, - { - "$ref": "#/definitions/AnalyzeConversationJobStatistics" - } - ] - }, - "AnalyzeConversationJobStatistics": { - "description": "Contains the statistics for the submitted job.", - "properties": { - "statistics": { - "$ref": "#/definitions/ConversationRequestStatistics" - } - }, - "type": "object" - }, - "ConversationTasksState": { - "description": "Contains the state for the tasks that are being executed as part of the submitted job for analyzing a conversation.", - "properties": { - "tasks": { - "properties": { - "completed": { - "description": "Count of tasks that finished successfully.", - "type": "integer", - "format": "int32" - }, - "failed": { - "description": "Count of tasks that failed.", - "type": "integer", - "format": "int32" - }, - "inProgress": { - "description": "Count of tasks that are currently in progress.", - "type": "integer", - "format": "int32" - }, - "total": { - "description": "Total count of tasks submitted as part of the job.", - "type": "integer", - "format": "int32" - }, - "items": { - "description": "List of results from tasks (if available).", - "type": "array", - "items": { - "$ref": "#/definitions/AnalyzeConversationJobResult" - } - } - }, - "required": [ - "total", - "completed", - "failed", - "inProgress" - ], - "type": "object" - } - }, - "required": [ - "tasks" - ], - "type": "object" - }, - "AnalyzeConversationJobResult": { - "type": "object", - "description": "Container for results of all tasks in the conversation job.", - "discriminator": "kind", - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeConversationResultsKind" - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/TaskState" - }, - { - "$ref": "common.json#/definitions/TaskIdentifier" - } - ], - "required": [ - "kind" - ] - }, - "MultiLanguageConversationAnalysisInput": { - "type": "object", - "required": [ - "conversations" - ], - "properties": { - "conversations": { - "type": "array", - "items": { - "$ref": "#/definitions/Conversation" - } - } - } - }, - "Conversation": { - "type": "object", - "description": "Complete ordered set of utterances (spoken or written) by one or more speakers to be used for analysis.", - "discriminator": "modality", - "required": [ - "id", - "language", - "modality" - ], - "properties": { - "id": { - "description": "Unique identifier for the conversation.", - "type": "string" - }, - "language": { - "description": "Language of the conversation item in BCP-47 format.", - "type": "string" - }, - "modality": { - "$ref": "#/definitions/Modality" - }, - "domain": { - "$ref": "#/definitions/ConversationDomain" - } - } - }, - "Modality": { - "type": "string", - "description": "Enumeration of supported conversational modalities.", - "enum": [ - "transcript", - "text" - ], - "x-ms-enum": { - "name": "InputModality", - "modelAsString": true - } - }, - "ConversationDomain": { - "type": "string", - "description": "Enumeration of supported conversational domains.", - "enum": [ - "finance", - "healthcare", - "generic" - ], - "x-ms-enum": { - "name": "ConversationDomain", - "modelAsString": true - } - }, - "TextConversation": { - "type": "object", - "x-ms-discriminator-value": "text", - "required": [ - "conversationItems" - ], - "allOf": [ - { - "$ref": "#/definitions/Conversation" - } - ], - "properties": { - "conversationItems": { - "description": "Ordered list of text conversation items in the conversation.", - "type": "array", - "items": { - "$ref": "#/definitions/TextConversationItem" - } - } - } - }, - "TranscriptConversation": { - "type": "object", - "x-ms-discriminator-value": "transcript", - "required": [ - "conversationItems" - ], - "allOf": [ - { - "$ref": "#/definitions/Conversation" - } - ], - "properties": { - "conversationItems": { - "description": "Ordered list of transcript conversation items in the conversation.", - "type": "array", - "items": { - "$ref": "#/definitions/TranscriptConversationItem" - } - } - } - }, - "TranscriptConversationItem": { - "type": "object", - "description": "Additional properties for supporting transcript conversation.", - "required": [ - "text", - "lexical", - "itn", - "maskedItn" - ], - "allOf": [ - { - "$ref": "#/definitions/ConversationItemBase" - } - ], - "properties": { - "itn": { - "type": "string", - "description": "Inverse text normalization (ITN) representation of input. The inverse-text-normalized form is the recognized text from Microsoft's speech-to-text API, with phone numbers, numbers, abbreviations, and other transformations applied." - }, - "maskedItn": { - "type": "string", - "description": "Inverse-text-normalized format with profanity masking applied." - }, - "text": { - "type": "string", - "description": "Display form of the recognized text from the speech-to-text API, with punctuation and capitalization added." - }, - "lexical": { - "type": "string", - "description": "Lexical form of the recognized text from the speech-to-text API, with the actual words recognized." - }, - "wordLevelTimings": { - "type": "array", - "description": "List of word-level audio timing information.", - "items": { - "$ref": "#/definitions/WordLevelTiming" - } - }, - "conversationItemLevelTiming": { - "description": "Audio timing at the conversation item level. This still can help with AI quality if word-level audio timings are not available.", - "$ref": "#/definitions/ConversationItemLevelTiming" - } - } - }, - "RedactedTranscriptContent": { - "type": "object", - "description": "Transcript content response that the service generates, with all necessary personally identifiable information redacted.", - "properties": { - "itn": { - "type": "string", - "description": "Redacted output for input in inverse-text-normalized format." - }, - "maskedItn": { - "type": "string", - "description": "Redacted output for input in masked inverse-text-normalized format." - }, - "text": { - "type": "string", - "description": "Redacted output for input in text (Microsoft's speech-to-text 'display') format." - }, - "lexical": { - "type": "string", - "description": "Redacted output for input in lexical format." - }, - "audioTimings": { - "type": "array", - "description": "List of redacted audio segments.", - "items": { - "$ref": "#/definitions/AudioTiming" - } - } - } - }, - "WordLevelTiming": { - "type": "object", - "description": "Word-level timing information that the speech-to-text API generates. The words in this object should have 1:1 correspondence with the lexical input to allow for audio redaction.", - "properties": { - "word": { - "type": "string", - "description": "Recognized word." - } - }, - "allOf": [ - { - "$ref": "#/definitions/AudioTiming" - } - ] - }, - "AudioTiming": { - "type": "object", - "description": "Audio timing information.", - "properties": { - "offset": { - "description": "Offset from the start of speech audio, in ticks. 1 tick = 100 nanoseconds.", - "type": "integer", - "format": "int64" - }, - "duration": { - "description": "Duration of word articulation, in ticks. 1 tick = 100 nanoseconds.", - "type": "integer", - "format": "int64" - } - } - }, - "ConversationResultBase": { - "type": "object", - "description": "Shared attributes for all conversational task results.", - "required": [ - "id", - "warnings" - ], - "properties": { - "id": { - "type": "string", - "description": "Unique, non-empty conversation identifier." - }, - "warnings": { - "type": "array", - "description": "Warnings encountered in processing the document.", - "items": { - "$ref": "common.json#/definitions/InputWarning" - } - }, - "statistics": { - "$ref": "#/definitions/ConversationStatistics" - } - } - }, - "ConversationStatistics": { - "type": "object", - "description": "If showStats=true was specified in the request, this field contains information about the conversation payload.", - "required": [ - "transactionsCount" - ], - "properties": { - "transactionsCount": { - "type": "integer", - "format": "int32", - "description": "Number of text units for the request." - } - } - }, - "ConversationRequestStatistics": { - "type": "object", - "description": "if showStats=true was specified in the request, this field contains information about the request payload.", - "required": [ - "conversationsCount", - "validConversationsCount", - "erroneousConversationsCount" - ], - "properties": { - "conversationsCount": { - "type": "integer", - "format": "int32", - "description": "Number of conversations submitted in the request." - }, - "validConversationsCount": { - "type": "integer", - "format": "int32", - "description": "Number of conversation documents. This excludes documents that are empty, over the size limit, or in unsupported languages." - }, - "erroneousConversationsCount": { - "type": "integer", - "format": "int32", - "description": "Number of invalid documents. This includes documents that are empty, over the size limit, or in unsupported languages." - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/RequestStatistics" - } - ] - }, - "ConversationItemLevelTiming": { - "type": "object", - "description": "Audio timing at the conversation item level.", - "allOf": [ - { - "$ref": "#/definitions/AudioTiming" - } - ] - }, - "AnalyzeConversationSummarizationTask": { - "type": "object", - "description": "Task definition for conversational summarization.", - "properties": { - "parameters": { - "$ref": "#/definitions/ConversationSummarizationTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeConversationLROTask" - } - ], - "x-ms-discriminator-value": "ConversationalSummarizationTask" - }, - "ConversationSummarizationTaskParameters": { - "type": "object", - "description": "Supported parameters for a conversational summarization task.", - "required": [ - "summaryAspects" - ], - "properties": { - "summaryAspects": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "issue", - "resolution", - "chapterTitle", - "narrative" - ], - "x-ms-enum": { - "name": "SummaryAspect", - "modelAsString": true, - "values": [ - { - "value": "issue", - "description": "Summary of issues in transcripts of web chats and service calls between customer-service agents and customers." - }, - { - "value": "resolution", - "description": "Summary of resolutions in transcripts of web chats and service calls between customer-service agents and customers." - }, - { - "value": "chapterTitle", - "description": "Chapter title of any conversation. It's usually one phrase or several phrases naturally combined. Long conversations tend to have more chapters. You can find the chapter boundary from the summary context." - }, - { - "value": "narrative", - "description": "Generic narrative summary of any conversation. It generally converts the conversational language into formal written language, compresses the text length, and keeps the salient information." - } - ] - } - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltTaskParameters" - }, - { - "$ref": "common.json#/definitions/AbstractiveSummarizationTaskParametersBase" - } - ] - }, - "AnalyzeConversationSummarizationResult": { - "type": "object", - "description": "Result for the summarization task on the conversation.", - "properties": { - "results": { - "$ref": "#/definitions/SummaryResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeConversationJobResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "ConversationalSummarizationResults" - }, - "SummaryResult": { - "type": "object", - "required": [ - "conversations" - ], - "properties": { - "conversations": { - "type": "array", - "items": { - "$ref": "#/definitions/ConversationsSummaryResult" - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ] - }, - "ConversationsSummaryResult": { - "type": "object", - "required": [ - "summaries" - ], - "properties": { - "summaries": { - "type": "array", - "items": { - "$ref": "#/definitions/SummaryResultItem" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/ConversationResultBase" - } - ] - }, - "SummaryResultItem": { - "type": "object", - "properties": { - "aspect": { - "type": "string" - }, - "text": { - "type": "string" - }, - "contexts": { - "type": "array", - "description": "Context list of the summary.", - "items": { - "$ref": "#/definitions/ItemizedSummaryContext" - } - } - }, - "required": [ - "aspect", - "text" - ] - }, - "ItemizedSummaryContext": { - "type": "object", - "description": "Context of the summary with a conversation item ID.", - "required": [ - "conversationItemId" - ], - "properties": { - "conversationItemId": { - "type": "string", - "description": "Reference to the ID of ConversationItem." - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/SummaryContext" - } - ] - } - }, - "parameters": { - "ConversationAnalysisOptions": { - "name": "ConversationAnalysisOptions", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/ConversationAnalysisOptions" - }, - "description": "Post body of the request.", - "x-ms-parameter-location": "method" - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/analyzedocuments.json b/dev/cognitiveservices/data-plane/Language/analyzedocuments.json deleted file mode 100644 index 56a63b03726c..000000000000 --- a/dev/cognitiveservices/data-plane/Language/analyzedocuments.json +++ /dev/null @@ -1,2235 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Microsoft Cognitive Language Service - Document Analysis", - "description": "The language service API is a suite of natural language processing (NLP) skills built with best-in-class Microsoft machine learning algorithms. The API can be used to analyze unstructured text for tasks such as sentiment analysis, key phrase extraction, language detection and question answering. Further documentation can be found in https://docs.microsoft.com/azure/cognitive-services/language-service/overview.0", - "version": "2023-11-15-preview" - }, - "securityDefinitions": { - "AADToken": { - "type": "oauth2", - "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", - "flow": "implicit", - "description": "These are the [Azure Active Directory OAuth2](https://docs.microsoft.com/azure/active-directory/develop/v1-overview) Flows. When paired with [Azure role-based access](https://docs.microsoft.com/azure/role-based-access-control/overview) control it can be used to control access to Azure Maps REST APIs. Azure role-based access controls are used to designate access to one or more Azure Maps resource account or sub-resources. Any user, group, or service principal can be granted access via a built-in role or a custom role composed of one or more permissions to Azure Maps REST APIs.\n\nTo implement scenarios, we recommend viewing [authentication concepts](https://aka.ms/amauth). In summary, this security definition provides a solution for modeling application(s) via objects capable of access control on specific APIs and scopes.\n\n#### Notes\n* This security definition **requires** the use of the `x-ms-client-id` header to indicate which Azure Maps resource the application is requesting access to. This can be acquired from the [Maps management API](https://aka.ms/amauthdetails).\n* \nThe `Authorization URL` is specific to the Azure public cloud instance. Sovereign clouds have unique Authorization URLs and Azure Active directory configurations. \n* \nThe Azure role-based access control is configured from the [Azure management plane](https://aka.ms/amrbac) via Azure portal, PowerShell, CLI, Azure SDKs, or REST APIs.\n* \nUsage of the [Azure Maps Web SDK](https://aka.ms/amaadmc) allows for configuration based setup of an application for multiple use cases.\n* Currently, Azure Active Directory [v1.0 or v2.0](https://docs.microsoft.com/azure/active-directory/develop/azure-ad-endpoint-comparison) supports Work, School, and Guests but does not support Personal accounts.", - "scopes": { - "https://cognitiveservices.azure.com/.default": "https://cognitiveservices.azure.com/.default" - } - }, - "apim_key": { - "type": "apiKey", - "description": "A subscription key for a Language service resource.", - "name": "Ocp-Apim-Subscription-Key", - "in": "header" - } - }, - "security": [ - { - "AADToken": [ - "https://cognitiveservices.azure.com/.default" - ] - }, - { - "apim_key": [] - } - ], - "x-ms-parameterized-host": { - "hostTemplate": "{Endpoint}/language", - "useSchemePrefix": false, - "parameters": [ - { - "name": "Endpoint", - "in": "path", - "description": "Supported Cognitive Services endpoint (e.g., https://.api.cognitiveservices.azure.com).", - "required": true, - "type": "string" - } - ] - }, - "paths": { - "/analyze-documents/jobs": { - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "description": "Submit a collection of documents for analysis. Specify one or more unique tasks to be executed as a long-running operation.", - "operationId": "AnalyzeDocuments_SubmitJob", - "summary": "Submit document analysis job", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "description": "Collection of documents to analyze and one or more tasks to execute.", - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/AnalyzeDocumentJobsInput" - }, - "required": true - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the analysis job.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-long-running-operation": true, - "x-ms-examples": { - "Successful PII redaction Request": { - "$ref": "./examples/analyzedocuments/SuccessfulPiiTaskSubmit.json" - } - } - } - }, - "/analyze-documents/jobs/{jobId}": { - "get": { - "produces": [ - "application/json" - ], - "description": "Get the status of an analysis job. A job may consist of one or more tasks. Once all tasks are succeeded, the job will transition to the succeeded state and results will be available for each task.", - "operationId": "AnalyzeDocuments_JobStatus", - "summary": "Get analysis status and results", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobId" - }, - { - "$ref": "#/parameters/ShowStats" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - } - ], - "responses": { - "200": { - "description": "Analysis job status and metadata.", - "schema": { - "$ref": "#/definitions/AnalyzeDocumentsJobState" - } - }, - "default": { - "description": "Unexpected error", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful PII redaction Result": { - "$ref": "./examples/analyzedocuments/SuccessfulPiiTaskResult.json" - } - } - } - }, - "/analyze-documents/jobs/{jobId}:cancel": { - "post": { - "produces": [ - "application/json" - ], - "description": "Cancel a long-running Document Analysis job.", - "operationId": "AnalyzeDocuments_CancelJob", - "summary": "Cancel a long-running Document Analysis job", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobId" - } - ], - "responses": { - "202": { - "description": "Cancel Job request has been received.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Unexpected error", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-long-running-operation": true, - "x-ms-examples": { - "Successful job cancellation request": { - "$ref": "./examples/analyzedocuments/SuccessfulAnalyzeTextJobsCancelRequest.json" - } - } - } - } - }, - "definitions": { - "AnalyzeDocumentJobsInput": { - "type": "object", - "properties": { - "displayName": { - "description": "Optional display name for the analysis job.", - "type": "string" - }, - "analysisInput": { - "$ref": "#/definitions/MultiLanguageAnalysisInput" - }, - "tasks": { - "description": "The set of tasks to execute on the input documents.", - "type": "array", - "items": { - "$ref": "#/definitions/AnalyzeDocumentsLROTask" - } - } - }, - "required": [ - "analysisInput", - "tasks" - ] - }, - "MultiLanguageAnalysisInput": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "items": { - "$ref": "#/definitions/MultiLanguageInput" - } - } - } - }, - "MultiLanguageInput": { - "type": "object", - "description": "Contains an input document to be analyzed by the service.", - "required": [ - "id", - "source", - "target" - ], - "properties": { - "id": { - "type": "string", - "description": "A unique, non-empty document identifier." - }, - "source": { - "$ref": "#/definitions/DocumentLocation" - }, - "target": { - "$ref": "#/definitions/DocumentLocation" - }, - "language": { - "type": "string", - "description": "(Optional) This is the 2 letter ISO 639-1 representation of a language. For example, use \"en\" for English; \"es\" for Spanish etc. For Auto Language Detection, use \"auto\". If not set, use \"en\" for English as default." - } - } - }, - "DocumentLocation": { - "type": "object", - "discriminator": "kind", - "properties": { - "kind": { - "$ref": "#/definitions/DocumentLocationKind" - } - }, - "required": [ - "kind" - ] - }, - "AzureBlobDocumentLocation": { - "type": "object", - "properties": { - "location": { - "description": "Location of the file to process", - "type": "string", - "example": "https://myblob.blob.core.windows.net/Container/document.pdf" - }, - "managedIdentityClientId": { - "description": "The managed identity client Id to use to authenticate with the storage account", - "type": "string" - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentLocation" - } - ], - "required": [ - "location" - ], - "x-ms-discriminator-value": "AzureBlob" - }, - "DocumentLocationKind": { - "type": "string", - "description": "Enumeration of supported document locations.", - "enum": [ - "AzureBlob" - ], - "x-ms-enum": { - "name": "DocumentLocationKind", - "modelAsString": true, - "values": [ - { - "name": "AzureBlob", - "value": "AzureBlob", - "description": "The document is a URL." - } - ] - } - }, - "AnalyzeDocumentsLROTask": { - "type": "object", - "discriminator": "kind", - "required": [ - "kind" - ], - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeDocumentsLROTaskKind" - } - }, - "allOf": [ - { - "$ref": "#/definitions/TaskIdentifier" - } - ] - }, - "AnalyzeDocumentsLROTaskKind": { - "type": "string", - "description": "Enumeration of supported long-running Document Analysis tasks.", - "enum": [ - "PiiEntityRecognition", - "ExtractiveSummarization", - "AbstractiveSummarization" - ], - "x-ms-enum": { - "name": "AnalyzeDocumentsLROTaskKind", - "modelAsString": true - } - }, - "AnalyzeDocumentsJobState": { - "allOf": [ - { - "$ref": "common.json#/definitions/JobState" - }, - { - "$ref": "#/definitions/TasksState" - }, - { - "$ref": "#/definitions/AnalyzeDocumentsJobStatistics" - } - ] - }, - "AnalyzeDocumentsJobStatistics": { - "properties": { - "statistics": { - "$ref": "common.json#/definitions/RequestStatistics" - } - }, - "type": "object" - }, - "TasksState": { - "properties": { - "tasks": { - "properties": { - "completed": { - "type": "integer", - "format": "int64" - }, - "failed": { - "type": "integer", - "format": "int64" - }, - "inProgress": { - "type": "integer", - "format": "int64" - }, - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/AnalyzeDocumentsLROResult" - } - } - }, - "required": [ - "total", - "completed", - "failed", - "inProgress" - ], - "type": "object" - } - }, - "required": [ - "tasks" - ], - "type": "object" - }, - "AnalyzeDocumentsLROResult": { - "type": "object", - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeDocumentsLROResultsKind" - }, - "results": { - "$ref": "#/definitions/AnalyzeDocumentsLROTaskResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/TaskState" - }, - { - "$ref": "#/definitions/TaskIdentifier" - } - ], - "required": [ - "kind" - ] - }, - "AnalyzeDocumentsLROResultsKind": { - "type": "string", - "description": "Enumeration of supported Document Analysis long-running operation task results.", - "enum": [ - "PiiEntityRecognitionLROResults", - "ExtractiveSummarizationLROResults", - "AbstractiveSummarizationLROResults" - ], - "x-ms-enum": { - "name": "AnalyzeDocumentsLROResultsKind", - "modelAsString": true - } - }, - "TaskState": { - "properties": { - "lastUpdateDateTime": { - "format": "date-time", - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "notStarted", - "running", - "succeeded", - "failed", - "cancelled", - "cancelling" - ], - "x-ms-enum": { - "modelAsString": true, - "name": "State" - } - } - }, - "required": [ - "status", - "lastUpdateDateTime" - ], - "type": "object" - }, - "AnalyzeDocumentsLROTaskResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "$ref": "#/definitions/DocumentResultWithDetectedLanguage" - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "DocumentResultWithDetectedLanguage": { - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - }, - { - "$ref": "#/definitions/DocumentDetectedLanguage" - } - ] - }, - "DocumentDetectedLanguage": { - "type": "object", - "properties": { - "detectedLanguage": { - "$ref": "#/definitions/DetectedLanguage", - "description": "If 'language' is set to 'auto' for the document in the request this field will contain a 2 letter ISO 639-1 representation of the language detected for this document." - } - } - }, - "DetectedLanguage": { - "type": "object", - "required": [ - "name", - "iso6391Name", - "confidenceScore" - ], - "properties": { - "name": { - "type": "string", - "description": "Long name of a detected language (e.g. English, French)." - }, - "iso6391Name": { - "type": "string", - "description": "A two letter representation of the detected language according to the ISO 639-1 standard (e.g. en, fr)." - }, - "confidenceScore": { - "type": "number", - "format": "double", - "description": "A confidence score between 0 and 1. Scores close to 1 indicate 100% certainty that the identified language is true." - } - } - }, - "DocumentResult": { - "type": "object", - "required": [ - "id", - "warnings" - ], - "properties": { - "id": { - "type": "string", - "description": "Unique, non-empty document identifier." - }, - "warnings": { - "type": "array", - "description": "Warnings encountered while processing document.", - "items": { - "$ref": "#/definitions/DocumentWarning" - } - }, - "statistics": { - "description": "if showStats=true was specified in the request this field will contain information about the document payload.", - "$ref": "#/definitions/DocumentStatistics" - }, - "source": { - "$ref": "#/definitions/DocumentLocation", - "description": "The location of the input document." - }, - "target": { - "type": "array", - "description": "The location of the result files.", - "items": { - "$ref": "#/definitions/DocumentLocation" - } - } - } - }, - "DocumentWarning": { - "type": "object", - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "type": "string", - "enum": [ - "LongWordsInDocument", - "DocumentTruncated" - ], - "x-ms-enum": { - "name": "WarningCodeValue", - "modelAsString": true - }, - "description": "Error code." - }, - "message": { - "type": "string", - "description": "Warning message." - }, - "targetRef": { - "type": "string", - "description": "A JSON pointer reference indicating the target object." - } - } - }, - "DocumentStatistics": { - "type": "object", - "required": [ - "charactersCount", - "transactionsCount" - ], - "properties": { - "charactersCount": { - "type": "integer", - "format": "int32", - "description": "Number of text elements recognized in the document." - }, - "transactionsCount": { - "type": "integer", - "format": "int32", - "description": "Number of transactions for the document." - } - }, - "description": "if showStats=true was specified in the request this field will contain information about the document payload." - }, - "PiiLROTask": { - "type": "object", - "description": "An object representing the task definition for a PII Entities Recognition task.", - "properties": { - "parameters": { - "$ref": "#/definitions/PiiTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeDocumentsLROTask" - } - ], - "x-ms-discriminator-value": "PiiEntityRecognition" - }, - "PiiCategory": { - "type": "string", - "description": "(Optional) describes the PII categories to return", - "enum": [ - "ABARoutingNumber", - "ARNationalIdentityNumber", - "AUBankAccountNumber", - "AUDriversLicenseNumber", - "AUMedicalAccountNumber", - "AUPassportNumber", - "AUTaxFileNumber", - "AUBusinessNumber", - "AUCompanyNumber", - "ATIdentityCard", - "ATTaxIdentificationNumber", - "ATValueAddedTaxNumber", - "AzureDocumentDBAuthKey", - "AzureIAASDatabaseConnectionAndSQLString", - "AzureIoTConnectionString", - "AzurePublishSettingPassword", - "AzureRedisCacheString", - "AzureSAS", - "AzureServiceBusString", - "AzureStorageAccountKey", - "AzureStorageAccountGeneric", - "BENationalNumber", - "BENationalNumberV2", - "BEValueAddedTaxNumber", - "BRCPFNumber", - "BRLegalEntityNumber", - "BRNationalIDRG", - "BGUniformCivilNumber", - "CABankAccountNumber", - "CADriversLicenseNumber", - "CAHealthServiceNumber", - "CAPassportNumber", - "CAPersonalHealthIdentification", - "CASocialInsuranceNumber", - "CLIdentityCardNumber", - "CNResidentIdentityCardNumber", - "CreditCardNumber", - "HRIdentityCardNumber", - "HRNationalIDNumber", - "HRPersonalIdentificationNumber", - "HRPersonalIdentificationOIBNumberV2", - "CYIdentityCard", - "CYTaxIdentificationNumber", - "CZPersonalIdentityNumber", - "CZPersonalIdentityV2", - "DKPersonalIdentificationNumber", - "DKPersonalIdentificationV2", - "DrugEnforcementAgencyNumber", - "EEPersonalIdentificationCode", - "EUDebitCardNumber", - "EUDriversLicenseNumber", - "EUGPSCoordinates", - "EUNationalIdentificationNumber", - "EUPassportNumber", - "EUSocialSecurityNumber", - "EUTaxIdentificationNumber", - "FIEuropeanHealthNumber", - "FINationalID", - "FINationalIDV2", - "FIPassportNumber", - "FRDriversLicenseNumber", - "FRHealthInsuranceNumber", - "FRNationalID", - "FRPassportNumber", - "FRSocialSecurityNumber", - "FRTaxIdentificationNumber", - "FRValueAddedTaxNumber", - "DEDriversLicenseNumber", - "DEPassportNumber", - "DEIdentityCardNumber", - "DETaxIdentificationNumber", - "DEValueAddedNumber", - "GRNationalIDCard", - "GRNationalIDV2", - "GRTaxIdentificationNumber", - "HKIdentityCardNumber", - "HUValueAddedNumber", - "HUPersonalIdentificationNumber", - "HUTaxIdentificationNumber", - "INPermanentAccount", - "INUniqueIdentificationNumber", - "IDIdentityCardNumber", - "InternationalBankingAccountNumber", - "IEPersonalPublicServiceNumber", - "IEPersonalPublicServiceNumberV2", - "ILBankAccountNumber", - "ILNationalID", - "ITDriversLicenseNumber", - "ITFiscalCode", - "ITValueAddedTaxNumber", - "JPBankAccountNumber", - "JPDriversLicenseNumber", - "JPPassportNumber", - "JPResidentRegistrationNumber", - "JPSocialInsuranceNumber", - "JPMyNumberCorporate", - "JPMyNumberPersonal", - "JPResidenceCardNumber", - "LVPersonalCode", - "LTPersonalCode", - "LUNationalIdentificationNumberNatural", - "LUNationalIdentificationNumberNonNatural", - "MYIdentityCardNumber", - "MTIdentityCardNumber", - "MTTaxIDNumber", - "NLCitizensServiceNumber", - "NLCitizensServiceNumberV2", - "NLTaxIdentificationNumber", - "NLValueAddedTaxNumber", - "NZBankAccountNumber", - "NZDriversLicenseNumber", - "NZInlandRevenueNumber", - "NZMinistryOfHealthNumber", - "NZSocialWelfareNumber", - "NOIdentityNumber", - "PHUnifiedMultiPurposeIDNumber", - "PLIdentityCard", - "PLNationalID", - "PLNationalIDV2", - "PLPassportNumber", - "PLTaxIdentificationNumber", - "PLREGONNumber", - "PTCitizenCardNumber", - "PTCitizenCardNumberV2", - "PTTaxIdentificationNumber", - "ROPersonalNumericalCode", - "RUPassportNumberDomestic", - "RUPassportNumberInternational", - "SANationalID", - "SGNationalRegistrationIdentityCardNumber", - "SKPersonalNumber", - "SITaxIdentificationNumber", - "SIUniqueMasterCitizenNumber", - "ZAIdentificationNumber", - "KRResidentRegistrationNumber", - "ESDNI", - "ESSocialSecurityNumber", - "ESTaxIdentificationNumber", - "SQLServerConnectionString", - "SENationalID", - "SENationalIDV2", - "SEPassportNumber", - "SETaxIdentificationNumber", - "SWIFTCode", - "CHSocialSecurityNumber", - "TWNationalID", - "TWPassportNumber", - "TWResidentCertificate", - "THPopulationIdentificationCode", - "TRNationalIdentificationNumber", - "UKDriversLicenseNumber", - "UKElectoralRollNumber", - "UKNationalHealthNumber", - "UKNationalInsuranceNumber", - "UKUniqueTaxpayerNumber", - "USUKPassportNumber", - "USBankAccountNumber", - "USDriversLicenseNumber", - "USIndividualTaxpayerIdentification", - "USSocialSecurityNumber", - "UAPassportNumberDomestic", - "UAPassportNumberInternational", - "Organization", - "Email", - "URL", - "Age", - "PhoneNumber", - "IPAddress", - "Date", - "Person", - "Address", - "All", - "Default" - ], - "x-ms-enum": { - "name": "PiiCategory", - "modelAsString": true, - "values": [ - { - "name": "ABARoutingNumber", - "value": "ABARoutingNumber", - "description": "ABA Routing number" - }, - { - "name": "ARNationalIdentityNumber", - "value": "ARNationalIdentityNumber", - "description": "AR National Identity Number" - }, - { - "name": "AUBankAccountNumber", - "value": "AUBankAccountNumber", - "description": "AT Identity Card" - }, - { - "name": "AUDriversLicenseNumber", - "value": "AUDriversLicenseNumber", - "description": "AU Driver's License Number" - }, - { - "name": "AUMedicalAccountNumber", - "value": "AUMedicalAccountNumber", - "description": "AU Medical Account Number" - }, - { - "name": "AUPassportNumber", - "value": "AUPassportNumber", - "description": "AU Passport Number" - }, - { - "name": "AUTaxFileNumber", - "value": "AUTaxFileNumber", - "description": "AU Tax File Number" - }, - { - "name": "AUBusinessNumber", - "value": "AUBusinessNumber", - "description": "AU Business Number" - }, - { - "name": "AUCompanyNumber", - "value": "AUCompanyNumber", - "description": "AU Company Number" - }, - { - "name": "ATIdentityCard", - "value": "ATIdentityCard", - "description": "AT Identity Card" - }, - { - "name": "ATTaxIdentificationNumber", - "value": "ATTaxIdentificationNumber", - "description": "AT Tax Identification Number" - }, - { - "name": "ATValueAddedTaxNumber", - "value": "ATValueAddedTaxNumber", - "description": "AT Value Added Tax Number" - }, - { - "name": "AzureDocumentDBAuthKey", - "value": "AzureDocumentDBAuthKey", - "description": "Azure Document DB Auth Key" - }, - { - "name": "AzureIAASDatabaseConnectionAndSQLString", - "value": "AzureIAASDatabaseConnectionAndSQLString", - "description": "Azure IAAS Database Connection And SQL String" - }, - { - "name": "AzureIoTConnectionString", - "value": "AzureIoTConnectionString", - "description": "Azure IoT Connection String" - }, - { - "name": "AzurePublishSettingPassword", - "value": "AzurePublishSettingPassword", - "description": "Azure Publish Setting Password" - }, - { - "name": "AzureRedisCacheString", - "value": "AzureRedisCacheString", - "description": "Azure Redis Cache String" - }, - { - "name": "AzureSAS", - "value": "AzureSAS", - "description": "Azure SAS" - }, - { - "name": "AzureServiceBusString", - "value": "AzureServiceBusString", - "description": "Azure Service Bus String" - }, - { - "name": "AzureStorageAccountKey", - "value": "AzureStorageAccountKey", - "description": "Azure Storage Account Key" - }, - { - "name": "AzureStorageAccountGeneric", - "value": "AzureStorageAccountGeneric", - "description": "Azure Storage Account Generic" - }, - { - "name": "BENationalNumber", - "value": "BENationalNumber", - "description": "BE National Number" - }, - { - "name": "BENationalNumberV2", - "value": "BENationalNumberV2", - "description": "BE National Number V2" - }, - { - "name": "BEValueAddedTaxNumber", - "value": "BEValueAddedTaxNumber", - "description": "BE Value Added Tax Number" - }, - { - "name": "BRCPFNumber", - "value": "BRCPFNumber", - "description": "BR CPF Number" - }, - { - "name": "BRLegalEntityNumber", - "value": "BRLegalEntityNumber", - "description": "BR Legal Entity Number" - }, - { - "name": "BRNationalIDRG", - "value": "BRNationalIDRG", - "description": "BR National ID RG" - }, - { - "name": "BGUniformCivilNumber", - "value": "BGUniformCivilNumber", - "description": "BG Uniform Civil Number" - }, - { - "name": "CABankAccountNumber", - "value": "CABankAccountNumber", - "description": "CA Bank Account Number" - }, - { - "name": "CADriversLicenseNumber", - "value": "CADriversLicenseNumber", - "description": "CA Driver's License Number" - }, - { - "name": "CAHealthServiceNumber", - "value": "CAHealthServiceNumber", - "description": "CA Health Service Number" - }, - { - "name": "CAPassportNumber", - "value": "CAPassportNumber", - "description": "CA Passport Number" - }, - { - "name": "CAPersonalHealthIdentification", - "value": "CAPersonalHealthIdentification", - "description": "CA Personal Health Identification" - }, - { - "name": "CASocialInsuranceNumber", - "value": "CASocialInsuranceNumber", - "description": "CA Social Insurance Number" - }, - { - "name": "CLIdentityCardNumber", - "value": "CLIdentityCardNumber", - "description": "CL Identity Card Number" - }, - { - "name": "CNResidentIdentityCardNumber", - "value": "CNResidentIdentityCardNumber", - "description": "CN Resident Identity Card Number" - }, - { - "name": "CreditCardNumber", - "value": "CreditCardNumber", - "description": "Credit Card Number" - }, - { - "name": "HRIdentityCardNumber", - "value": "HRIdentityCardNumber", - "description": "HR Identity Card Number" - }, - { - "name": "HRNationalIDNumber", - "value": "HRNationalIDNumber", - "description": "HR National ID Number" - }, - { - "name": "HRPersonalIdentificationNumber", - "value": "HRPersonalIdentificationNumber", - "description": "HR Personal Identification Number" - }, - { - "name": "HRPersonalIdentificationOIBNumberV2", - "value": "HRPersonalIdentificationOIBNumberV2", - "description": "HR Personal Identification OIB Number V2" - }, - { - "name": "CYIdentityCard", - "value": "CYIdentityCard", - "description": "CY Identity Card" - }, - { - "name": "CYTaxIdentificationNumber", - "value": "CYTaxIdentificationNumber", - "description": "CY Tax Identification Number" - }, - { - "name": "CZPersonalIdentityNumber", - "value": "CZPersonalIdentityNumber", - "description": "CZ Personal Identity Number" - }, - { - "name": "CZPersonalIdentityV2", - "value": "CZPersonalIdentityV2", - "description": "CZ Personal Identity V2" - }, - { - "name": "DKPersonalIdentificationNumber", - "value": "DKPersonalIdentificationNumber", - "description": "DK Personal Identification Number" - }, - { - "name": "DKPersonalIdentificationV2", - "value": "DKPersonalIdentificationV2", - "description": "DK Personal Identification V2" - }, - { - "name": "DrugEnforcementAgencyNumber", - "value": "DrugEnforcementAgencyNumber", - "description": "Drug Enforcement Agency Number" - }, - { - "name": "EEPersonalIdentificationCode", - "value": "EEPersonalIdentificationCode", - "description": "EE Personal Identification Code" - }, - { - "name": "EUDebitCardNumber", - "value": "EUDebitCardNumber", - "description": "EU Debit Card Number" - }, - { - "name": "EUDriversLicenseNumber", - "value": "EUDriversLicenseNumber", - "description": "EU Driver's License Number" - }, - { - "name": "EUGPSCoordinates", - "value": "EUGPSCoordinates", - "description": "EU GPS Coordinates" - }, - { - "name": "EUNationalIdentificationNumber", - "value": "EUNationalIdentificationNumber", - "description": "EU National Identification Number" - }, - { - "name": "EUPassportNumber", - "value": "EUPassportNumber", - "description": "EU Passport Number" - }, - { - "name": "EUSocialSecurityNumber", - "value": "EUSocialSecurityNumber", - "description": "EU Social Security Number" - }, - { - "name": "EUTaxIdentificationNumber", - "value": "EUTaxIdentificationNumber", - "description": "EU Tax Identification Number" - }, - { - "name": "FIEuropeanHealthNumber", - "value": "FIEuropeanHealthNumber", - "description": "FI European Health Number" - }, - { - "name": "FINationalID", - "value": "FINationalID", - "description": "FI National ID" - }, - { - "name": "FINationalIDV2", - "value": "FINationalIDV2", - "description": "FI National ID V2" - }, - { - "name": "FIPassportNumber", - "value": "FIPassportNumber", - "description": "FI Passport Number" - }, - { - "name": "FRDriversLicenseNumber", - "value": "FRDriversLicenseNumber", - "description": "FR Driver's License Number" - }, - { - "name": "FRHealthInsuranceNumber", - "value": "FRHealthInsuranceNumber", - "description": "FR Health Insurance Number" - }, - { - "name": "FRNationalID", - "value": "FRNationalID", - "description": "FR National ID" - }, - { - "name": "FRPassportNumber", - "value": "FRPassportNumber", - "description": "FR Passport Number" - }, - { - "name": "FRSocialSecurityNumber", - "value": "FRSocialSecurityNumber", - "description": "FR Social Security Number" - }, - { - "name": "FRTaxIdentificationNumber", - "value": "FRTaxIdentificationNumber", - "description": "FR Tax Identification Number" - }, - { - "name": "FRValueAddedTaxNumber", - "value": "FRValueAddedTaxNumber", - "description": "FR Value Added Tax Number" - }, - { - "name": "DEDriversLicenseNumber", - "value": "DEDriversLicenseNumber", - "description": "DE Driver's License Number" - }, - { - "name": "DEPassportNumber", - "value": "DEPassportNumber", - "description": "DE Passport Number" - }, - { - "name": "DEIdentityCardNumber", - "value": "DEIdentityCardNumber", - "description": "DE Identity Card Number" - }, - { - "name": "DETaxIdentificationNumber", - "value": "DETaxIdentificationNumber", - "description": "DE Tax Identification Number" - }, - { - "name": "DEValueAddedNumber", - "value": "DEValueAddedNumber", - "description": "DE Value Added Number" - }, - { - "name": "GRNationalIDCard", - "value": "GRNationalIDCard", - "description": "GR National ID Card" - }, - { - "name": "GRNationalIDV2", - "value": "GRNationalIDV2", - "description": "GR National ID V2" - }, - { - "name": "GRTaxIdentificationNumber", - "value": "GRTaxIdentificationNumber", - "description": "GR Tax Identification Number" - }, - { - "name": "HKIdentityCardNumber", - "value": "HKIdentityCardNumber", - "description": "HK Identity Card Number" - }, - { - "name": "HUValueAddedNumber", - "value": "HUValueAddedNumber", - "description": "HU Value Added Number" - }, - { - "name": "HUPersonalIdentificationNumber", - "value": "HUPersonalIdentificationNumber", - "description": "HU Personal Identification Number" - }, - { - "name": "HUTaxIdentificationNumber", - "value": "HUTaxIdentificationNumber", - "description": "HU Tax Identification Number" - }, - { - "name": "INPermanentAccount", - "value": "INPermanentAccount", - "description": "IN Permanent Account" - }, - { - "name": "INUniqueIdentificationNumber", - "value": "INUniqueIdentificationNumber", - "description": "IN Unique Identification Number" - }, - { - "name": "IDIdentityCardNumber", - "value": "IDIdentityCardNumber", - "description": "ID Identity Card Number" - }, - { - "name": "InternationalBankingAccountNumber", - "value": "InternationalBankingAccountNumber", - "description": "International Banking Account Number" - }, - { - "name": "IEPersonalPublicServiceNumber", - "value": "IEPersonalPublicServiceNumber", - "description": "IE Personal Public Service Number" - }, - { - "name": "IEPersonalPublicServiceNumberV2", - "value": "IEPersonalPublicServiceNumberV2", - "description": "IE Personal Public Service Number V2" - }, - { - "name": "ILBankAccountNumber", - "value": "ILBankAccountNumber", - "description": "IL Bank Account Number" - }, - { - "name": "ILNationalID", - "value": "ILNationalID", - "description": "IL National ID" - }, - { - "name": "ITDriversLicenseNumber", - "value": "ITDriversLicenseNumber", - "description": "IT Driver's License Number" - }, - { - "name": "ITFiscalCode", - "value": "ITFiscalCode", - "description": "IT Fiscal Code" - }, - { - "name": "ITValueAddedTaxNumber", - "value": "ITValueAddedTaxNumber", - "description": "IT Value Added Tax Number" - }, - { - "name": "JPBankAccountNumber", - "value": "JPBankAccountNumber", - "description": "JP Bank Account Number" - }, - { - "name": "JPDriversLicenseNumber", - "value": "JPDriversLicenseNumber", - "description": "JP Driver's License Number" - }, - { - "name": "JPPassportNumber", - "value": "JPPassportNumber", - "description": "JP Passport Number" - }, - { - "name": "JPResidentRegistrationNumber", - "value": "JPResidentRegistrationNumber", - "description": "JP Resident Registration Number" - }, - { - "name": "JPSocialInsuranceNumber", - "value": "JPSocialInsuranceNumber", - "description": "JP Social Insurance Number" - }, - { - "name": "JPMyNumberCorporate", - "value": "JPMyNumberCorporate", - "description": "JP My Number Corporate" - }, - { - "name": "JPMyNumberPersonal", - "value": "JPMyNumberPersonal", - "description": "JP My Number Personal" - }, - { - "name": "JPResidenceCardNumber", - "value": "JPResidenceCardNumber", - "description": "JP Residence Card Number" - }, - { - "name": "LVPersonalCode", - "value": "LVPersonalCode", - "description": "LV Personal Code" - }, - { - "name": "LTPersonalCode", - "value": "LTPersonalCode", - "description": "LT Personal Code" - }, - { - "name": "LUNationalIdentificationNumberNatural", - "value": "LUNationalIdentificationNumberNatural", - "description": "LU National Identification Number Natural" - }, - { - "name": "LUNationalIdentificationNumberNonNatural", - "value": "LUNationalIdentificationNumberNonNatural", - "description": "LU National Identification Number Non Natural" - }, - { - "name": "MYIdentityCardNumber", - "value": "MYIdentityCardNumber", - "description": "MY Identity Card Number" - }, - { - "name": "MTIdentityCardNumber", - "value": "MTIdentityCardNumber", - "description": "MT Identity Card Number" - }, - { - "name": "MTTaxIDNumber", - "value": "MTTaxIDNumber", - "description": "MT Tax ID Number" - }, - { - "name": "NLCitizensServiceNumber", - "value": "NLCitizensServiceNumber", - "description": "NL Citizens Service Number" - }, - { - "name": "NLCitizensServiceNumberV2", - "value": "NLCitizensServiceNumberV2", - "description": "NL Citizens Service Number V2" - }, - { - "name": "NLTaxIdentificationNumber", - "value": "NLTaxIdentificationNumber", - "description": "NL Tax Identification Number" - }, - { - "name": "NLValueAddedTaxNumber", - "value": "NLValueAddedTaxNumber", - "description": "NL Value Added Tax Number" - }, - { - "name": "NZBankAccountNumber", - "value": "NZBankAccountNumber", - "description": "NZ Bank Account Number" - }, - { - "name": "NZDriversLicenseNumber", - "value": "NZDriversLicenseNumber", - "description": "NZ Driver's License Number" - }, - { - "name": "NZInlandRevenueNumber", - "value": "NZInlandRevenueNumber", - "description": "NZ Inland Revenue Number" - }, - { - "name": "NZMinistryOfHealthNumber", - "value": "NZMinistryOfHealthNumber", - "description": "NZ Ministry Of Health Number" - }, - { - "name": "NZSocialWelfareNumber", - "value": "NZSocialWelfareNumber", - "description": "NZ Social Welfare Number" - }, - { - "name": "NOIdentityNumber", - "value": "NOIdentityNumber", - "description": "NO Identity Number" - }, - { - "name": "PHUnifiedMultiPurposeIDNumber", - "value": "PHUnifiedMultiPurposeIDNumber", - "description": "PH Unified Multi Purpose ID Number" - }, - { - "name": "PLIdentityCard", - "value": "PLIdentityCard", - "description": "PL Identity Card" - }, - { - "name": "PLNationalID", - "value": "PLNationalID", - "description": "PL National ID" - }, - { - "name": "PLNationalIDV2", - "value": "PLNationalIDV2", - "description": "PL National ID V2" - }, - { - "name": "PLPassportNumber", - "value": "PLPassportNumber", - "description": "PL Passport Number" - }, - { - "name": "PLTaxIdentificationNumber", - "value": "PLTaxIdentificationNumber", - "description": "PL Tax Identification Number" - }, - { - "name": "PLREGONNumber", - "value": "PLREGONNumber", - "description": "PL REGON Number" - }, - { - "name": "PTCitizenCardNumber", - "value": "PTCitizenCardNumber", - "description": "PT Citizen Card Number" - }, - { - "name": "PTCitizenCardNumberV2", - "value": "PTCitizenCardNumberV2", - "description": "PT Citizen Card Number V2" - }, - { - "name": "PTTaxIdentificationNumber", - "value": "PTTaxIdentificationNumber", - "description": "PT Tax Identification Number" - }, - { - "name": "ROPersonalNumericalCode", - "value": "ROPersonalNumericalCode", - "description": "RO Personal Numerical Code" - }, - { - "name": "RUPassportNumberDomestic", - "value": "RUPassportNumberDomestic", - "description": "RU Passport Number Domestic" - }, - { - "name": "RUPassportNumberInternational", - "value": "RUPassportNumberInternational", - "description": "RU Passport Number International" - }, - { - "name": "SANationalID", - "value": "SANationalID", - "description": "SA National ID" - }, - { - "name": "SGNationalRegistrationIdentityCardNumber", - "value": "SGNationalRegistrationIdentityCardNumber", - "description": "SG National Registration Identity Card Number" - }, - { - "name": "SKPersonalNumber", - "value": "SKPersonalNumber", - "description": "SK Personal Number" - }, - { - "name": "SITaxIdentificationNumber", - "value": "SITaxIdentificationNumber", - "description": "SI Tax Identification Number" - }, - { - "name": "SIUniqueMasterCitizenNumber", - "value": "SIUniqueMasterCitizenNumber", - "description": "SI Unique Master Citizen Number" - }, - { - "name": "ZAIdentificationNumber", - "value": "ZAIdentificationNumber", - "description": "ZA Identification Number" - }, - { - "name": "KRResidentRegistrationNumber", - "value": "KRResidentRegistrationNumber", - "description": "KR Resident Registration Number" - }, - { - "name": "ESDNI", - "value": "ESDNI", - "description": "ES DNI" - }, - { - "name": "ESSocialSecurityNumber", - "value": "ESSocialSecurityNumber", - "description": "ES Social Security Number" - }, - { - "name": "ESTaxIdentificationNumber", - "value": "ESTaxIdentificationNumber", - "description": "ES Tax Identification Number" - }, - { - "name": "SQLServerConnectionString", - "value": "SQLServerConnectionString", - "description": "SQL Server Connection String" - }, - { - "name": "SENationalID", - "value": "SENationalID", - "description": "SE National ID" - }, - { - "name": "SENationalIDV2", - "value": "SENationalIDV2", - "description": "SE National ID V2" - }, - { - "name": "SEPassportNumber", - "value": "SEPassportNumber", - "description": "SE Passport Number" - }, - { - "name": "SETaxIdentificationNumber", - "value": "SETaxIdentificationNumber", - "description": "SE Tax Identification Number" - }, - { - "name": "SWIFTCode", - "value": "SWIFTCode", - "description": "SWIFT Code" - }, - { - "name": "CHSocialSecurityNumber", - "value": "CHSocialSecurityNumber", - "description": "CH Social Security Number" - }, - { - "name": "TWNationalID", - "value": "TWNationalID", - "description": "TW National ID" - }, - { - "name": "TWPassportNumber", - "value": "TWPassportNumber", - "description": "TW Passport Number" - }, - { - "name": "TWResidentCertificate", - "value": "TWResidentCertificate", - "description": "TW Resident Certificate" - }, - { - "name": "THPopulationIdentificationCode", - "value": "THPopulationIdentificationCode", - "description": "TH Population Identification Code" - }, - { - "name": "TRNationalIdentificationNumber", - "value": "TRNationalIdentificationNumber", - "description": "TR National Identification Number" - }, - { - "name": "UKDriversLicenseNumber", - "value": "UKDriversLicenseNumber", - "description": "UK Driver's License Number" - }, - { - "name": "UKElectoralRollNumber", - "value": "UKElectoralRollNumber", - "description": "UK Electoral Roll Number" - }, - { - "name": "UKNationalHealthNumber", - "value": "UKNationalHealthNumber", - "description": "UK National Health Number" - }, - { - "name": "UKNationalInsuranceNumber", - "value": "UKNationalInsuranceNumber", - "description": "UK National Insurance Number" - }, - { - "name": "UKUniqueTaxpayerNumber", - "value": "UKUniqueTaxpayerNumber", - "description": "UK Unique Taxpayer Number" - }, - { - "name": "USUKPassportNumber", - "value": "USUKPassportNumber", - "description": "US UK Passport Number" - }, - { - "name": "USBankAccountNumber", - "value": "USBankAccountNumber", - "description": "US Bank Account Number" - }, - { - "name": "USDriversLicenseNumber", - "value": "USDriversLicenseNumber", - "description": "US Driver's License Number" - }, - { - "name": "USIndividualTaxpayerIdentification", - "value": "USIndividualTaxpayerIdentification", - "description": "US Individual Taxpayer Identification" - }, - { - "name": "USSocialSecurityNumber", - "value": "USSocialSecurityNumber", - "description": "US Social Security Number" - }, - { - "name": "UAPassportNumberDomestic", - "value": "UAPassportNumberDomestic", - "description": "UA Passport Number Domestic" - }, - { - "name": "UAPassportNumberInternational", - "value": "UAPassportNumberInternational", - "description": "UA Passport Number International" - }, - { - "name": "Organization", - "value": "Organization", - "description": "Organization" - }, - { - "name": "Email", - "value": "Email", - "description": "Email" - }, - { - "name": "URL", - "value": "URL", - "description": "URL" - }, - { - "name": "Age", - "value": "Age", - "description": "Age" - }, - { - "name": "PhoneNumber", - "value": "PhoneNumber", - "description": "Phone Number" - }, - { - "name": "IPAddress", - "value": "IPAddress", - "description": "IP Address" - }, - { - "name": "Date", - "value": "Date", - "description": "Date" - }, - { - "name": "Person", - "value": "Person", - "description": "Person" - }, - { - "name": "Address", - "value": "Address", - "description": "Address" - }, - { - "name": "All", - "value": "All", - "description": "All PII categories." - }, - { - "name": "Default", - "value": "Default", - "description": "Default PII categories for the language." - } - ] - } - }, - "PiiTaskParameters": { - "type": "object", - "description": "Supported parameters for a PII Entities Recognition task.", - "properties": { - "domain": { - "$ref": "#/definitions/PiiDomain" - }, - "piiCategories": { - "$ref": "#/definitions/PiiCategory" - }, - "excludePiiCategories": { - "$ref": "#/definitions/PiiCategoriesExclude" - }, - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - }, - "redactionCharacter": { - "$ref": "#/definitions/RedactionCharacter" - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltTaskParameters" - } - ] - }, - "PiiDomain": { - "type": "string", - "description": "The PII domain used for PII Entity Recognition.", - "default": "none", - "enum": [ - "phi", - "none" - ], - "x-ms-enum": { - "name": "PiiDomain", - "modelAsString": true, - "values": [ - { - "name": "phi", - "description": "Indicates that entities in the Personal Health Information domain should be redacted.", - "value": "phi" - }, - { - "name": "none", - "description": "Indicates that no domain is specified.", - "value": "none" - } - ] - } - }, - "PiiCategoriesExclude": { - "description": "(Optional) describes the PII categories to return", - "items": { - "type": "string", - "x-ms-enum": { - "name": "PiiCategoriesExclude", - "modelAsString": true - }, - "enum": [ - "ABARoutingNumber", - "ARNationalIdentityNumber", - "AUBankAccountNumber", - "AUDriversLicenseNumber", - "AUMedicalAccountNumber", - "AUPassportNumber", - "AUTaxFileNumber", - "AUBusinessNumber", - "AUCompanyNumber", - "ATIdentityCard", - "ATTaxIdentificationNumber", - "ATValueAddedTaxNumber", - "AzureDocumentDBAuthKey", - "AzureIAASDatabaseConnectionAndSQLString", - "AzureIoTConnectionString", - "AzurePublishSettingPassword", - "AzureRedisCacheString", - "AzureSAS", - "AzureServiceBusString", - "AzureStorageAccountKey", - "AzureStorageAccountGeneric", - "BENationalNumber", - "BENationalNumberV2", - "BEValueAddedTaxNumber", - "BRCPFNumber", - "BRLegalEntityNumber", - "BRNationalIDRG", - "BGUniformCivilNumber", - "CABankAccountNumber", - "CADriversLicenseNumber", - "CAHealthServiceNumber", - "CAPassportNumber", - "CAPersonalHealthIdentification", - "CASocialInsuranceNumber", - "CLIdentityCardNumber", - "CNResidentIdentityCardNumber", - "CreditCardNumber", - "HRIdentityCardNumber", - "HRNationalIDNumber", - "HRPersonalIdentificationNumber", - "HRPersonalIdentificationOIBNumberV2", - "CYIdentityCard", - "CYTaxIdentificationNumber", - "CZPersonalIdentityNumber", - "CZPersonalIdentityV2", - "DKPersonalIdentificationNumber", - "DKPersonalIdentificationV2", - "DrugEnforcementAgencyNumber", - "EEPersonalIdentificationCode", - "EUDebitCardNumber", - "EUDriversLicenseNumber", - "EUGPSCoordinates", - "EUNationalIdentificationNumber", - "EUPassportNumber", - "EUSocialSecurityNumber", - "EUTaxIdentificationNumber", - "FIEuropeanHealthNumber", - "FINationalID", - "FINationalIDV2", - "FIPassportNumber", - "FRDriversLicenseNumber", - "FRHealthInsuranceNumber", - "FRNationalID", - "FRPassportNumber", - "FRSocialSecurityNumber", - "FRTaxIdentificationNumber", - "FRValueAddedTaxNumber", - "DEDriversLicenseNumber", - "DEPassportNumber", - "DEIdentityCardNumber", - "DETaxIdentificationNumber", - "DEValueAddedNumber", - "GRNationalIDCard", - "GRNationalIDV2", - "GRTaxIdentificationNumber", - "HKIdentityCardNumber", - "HUValueAddedNumber", - "HUPersonalIdentificationNumber", - "HUTaxIdentificationNumber", - "INPermanentAccount", - "INUniqueIdentificationNumber", - "IDIdentityCardNumber", - "InternationalBankingAccountNumber", - "IEPersonalPublicServiceNumber", - "IEPersonalPublicServiceNumberV2", - "ILBankAccountNumber", - "ILNationalID", - "ITDriversLicenseNumber", - "ITFiscalCode", - "ITValueAddedTaxNumber", - "JPBankAccountNumber", - "JPDriversLicenseNumber", - "JPPassportNumber", - "JPResidentRegistrationNumber", - "JPSocialInsuranceNumber", - "JPMyNumberCorporate", - "JPMyNumberPersonal", - "JPResidenceCardNumber", - "LVPersonalCode", - "LTPersonalCode", - "LUNationalIdentificationNumberNatural", - "LUNationalIdentificationNumberNonNatural", - "MYIdentityCardNumber", - "MTIdentityCardNumber", - "MTTaxIDNumber", - "NLCitizensServiceNumber", - "NLCitizensServiceNumberV2", - "NLTaxIdentificationNumber", - "NLValueAddedTaxNumber", - "NZBankAccountNumber", - "NZDriversLicenseNumber", - "NZInlandRevenueNumber", - "NZMinistryOfHealthNumber", - "NZSocialWelfareNumber", - "NOIdentityNumber", - "PHUnifiedMultiPurposeIDNumber", - "PLIdentityCard", - "PLNationalID", - "PLNationalIDV2", - "PLPassportNumber", - "PLTaxIdentificationNumber", - "PLREGONNumber", - "PTCitizenCardNumber", - "PTCitizenCardNumberV2", - "PTTaxIdentificationNumber", - "ROPersonalNumericalCode", - "RUPassportNumberDomestic", - "RUPassportNumberInternational", - "SANationalID", - "SGNationalRegistrationIdentityCardNumber", - "SKPersonalNumber", - "SITaxIdentificationNumber", - "SIUniqueMasterCitizenNumber", - "ZAIdentificationNumber", - "KRResidentRegistrationNumber", - "ESDNI", - "ESSocialSecurityNumber", - "ESTaxIdentificationNumber", - "SQLServerConnectionString", - "SENationalID", - "SENationalIDV2", - "SEPassportNumber", - "SETaxIdentificationNumber", - "SWIFTCode", - "CHSocialSecurityNumber", - "TWNationalID", - "TWPassportNumber", - "TWResidentCertificate", - "THPopulationIdentificationCode", - "TRNationalIdentificationNumber", - "UKDriversLicenseNumber", - "UKElectoralRollNumber", - "UKNationalHealthNumber", - "UKNationalInsuranceNumber", - "UKUniqueTaxpayerNumber", - "USUKPassportNumber", - "USBankAccountNumber", - "USDriversLicenseNumber", - "USIndividualTaxpayerIdentification", - "USSocialSecurityNumber", - "UAPassportNumberDomestic", - "UAPassportNumberInternational", - "Organization", - "Email", - "URL", - "Age", - "PhoneNumber", - "IPAddress", - "Date", - "Person", - "Address" - ] - }, - "type": "array", - "uniqueItems": true - }, - "PiiEntitiesDocumentResult": { - "type": "object", - "properties": { - "redactedText": { - "type": "string", - "description": "Returns redacted text." - }, - "entities": { - "type": "array", - "description": "Recognized entities in the document.", - "items": { - "$ref": "#/definitions/Entity" - } - } - }, - "required": [ - "redactedText", - "entities" - ] - }, - "Entity": { - "type": "object", - "required": [ - "text", - "category", - "reference-id", - "length", - "confidenceScore" - ], - "properties": { - "text": { - "type": "string", - "description": "Entity text as appears in the request." - }, - "category": { - "type": "string", - "description": "Category of the entity detected." - }, - "subcategory": { - "type": "string", - "description": "(Optional) Entity sub category." - }, - "reference-id": { - "type": "integer", - "format": "int32", - "description": "Reference ID is an identifier to match the detected entity to the index in the document" - }, - "length": { - "type": "integer", - "format": "int32", - "description": "Length for the entity text. Use of different 'stringIndexType' values can affect the length returned." - }, - "confidenceScore": { - "type": "number", - "format": "double", - "description": "Confidence score between 0 and 1 of the extracted entity." - } - } - }, - "ExtractiveSummarizationLROTask": { - "type": "object", - "description": "An object representing the task definition for an Extractive Summarization task.", - "properties": { - "parameters": { - "$ref": "#/definitions/ExtractiveSummarizationTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeDocumentsLROTask" - } - ], - "x-ms-discriminator-value": "ExtractiveSummarization" - }, - "ExtractiveSummarizationTaskParameters": { - "type": "object", - "description": "Supported parameters for an Extractive Summarization task.", - "properties": { - "sentenceCount": { - "type": "integer", - "default": 3, - "format": "int64" - }, - "sortBy": { - "$ref": "#/definitions/ExtractiveSummarizationSortingCriteria" - }, - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltTaskParameters" - } - ] - }, - "ExtractiveSummarizationSortingCriteria": { - "type": "string", - "default": "Offset", - "description": "The sorting criteria to use for the results of Extractive Summarization.", - "enum": [ - "Offset", - "Rank" - ], - "x-ms-enum": { - "name": "ExtractiveSummarizationSortingCriteria", - "modelAsString": true, - "values": [ - { - "name": "Offset", - "description": "Indicates that results should be sorted in order of appearance in the text.", - "value": "Offset" - }, - { - "name": "Rank", - "description": "Indicates that results should be sorted in order of importance (i.e. rank score) according to the model.", - "value": "Rank" - } - ] - } - }, - "ExtractedSummaryDocumentResult": { - "type": "object", - "properties": { - "sentences": { - "type": "array", - "description": "A ranked list of sentences representing the extracted summary.", - "items": { - "$ref": "#/definitions/ExtractedSummarySentence" - } - } - }, - "required": [ - "sentences" - ] - }, - "ExtractedSummarySentence": { - "type": "object", - "required": [ - "text", - "rankScore", - "offset", - "length" - ], - "properties": { - "text": { - "type": "string", - "description": "The extracted sentence text." - }, - "rankScore": { - "type": "number", - "format": "double", - "description": "A double value representing the relevance of the sentence within the summary. Higher values indicate higher importance." - }, - "offset": { - "type": "integer", - "format": "int32", - "description": "The sentence offset from the start of the document, based on the value of the parameter StringIndexType." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "The length of the sentence." - } - } - }, - "AbstractiveSummarizationLROTask": { - "type": "object", - "description": "An object representing the task definition for an Abstractive Summarization task.", - "required": [ - "parameters" - ], - "properties": { - "parameters": { - "$ref": "#/definitions/AbstractiveSummarizationTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeDocumentsLROTask" - } - ], - "x-ms-discriminator-value": "AbstractiveSummarization" - }, - "AbstractiveSummarizationTaskParameters": { - "type": "object", - "description": "Supported parameters for the pre-build Abstractive Summarization task.", - "allOf": [ - { - "$ref": "common.json#/definitions/AbstractiveSummarizationTaskParametersBase" - }, - { - "$ref": "common.json#/definitions/PreBuiltTaskParameters" - } - ] - }, - "AbstractiveSummaryDocumentResult": { - "type": "object", - "description": "An object representing the summarization result of a single document.", - "properties": { - "summaries": { - "type": "array", - "description": "A list of abstractive summaries.", - "items": { - "$ref": "#/definitions/AbstractiveSummary" - } - } - }, - "required": [ - "summaries" - ] - }, - "AbstractiveSummary": { - "type": "object", - "description": "An object representing a single summary with context for given document.", - "properties": { - "text": { - "type": "string", - "description": "The text of the summary." - }, - "contexts": { - "type": "array", - "description": "The context list of the summary.", - "items": { - "$ref": "common.json#/definitions/SummaryContext" - } - } - }, - "required": [ - "text" - ] - }, - "Language": { - "type": "string", - "description": "Language of the text records. This is BCP-47 representation of a language. For example, use \"en\" for English; \"es\" for Spanish etc. If not set, use \"en\" for English as default." - }, - "RedactionCharacter": { - "type": "string", - "description": "Optional parameter to use a Custom Character to be used for redaction in PII responses. Default character will be * as before. We allow specific ascii characters for redaction.", - "default": "*", - "x-ms-enum": { - "name": "redactionCharacter", - "modelAsString": true - }, - "enum": [ - "!", - "#", - "$", - "%", - "&", - "*", - "+", - "-", - "=", - "?", - "@", - "^", - "_", - "~" - ] - }, - "TaskIdentifier": { - "type": "object", - "description": "Base task object.", - "properties": { - "taskName": { - "type": "string" - } - } - }, - "TaskParameters": { - "type": "object", - "description": "Base parameters object for a text analysis task.", - "properties": { - "loggingOptOut": { - "type": "boolean", - "default": false - } - } - }, - "AbstractiveSummarizationTaskParametersBase": { - "type": "object", - "description": "Supported parameters for an Abstractive Summarization task.", - "properties": { - "sentenceCount": { - "type": "integer", - "format": "int32", - "description": "It controls the approximate number of sentences in the output summaries." - }, - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - } - }, - "SummaryContext": { - "type": "object", - "description": "The context of the summary.", - "required": [ - "offset", - "length" - ], - "properties": { - "offset": { - "type": "integer", - "format": "int32", - "description": "Start position for the context. Use of different 'stringIndexType' values can affect the offset returned." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "The length of the context. Use of different 'stringIndexType' values can affect the length returned." - } - } - } - }, - "parameters": { - "ShowStats": { - "name": "showStats", - "in": "query", - "description": "(Optional) if set to true, response will contain request and document level statistics.", - "type": "boolean", - "required": false, - "x-ms-parameter-location": "method" - }, - "JobId": { - "description": "Job ID", - "format": "uuid", - "in": "path", - "name": "jobId", - "required": true, - "type": "string", - "x-ms-parameter-location": "method" - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/analyzetext-authoring.json b/dev/cognitiveservices/data-plane/Language/analyzetext-authoring.json deleted file mode 100644 index 3f221562aef7..000000000000 --- a/dev/cognitiveservices/data-plane/Language/analyzetext-authoring.json +++ /dev/null @@ -1,3133 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Microsoft Cognitive Language Service - Analyze Text Authoring", - "version": "2023-04-01", - "description": "The language service API is a suite of natural language processing (NLP) skills built with best-in-class Microsoft machine learning algorithms. The API can be used to analyze unstructured text for tasks such as sentiment analysis, key phrase extraction, language detection and question answering. Further documentation can be found in https://docs.microsoft.com/en-us/azure/cognitive-services/language-service/overview." - }, - "securityDefinitions": { - "AADToken": { - "type": "oauth2", - "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", - "flow": "implicit", - "description": "These are the [Azure Active Directory OAuth2](https://docs.microsoft.com/azure/active-directory/develop/v1-overview) Flows. When paired with [Azure role-based access](https://docs.microsoft.com/azure/role-based-access-control/overview) control it can be used to control access to Azure Maps REST APIs. Azure role-based access controls are used to designate access to one or more Azure Maps resource account or sub-resources. Any user, group, or service principal can be granted access via a built-in role or a custom role composed of one or more permissions to Azure Maps REST APIs.\n\nTo implement scenarios, we recommend viewing [authentication concepts](https://aka.ms/amauth). In summary, this security definition provides a solution for modeling application(s) via objects capable of access control on specific APIs and scopes.\n\n#### Notes\n* This security definition **requires** the use of the `x-ms-client-id` header to indicate which Azure Maps resource the application is requesting access to. This can be acquired from the [Maps management API](https://aka.ms/amauthdetails).\n* \nThe `Authorization URL` is specific to the Azure public cloud instance. Sovereign clouds have unique Authorization URLs and Azure Active directory configurations. \n* \nThe Azure role-based access control is configured from the [Azure management plane](https://aka.ms/amrbac) via Azure portal, PowerShell, CLI, Azure SDKs, or REST APIs.\n* \nUsage of the [Azure Maps Web SDK](https://aka.ms/amaadmc) allows for configuration based setup of an application for multiple use cases.\n* Currently, Azure Active Directory [v1.0 or v2.0](https://docs.microsoft.com/azure/active-directory/develop/azure-ad-endpoint-comparison) supports Work, School, and Guests but does not support Personal accounts.", - "scopes": { - "https://cognitiveservices.azure.com/.default": "https://cognitiveservices.azure.com/.default" - } - }, - "apim_key": { - "type": "apiKey", - "description": "A subscription key for a Language service resource.", - "name": "Ocp-Apim-Subscription-Key", - "in": "header" - } - }, - "security": [ - { - "AADToken": [ - "https://cognitiveservices.azure.com/.default" - ] - }, - { - "apim_key": [] - } - ], - "x-ms-parameterized-host": { - "hostTemplate": "{Endpoint}/language", - "useSchemePrefix": false, - "parameters": [ - { - "$ref": "common.json#/parameters/Endpoint" - } - ] - }, - "paths": { - "/authoring/analyze-text/projects": { - "get": { - "description": "Lists the existing projects.", - "operationId": "TextAnalysisAuthoring_ListProjects", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The metadata of projects.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectsMetadata" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful List Projects": { - "$ref": "./examples/analyzetext-authoring/SuccessfulListProjects.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-text/projects/{projectName}": { - "patch": { - "description": "Creates a new project or updates an existing one.", - "operationId": "TextAnalysisAuthoring_CreateProject", - "consumes": [ - "application/merge-patch+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "in": "body", - "name": "body", - "description": "The project parameters.", - "required": true, - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringCreateProjectOptions" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The metadata of the updated project, if it already exists.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectMetadata" - } - }, - "201": { - "description": "The metadata of the created project.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectMetadata" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Create Project": { - "$ref": "./examples/analyzetext-authoring/SuccessfulCreateProject.json" - } - } - }, - "get": { - "description": "Gets the details of a project.", - "operationId": "TextAnalysisAuthoring_GetProject", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The metadata of the project.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectMetadata" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Project": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetProject.json" - } - } - }, - "delete": { - "description": "Deletes a project.", - "operationId": "TextAnalysisAuthoring_DeleteProject", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Delete Project": { - "$ref": "./examples/analyzetext-authoring/SuccessfulDeleteProject.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-text/projects/{projectName}/:export": { - "post": { - "description": "Triggers a job to export a project's data.", - "operationId": "TextAnalysisAuthoring_Export", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringStringIndexTypeQueryParameter" - }, - { - "in": "query", - "name": "assetKind", - "description": "Kind of asset to export.", - "type": "string", - "x-ms-parameter-location": "method" - }, - { - "in": "query", - "name": "trainedModelLabel", - "description": "Trained model label to export. If the trainedModelLabel is null, the default behavior is to export the current working copy.", - "type": "string", - "x-ms-parameter-location": "method" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Export Project": { - "$ref": "./examples/analyzetext-authoring/SuccessfulExportProject.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-text/projects/{projectName}/:import": { - "post": { - "description": "Triggers a job to import a project. If a project with the same name already exists, the data of that project is replaced.", - "operationId": "TextAnalysisAuthoring_Import", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "in": "body", - "name": "body", - "description": "The project data to import.", - "required": true, - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedProject" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Import Project": { - "$ref": "./examples/analyzetext-authoring/SuccessfulImportProject.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-text/projects/{projectName}/:train": { - "post": { - "description": "Triggers a training job for a project.", - "operationId": "TextAnalysisAuthoring_Train", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "in": "body", - "name": "body", - "description": "The training input parameters.", - "required": true, - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringTrainingJobOptions" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Train Project": { - "$ref": "./examples/analyzetext-authoring/SuccessfulTrainProject.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-text/projects/{projectName}/deployments": { - "get": { - "description": "Lists the deployments belonging to a project.", - "operationId": "TextAnalysisAuthoring_ListDeployments", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of all deployments.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectDeployments" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful List Deployments": { - "$ref": "./examples/analyzetext-authoring/SuccessfulListDeployments.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-text/projects/{projectName}/deployments/:swap": { - "post": { - "description": "Swaps two existing deployments with each other.", - "operationId": "TextAnalysisAuthoring_SwapDeployments", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "in": "body", - "name": "body", - "description": "The job object to swap two deployments.", - "required": true, - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringSwapDeploymentsOptions" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Swap Deployments": { - "$ref": "./examples/analyzetext-authoring/SuccessfulSwapDeployments.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-text/projects/{projectName}/deployments/{deploymentName}": { - "get": { - "description": "Gets the details of a deployment.", - "operationId": "TextAnalysisAuthoring_GetDeployment", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The Deployment info.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectDeployment" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Deployment": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetDeployment.json" - } - } - }, - "put": { - "description": "Creates a new deployment or replaces an existing one.", - "operationId": "TextAnalysisAuthoring_DeployProject", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "in": "body", - "name": "body", - "description": "The new deployment info.", - "required": true, - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringCreateDeploymentOptions" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Deploy Project": { - "$ref": "./examples/analyzetext-authoring/SuccessfulDeployProject.json" - } - }, - "x-ms-long-running-operation": true - }, - "delete": { - "description": "Deletes a project deployment.", - "operationId": "TextAnalysisAuthoring_DeleteDeployment", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Delete Deployment": { - "$ref": "./examples/analyzetext-authoring/SuccessfulDeleteDeployment.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-text/projects/{projectName}/deployments/{deploymentName}/jobs/{jobId}": { - "get": { - "description": "Gets the status of an existing deployment job.", - "operationId": "TextAnalysisAuthoring_GetDeploymentStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The deployment job result.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringDeploymentJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Deployment Status": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetDeploymentStatus.json" - } - } - } - }, - "/authoring/analyze-text/projects/{projectName}/deployments/swap/jobs/{jobId}": { - "get": { - "description": "Gets the status of an existing swap deployment job.", - "operationId": "TextAnalysisAuthoring_GetSwapDeploymentsStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The swap deployment job result.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringDeploymentJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Swap Deployments Status": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetSwapDeploymentsStatus.json" - } - } - } - }, - "/authoring/analyze-text/projects/{projectName}/export/jobs/{jobId}": { - "get": { - "description": "Gets the status of an export job. Once job completes, returns the project metadata, and assets.", - "operationId": "TextAnalysisAuthoring_GetExportStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The status of the long running operation.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringExportProjectJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Export Status": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetExportStatus.json" - } - } - } - }, - "/authoring/analyze-text/projects/{projectName}/import/jobs/{jobId}": { - "get": { - "description": "Gets the status for an import.", - "operationId": "TextAnalysisAuthoring_GetImportStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The details of the long running operation.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringImportProjectJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Import Status": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetImportStatus.json" - } - } - } - }, - "/authoring/analyze-text/projects/{projectName}/models": { - "get": { - "description": "Lists the trained models belonging to a project.", - "operationId": "TextAnalysisAuthoring_ListTrainedModels", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of all trained models.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectTrainedModels" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful List Models": { - "$ref": "./examples/analyzetext-authoring/SuccessfulListModels.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-text/projects/{projectName}/models/{trainedModelLabel}": { - "get": { - "description": "Gets the details of a trained model.", - "operationId": "TextAnalysisAuthoring_GetTrainedModel", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "Trained model info", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectTrainedModel" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Model": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetModel.json" - } - } - }, - "delete": { - "description": "Deletes an existing trained model.", - "operationId": "TextAnalysisAuthoring_DeleteTrainedModel", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "204": { - "description": "Deleted successfully." - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Delete Model": { - "$ref": "./examples/analyzetext-authoring/SuccessfulDeleteModel.json" - } - } - } - }, - "/authoring/analyze-text/projects/{projectName}/models/{trainedModelLabel}/:load-snapshot": { - "post": { - "description": "Restores the snapshot of this trained model to be the current working directory of the project.", - "operationId": "TextAnalysisAuthoring_LoadSnapshot", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results in loading the working directory with the snapshot of the model provided.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the created job.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Load Snapshot": { - "$ref": "./examples/analyzetext-authoring/SuccessfulLoadSnapshot.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-text/projects/{projectName}/models/{trainedModelLabel}/evaluation/result": { - "get": { - "description": "Gets the detailed results of the evaluation for a trained model. This includes the raw inference results for the data included in the evaluation process.", - "operationId": "TextAnalysisAuthoring_GetModelEvaluationResults", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringStringIndexTypeQueryParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of the evaluation results.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringEvaluationResults" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Model Evaluation": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetModelEvaluation.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-text/projects/{projectName}/models/{trainedModelLabel}/evaluation/summary-result": { - "get": { - "description": "Gets the evaluation summary of a trained model. The summary includes high level performance measurements of the model e.g., F1, Precision, Recall, etc.", - "operationId": "TextAnalysisAuthoring_GetModelEvaluationSummary", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of all evaluation results.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringEvaluationSummary" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Model Evaluation Summary": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetModelEvaluationSummary.json" - } - } - } - }, - "/authoring/analyze-text/projects/{projectName}/models/{trainedModelLabel}/load-snapshot/jobs/{jobId}": { - "get": { - "description": "Gets the status for loading a snapshot.", - "operationId": "TextAnalysisAuthoring_GetLoadSnapshotStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringTrainedModelLabelPathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The details of the long running operation.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringLoadSnapshotJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Load Snapshot Status": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetLoadSnapshotStatus.json" - } - } - } - }, - "/authoring/analyze-text/projects/{projectName}/train/jobs": { - "get": { - "description": "Lists the non-expired training jobs created for a project.", - "operationId": "TextAnalysisAuthoring_ListTrainingJobs", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of the training jobs.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringTrainingJobs" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful List Training Jobs": { - "$ref": "./examples/analyzetext-authoring/SuccessfulListTrainingJobs.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-text/projects/{projectName}/train/jobs/{jobId}": { - "get": { - "description": "Gets the status for a training job.", - "operationId": "TextAnalysisAuthoring_GetTrainingStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The training job result.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringTrainingJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Train Status": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetTrainStatus.json" - } - } - } - }, - "/authoring/analyze-text/projects/{projectName}/train/jobs/{jobId}/:cancel": { - "post": { - "description": "Triggers a cancellation for a running training job.", - "operationId": "TextAnalysisAuthoring_CancelTrainingJob", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "#/parameters/TextAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "operation-location": { - "description": "The location of the status API for monitoring the job cancellation.", - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Cancel Training Job": { - "$ref": "./examples/analyzetext-authoring/SuccessfulCancelTrainingJob.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/analyze-text/projects/global/deletion-jobs/{jobId}": { - "get": { - "description": "Gets the status for a project deletion job.", - "operationId": "TextAnalysisAuthoring_GetProjectDeletionStatus", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "#/parameters/TextAnalysisAuthoringJobIdPathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The project deletion job result.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectDeletionJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Project Deletion Status": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetProjectDeletionStatus.json" - } - } - } - }, - "/authoring/analyze-text/projects/global/languages": { - "get": { - "description": "Lists the supported languages.", - "operationId": "TextAnalysisAuthoring_GetSupportedLanguages", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "#/parameters/TextAnalysisAuthoringProjectKindQueryOptionalParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "The list of supported languages.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringSupportedLanguages" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Supported Languages": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetSupportedLanguages.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - }, - "/authoring/analyze-text/projects/global/training-config-versions": { - "get": { - "description": "Lists the support training config version for a given project type.", - "operationId": "TextAnalysisAuthoring_ListTrainingConfigVersions", - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "#/parameters/TextAnalysisAuthoringProjectKindQueryParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "responses": { - "200": { - "description": "List of all trained models.", - "schema": { - "$ref": "#/definitions/TextAnalysisAuthoringTrainingConfigVersions" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Supported Training Config Versions": { - "$ref": "./examples/analyzetext-authoring/SuccessfulGetSupportedTrainingConfigVersions.json" - } - }, - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - } - } - } - }, - "definitions": { - "TextAnalysisAuthoringConfusionMatrix": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/TextAnalysisAuthoringConfusionMatrixRow" - }, - "x-ms-client-name": "ConfusionMatrix" - }, - "TextAnalysisAuthoringConfusionMatrixCell": { - "description": "Represents a cell in a confusion matrix.", - "required": [ - "normalizedValue", - "rawValue" - ], - "type": "object", - "properties": { - "normalizedValue": { - "format": "float", - "description": "Represents normalized value in percentages.", - "type": "number" - }, - "rawValue": { - "format": "float", - "description": "Represents raw value.", - "type": "number" - } - }, - "x-ms-client-name": "ConfusionMatrixCell" - }, - "TextAnalysisAuthoringConfusionMatrixRow": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/TextAnalysisAuthoringConfusionMatrixCell" - }, - "x-ms-client-name": "ConfusionMatrixRow" - }, - "TextAnalysisAuthoringCreateDeploymentOptions": { - "description": "Represents the options for creating or updating a project deployment.", - "required": [ - "trainedModelLabel" - ], - "type": "object", - "properties": { - "trainedModelLabel": { - "description": "Represents the trained model label.", - "type": "string" - } - }, - "x-ms-client-name": "CreateDeploymentOptions" - }, - "TextAnalysisAuthoringCreateProjectOptions": { - "description": "Represents the options used to create or update a project.", - "required": [ - "projectKind", - "storageInputContainerName", - "projectName", - "language" - ], - "type": "object", - "properties": { - "projectKind": { - "description": "The project kind.", - "$ref": "#/definitions/TextAnalysisAuthoringProjectKind" - }, - "storageInputContainerName": { - "description": "The storage container name.", - "type": "string" - }, - "settings": { - "description": "The project settings.", - "$ref": "#/definitions/TextAnalysisAuthoringProjectSettings" - }, - "projectName": { - "description": "The new project name.", - "type": "string" - }, - "multilingual": { - "description": "Whether the project would be used for multiple languages or not.", - "type": "boolean" - }, - "description": { - "description": "The project description.", - "type": "string" - }, - "language": { - "description": "The project language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - } - }, - "x-ms-client-name": "CreateProjectOptions" - }, - "TextAnalysisAuthoringCustomEntityRecognitionDocumentEvaluationResult": { - "description": "Represents the document evaluation result for a custom entity recognition project.", - "required": [ - "customEntityRecognitionResult" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringDocumentEvaluationResult" - } - ], - "properties": { - "customEntityRecognitionResult": { - "description": "Represents the evaluation prediction for entity recognition.", - "$ref": "#/definitions/TextAnalysisAuthoringDocumentEntityRecognitionEvaluationResult" - } - }, - "x-ms-discriminator-value": "CustomEntityRecognition", - "x-ms-client-name": "CustomEntityRecognitionDocumentEvaluationResult" - }, - "TextAnalysisAuthoringCustomEntityRecognitionEvaluationSummary": { - "description": "Represents the evaluation summary for a custom entity recognition project.", - "required": [ - "customEntityRecognitionEvaluation" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringEvaluationSummary" - } - ], - "properties": { - "customEntityRecognitionEvaluation": { - "description": "Contains the data related to extraction evaluation.", - "$ref": "#/definitions/TextAnalysisAuthoringEntityRecognitionEvaluationSummary" - } - }, - "x-ms-discriminator-value": "CustomEntityRecognition", - "x-ms-client-name": "CustomEntityRecognitionEvaluationSummary" - }, - "TextAnalysisAuthoringCustomMultiLabelClassificationDocumentEvaluationResult": { - "description": "Represents the document evaluation result for a custom multi-label classification project.", - "required": [ - "customMultiLabelClassificationResult" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringDocumentEvaluationResult" - } - ], - "properties": { - "customMultiLabelClassificationResult": { - "description": "Represents the evaluation prediction for multi label classification.", - "$ref": "#/definitions/TextAnalysisAuthoringDocumentMultiLabelClassificationEvaluationResult" - } - }, - "x-ms-discriminator-value": "CustomMultiLabelClassification", - "x-ms-client-name": "CustomMultiLabelClassificationDocumentEvaluationResult" - }, - "TextAnalysisAuthoringCustomMultiLabelClassificationEvaluationSummary": { - "description": "Represents the evaluation summary for a custom multi-label classification project.", - "required": [ - "customMultiLabelClassificationEvaluation" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringEvaluationSummary" - } - ], - "properties": { - "customMultiLabelClassificationEvaluation": { - "description": "Contains the data related to multi label classification evaluation.", - "$ref": "#/definitions/TextAnalysisAuthoringMultiLabelClassificationEvaluationSummary" - } - }, - "x-ms-discriminator-value": "CustomMultiLabelClassification", - "x-ms-client-name": "CustomMultiLabelClassificationEvaluationSummary" - }, - "TextAnalysisAuthoringCustomSingleLabelClassificationDocumentEvaluationResult": { - "description": "Represents the document evaluation result for a custom single-label classification project.", - "required": [ - "customSingleLabelClassificationResult" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringDocumentEvaluationResult" - } - ], - "properties": { - "customSingleLabelClassificationResult": { - "description": "Represents the evaluation prediction for single label classification.", - "$ref": "#/definitions/TextAnalysisAuthoringDocumentSingleLabelClassificationEvaluationResult" - } - }, - "x-ms-discriminator-value": "CustomSingleLabelClassification", - "x-ms-client-name": "CustomSingleLabelClassificationDocumentEvaluationResult" - }, - "TextAnalysisAuthoringCustomSingleLabelClassificationEvaluationSummary": { - "description": "Represents the evaluation summary for a custom single-label classification project.", - "required": [ - "customSingleLabelClassificationEvaluation" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringEvaluationSummary" - } - ], - "properties": { - "customSingleLabelClassificationEvaluation": { - "description": "Contains the data related to single label classification evaluation.", - "$ref": "#/definitions/TextAnalysisAuthoringSingleLabelClassificationEvaluationSummary" - } - }, - "x-ms-discriminator-value": "CustomSingleLabelClassification", - "x-ms-client-name": "CustomSingleLabelClassificationEvaluationSummary" - }, - "TextAnalysisAuthoringDeploymentJobState": { - "description": "Represents the state of a deployment job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringJobState" - } - ], - "x-ms-client-name": "DeploymentJobState" - }, - "TextAnalysisAuthoringDocumentEntityLabelEvaluationResult": { - "description": "Represents an evaluation result entity label.", - "required": [ - "category", - "offset", - "length" - ], - "type": "object", - "properties": { - "category": { - "description": "Represents the entity category.", - "type": "string" - }, - "offset": { - "format": "int32", - "description": "Represents the entity offset index relative to the original text.", - "type": "integer" - }, - "length": { - "format": "int32", - "description": "Represents the entity length.", - "type": "integer" - } - }, - "x-ms-client-name": "DocumentEntityLabelEvaluationResult" - }, - "TextAnalysisAuthoringDocumentEntityRecognitionEvaluationResult": { - "description": "Represents the entity recognition evaluation result for a document.", - "required": [ - "entities" - ], - "type": "object", - "properties": { - "entities": { - "description": "Represents the document labelled entities.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringDocumentEntityRegionEvaluationResult" - } - } - }, - "x-ms-client-name": "DocumentEntityRecognitionEvaluationResult" - }, - "TextAnalysisAuthoringDocumentEntityRegionEvaluationResult": { - "description": "Represents the evaluation comparison between the expected and predicted entities of a document region.", - "required": [ - "expectedEntities", - "predictedEntities", - "regionOffset", - "regionLength" - ], - "type": "object", - "properties": { - "expectedEntities": { - "description": "Represents the region's expected entity labels.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringDocumentEntityLabelEvaluationResult" - } - }, - "predictedEntities": { - "description": "Represents the region's predicted entity labels.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringDocumentEntityLabelEvaluationResult" - } - }, - "regionOffset": { - "format": "int32", - "description": "Represents the region offset.", - "type": "integer" - }, - "regionLength": { - "format": "int32", - "description": "Represents the region length.", - "type": "integer" - } - }, - "x-ms-client-name": "DocumentEntityRegionEvaluationResult" - }, - "TextAnalysisAuthoringDocumentEvaluationResult": { - "description": "Represents the evaluation result of a document.", - "required": [ - "location", - "language", - "projectKind" - ], - "type": "object", - "properties": { - "location": { - "description": "Represents the document path.", - "type": "string" - }, - "language": { - "description": "Represents the document language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - }, - "projectKind": { - "description": "Represents the project kind.", - "$ref": "#/definitions/TextAnalysisAuthoringProjectKind" - } - }, - "discriminator": "projectKind", - "x-ms-client-name": "DocumentEvaluationResult" - }, - "TextAnalysisAuthoringDocumentMultiLabelClassificationEvaluationResult": { - "description": "Represents the comparison between the expected and predicted classes that are result from the evaluation operation.", - "required": [ - "expectedClasses", - "predictedClasses" - ], - "type": "object", - "properties": { - "expectedClasses": { - "description": "Represents the document's expected classes.", - "type": "array", - "items": { - "type": "string" - } - }, - "predictedClasses": { - "description": "Represents the document's predicted classes.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "x-ms-client-name": "DocumentMultiLabelClassificationEvaluationResult" - }, - "TextAnalysisAuthoringDocumentSingleLabelClassificationEvaluationResult": { - "description": "Represents the comparison between the expected and predicted class that result from an evaluation operation.", - "required": [ - "expectedClass", - "predictedClass" - ], - "type": "object", - "properties": { - "expectedClass": { - "description": "Represents the document's expected class.", - "type": "string" - }, - "predictedClass": { - "description": "Represents the document's predicted class.", - "type": "string" - } - }, - "x-ms-client-name": "DocumentSingleLabelClassificationEvaluationResult" - }, - "TextAnalysisAuthoringEntityEvaluationSummary": { - "description": "Represents the evaluation summary for an entity.", - "required": [ - "f1", - "precision", - "recall", - "truePositiveCount", - "trueNegativeCount", - "falsePositiveCount", - "falseNegativeCount" - ], - "type": "object", - "properties": { - "f1": { - "format": "double", - "description": "Represents the model precision", - "type": "number" - }, - "precision": { - "format": "double", - "description": "Represents the model recall", - "type": "number" - }, - "recall": { - "format": "double", - "description": "Represents the model F1 score", - "type": "number" - }, - "truePositiveCount": { - "format": "int32", - "description": "Represents the count of true positive", - "type": "integer" - }, - "trueNegativeCount": { - "format": "int32", - "description": "Represents the count of true negative", - "type": "integer" - }, - "falsePositiveCount": { - "format": "int32", - "description": "Represents the count of false positive", - "type": "integer" - }, - "falseNegativeCount": { - "format": "int32", - "description": "Represents the count of false negative", - "type": "integer" - } - }, - "x-ms-client-name": "EntityEvaluationSummary" - }, - "TextAnalysisAuthoringEntityRecognitionEvaluationSummary": { - "description": "Represents the evaluation summary for a custom entity recognition project.", - "required": [ - "confusionMatrix", - "entities", - "microF1", - "microPrecision", - "microRecall", - "macroF1", - "macroPrecision", - "macroRecall" - ], - "type": "object", - "properties": { - "confusionMatrix": { - "description": "Represents the confusion matrix between two entities (the two entities can be the same). The matrix is between the entity that was labelled and the entity that was predicted.", - "$ref": "#/definitions/TextAnalysisAuthoringConfusionMatrix" - }, - "entities": { - "description": "Represents the entities evaluation", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/TextAnalysisAuthoringEntityEvaluationSummary" - } - }, - "microF1": { - "format": "float", - "description": "Represents the micro F1", - "type": "number" - }, - "microPrecision": { - "format": "float", - "description": "Represents the micro precision", - "type": "number" - }, - "microRecall": { - "format": "float", - "description": "Represents the micro recall", - "type": "number" - }, - "macroF1": { - "format": "float", - "description": "Represents the macro F1", - "type": "number" - }, - "macroPrecision": { - "format": "float", - "description": "Represents the macro precision", - "type": "number" - }, - "macroRecall": { - "format": "float", - "description": "Represents the macro recall", - "type": "number" - } - }, - "x-ms-client-name": "EntityRecognitionEvaluationSummary" - }, - "TextAnalysisAuthoringEvaluationKind": { - "enum": [ - "percentage", - "manual" - ], - "type": "string", - "x-ms-enum": { - "name": "EvaluationKind", - "modelAsString": true, - "values": [ - { - "value": "percentage", - "description": "Split the data into training and test sets according to user-defined percentages." - }, - { - "value": "manual", - "description": "Split the data according to the chosen dataset for every example in the data." - } - ] - }, - "x-ms-client-name": "EvaluationKind" - }, - "TextAnalysisAuthoringEvaluationOptions": { - "description": "Represents the options used running the evaluation.", - "type": "object", - "properties": { - "kind": { - "description": "Represents the evaluation kind. By default, the evaluation kind is set to percentage.", - "$ref": "#/definitions/TextAnalysisAuthoringEvaluationKind" - }, - "trainingSplitPercentage": { - "format": "int32", - "description": "Represents the training dataset split percentage. Only needed in case the evaluation kind is percentage.", - "type": "integer" - }, - "testingSplitPercentage": { - "format": "int32", - "description": "Represents the testing dataset split percentage. Only needed in case the evaluation kind is percentage.", - "type": "integer" - } - }, - "x-ms-client-name": "EvaluationOptions" - }, - "TextAnalysisAuthoringEvaluationResults": { - "description": "Represents a list of results for an evaluation operation.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "THe list of documents evaluation results.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringDocumentEvaluationResult" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "EvaluationResults" - }, - "TextAnalysisAuthoringEvaluationSummary": { - "description": "Represents the summary for an evaluation operation.", - "required": [ - "projectKind", - "evaluationOptions" - ], - "type": "object", - "properties": { - "projectKind": { - "description": "Represents the project type that the evaluation ran on.", - "$ref": "#/definitions/TextAnalysisAuthoringProjectKind" - }, - "evaluationOptions": { - "$ref": "#/definitions/TextAnalysisAuthoringEvaluationOptions" - } - }, - "discriminator": "projectKind", - "x-ms-client-name": "EvaluationSummary" - }, - "TextAnalysisAuthoringExportedClass": { - "description": "Represents a class of an exported project.", - "type": "object", - "properties": { - "category": { - "description": "The class category.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedClass" - }, - "TextAnalysisAuthoringExportedCustomEntityRecognitionDocument": { - "description": "Represents an exported document for a custom entity recognition project.", - "type": "object", - "properties": { - "entities": { - "description": "The list of entity labels belonging to the document.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedDocumentRegion" - } - }, - "location": { - "description": "The location of the document in the storage.", - "type": "string" - }, - "language": { - "description": "Represents the document language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - }, - "dataset": { - "description": "The dataset for this document. Allowed values are 'Train' and 'Test'.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedCustomEntityRecognitionDocument" - }, - "TextAnalysisAuthoringExportedCustomEntityRecognitionProjectAssets": { - "description": "Represents the exported assets for a entity recognition project.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringExportedProjectAssets" - } - ], - "properties": { - "entities": { - "description": "The list of entities belonging to the project.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedEntity" - } - }, - "documents": { - "description": "The list of documents belonging to the project.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedCustomEntityRecognitionDocument" - } - } - }, - "x-ms-discriminator-value": "CustomEntityRecognition", - "x-ms-client-name": "ExportedCustomEntityRecognitionProjectAssets" - }, - "TextAnalysisAuthoringExportedCustomMultiLabelClassificationDocument": { - "description": "Represents an exported document of a custom multi-label classification project.", - "type": "object", - "properties": { - "classes": { - "description": "The document classes.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedDocumentClass" - } - }, - "location": { - "description": "The location of the document in the storage.", - "type": "string" - }, - "language": { - "description": "Represents the document language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - }, - "dataset": { - "description": "The dataset for this document. Allowed values are 'Train' and 'Test'.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedCustomMultiLabelClassificationDocument" - }, - "TextAnalysisAuthoringExportedCustomMultiLabelClassificationProjectAssets": { - "description": "Represents the exported assets for a custom multi-label classification project.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringExportedProjectAssets" - } - ], - "properties": { - "classes": { - "description": "The list of classes in the project.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedClass" - } - }, - "documents": { - "description": "The list of documents in the project.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedCustomMultiLabelClassificationDocument" - } - } - }, - "x-ms-discriminator-value": "CustomMultiLabelClassification", - "x-ms-client-name": "ExportedCustomMultiLabelClassificationProjectAssets" - }, - "TextAnalysisAuthoringExportedCustomSingleLabelClassificationDocument": { - "description": "Represents an exported document for a custom single-label classification project.", - "type": "object", - "properties": { - "class": { - "description": "The class of the documents.", - "$ref": "#/definitions/TextAnalysisAuthoringExportedDocumentClass" - }, - "location": { - "description": "The location of the document in the storage.", - "type": "string" - }, - "language": { - "description": "Represents the document language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - }, - "dataset": { - "description": "The dataset for this document. Allowed values are 'Train' and 'Test'.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedCustomSingleLabelClassificationDocument" - }, - "TextAnalysisAuthoringExportedCustomSingleLabelClassificationProjectAssets": { - "description": "Represents the exported assets for a single-label classification project.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringExportedProjectAssets" - } - ], - "properties": { - "classes": { - "description": "The list of classes belonging to this project.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedClass" - } - }, - "documents": { - "description": "The list of documents belonging to this project.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedCustomSingleLabelClassificationDocument" - } - } - }, - "x-ms-discriminator-value": "CustomSingleLabelClassification", - "x-ms-client-name": "ExportedCustomSingleLabelClassificationProjectAssets" - }, - "TextAnalysisAuthoringExportedDocumentClass": { - "description": "Represents a classification label for a document.", - "type": "object", - "properties": { - "category": { - "type": "string" - } - }, - "x-ms-client-name": "ExportedDocumentClass" - }, - "TextAnalysisAuthoringExportedDocumentEntityLabel": { - "description": "Represents an entity label for a document.", - "type": "object", - "properties": { - "category": { - "description": "The entity category.", - "type": "string" - }, - "offset": { - "format": "int32", - "description": "Start position for the entity text.", - "type": "integer" - }, - "length": { - "format": "int32", - "description": "Length for the entity text.", - "type": "integer" - } - }, - "x-ms-client-name": "ExportedDocumentEntityLabel" - }, - "TextAnalysisAuthoringExportedDocumentRegion": { - "description": "Represents a region in a document for entity labeling.", - "type": "object", - "properties": { - "regionOffset": { - "format": "int32", - "description": "Start position for the region.", - "type": "integer" - }, - "regionLength": { - "format": "int32", - "description": "Length for the region text.", - "type": "integer" - }, - "labels": { - "description": "The list of entity labels belonging to this region.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringExportedDocumentEntityLabel" - } - } - }, - "x-ms-client-name": "ExportedDocumentRegion" - }, - "TextAnalysisAuthoringExportedEntity": { - "description": "Represents an entity in an exported project.", - "type": "object", - "properties": { - "category": { - "description": "The entity category.", - "type": "string" - } - }, - "x-ms-client-name": "ExportedEntity" - }, - "TextAnalysisAuthoringExportedProject": { - "description": "Represents an exported project.", - "required": [ - "projectFileVersion", - "stringIndexType", - "metadata" - ], - "type": "object", - "properties": { - "projectFileVersion": { - "description": "The version of the exported file.", - "type": "string" - }, - "stringIndexType": { - "description": "Specifies the method used to interpret string offsets. For additional information see https://aka.ms/text-analytics-offsets.", - "$ref": "#/definitions/TextAnalysisAuthoringStringIndexType" - }, - "metadata": { - "description": "Represents the project metadata.", - "$ref": "#/definitions/TextAnalysisAuthoringCreateProjectOptions" - }, - "assets": { - "description": "Represents the project assets.", - "$ref": "#/definitions/TextAnalysisAuthoringExportedProjectAssets" - } - }, - "x-ms-client-name": "ExportedProject" - }, - "TextAnalysisAuthoringExportedProjectAssets": { - "description": "Represents the assets of an exported project.", - "required": [ - "projectKind" - ], - "type": "object", - "properties": { - "projectKind": { - "description": "The type of the project the assets belong to.", - "$ref": "#/definitions/TextAnalysisAuthoringProjectKind" - } - }, - "discriminator": "projectKind", - "x-ms-client-name": "ExportedProjectAssets" - }, - "TextAnalysisAuthoringExportProjectJobState": { - "description": "Represents the state of an export job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringJobState" - } - ], - "properties": { - "resultUrl": { - "description": "The URL to use in order to download the exported project.", - "type": "string" - } - }, - "x-ms-client-name": "ExportProjectJobState" - }, - "TextAnalysisAuthoringImportProjectJobState": { - "description": "Represents the state of an import job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringJobState" - } - ], - "x-ms-client-name": "ImportProjectJobState" - }, - "TextAnalysisAuthoringJobState": { - "description": "Represents a job's state.", - "required": [ - "jobId", - "createdDateTime", - "lastUpdatedDateTime", - "status" - ], - "type": "object", - "properties": { - "jobId": { - "description": "The job ID.", - "type": "string" - }, - "createdDateTime": { - "format": "date-time", - "description": "The creation date time of the job.", - "type": "string" - }, - "lastUpdatedDateTime": { - "format": "date-time", - "description": "The last date time the job was updated.", - "type": "string" - }, - "expirationDateTime": { - "format": "date-time", - "description": "The expiration date time of the job.", - "type": "string" - }, - "status": { - "description": "The job status.", - "$ref": "#/definitions/TextAnalysisAuthoringJobStatus" - }, - "warnings": { - "description": "The warnings that were encountered while executing the job.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringWarning" - } - }, - "errors": { - "description": "The errors encountered while executing the job.", - "type": "array", - "items": { - "$ref": "common.json#/definitions/Error" - } - } - }, - "x-ms-client-name": "JobState" - }, - "TextAnalysisAuthoringJobStatus": { - "enum": [ - "notStarted", - "running", - "succeeded", - "failed", - "cancelled", - "cancelling", - "partiallyCompleted" - ], - "type": "string", - "x-ms-enum": { - "name": "JobStatus", - "modelAsString": true - }, - "x-ms-client-name": "JobStatus" - }, - "TextAnalysisAuthoringLoadSnapshotJobState": { - "description": "Represents the state of loading a snapshot job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringJobState" - } - ], - "x-ms-client-name": "LoadSnapshotJobState" - }, - "TextAnalysisAuthoringMultiLabelClassEvaluationSummary": { - "description": "Represents the evaluation summary of a class in a multi-label classification project.", - "required": [ - "f1", - "precision", - "recall", - "truePositiveCount", - "trueNegativeCount", - "falsePositiveCount", - "falseNegativeCount" - ], - "type": "object", - "properties": { - "f1": { - "format": "double", - "description": "Represents the model precision", - "type": "number" - }, - "precision": { - "format": "double", - "description": "Represents the model recall", - "type": "number" - }, - "recall": { - "format": "double", - "description": "Represents the model F1 score", - "type": "number" - }, - "truePositiveCount": { - "format": "int32", - "description": "Represents the count of true positive", - "type": "integer" - }, - "trueNegativeCount": { - "format": "int32", - "description": "Represents the count of true negative", - "type": "integer" - }, - "falsePositiveCount": { - "format": "int32", - "description": "Represents the count of false positive", - "type": "integer" - }, - "falseNegativeCount": { - "format": "int32", - "description": "Represents the count of false negative", - "type": "integer" - } - }, - "x-ms-client-name": "MultiLabelClassEvaluationSummary" - }, - "TextAnalysisAuthoringMultiLabelClassificationEvaluationSummary": { - "description": "Represents the evaluation summary for a multi-label classification project.", - "required": [ - "classes", - "microF1", - "microPrecision", - "microRecall", - "macroF1", - "macroPrecision", - "macroRecall" - ], - "type": "object", - "properties": { - "classes": { - "description": "Represents the classes evaluation", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/TextAnalysisAuthoringMultiLabelClassEvaluationSummary" - } - }, - "microF1": { - "format": "float", - "description": "Represents the micro F1", - "type": "number" - }, - "microPrecision": { - "format": "float", - "description": "Represents the micro precision", - "type": "number" - }, - "microRecall": { - "format": "float", - "description": "Represents the micro recall", - "type": "number" - }, - "macroF1": { - "format": "float", - "description": "Represents the macro F1", - "type": "number" - }, - "macroPrecision": { - "format": "float", - "description": "Represents the macro precision", - "type": "number" - }, - "macroRecall": { - "format": "float", - "description": "Represents the macro recall", - "type": "number" - } - }, - "x-ms-client-name": "MultiLabelClassificationEvaluationSummary" - }, - "TextAnalysisAuthoringProjectDeletionJobState": { - "description": "Represents the state of a project deletion job.", - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringJobState" - } - ], - "x-ms-client-name": "ProjectDeletionJobState" - }, - "TextAnalysisAuthoringProjectDeployment": { - "description": "Represents a project deployment.", - "required": [ - "deploymentName", - "modelId", - "lastTrainedDateTime", - "lastDeployedDateTime", - "deploymentExpirationDate", - "modelTrainingConfigVersion" - ], - "type": "object", - "properties": { - "deploymentName": { - "description": "Represents deployment name.", - "type": "string" - }, - "modelId": { - "description": "Represents deployment modelId.", - "type": "string" - }, - "lastTrainedDateTime": { - "format": "date-time", - "description": "Represents deployment last trained time.", - "type": "string" - }, - "lastDeployedDateTime": { - "format": "date-time", - "description": "Represents deployment last deployed time.", - "type": "string" - }, - "deploymentExpirationDate": { - "format": "date", - "description": "Represents deployment expiration date in the runtime.", - "type": "string" - }, - "modelTrainingConfigVersion": { - "description": "Represents model training config version.", - "type": "string" - } - }, - "x-ms-client-name": "ProjectDeployment" - }, - "TextAnalysisAuthoringProjectDeployments": { - "description": "Represents a list of retrieved deployments.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of retrieved deployments.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectDeployment" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "ProjectDeployments" - }, - "TextAnalysisAuthoringProjectKind": { - "enum": [ - "CustomSingleLabelClassification", - "CustomMultiLabelClassification", - "CustomEntityRecognition" - ], - "type": "string", - "x-ms-enum": { - "name": "ProjectKind", - "modelAsString": true, - "values": [ - { - "value": "CustomSingleLabelClassification", - "description": "For building a classification model to classify text using your own data. Each file will have only one label. For example, file 1 is classified as A and file 2 is classified as B." - }, - { - "value": "CustomMultiLabelClassification", - "description": "For building a classification model to classify text using your own data. Each file can have one or many labels. For example, file 1 is classified as A, B, and C and file 2 is classified as B and C." - }, - { - "value": "CustomEntityRecognition", - "description": "For building an extraction model to identify your domain categories using your own data." - } - ] - }, - "x-ms-client-name": "ProjectKind" - }, - "TextAnalysisAuthoringProjectMetadata": { - "description": "Represents the metadata of a project.", - "required": [ - "createdDateTime", - "lastModifiedDateTime", - "projectKind", - "storageInputContainerName", - "projectName", - "language" - ], - "type": "object", - "properties": { - "createdDateTime": { - "format": "date-time", - "description": "Represents the project creation datetime.", - "type": "string" - }, - "lastModifiedDateTime": { - "format": "date-time", - "description": "Represents the project last modification datetime.", - "type": "string" - }, - "lastTrainedDateTime": { - "format": "date-time", - "description": "Represents the project last training datetime.", - "type": "string" - }, - "lastDeployedDateTime": { - "format": "date-time", - "description": "Represents the project last deployment datetime.", - "type": "string" - }, - "projectKind": { - "description": "The project kind.", - "$ref": "#/definitions/TextAnalysisAuthoringProjectKind" - }, - "storageInputContainerName": { - "description": "The storage container name.", - "type": "string" - }, - "settings": { - "description": "The project settings.", - "$ref": "#/definitions/TextAnalysisAuthoringProjectSettings" - }, - "projectName": { - "description": "The new project name.", - "type": "string" - }, - "multilingual": { - "description": "Whether the project would be used for multiple languages or not.", - "type": "boolean" - }, - "description": { - "description": "The project description.", - "type": "string" - }, - "language": { - "description": "The project language. This is BCP-47 representation of a language. For example, use \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - } - }, - "x-ms-client-name": "ProjectMetadata" - }, - "TextAnalysisAuthoringProjectSettings": { - "description": "Represents the settings used to define the project behavior.", - "type": "object", - "properties": { - "confidenceThreshold": { - "format": "float", - "description": "The threshold of the class with the highest confidence, at which the prediction will automatically be changed to \"None\". The value of the threshold should be between 0 and 1 inclusive.", - "type": "number" - } - }, - "x-ms-client-name": "ProjectSettings" - }, - "TextAnalysisAuthoringProjectsMetadata": { - "description": "Represents a list of retrieved projects' metadata.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of projects.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectMetadata" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "ProjectsMetadata" - }, - "TextAnalysisAuthoringProjectTrainedModel": { - "description": "Represents a trained model.", - "required": [ - "label", - "modelId", - "lastTrainedDateTime", - "lastTrainingDurationInSeconds", - "modelExpirationDate", - "modelTrainingConfigVersion", - "hasSnapshot" - ], - "type": "object", - "properties": { - "label": { - "description": "The trained model label.", - "type": "string" - }, - "modelId": { - "description": "The model ID.", - "type": "string" - }, - "lastTrainedDateTime": { - "format": "date-time", - "description": "The last trained date time of the model.", - "type": "string" - }, - "lastTrainingDurationInSeconds": { - "format": "int32", - "description": "The duration of the model's last training request in seconds.", - "type": "integer" - }, - "modelExpirationDate": { - "format": "date", - "description": "The model expiration date.", - "type": "string" - }, - "modelTrainingConfigVersion": { - "description": "The model training config version.", - "type": "string" - }, - "hasSnapshot": { - "description": "The flag to indicate if the trained model has a snapshot ready.", - "type": "boolean" - } - }, - "x-ms-client-name": "ProjectTrainedModel" - }, - "TextAnalysisAuthoringProjectTrainedModels": { - "description": "Represents a list of retrieved trained models.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of retrieved jobs.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringProjectTrainedModel" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "ProjectTrainedModels" - }, - "TextAnalysisAuthoringSingleLabelClassEvaluationSummary": { - "description": "Represents the evaluation summary for a class in a single-label classification project.", - "required": [ - "f1", - "precision", - "recall", - "truePositiveCount", - "trueNegativeCount", - "falsePositiveCount", - "falseNegativeCount" - ], - "type": "object", - "properties": { - "f1": { - "format": "double", - "description": "Represents the model precision", - "type": "number" - }, - "precision": { - "format": "double", - "description": "Represents the model recall", - "type": "number" - }, - "recall": { - "format": "double", - "description": "Represents the model F1 score", - "type": "number" - }, - "truePositiveCount": { - "format": "int32", - "description": "Represents the count of true positive", - "type": "integer" - }, - "trueNegativeCount": { - "format": "int32", - "description": "Represents the count of true negative", - "type": "integer" - }, - "falsePositiveCount": { - "format": "int32", - "description": "Represents the count of false positive", - "type": "integer" - }, - "falseNegativeCount": { - "format": "int32", - "description": "Represents the count of false negative", - "type": "integer" - } - }, - "x-ms-client-name": "SingleLabelClassEvaluationSummary" - }, - "TextAnalysisAuthoringSingleLabelClassificationEvaluationSummary": { - "description": "Represents the evaluation summary for a custom single-label classification project.", - "required": [ - "confusionMatrix", - "classes", - "microF1", - "microPrecision", - "microRecall", - "macroF1", - "macroPrecision", - "macroRecall" - ], - "type": "object", - "properties": { - "confusionMatrix": { - "description": "Represents the confusion matrix between two classes (the two classes can be the same). The matrix is between the class that was labelled and the class that was predicted.", - "$ref": "#/definitions/TextAnalysisAuthoringConfusionMatrix" - }, - "classes": { - "description": "Represents the classes evaluation", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/TextAnalysisAuthoringSingleLabelClassEvaluationSummary" - } - }, - "microF1": { - "format": "float", - "description": "Represents the micro F1", - "type": "number" - }, - "microPrecision": { - "format": "float", - "description": "Represents the micro precision", - "type": "number" - }, - "microRecall": { - "format": "float", - "description": "Represents the micro recall", - "type": "number" - }, - "macroF1": { - "format": "float", - "description": "Represents the macro F1", - "type": "number" - }, - "macroPrecision": { - "format": "float", - "description": "Represents the macro precision", - "type": "number" - }, - "macroRecall": { - "format": "float", - "description": "Represents the macro recall", - "type": "number" - } - }, - "x-ms-client-name": "SingleLabelClassificationEvaluationSummary" - }, - "TextAnalysisAuthoringStringIndexType": { - "enum": [ - "Utf16CodeUnit" - ], - "type": "string", - "x-ms-enum": { - "name": "StringIndexType", - "modelAsString": true, - "values": [ - { - "value": "Utf16CodeUnit", - "description": "The offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." - } - ] - }, - "x-ms-client-name": "StringIndexType" - }, - "TextAnalysisAuthoringSubTrainingJobState": { - "description": "Represents the detailed state of a training sub-operation.", - "required": [ - "percentComplete", - "status" - ], - "type": "object", - "properties": { - "percentComplete": { - "format": "int32", - "description": "Represents progress percentage.", - "type": "integer" - }, - "startDateTime": { - "format": "date-time", - "description": "Represents the start date time.", - "type": "string" - }, - "endDateTime": { - "format": "date-time", - "description": "Represents the end date time.", - "type": "string" - }, - "status": { - "description": "Represents the status of the sub-operation.", - "$ref": "#/definitions/TextAnalysisAuthoringJobStatus" - } - }, - "x-ms-client-name": "SubTrainingJobState" - }, - "TextAnalysisAuthoringSupportedLanguage": { - "description": "Represents a supported language.", - "required": [ - "languageName", - "languageCode" - ], - "type": "object", - "properties": { - "languageName": { - "description": "The language name.", - "type": "string" - }, - "languageCode": { - "description": "The language code. This is BCP-47 representation of a language. For example, \"en\" for English, \"en-gb\" for English (UK), \"es\" for Spanish etc.", - "type": "string" - } - }, - "x-ms-client-name": "SupportedLanguage" - }, - "TextAnalysisAuthoringSupportedLanguages": { - "description": "Represents a list of retrieved languages.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of the languages.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringSupportedLanguage" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "SupportedLanguages" - }, - "TextAnalysisAuthoringSwapDeploymentsOptions": { - "description": "Represents the options for swapping two deployments together.", - "required": [ - "firstDeploymentName", - "secondDeploymentName" - ], - "type": "object", - "properties": { - "firstDeploymentName": { - "description": "Represents the first deployment name.", - "type": "string" - }, - "secondDeploymentName": { - "description": "Represents the second deployment name.", - "type": "string" - } - }, - "x-ms-client-name": "SwapDeploymentsOptions" - }, - "TextAnalysisAuthoringTrainingConfigVersion": { - "description": "Represents a training config version.", - "required": [ - "trainingConfigVersion", - "modelExpirationDate" - ], - "type": "object", - "properties": { - "trainingConfigVersion": { - "description": "Represents the version of the config.", - "type": "string" - }, - "modelExpirationDate": { - "format": "date", - "description": "Represents the training config version expiration date.", - "type": "string" - } - }, - "x-ms-client-name": "TrainingConfigVersion" - }, - "TextAnalysisAuthoringTrainingConfigVersions": { - "description": "Represents a list of training config versions.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of the training config versions.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringTrainingConfigVersion" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "TrainingConfigVersions" - }, - "TextAnalysisAuthoringTrainingJobOptions": { - "description": "Represents the options for starting a new training job.", - "required": [ - "modelLabel", - "trainingConfigVersion" - ], - "type": "object", - "properties": { - "modelLabel": { - "description": "Represents the output model label.", - "type": "string" - }, - "trainingConfigVersion": { - "description": "Represents training config version.", - "type": "string" - }, - "evaluationOptions": { - "description": "Represents the evaluation options. By default, the evaluation kind is percentage, with training split percentage as 80, and testing split percentage as 20.", - "$ref": "#/definitions/TextAnalysisAuthoringEvaluationOptions" - } - }, - "x-ms-client-name": "TrainingJobOptions" - }, - "TextAnalysisAuthoringTrainingJobResult": { - "description": "Represents the output of a training job.", - "required": [ - "modelLabel", - "trainingConfigVersion", - "trainingStatus" - ], - "type": "object", - "properties": { - "modelLabel": { - "description": "Represents trained model label.", - "type": "string" - }, - "trainingConfigVersion": { - "description": "Represents training config version.", - "type": "string" - }, - "trainingStatus": { - "description": "Represents model train status.", - "$ref": "#/definitions/TextAnalysisAuthoringSubTrainingJobState" - }, - "evaluationStatus": { - "description": "Represents model evaluation status.", - "$ref": "#/definitions/TextAnalysisAuthoringSubTrainingJobState" - }, - "estimatedEndDateTime": { - "format": "date-time", - "description": "Represents the estimate end date time for training and evaluation.", - "type": "string" - } - }, - "x-ms-client-name": "TrainingJobResult" - }, - "TextAnalysisAuthoringTrainingJobs": { - "description": "Represents a list of retrieved training jobs.", - "required": [ - "value", - "nextLink" - ], - "type": "object", - "properties": { - "value": { - "description": "The list of jobs.", - "type": "array", - "items": { - "$ref": "#/definitions/TextAnalysisAuthoringTrainingJobState" - } - }, - "nextLink": { - "description": "The next page link.", - "type": "string", - "x-nullable": true - } - }, - "x-ms-client-name": "TrainingJobs" - }, - "TextAnalysisAuthoringTrainingJobState": { - "description": "Represents the state of a training job.", - "required": [ - "result" - ], - "type": "object", - "allOf": [ - { - "$ref": "#/definitions/TextAnalysisAuthoringJobState" - } - ], - "properties": { - "result": { - "description": "Represents training tasks detailed result.", - "$ref": "#/definitions/TextAnalysisAuthoringTrainingJobResult" - } - }, - "x-ms-client-name": "TrainingJobState" - }, - "TextAnalysisAuthoringWarning": { - "description": "Represents a warning that was encountered while executing the request.", - "required": [ - "code", - "message" - ], - "type": "object", - "properties": { - "code": { - "description": "The warning code.", - "type": "string" - }, - "message": { - "description": "The warning message.", - "type": "string" - } - }, - "x-ms-client-name": "Warning" - } - }, - "parameters": { - "TextAnalysisAuthoringStringIndexTypeQueryParameter": { - "in": "query", - "name": "stringIndexType", - "description": "Specifies the method used to interpret string offsets. For additional information see https://aka.ms/text-analytics-offsets.", - "required": true, - "type": "string", - "enum": [ - "Utf16CodeUnit" - ], - "x-ms-enum": { - "name": "StringIndexType", - "modelAsString": true, - "values": [ - { - "value": "Utf16CodeUnit", - "description": "The offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." - } - ] - }, - "x-ms-parameter-location": "method" - }, - "TextAnalysisAuthoringJobIdPathParameter": { - "in": "path", - "name": "jobId", - "description": "The job ID.", - "required": true, - "type": "string", - "x-ms-parameter-location": "method" - }, - "TextAnalysisAuthoringTrainedModelLabelPathParameter": { - "in": "path", - "name": "trainedModelLabel", - "description": "The trained model label.", - "required": true, - "type": "string", - "x-ms-parameter-location": "method" - }, - "TextAnalysisAuthoringProjectKindQueryOptionalParameter": { - "in": "query", - "name": "projectKind", - "description": "The project kind, default value is CustomSingleLabelClassification.", - "type": "string", - "enum": [ - "CustomSingleLabelClassification", - "CustomMultiLabelClassification", - "CustomEntityRecognition" - ], - "x-ms-enum": { - "name": "ProjectKind", - "modelAsString": true, - "values": [ - { - "value": "CustomSingleLabelClassification", - "description": "For building a classification model to classify text using your own data. Each file will have only one label. For example, file 1 is classified as A and file 2 is classified as B." - }, - { - "value": "CustomMultiLabelClassification", - "description": "For building a classification model to classify text using your own data. Each file can have one or many labels. For example, file 1 is classified as A, B, and C and file 2 is classified as B and C." - }, - { - "value": "CustomEntityRecognition", - "description": "For building an extraction model to identify your domain categories using your own data." - } - ] - }, - "x-ms-parameter-location": "method" - }, - "TextAnalysisAuthoringProjectKindQueryParameter": { - "in": "query", - "name": "projectKind", - "description": "The project kind.", - "required": true, - "type": "string", - "enum": [ - "CustomSingleLabelClassification", - "CustomMultiLabelClassification", - "CustomEntityRecognition" - ], - "x-ms-enum": { - "name": "ProjectKind", - "modelAsString": true, - "values": [ - { - "value": "CustomSingleLabelClassification", - "description": "For building a classification model to classify text using your own data. Each file will have only one label. For example, file 1 is classified as A and file 2 is classified as B." - }, - { - "value": "CustomMultiLabelClassification", - "description": "For building a classification model to classify text using your own data. Each file can have one or many labels. For example, file 1 is classified as A, B, and C and file 2 is classified as B and C." - }, - { - "value": "CustomEntityRecognition", - "description": "For building an extraction model to identify your domain categories using your own data." - } - ] - }, - "x-ms-parameter-location": "method" - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/analyzetext.json b/dev/cognitiveservices/data-plane/Language/analyzetext.json deleted file mode 100644 index 528bd0ea4ce6..000000000000 --- a/dev/cognitiveservices/data-plane/Language/analyzetext.json +++ /dev/null @@ -1,2882 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Microsoft Cognitive Language Service - Text Analysis", - "description": "The language service API is a suite of natural language processing (NLP) skills built with best-in-class Microsoft machine learning algorithms. The API can be used to analyze unstructured text for tasks such as sentiment analysis, key phrase extraction, language detection and question answering. Further documentation can be found in https://docs.microsoft.com/azure/cognitive-services/language-service/overview.", - "version": "2023-04-01" - }, - "securityDefinitions": { - "AADToken": { - "type": "oauth2", - "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", - "flow": "implicit", - "description": "These are the [Azure Active Directory OAuth2](https://docs.microsoft.com/azure/active-directory/develop/v1-overview) Flows. When paired with [Azure role-based access](https://docs.microsoft.com/azure/role-based-access-control/overview) control it can be used to control access to Azure Maps REST APIs. Azure role-based access controls are used to designate access to one or more Azure Maps resource account or sub-resources. Any user, group, or service principal can be granted access via a built-in role or a custom role composed of one or more permissions to Azure Maps REST APIs.\n\nTo implement scenarios, we recommend viewing [authentication concepts](https://aka.ms/amauth). In summary, this security definition provides a solution for modeling application(s) via objects capable of access control on specific APIs and scopes.\n\n#### Notes\n* This security definition **requires** the use of the `x-ms-client-id` header to indicate which Azure Maps resource the application is requesting access to. This can be acquired from the [Maps management API](https://aka.ms/amauthdetails).\n* \nThe `Authorization URL` is specific to the Azure public cloud instance. Sovereign clouds have unique Authorization URLs and Azure Active directory configurations. \n* \nThe Azure role-based access control is configured from the [Azure management plane](https://aka.ms/amrbac) via Azure portal, PowerShell, CLI, Azure SDKs, or REST APIs.\n* \nUsage of the [Azure Maps Web SDK](https://aka.ms/amaadmc) allows for configuration based setup of an application for multiple use cases.\n* Currently, Azure Active Directory [v1.0 or v2.0](https://docs.microsoft.com/azure/active-directory/develop/azure-ad-endpoint-comparison) supports Work, School, and Guests but does not support Personal accounts.", - "scopes": { - "https://cognitiveservices.azure.com/.default": "https://cognitiveservices.azure.com/.default" - } - }, - "apim_key": { - "type": "apiKey", - "description": "A subscription key for a Language service resource.", - "name": "Ocp-Apim-Subscription-Key", - "in": "header" - } - }, - "security": [ - { - "AADToken": [ - "https://cognitiveservices.azure.com/.default" - ] - }, - { - "apim_key": [] - } - ], - "x-ms-parameterized-host": { - "hostTemplate": "{Endpoint}/language", - "useSchemePrefix": false, - "parameters": [ - { - "$ref": "common.json#/parameters/Endpoint" - } - ] - }, - "paths": { - "/:analyze-text": { - "post": { - "summary": "Request text analysis over a collection of documents.", - "description": "Submit a collection of text documents for analysis. Specify a single unique task to be executed immediately.", - "operationId": "AnalyzeText", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/ShowStats" - }, - { - "description": "Collection of documents to analyze and a single task to execute.", - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/AnalyzeTextTask" - }, - "required": true - } - ], - "responses": { - "200": { - "description": "A successful call result", - "schema": { - "$ref": "#/definitions/AnalyzeTextTaskResult" - } - }, - "default": { - "description": "Unexpected error", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Entity Linking Request": { - "$ref": "./examples/SuccessfulEntityLinkingRequest.json" - }, - "Successful Entity Recognition Request": { - "$ref": "./examples/SuccessfulEntityRecognitionRequest.json" - }, - "Successful Key Phrase Extraction Request": { - "$ref": "./examples/SuccessfulKeyPhraseExtractionRequest.json" - }, - "Successful PII Entity Recognition Request": { - "$ref": "./examples/SuccessfulPiiEntityRecognitionRequest.json" - }, - "Successful Language Detection Request": { - "$ref": "./examples/SuccessfulLanguageDetectionRequest.json" - }, - "Successful Sentiment Analysis Request": { - "$ref": "./examples/SuccessfulSentimentAnalysisRequest.json" - } - } - } - }, - "/analyze-text/jobs": { - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "description": "Submit a collection of text documents for analysis. Specify one or more unique tasks to be executed as a long-running operation.", - "operationId": "AnalyzeText_SubmitJob", - "summary": "Submit text analysis job", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "description": "Collection of documents to analyze and one or more tasks to execute.", - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/AnalyzeTextJobsInput" - }, - "required": true - } - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the analysis job.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Submit Analysis Job Request": { - "$ref": "./examples/SuccessfulAnalyzeTextJobsMultipleTaskSubmitRequest.json" - }, - "Successful Submit Abstractive Summarization Task": { - "$ref": "./examples/text/SuccessfulAbstractiveSummarizationTaskSubmit.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/analyze-text/jobs/{jobId}": { - "get": { - "produces": [ - "application/json" - ], - "description": "Get the status of an analysis job. A job may consist of one or more tasks. Once all tasks are succeeded, the job will transition to the succeeded state and results will be available for each task.", - "operationId": "AnalyzeText_JobStatus", - "summary": "Get analysis status and results", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobId" - }, - { - "$ref": "#/parameters/ShowStats" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - } - ], - "responses": { - "200": { - "description": "Analysis job status and metadata.", - "schema": { - "$ref": "#/definitions/AnalyzeTextJobState" - } - }, - "default": { - "description": "Unexpected error", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Get Text Analysis Job Status Request": { - "$ref": "./examples/SuccessfulAnalyzeTextJobsMultipleTaskStatusRequest.json" - }, - "Successful Get Abstractive Summarization Result": { - "$ref": "./examples/text/SuccessfulAbstractiveSummarizationTaskResult.json" - } - } - } - }, - "/analyze-text/jobs/{jobId}:cancel": { - "post": { - "produces": [ - "application/json" - ], - "description": "Cancel a long-running Text Analysis job.", - "operationId": "AnalyzeText_CancelJob", - "summary": "Cancel a long-running Text Analysis job", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobId" - } - ], - "responses": { - "202": { - "description": "Cancel Job request has been received.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Unexpected error", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful Job Delete Request": { - "$ref": ".//examples//SuccessfulAnalyzeTextJobsCancelRequest.json" - } - }, - "x-ms-long-running-operation": true - } - } - }, - "definitions": { - "AnalyzeTextTaskKind": { - "type": "string", - "description": "Enumeration of supported Text Analysis tasks.", - "enum": [ - "SentimentAnalysis", - "EntityRecognition", - "PiiEntityRecognition", - "KeyPhraseExtraction", - "LanguageDetection", - "EntityLinking" - ], - "x-ms-enum": { - "name": "AnalyzeTextTaskKind", - "modelAsString": true - } - }, - "AnalyzeTextLROTaskKind": { - "type": "string", - "description": "Enumeration of supported long-running Text Analysis tasks.", - "enum": [ - "SentimentAnalysis", - "EntityRecognition", - "PiiEntityRecognition", - "KeyPhraseExtraction", - "EntityLinking", - "Healthcare", - "ExtractiveSummarization", - "CustomEntityRecognition", - "CustomSingleLabelClassification", - "CustomMultiLabelClassification", - "AbstractiveSummarization" - ], - "x-ms-enum": { - "name": "AnalyzeTextLROTaskKind", - "modelAsString": true - } - }, - "AnalyzeTextTaskResultsKind": { - "type": "string", - "description": "Enumeration of supported Text Analysis task results.", - "enum": [ - "SentimentAnalysisResults", - "EntityRecognitionResults", - "PiiEntityRecognitionResults", - "KeyPhraseExtractionResults", - "LanguageDetectionResults", - "EntityLinkingResults" - ], - "x-ms-enum": { - "name": "AnalyzeTextTaskResultsKind", - "modelAsString": true - } - }, - "AnalyzeTextLROResultsKind": { - "type": "string", - "description": "Enumeration of supported Text Analysis long-running operation task results.", - "enum": [ - "SentimentAnalysisLROResults", - "EntityRecognitionLROResults", - "PiiEntityRecognitionLROResults", - "KeyPhraseExtractionLROResults", - "EntityLinkingLROResults", - "HealthcareLROResults", - "ExtractiveSummarizationLROResults", - "CustomEntityRecognitionLROResults", - "CustomSingleLabelClassificationLROResults", - "CustomMultiLabelClassificationLROResults", - "AbstractiveSummarizationLROResults" - ], - "x-ms-enum": { - "name": "AnalyzeTextLROResultsKind", - "modelAsString": true - } - }, - "MultiLanguageAnalysisInput": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "items": { - "$ref": "#/definitions/MultiLanguageInput" - } - } - } - }, - "LanguageDetectionAnalysisInput": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "items": { - "$ref": "#/definitions/LanguageInput" - } - } - } - }, - "AnalyzeTextTask": { - "type": "object", - "discriminator": "kind", - "required": [ - "kind" - ], - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeTextTaskKind" - } - } - }, - "AnalyzeTextLROTask": { - "type": "object", - "discriminator": "kind", - "required": [ - "kind" - ], - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeTextLROTaskKind" - } - }, - "allOf": [ - { - "$ref": "#/definitions/TaskIdentifier" - } - ] - }, - "AnalyzeTextTaskResult": { - "type": "object", - "discriminator": "kind", - "required": [ - "kind" - ], - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeTextTaskResultsKind" - } - } - }, - "AnalyzeTextEntityLinkingInput": { - "type": "object", - "properties": { - "analysisInput": { - "$ref": "#/definitions/MultiLanguageAnalysisInput" - }, - "parameters": { - "$ref": "#/definitions/EntityLinkingTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTask" - } - ], - "x-ms-discriminator-value": "EntityLinking" - }, - "AnalyzeTextEntityRecognitionInput": { - "type": "object", - "properties": { - "analysisInput": { - "$ref": "#/definitions/MultiLanguageAnalysisInput" - }, - "parameters": { - "$ref": "#/definitions/EntitiesTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTask" - } - ], - "x-ms-discriminator-value": "EntityRecognition" - }, - "AnalyzeTextKeyPhraseExtractionInput": { - "type": "object", - "properties": { - "analysisInput": { - "$ref": "#/definitions/MultiLanguageAnalysisInput" - }, - "parameters": { - "$ref": "#/definitions/KeyPhraseTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTask" - } - ], - "x-ms-discriminator-value": "KeyPhraseExtraction" - }, - "AnalyzeTextPiiEntitiesRecognitionInput": { - "type": "object", - "properties": { - "analysisInput": { - "$ref": "#/definitions/MultiLanguageAnalysisInput" - }, - "parameters": { - "$ref": "#/definitions/PiiTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTask" - } - ], - "x-ms-discriminator-value": "PiiEntityRecognition" - }, - "AnalyzeTextLanguageDetectionInput": { - "type": "object", - "properties": { - "analysisInput": { - "$ref": "#/definitions/LanguageDetectionAnalysisInput" - }, - "parameters": { - "$ref": "#/definitions/LanguageDetectionTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTask" - } - ], - "x-ms-discriminator-value": "LanguageDetection" - }, - "AnalyzeTextSentimentAnalysisInput": { - "type": "object", - "properties": { - "analysisInput": { - "$ref": "#/definitions/MultiLanguageAnalysisInput" - }, - "parameters": { - "$ref": "#/definitions/SentimentAnalysisTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTask" - } - ], - "x-ms-discriminator-value": "SentimentAnalysis" - }, - "AnalyzeTextJobsInput": { - "type": "object", - "properties": { - "displayName": { - "description": "Optional display name for the analysis job.", - "type": "string" - }, - "analysisInput": { - "$ref": "#/definitions/MultiLanguageAnalysisInput" - }, - "tasks": { - "description": "The set of tasks to execute on the input documents.", - "type": "array", - "items": { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - } - }, - "required": [ - "analysisInput", - "tasks" - ] - }, - "TaskIdentifier": { - "type": "object", - "description": "Base task object.", - "properties": { - "taskName": { - "type": "string" - } - } - }, - "TaskParameters": { - "type": "object", - "description": "Base parameters object for a text analysis task.", - "properties": { - "loggingOptOut": { - "type": "boolean", - "default": false - } - } - }, - "PreBuiltTaskParameters": { - "type": "object", - "description": "Parameters object for a text analysis task using pre-built models.", - "properties": { - "modelVersion": { - "type": "string", - "default": "latest" - } - }, - "allOf": [ - { - "$ref": "#/definitions/TaskParameters" - } - ] - }, - "CustomTaskParameters": { - "type": "object", - "description": "Parameters object for a text analysis task using custom models.", - "properties": { - "projectName": { - "type": "string", - "description": "This field indicates the project name for the model." - }, - "deploymentName": { - "type": "string", - "description": "This field indicates the deployment name for the model." - } - }, - "allOf": [ - { - "$ref": "#/definitions/TaskParameters" - } - ], - "required": [ - "projectName", - "deploymentName" - ] - }, - "CustomResult": { - "type": "object", - "properties": { - "errors": { - "type": "array", - "description": "Errors by document id.", - "items": { - "$ref": "common.json#/definitions/DocumentError" - } - }, - "statistics": { - "$ref": "common.json#/definitions/RequestStatistics" - }, - "projectName": { - "type": "string", - "description": "This field indicates the project name for the model." - }, - "deploymentName": { - "type": "string", - "description": "This field indicates the deployment name for the model." - } - }, - "required": [ - "errors", - "projectName", - "deploymentName" - ] - }, - "CustomEntitiesTaskParameters": { - "type": "object", - "description": "Supported parameters for a Custom Entities task.", - "properties": { - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - }, - "allOf": [ - { - "$ref": "#/definitions/CustomTaskParameters" - } - ] - }, - "CustomEntitiesLROTask": { - "type": "object", - "description": "Use custom models to ease the process of information extraction from unstructured documents like contracts or financial documents", - "properties": { - "parameters": { - "$ref": "#/definitions/CustomEntitiesTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "CustomEntityRecognition" - }, - "CustomEntitiesResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "allOf": [ - { - "$ref": "#/definitions/EntitiesDocumentResult" - } - ] - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/CustomResult" - } - ], - "required": [ - "documents" - ] - }, - "CustomSingleLabelClassificationTaskParameters": { - "type": "object", - "description": "Supported parameters for a Custom Single Classification task.", - "allOf": [ - { - "$ref": "#/definitions/CustomTaskParameters" - } - ] - }, - "CustomSingleLabelClassificationLROTask": { - "type": "object", - "description": "Use custom models to classify text into single label taxonomy", - "properties": { - "parameters": { - "$ref": "#/definitions/CustomSingleLabelClassificationTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "CustomSingleLabelClassification" - }, - "CustomLabelClassificationResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "allOf": [ - { - "$ref": "#/definitions/ClassificationDocumentResult" - } - ] - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/CustomResult" - } - ], - "required": [ - "documents" - ] - }, - "ClassificationResult": { - "type": "object", - "required": [ - "category", - "confidenceScore" - ], - "properties": { - "category": { - "type": "string", - "description": "Classification type." - }, - "confidenceScore": { - "type": "number", - "format": "double", - "description": "Confidence score between 0 and 1 of the recognized class." - } - } - }, - "CustomMultiLabelClassificationTaskParameters": { - "type": "object", - "description": "Supported parameters for a Custom Multi Classification task.", - "allOf": [ - { - "$ref": "#/definitions/CustomTaskParameters" - } - ] - }, - "CustomMultiLabelClassificationLROTask": { - "type": "object", - "description": "Use custom models to classify text into multi label taxonomy", - "properties": { - "parameters": { - "$ref": "#/definitions/CustomMultiLabelClassificationTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "CustomMultiLabelClassification" - }, - "ClassificationDocumentResult": { - "type": "object", - "properties": { - "class": { - "type": "array", - "items": { - "$ref": "#/definitions/ClassificationResult" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ], - "required": [ - "class" - ] - }, - "HealthcareTaskParameters": { - "type": "object", - "description": "Supported parameters for a Healthcare task.", - "properties": { - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - }, - "allOf": [ - { - "$ref": "#/definitions/PreBuiltTaskParameters" - } - ] - }, - "HealthcareLROTask": { - "type": "object", - "properties": { - "parameters": { - "$ref": "#/definitions/HealthcareTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "Healthcare" - }, - "HealthcareResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "items": { - "allOf": [ - { - "$ref": "#/definitions/HealthcareEntitiesDocumentResult" - } - ] - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "HealthcareEntitiesDocumentResult": { - "type": "object", - "properties": { - "entities": { - "description": "Healthcare entities.", - "type": "array", - "items": { - "$ref": "#/definitions/HealthcareEntity" - } - }, - "relations": { - "type": "array", - "description": "Healthcare entity relations.", - "items": { - "$ref": "#/definitions/HealthcareRelation" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ], - "required": [ - "entities", - "relations" - ] - }, - "HealthcareEntity": { - "type": "object", - "properties": { - "text": { - "type": "string", - "description": "Entity text as appears in the request." - }, - "category": { - "x-ms-enum": { - "name": "healthcareEntityCategory", - "modelAsString": true - }, - "type": "string", - "description": "Healthcare Entity Category.", - "enum": [ - "BodyStructure", - "Age", - "Gender", - "Ethnicity", - "ExaminationName", - "Date", - "Direction", - "Frequency", - "MeasurementValue", - "MeasurementUnit", - "RelationalOperator", - "Time", - "Course", - "GeneOrProtein", - "Variant", - "Expression", - "MutationType", - "AdministrativeEvent", - "CareEnvironment", - "HealthcareProfession", - "Diagnosis", - "SymptomOrSign", - "ConditionQualifier", - "ConditionScale", - "MedicationClass", - "MedicationName", - "Dosage", - "MedicationForm", - "MedicationRoute", - "FamilyRelation", - "TreatmentName", - "Allergen", - "Employment", - "LivingStatus", - "SubstanceUse", - "SubstanceUseAmount" - ] - }, - "subcategory": { - "type": "string", - "description": "(Optional) Entity sub type." - }, - "offset": { - "type": "integer", - "format": "int32", - "description": "Start position for the entity text. Use of different 'stringIndexType' values can affect the offset returned." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "Length for the entity text. Use of different 'stringIndexType' values can affect the length returned." - }, - "confidenceScore": { - "type": "number", - "format": "double", - "description": "Confidence score between 0 and 1 of the extracted entity." - }, - "assertion": { - "type": "object", - "$ref": "#/definitions/HealthcareAssertion" - }, - "name": { - "description": "Preferred name for the entity. Example: 'histologically' would have a 'name' of 'histologic'.", - "type": "string" - }, - "links": { - "description": "Entity references in known data sources.", - "type": "array", - "items": { - "$ref": "#/definitions/HealthcareEntityLink" - } - } - }, - "required": [ - "text", - "category", - "offset", - "length", - "confidenceScore" - ] - }, - "HealthcareRelation": { - "type": "object", - "description": "Every relation is an entity graph of a certain relationType, where all entities are connected and have specific roles within the relation context.", - "required": [ - "relationType", - "entities" - ], - "properties": { - "relationType": { - "description": "Type of relation. Examples include: `DosageOfMedication` or 'FrequencyOfMedication', etc.", - "type": "string", - "enum": [ - "Abbreviation", - "BodySiteOfCondition", - "BodySiteOfTreatment", - "CourseOfCondition", - "CourseOfExamination", - "CourseOfMedication", - "CourseOfTreatment", - "DirectionOfBodyStructure", - "DirectionOfCondition", - "DirectionOfExamination", - "DirectionOfTreatment", - "DosageOfMedication", - "ExaminationFindsCondition", - "ExpressionOfGene", - "ExpressionOfVariant", - "FormOfMedication", - "FrequencyOfCondition", - "FrequencyOfMedication", - "FrequencyOfTreatment", - "MutationTypeOfGene", - "MutationTypeOfVariant", - "QualifierOfCondition", - "RelationOfExamination", - "RouteOfMedication", - "ScaleOfCondition", - "TimeOfCondition", - "TimeOfEvent", - "TimeOfExamination", - "TimeOfMedication", - "TimeOfTreatment", - "UnitOfCondition", - "UnitOfExamination", - "ValueOfCondition", - "ValueOfExamination", - "VariantOfGene" - ], - "x-ms-enum": { - "name": "relationType", - "modelAsString": true - } - }, - "confidenceScore": { - "type": "number", - "format": "double", - "description": "Confidence score between 0 and 1 of the extracted relation." - }, - "entities": { - "description": "The entities in the relation.", - "type": "array", - "items": { - "$ref": "#/definitions/HealthcareRelationEntity" - } - } - } - }, - "HealthcareAssertion": { - "type": "object", - "properties": { - "conditionality": { - "description": "Describes any conditionality on the entity.", - "type": "string", - "enum": [ - "hypothetical", - "conditional" - ], - "x-ms-enum": { - "name": "Conditionality", - "modelAsString": false - } - }, - "certainty": { - "description": "Describes the entities certainty and polarity.", - "type": "string", - "enum": [ - "positive", - "positivePossible", - "neutralPossible", - "negativePossible", - "negative" - ], - "x-ms-enum": { - "name": "Certainty", - "modelAsString": false - } - }, - "association": { - "description": "Describes if the entity is the subject of the text or if it describes someone else.", - "type": "string", - "enum": [ - "subject", - "other" - ], - "x-ms-enum": { - "name": "Association", - "modelAsString": false - } - } - } - }, - "HealthcareRelationEntity": { - "type": "object", - "required": [ - "ref", - "role" - ], - "properties": { - "ref": { - "description": "Reference link object, using a JSON pointer RFC 6901 (URI Fragment Identifier Representation), pointing to the entity .", - "type": "string" - }, - "role": { - "description": "Role of entity in the relationship. For example: 'CD20-positive diffuse large B-cell lymphoma' has the following entities with their roles in parenthesis: CD20 (GeneOrProtein), Positive (Expression), diffuse large B-cell lymphoma (Diagnosis).", - "type": "string" - } - } - }, - "HealthcareEntityLink": { - "type": "object", - "required": [ - "dataSource", - "id" - ], - "properties": { - "dataSource": { - "description": "Entity Catalog. Examples include: UMLS, CHV, MSH, etc.", - "type": "string" - }, - "id": { - "description": "Entity id in the given source catalog.", - "type": "string" - } - } - }, - "SentimentAnalysisTaskParameters": { - "type": "object", - "description": "Supported parameters for a Sentiment Analysis task.", - "properties": { - "opinionMining": { - "type": "boolean", - "default": false - }, - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - }, - "allOf": [ - { - "$ref": "#/definitions/PreBuiltTaskParameters" - } - ] - }, - "SentimentAnalysisLROTask": { - "type": "object", - "description": "An object representing the task definition for a Sentiment Analysis task.", - "properties": { - "parameters": { - "$ref": "#/definitions/SentimentAnalysisTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "SentimentAnalysis" - }, - "SentimentTaskResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/SentimentResponse" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTaskResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "SentimentAnalysisResults" - }, - "SentimentResponse": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Sentiment analysis per document.", - "items": { - "allOf": [ - { - "$ref": "#/definitions/SentimentDocumentResult" - } - ] - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "SentimentDocumentResult": { - "type": "object", - "properties": { - "sentiment": { - "type": "string", - "description": "Predicted sentiment for document (Negative, Neutral, Positive, or Mixed).", - "enum": [ - "positive", - "neutral", - "negative", - "mixed" - ], - "x-ms-enum": { - "name": "DocumentSentimentValue", - "modelAsString": false - } - }, - "confidenceScores": { - "description": "Document level sentiment confidence scores between 0 and 1 for each sentiment class.", - "$ref": "#/definitions/SentimentConfidenceScorePerLabel" - }, - "sentences": { - "type": "array", - "description": "Sentence level sentiment analysis.", - "items": { - "$ref": "#/definitions/SentenceSentiment" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ], - "required": [ - "sentiment", - "confidenceScores", - "sentences" - ] - }, - "SentimentConfidenceScorePerLabel": { - "type": "object", - "required": [ - "positive", - "neutral", - "negative" - ], - "properties": { - "positive": { - "type": "number", - "format": "double", - "description": "Confidence score for positive sentiment" - }, - "neutral": { - "type": "number", - "format": "double", - "description": "Confidence score for neutral sentiment" - }, - "negative": { - "type": "number", - "format": "double", - "description": "Confidence score for negative sentiment" - } - }, - "description": "Represents the confidence scores between 0 and 1 across all sentiment classes: positive, neutral, negative." - }, - "SentenceSentiment": { - "type": "object", - "required": [ - "text", - "sentiment", - "confidenceScores", - "offset", - "length" - ], - "properties": { - "text": { - "type": "string", - "description": "The sentence text." - }, - "sentiment": { - "type": "string", - "description": "The predicted Sentiment for the sentence.", - "enum": [ - "positive", - "neutral", - "negative" - ], - "x-ms-enum": { - "name": "SentenceSentimentValue", - "modelAsString": false - } - }, - "confidenceScores": { - "description": "The sentiment confidence score between 0 and 1 for the sentence for all classes.", - "$ref": "#/definitions/SentimentConfidenceScorePerLabel" - }, - "offset": { - "type": "integer", - "format": "int32", - "description": "The sentence offset from the start of the document." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "The length of the sentence." - }, - "targets": { - "type": "array", - "description": "The array of sentence targets for the sentence.", - "items": { - "$ref": "#/definitions/SentenceTarget" - } - }, - "assessments": { - "type": "array", - "description": "The array of assessments for the sentence.", - "items": { - "$ref": "#/definitions/SentenceAssessment" - } - } - } - }, - "SentenceTarget": { - "type": "object", - "required": [ - "confidenceScores", - "length", - "offset", - "relations", - "sentiment", - "text" - ], - "properties": { - "sentiment": { - "type": "string", - "enum": [ - "positive", - "mixed", - "negative" - ], - "x-ms-enum": { - "name": "TokenSentimentValue", - "modelAsString": false - }, - "description": "Targeted sentiment in the sentence." - }, - "confidenceScores": { - "description": "Target sentiment confidence scores for the target in the sentence.", - "$ref": "#/definitions/TargetConfidenceScoreLabel" - }, - "offset": { - "type": "integer", - "format": "int32", - "description": "The target offset from the start of the sentence." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "The length of the target." - }, - "text": { - "type": "string", - "description": "The target text detected." - }, - "relations": { - "type": "array", - "description": "The array of either assessment or target objects which is related to the target.", - "items": { - "$ref": "#/definitions/TargetRelation" - } - } - } - }, - "SentenceAssessment": { - "type": "object", - "required": [ - "confidenceScores", - "isNegated", - "length", - "offset", - "sentiment", - "text" - ], - "properties": { - "sentiment": { - "type": "string", - "enum": [ - "positive", - "mixed", - "negative" - ], - "x-ms-enum": { - "name": "TokenSentimentValue", - "modelAsString": false - }, - "description": "Assessment sentiment in the sentence." - }, - "confidenceScores": { - "description": "Assessment sentiment confidence scores in the sentence.", - "$ref": "#/definitions/TargetConfidenceScoreLabel" - }, - "offset": { - "type": "integer", - "format": "int32", - "description": "The assessment offset from the start of the sentence." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "The length of the assessment." - }, - "text": { - "type": "string", - "description": "The assessment text detected." - }, - "isNegated": { - "type": "boolean", - "description": "The indicator representing if the assessment is negated." - } - } - }, - "TargetRelation": { - "type": "object", - "required": [ - "ref", - "relationType" - ], - "properties": { - "relationType": { - "type": "string", - "enum": [ - "assessment", - "target" - ], - "x-ms-enum": { - "name": "TargetRelationType", - "modelAsString": false - }, - "description": "The type related to the target." - }, - "ref": { - "type": "string", - "description": "The JSON pointer indicating the linked object." - } - } - }, - "TargetConfidenceScoreLabel": { - "type": "object", - "required": [ - "negative", - "positive" - ], - "properties": { - "positive": { - "type": "number", - "format": "double", - "description": "Confidence score for positive sentiment" - }, - "negative": { - "type": "number", - "format": "double", - "description": "Confidence score for negative sentiment" - } - }, - "description": "Represents the confidence scores across all sentiment classes: positive and negative." - }, - "EntitiesTaskParameters": { - "type": "object", - "description": "Supported parameters for an Entity Recognition task.", - "properties": { - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - }, - "allOf": [ - { - "$ref": "#/definitions/PreBuiltTaskParameters" - } - ] - }, - "EntitiesLROTask": { - "type": "object", - "description": "An object representing the task definition for an Entities Recognition task.", - "properties": { - "parameters": { - "$ref": "#/definitions/EntitiesTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "EntityRecognition" - }, - "EntitiesTaskResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/EntitiesResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTaskResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "EntityRecognitionResults" - }, - "EntitiesResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "allOf": [ - { - "$ref": "#/definitions/EntitiesDocumentResult" - } - ] - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "EntitiesDocumentResult": { - "type": "object", - "properties": { - "entities": { - "type": "array", - "description": "Recognized entities in the document.", - "items": { - "$ref": "#/definitions/Entity" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ], - "required": [ - "entities" - ] - }, - "Entity": { - "type": "object", - "required": [ - "text", - "category", - "offset", - "length", - "confidenceScore" - ], - "properties": { - "text": { - "type": "string", - "description": "Entity text as appears in the request." - }, - "category": { - "type": "string", - "description": "Entity type." - }, - "subcategory": { - "type": "string", - "description": "(Optional) Entity sub type." - }, - "offset": { - "type": "integer", - "format": "int32", - "description": "Start position for the entity text. Use of different 'stringIndexType' values can affect the offset returned." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "Length for the entity text. Use of different 'stringIndexType' values can affect the length returned." - }, - "confidenceScore": { - "type": "number", - "format": "double", - "description": "Confidence score between 0 and 1 of the extracted entity." - } - } - }, - "EntityLinkingTaskParameters": { - "type": "object", - "description": "Supported parameters for an Entity Linking task.", - "properties": { - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - }, - "allOf": [ - { - "$ref": "#/definitions/PreBuiltTaskParameters" - } - ] - }, - "EntityLinkingLROTask": { - "type": "object", - "description": "An object representing the task definition for an Entity Linking task.", - "properties": { - "parameters": { - "$ref": "#/definitions/EntityLinkingTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "EntityLinking" - }, - "EntityLinkingTaskResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/EntityLinkingResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTaskResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "EntityLinkingResults" - }, - "EntityLinkingResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "allOf": [ - { - "$ref": "#/definitions/LinkedEntitiesDocumentResult" - } - ] - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "LinkedEntitiesDocumentResult": { - "type": "object", - "required": [ - "entities" - ], - "properties": { - "entities": { - "type": "array", - "description": "Recognized well known entities in the document.", - "items": { - "$ref": "#/definitions/LinkedEntity" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ] - }, - "LinkedEntity": { - "type": "object", - "required": [ - "name", - "matches", - "language", - "url", - "dataSource" - ], - "properties": { - "name": { - "type": "string", - "description": "Entity Linking formal name." - }, - "matches": { - "type": "array", - "description": "List of instances this entity appears in the text.", - "items": { - "$ref": "#/definitions/Match" - } - }, - "language": { - "type": "string", - "description": "Language used in the data source." - }, - "id": { - "type": "string", - "description": "Unique identifier of the recognized entity from the data source." - }, - "url": { - "type": "string", - "description": "URL for the entity's page from the data source." - }, - "dataSource": { - "type": "string", - "description": "Data source used to extract entity linking, such as Wiki/Bing etc." - }, - "bingId": { - "type": "string", - "description": "Bing Entity Search API unique identifier of the recognized entity." - } - } - }, - "Match": { - "type": "object", - "required": [ - "confidenceScore", - "text", - "offset", - "length" - ], - "properties": { - "confidenceScore": { - "type": "number", - "format": "double", - "description": "If a well known item is recognized, a decimal number denoting the confidence level between 0 and 1 will be returned." - }, - "text": { - "type": "string", - "description": "Entity text as appears in the request." - }, - "offset": { - "type": "integer", - "format": "int32", - "description": "Start position for the entity match text." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "Length for the entity match text." - } - } - }, - "PiiTaskParameters": { - "type": "object", - "description": "Supported parameters for a PII Entities Recognition task.", - "properties": { - "domain": { - "$ref": "#/definitions/PiiDomain" - }, - "piiCategories": { - "$ref": "#/definitions/PiiCategories" - }, - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - }, - "allOf": [ - { - "$ref": "#/definitions/PreBuiltTaskParameters" - } - ] - }, - "PiiLROTask": { - "type": "object", - "description": "An object representing the task definition for a PII Entities Recognition task.", - "properties": { - "parameters": { - "$ref": "#/definitions/PiiTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "PiiEntityRecognition" - }, - "PiiTaskResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/PiiResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTaskResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "PiiEntityRecognitionResults" - }, - "PiiResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "allOf": [ - { - "$ref": "#/definitions/PiiEntitiesDocumentResult" - } - ] - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "PiiDomain": { - "type": "string", - "description": "The PII domain used for PII Entity Recognition.", - "default": "none", - "enum": [ - "phi", - "none" - ], - "x-ms-enum": { - "name": "PiiDomain", - "modelAsString": true, - "values": [ - { - "name": "phi", - "description": "Indicates that entities in the Personal Health Information domain should be redacted.", - "value": "phi" - }, - { - "name": "none", - "description": "Indicates that no domain is specified.", - "value": "none" - } - ] - } - }, - "PiiEntitiesDocumentResult": { - "type": "object", - "properties": { - "redactedText": { - "type": "string", - "description": "Returns redacted text." - }, - "entities": { - "type": "array", - "description": "Recognized entities in the document.", - "items": { - "$ref": "#/definitions/Entity" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ], - "required": [ - "redactedText", - "entities" - ] - }, - "PiiCategories": { - "description": "(Optional) describes the PII categories to return", - "items": { - "type": "string", - "x-ms-enum": { - "name": "PiiCategory", - "modelAsString": true - }, - "enum": [ - "ABARoutingNumber", - "ARNationalIdentityNumber", - "AUBankAccountNumber", - "AUDriversLicenseNumber", - "AUMedicalAccountNumber", - "AUPassportNumber", - "AUTaxFileNumber", - "AUBusinessNumber", - "AUCompanyNumber", - "ATIdentityCard", - "ATTaxIdentificationNumber", - "ATValueAddedTaxNumber", - "AzureDocumentDBAuthKey", - "AzureIAASDatabaseConnectionAndSQLString", - "AzureIoTConnectionString", - "AzurePublishSettingPassword", - "AzureRedisCacheString", - "AzureSAS", - "AzureServiceBusString", - "AzureStorageAccountKey", - "AzureStorageAccountGeneric", - "BENationalNumber", - "BENationalNumberV2", - "BEValueAddedTaxNumber", - "BRCPFNumber", - "BRLegalEntityNumber", - "BRNationalIDRG", - "BGUniformCivilNumber", - "CABankAccountNumber", - "CADriversLicenseNumber", - "CAHealthServiceNumber", - "CAPassportNumber", - "CAPersonalHealthIdentification", - "CASocialInsuranceNumber", - "CLIdentityCardNumber", - "CNResidentIdentityCardNumber", - "CreditCardNumber", - "HRIdentityCardNumber", - "HRNationalIDNumber", - "HRPersonalIdentificationNumber", - "HRPersonalIdentificationOIBNumberV2", - "CYIdentityCard", - "CYTaxIdentificationNumber", - "CZPersonalIdentityNumber", - "CZPersonalIdentityV2", - "DKPersonalIdentificationNumber", - "DKPersonalIdentificationV2", - "DrugEnforcementAgencyNumber", - "EEPersonalIdentificationCode", - "EUDebitCardNumber", - "EUDriversLicenseNumber", - "EUGPSCoordinates", - "EUNationalIdentificationNumber", - "EUPassportNumber", - "EUSocialSecurityNumber", - "EUTaxIdentificationNumber", - "FIEuropeanHealthNumber", - "FINationalID", - "FINationalIDV2", - "FIPassportNumber", - "FRDriversLicenseNumber", - "FRHealthInsuranceNumber", - "FRNationalID", - "FRPassportNumber", - "FRSocialSecurityNumber", - "FRTaxIdentificationNumber", - "FRValueAddedTaxNumber", - "DEDriversLicenseNumber", - "DEPassportNumber", - "DEIdentityCardNumber", - "DETaxIdentificationNumber", - "DEValueAddedNumber", - "GRNationalIDCard", - "GRNationalIDV2", - "GRTaxIdentificationNumber", - "HKIdentityCardNumber", - "HUValueAddedNumber", - "HUPersonalIdentificationNumber", - "HUTaxIdentificationNumber", - "INPermanentAccount", - "INUniqueIdentificationNumber", - "IDIdentityCardNumber", - "InternationalBankingAccountNumber", - "IEPersonalPublicServiceNumber", - "IEPersonalPublicServiceNumberV2", - "ILBankAccountNumber", - "ILNationalID", - "ITDriversLicenseNumber", - "ITFiscalCode", - "ITValueAddedTaxNumber", - "JPBankAccountNumber", - "JPDriversLicenseNumber", - "JPPassportNumber", - "JPResidentRegistrationNumber", - "JPSocialInsuranceNumber", - "JPMyNumberCorporate", - "JPMyNumberPersonal", - "JPResidenceCardNumber", - "LVPersonalCode", - "LTPersonalCode", - "LUNationalIdentificationNumberNatural", - "LUNationalIdentificationNumberNonNatural", - "MYIdentityCardNumber", - "MTIdentityCardNumber", - "MTTaxIDNumber", - "NLCitizensServiceNumber", - "NLCitizensServiceNumberV2", - "NLTaxIdentificationNumber", - "NLValueAddedTaxNumber", - "NZBankAccountNumber", - "NZDriversLicenseNumber", - "NZInlandRevenueNumber", - "NZMinistryOfHealthNumber", - "NZSocialWelfareNumber", - "NOIdentityNumber", - "PHUnifiedMultiPurposeIDNumber", - "PLIdentityCard", - "PLNationalID", - "PLNationalIDV2", - "PLPassportNumber", - "PLTaxIdentificationNumber", - "PLREGONNumber", - "PTCitizenCardNumber", - "PTCitizenCardNumberV2", - "PTTaxIdentificationNumber", - "ROPersonalNumericalCode", - "RUPassportNumberDomestic", - "RUPassportNumberInternational", - "SANationalID", - "SGNationalRegistrationIdentityCardNumber", - "SKPersonalNumber", - "SITaxIdentificationNumber", - "SIUniqueMasterCitizenNumber", - "ZAIdentificationNumber", - "KRResidentRegistrationNumber", - "ESDNI", - "ESSocialSecurityNumber", - "ESTaxIdentificationNumber", - "SQLServerConnectionString", - "SENationalID", - "SENationalIDV2", - "SEPassportNumber", - "SETaxIdentificationNumber", - "SWIFTCode", - "CHSocialSecurityNumber", - "TWNationalID", - "TWPassportNumber", - "TWResidentCertificate", - "THPopulationIdentificationCode", - "TRNationalIdentificationNumber", - "UKDriversLicenseNumber", - "UKElectoralRollNumber", - "UKNationalHealthNumber", - "UKNationalInsuranceNumber", - "UKUniqueTaxpayerNumber", - "USUKPassportNumber", - "USBankAccountNumber", - "USDriversLicenseNumber", - "USIndividualTaxpayerIdentification", - "USSocialSecurityNumber", - "UAPassportNumberDomestic", - "UAPassportNumberInternational", - "Organization", - "Email", - "URL", - "Age", - "PhoneNumber", - "IPAddress", - "Date", - "Person", - "Address", - "All", - "Default" - ] - }, - "type": "array", - "uniqueItems": true - }, - "KeyPhraseTaskParameters": { - "type": "object", - "description": "Supported parameters for a Key Phrase Extraction task.", - "allOf": [ - { - "$ref": "#/definitions/PreBuiltTaskParameters" - } - ] - }, - "ExtractiveSummarizationTaskParameters": { - "type": "object", - "description": "Supported parameters for an Extractive Summarization task.", - "properties": { - "sentenceCount": { - "type": "integer", - "default": 3 - }, - "sortBy": { - "$ref": "#/definitions/ExtractiveSummarizationSortingCriteria" - }, - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltTaskParameters" - } - ] - }, - "ExtractiveSummarizationLROTask": { - "type": "object", - "description": "An object representing the task definition for an Extractive Summarization task.", - "properties": { - "parameters": { - "$ref": "#/definitions/ExtractiveSummarizationTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "ExtractiveSummarization" - }, - "ExtractiveSummarizationResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "$ref": "#/definitions/ExtractedSummaryDocumentResult" - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "ExtractiveSummarizationSortingCriteria": { - "type": "string", - "default": "Offset", - "description": "The sorting criteria to use for the results of Extractive Summarization.", - "enum": [ - "Offset", - "Rank" - ], - "x-ms-enum": { - "name": "ExtractiveSummarizationSortingCriteria", - "modelAsString": true, - "values": [ - { - "name": "Offset", - "description": "Indicates that results should be sorted in order of appearance in the text.", - "value": "Offset" - }, - { - "name": "Rank", - "description": "Indicates that results should be sorted in order of importance (i.e. rank score) according to the model.", - "value": "Rank" - } - ] - } - }, - "ExtractedSummaryDocumentResult": { - "type": "object", - "properties": { - "sentences": { - "type": "array", - "description": "A ranked list of sentences representing the extracted summary.", - "items": { - "$ref": "#/definitions/ExtractedSummarySentence" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ], - "required": [ - "sentences" - ] - }, - "ExtractedSummarySentence": { - "type": "object", - "required": [ - "text", - "rankScore", - "offset", - "length" - ], - "properties": { - "text": { - "type": "string", - "description": "The extracted sentence text." - }, - "rankScore": { - "type": "number", - "format": "double", - "description": "A double value representing the relevance of the sentence within the summary. Higher values indicate higher importance." - }, - "offset": { - "type": "integer", - "format": "int32", - "description": "The sentence offset from the start of the document, based on the value of the parameter StringIndexType." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "The length of the sentence." - } - } - }, - "KeyPhraseLROTask": { - "type": "object", - "description": "An object representing the task definition for a Key Phrase Extraction task.", - "properties": { - "parameters": { - "$ref": "#/definitions/KeyPhraseTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "KeyPhraseExtraction" - }, - "KeyPhraseTaskResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/KeyPhraseResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTaskResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "KeyPhraseExtractionResults" - }, - "KeyPhraseResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "allOf": [ - { - "$ref": "#/definitions/KeyPhrasesDocumentResult" - } - ] - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "KeyPhrasesDocumentResult": { - "type": "object", - "properties": { - "keyPhrases": { - "type": "array", - "description": "A list of representative words or phrases. The number of key phrases returned is proportional to the number of words in the input document.", - "items": { - "type": "string" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ], - "required": [ - "keyPhrases" - ] - }, - "LanguageDetectionTaskParameters": { - "type": "object", - "description": "Supported parameters for a Language Detection task.", - "allOf": [ - { - "$ref": "#/definitions/PreBuiltTaskParameters" - } - ] - }, - "LanguageDetectionTaskResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/LanguageDetectionResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextTaskResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "LanguageDetectionResults" - }, - "LanguageDetectionResult": { - "type": "object", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "$ref": "#/definitions/LanguageDetectionDocumentResult" - } - } - }, - "allOf": [ - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "LanguageDetectionDocumentResult": { - "type": "object", - "properties": { - "detectedLanguage": { - "description": "Detected Language.", - "$ref": "#/definitions/DetectedLanguage" - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ], - "required": [ - "detectedLanguage" - ] - }, - "DetectedLanguage": { - "type": "object", - "required": [ - "name", - "iso6391Name", - "confidenceScore" - ], - "properties": { - "name": { - "type": "string", - "description": "Long name of a detected language (e.g. English, French)." - }, - "iso6391Name": { - "type": "string", - "description": "A two letter representation of the detected language according to the ISO 639-1 standard (e.g. en, fr)." - }, - "confidenceScore": { - "type": "number", - "format": "double", - "description": "A confidence score between 0 and 1. Scores close to 1 indicate 100% certainty that the identified language is true." - } - } - }, - "AnalyzeTextJobState": { - "allOf": [ - { - "$ref": "common.json#/definitions/JobState" - }, - { - "$ref": "#/definitions/TasksState" - }, - { - "$ref": "#/definitions/AnalyzeTextJobStatistics" - } - ] - }, - "Pagination": { - "properties": { - "nextLink": { - "type": "string" - } - }, - "type": "object" - }, - "JobErrors": { - "properties": { - "errors": { - "items": { - "$ref": "common.json#/definitions/Error" - }, - "type": "array" - } - }, - "type": "object" - }, - "AnalyzeTextJobStatistics": { - "properties": { - "statistics": { - "$ref": "common.json#/definitions/RequestStatistics" - } - }, - "type": "object" - }, - "TasksState": { - "properties": { - "tasks": { - "properties": { - "completed": { - "type": "integer" - }, - "failed": { - "type": "integer" - }, - "inProgress": { - "type": "integer" - }, - "total": { - "type": "integer" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - } - }, - "required": [ - "total", - "completed", - "failed", - "inProgress" - ], - "type": "object" - } - }, - "required": [ - "tasks" - ], - "type": "object" - }, - "TaskState": { - "properties": { - "lastUpdateDateTime": { - "format": "date-time", - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "notStarted", - "running", - "succeeded", - "failed", - "cancelled", - "cancelling" - ], - "x-ms-enum": { - "modelAsString": true, - "name": "State" - } - } - }, - "required": [ - "status", - "lastUpdateDateTime" - ], - "type": "object" - }, - "AnalyzeTextLROResult": { - "type": "object", - "discriminator": "kind", - "properties": { - "kind": { - "$ref": "#/definitions/AnalyzeTextLROResultsKind" - } - }, - "allOf": [ - { - "$ref": "#/definitions/TaskState" - }, - { - "$ref": "#/definitions/TaskIdentifier" - } - ], - "required": [ - "kind" - ] - }, - "EntityRecognitionLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/EntitiesResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "EntityRecognitionLROResults" - }, - "CustomEntityRecognitionLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/CustomEntitiesResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "CustomEntityRecognitionLROResults" - }, - "CustomSingleLabelClassificationLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/CustomLabelClassificationResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "CustomSingleLabelClassificationLROResults" - }, - "CustomMultiLabelClassificationLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/CustomLabelClassificationResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "CustomMultiLabelClassificationLROResults" - }, - "EntityLinkingLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/EntityLinkingResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "EntityLinkingLROResults" - }, - "PiiEntityRecognitionLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/PiiResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "PiiEntityRecognitionLROResults" - }, - "ExtractiveSummarizationLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/ExtractiveSummarizationResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "ExtractiveSummarizationLROResults" - }, - "HealthcareLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/HealthcareResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "HealthcareLROResults" - }, - "SentimentLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/SentimentResponse" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "SentimentAnalysisLROResults" - }, - "KeyPhraseExtractionLROResult": { - "type": "object", - "properties": { - "results": { - "$ref": "#/definitions/KeyPhraseResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "KeyPhraseExtractionLROResults" - }, - "DocumentResponse": { - "type": "object", - "properties": {} - }, - "DocumentResult": { - "type": "object", - "required": [ - "id", - "warnings" - ], - "properties": { - "id": { - "type": "string", - "description": "Unique, non-empty document identifier." - }, - "warnings": { - "type": "array", - "description": "Warnings encountered while processing document.", - "items": { - "$ref": "#/definitions/DocumentWarning" - } - }, - "statistics": { - "description": "if showStats=true was specified in the request this field will contain information about the document payload.", - "$ref": "#/definitions/DocumentStatistics" - } - } - }, - "DocumentWarning": { - "type": "object", - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "type": "string", - "enum": [ - "LongWordsInDocument", - "DocumentTruncated" - ], - "x-ms-enum": { - "name": "WarningCodeValue", - "modelAsString": true - }, - "description": "Error code." - }, - "message": { - "type": "string", - "description": "Warning message." - }, - "targetRef": { - "type": "string", - "description": "A JSON pointer reference indicating the target object." - } - } - }, - "DocumentStatistics": { - "type": "object", - "required": [ - "charactersCount", - "transactionsCount" - ], - "properties": { - "charactersCount": { - "type": "integer", - "format": "int32", - "description": "Number of text elements recognized in the document." - }, - "transactionsCount": { - "type": "integer", - "format": "int32", - "description": "Number of transactions for the document." - } - }, - "description": "if showStats=true was specified in the request this field will contain information about the document payload." - }, - "MultiLanguageInput": { - "type": "object", - "description": "Contains an input document to be analyzed by the service.", - "required": [ - "id", - "text" - ], - "properties": { - "id": { - "type": "string", - "description": "A unique, non-empty document identifier." - }, - "text": { - "type": "string", - "description": "The input text to process." - }, - "language": { - "type": "string", - "description": "(Optional) This is the 2 letter ISO 639-1 representation of a language. For example, use \"en\" for English; \"es\" for Spanish etc. If not set, use \"en\" for English as default." - } - } - }, - "LanguageInput": { - "type": "object", - "required": [ - "id", - "text" - ], - "properties": { - "id": { - "type": "string", - "description": "Unique, non-empty document identifier." - }, - "text": { - "type": "string" - }, - "countryHint": { - "type": "string" - } - } - }, - "AbstractiveSummarizationLROTask": { - "type": "object", - "description": "An object representing the task definition for an Abstractive Summarization task.", - "required": [ - "parameters" - ], - "properties": { - "parameters": { - "$ref": "#/definitions/AbstractiveSummarizationTaskParameters" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROTask" - } - ], - "x-ms-discriminator-value": "AbstractiveSummarization" - }, - "AbstractiveSummarizationTaskParameters": { - "type": "object", - "description": "Supported parameters for the pre-build Abstractive Summarization task.", - "allOf": [ - { - "$ref": "common.json#/definitions/AbstractiveSummarizationTaskParametersBase" - }, - { - "$ref": "common.json#/definitions/PreBuiltTaskParameters" - } - ] - }, - "AbstractiveSummarizationLROResult": { - "type": "object", - "description": "An object representing the results for an Abstractive Summarization task.", - "properties": { - "results": { - "$ref": "#/definitions/AbstractiveSummarizationResult" - } - }, - "allOf": [ - { - "$ref": "#/definitions/AnalyzeTextLROResult" - } - ], - "required": [ - "results" - ], - "x-ms-discriminator-value": "AbstractiveSummarizationLROResults" - }, - "AbstractiveSummarizationResultBase": { - "type": "object", - "description": "An object representing the summarization results of each document.", - "properties": { - "documents": { - "type": "array", - "description": "Response by document", - "items": { - "$ref": "#/definitions/AbstractiveSummaryDocumentResult" - } - } - }, - "required": [ - "documents" - ] - }, - "AbstractiveSummarizationResult": { - "type": "object", - "description": "An object representing the pre-build summarization results of each document.", - "allOf": [ - { - "$ref": "#/definitions/AbstractiveSummarizationResultBase" - }, - { - "$ref": "common.json#/definitions/PreBuiltResult" - } - ], - "required": [ - "documents" - ] - }, - "AbstractiveSummaryDocumentResult": { - "type": "object", - "description": "An object representing the summarization result of a single document.", - "properties": { - "summaries": { - "type": "array", - "description": "A list of abstractive summaries.", - "items": { - "$ref": "#/definitions/AbstractiveSummary" - } - } - }, - "allOf": [ - { - "$ref": "#/definitions/DocumentResult" - } - ], - "required": [ - "summaries" - ] - }, - "AbstractiveSummary": { - "type": "object", - "description": "An object representing a single summary with context for given document.", - "properties": { - "text": { - "type": "string", - "description": "The text of the summary." - }, - "contexts": { - "type": "array", - "description": "The context list of the summary.", - "items": { - "$ref": "common.json#/definitions/SummaryContext" - } - } - }, - "required": [ - "text" - ] - } - }, - "parameters": { - "ShowStats": { - "name": "showStats", - "in": "query", - "description": "(Optional) if set to true, response will contain request and document level statistics.", - "type": "boolean", - "required": false, - "x-ms-parameter-location": "method" - }, - "JobId": { - "description": "Job ID", - "format": "uuid", - "in": "path", - "name": "jobId", - "required": true, - "type": "string", - "x-ms-parameter-location": "method" - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/common.json b/dev/cognitiveservices/data-plane/Language/common.json deleted file mode 100644 index 7f583cdb4d14..000000000000 --- a/dev/cognitiveservices/data-plane/Language/common.json +++ /dev/null @@ -1,929 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Microsoft Cognitive Language Service", - "description": "The language service API is a suite of natural language processing (NLP) skills built with best-in-class Microsoft machine learning algorithms. The API can be used to analyze unstructured text for tasks such as sentiment analysis, key phrase extraction, language detection and question answering. Further documentation can be found in https://docs.microsoft.com/en-us/azure/cognitive-services/text-analytics/overview.", - "version": "2023-04-01" - }, - "paths": {}, - "definitions": { - "ErrorResponse": { - "type": "object", - "description": "Error response.", - "additionalProperties": false, - "properties": { - "error": { - "description": "The error object.", - "$ref": "#/definitions/Error" - } - }, - "required": [ - "error" - ] - }, - "Error": { - "type": "object", - "description": "The error object.", - "additionalProperties": true, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "description": "One of a server-defined set of error codes.", - "$ref": "#/definitions/ErrorCode" - }, - "message": { - "type": "string", - "description": "A human-readable representation of the error." - }, - "target": { - "type": "string", - "description": "The target of the error." - }, - "details": { - "type": "array", - "description": "An array of details about specific errors that led to this reported error.", - "items": { - "$ref": "#/definitions/Error" - } - }, - "innererror": { - "description": "An object containing more specific information than the current object about the error.", - "$ref": "#/definitions/InnerErrorModel" - } - } - }, - "InnerErrorModel": { - "type": "object", - "description": "An object containing more specific information about the error. As per Microsoft One API guidelines - https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses.", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "description": "One of a server-defined set of error codes.", - "$ref": "#/definitions/InnerErrorCode" - }, - "message": { - "type": "string", - "description": "Error message." - }, - "details": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Error details." - }, - "target": { - "type": "string", - "description": "Error target." - }, - "innererror": { - "description": "An object containing more specific information than the current object about the error.", - "$ref": "#/definitions/InnerErrorModel" - } - } - }, - "ErrorCode": { - "type": "string", - "description": "Human-readable error code.", - "x-ms-enum": { - "name": "ErrorCode", - "modelAsString": true - }, - "enum": [ - "InvalidRequest", - "InvalidArgument", - "Unauthorized", - "Forbidden", - "NotFound", - "ProjectNotFound", - "OperationNotFound", - "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchIndexNotFound", - "TooManyRequests", - "AzureCognitiveSearchThrottling", - "AzureCognitiveSearchIndexLimitReached", - "InternalServerError", - "ServiceUnavailable", - "Timeout", - "QuotaExceeded", - "Conflict", - "Warning" - ] - }, - "InnerErrorCode": { - "type": "string", - "description": "Human-readable error code.", - "x-ms-enum": { - "name": "InnerErrorCode", - "modelAsString": true - }, - "enum": [ - "InvalidRequest", - "InvalidParameterValue", - "KnowledgeBaseNotFound", - "AzureCognitiveSearchNotFound", - "AzureCognitiveSearchThrottling", - "ExtractionFailure", - "InvalidRequestBodyFormat", - "EmptyRequest", - "MissingInputDocuments", - "InvalidDocument", - "ModelVersionIncorrect", - "InvalidDocumentBatch", - "UnsupportedLanguageCode", - "InvalidCountryHint" - ] - }, - "Language": { - "type": "string", - "description": "Language of the text records. This is BCP-47 representation of a language. For example, use \"en\" for English; \"es\" for Spanish etc. If not set, use \"en\" for English as default." - }, - "StringIndexType": { - "type": "string", - "description": "Specifies the method used to interpret string offsets. Defaults to Text Elements (Graphemes) according to Unicode v8.0.0. For additional information see https://aka.ms/text-analytics-offsets.", - "default": "TextElements_v8", - "enum": [ - "TextElements_v8", - "UnicodeCodePoint", - "Utf16CodeUnit" - ], - "x-ms-enum": { - "name": "StringIndexType", - "modelAsString": true, - "values": [ - { - "value": "TextElements_v8", - "description": "Returned offset and length values will correspond to TextElements (Graphemes and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this option if your application is written in .Net Framework or .Net Core and you will be using StringInfo." - }, - { - "value": "UnicodeCodePoint", - "description": "Returned offset and length values will correspond to Unicode code points. Use this option if your application is written in a language that support Unicode, for example Python." - }, - { - "value": "Utf16CodeUnit", - "description": "Returned offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." - } - ] - } - }, - "TaskState": { - "description": "Returns the current state of the task.", - "properties": { - "lastUpdateDateTime": { - "description": "The last updated time in UTC for the task.", - "format": "date-time", - "type": "string" - }, - "status": { - "description": "The status of the task at the mentioned last update time.", - "enum": [ - "notStarted", - "running", - "succeeded", - "failed", - "cancelled", - "cancelling" - ], - "x-ms-enum": { - "modelAsString": true, - "name": "State" - } - } - }, - "required": [ - "status", - "lastUpdateDateTime" - ], - "type": "object" - }, - "TaskIdentifier": { - "type": "object", - "description": "Base task object.", - "properties": { - "taskName": { - "type": "string" - } - } - }, - "TaskParameters": { - "type": "object", - "description": "Base parameters object for a text analysis task.", - "properties": { - "loggingOptOut": { - "type": "boolean", - "default": false - } - } - }, - "PreBuiltTaskParameters": { - "type": "object", - "description": "Parameters object for a text analysis task using pre-built models.", - "properties": { - "modelVersion": { - "type": "string", - "default": "latest" - } - }, - "allOf": [ - { - "$ref": "#/definitions/TaskParameters" - } - ] - }, - "JobState": { - "properties": { - "displayName": { - "type": "string" - }, - "createdDateTime": { - "format": "date-time", - "type": "string" - }, - "expirationDateTime": { - "format": "date-time", - "type": "string" - }, - "jobId": { - "type": "string" - }, - "lastUpdatedDateTime": { - "format": "date-time", - "type": "string" - }, - "status": { - "enum": [ - "notStarted", - "running", - "succeeded", - "partiallyCompleted", - "failed", - "cancelled", - "cancelling" - ], - "type": "string", - "x-ms-enum": { - "modelAsString": true, - "name": "State" - } - }, - "errors": { - "items": { - "$ref": "#/definitions/Error" - }, - "type": "array" - }, - "nextLink": { - "type": "string" - } - }, - "required": [ - "jobId", - "lastUpdatedDateTime", - "createdDateTime", - "status" - ] - }, - "JobErrors": { - "properties": { - "errors": { - "items": { - "$ref": "#/definitions/Error" - }, - "type": "array" - } - }, - "type": "object" - }, - "DocumentError": { - "type": "object", - "description": "Contains details of errors encountered during a job execution.", - "required": [ - "id", - "error" - ], - "properties": { - "id": { - "type": "string", - "description": "The ID of the input document." - }, - "error": { - "type": "object", - "description": "Error encountered.", - "$ref": "#/definitions/Error" - } - } - }, - "InputWarning": { - "type": "object", - "description": "Contains details of warnings encountered during a job execution.", - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "type": "string", - "description": "Warning code." - }, - "message": { - "type": "string", - "description": "Warning message." - }, - "targetRef": { - "type": "string", - "description": "A JSON pointer reference indicating the target object." - } - } - }, - "RequestStatistics": { - "type": "object", - "required": [ - "documentsCount", - "validDocumentsCount", - "erroneousDocumentsCount", - "transactionsCount" - ], - "properties": { - "documentsCount": { - "type": "integer", - "format": "int32", - "description": "Number of documents submitted in the request." - }, - "validDocumentsCount": { - "type": "integer", - "format": "int32", - "description": "Number of valid documents. This excludes empty, over-size limit or non-supported languages documents." - }, - "erroneousDocumentsCount": { - "type": "integer", - "format": "int32", - "description": "Number of invalid documents. This includes empty, over-size limit or non-supported languages documents." - }, - "transactionsCount": { - "type": "integer", - "format": "int64", - "description": "Number of transactions for the request." - } - }, - "description": "if showStats=true was specified in the request this field will contain information about the request payload." - }, - "PreBuiltResult": { - "type": "object", - "properties": { - "errors": { - "type": "array", - "description": "Errors by document id.", - "items": { - "$ref": "#/definitions/DocumentError" - } - }, - "statistics": { - "$ref": "#/definitions/RequestStatistics" - }, - "modelVersion": { - "type": "string", - "description": "This field indicates which model is used for scoring." - } - }, - "required": [ - "errors", - "modelVersion" - ] - }, - "AnswersResult": { - "type": "object", - "description": "Represents List of Question Answers.", - "additionalProperties": false, - "properties": { - "answers": { - "type": "array", - "description": "Represents Answer Result list.", - "items": { - "$ref": "#/definitions/KnowledgeBaseAnswer" - } - } - } - }, - "KnowledgeBaseAnswer": { - "type": "object", - "description": "Represents knowledge base answer.", - "additionalProperties": false, - "properties": { - "questions": { - "type": "array", - "description": "List of questions associated with the answer.", - "items": { - "type": "string" - } - }, - "answer": { - "type": "string", - "description": "Answer text." - }, - "confidenceScore": { - "type": "number", - "format": "double", - "x-ms-client-name": "confidence", - "description": "Answer confidence score, value ranges from 0 to 1.", - "maximum": 1, - "minimum": 0 - }, - "id": { - "type": "integer", - "x-ms-client-name": "qnaId", - "description": "ID of the QnA result.", - "format": "int32" - }, - "source": { - "type": "string", - "description": "Source of QnA result." - }, - "metadata": { - "type": "object", - "description": "Metadata associated with the answer, useful to categorize or filter question answers.", - "additionalProperties": { - "type": "string" - } - }, - "dialog": { - "type": "object", - "$ref": "#/definitions/KnowledgeBaseAnswerDialog" - }, - "answerSpan": { - "type": "object", - "x-ms-client-name": "shortAnswer", - "description": "Answer span object of QnA with respect to user's question.", - "$ref": "#/definitions/AnswerSpan" - } - } - }, - "KnowledgeBaseAnswerDialog": { - "type": "object", - "description": "Dialog associated with Answer.", - "properties": { - "isContextOnly": { - "type": "boolean", - "description": "To mark if a prompt is relevant only with a previous question or not. If true, do not include this QnA as search result for queries without context; otherwise, if false, ignores context and includes this QnA in search result." - }, - "prompts": { - "type": "array", - "description": "List of prompts associated with the answer.", - "maxItems": 20, - "items": { - "$ref": "#/definitions/KnowledgeBaseAnswerPrompt" - } - } - } - }, - "KnowledgeBaseAnswerPrompt": { - "type": "object", - "description": "Prompt for an answer.", - "properties": { - "displayOrder": { - "type": "integer", - "description": "Index of the prompt - used in ordering of the prompts.", - "format": "int32" - }, - "qnaId": { - "type": "integer", - "description": "QnA ID corresponding to the prompt.", - "format": "int32" - }, - "displayText": { - "type": "string", - "description": "Text displayed to represent a follow up question prompt.", - "maxLength": 200 - } - } - }, - "AnswerSpan": { - "type": "object", - "description": "Answer span object of QnA.", - "additionalProperties": false, - "properties": { - "text": { - "type": "string", - "description": "Predicted text of answer span." - }, - "confidenceScore": { - "type": "number", - "x-ms-client-name": "confidence", - "description": "Predicted score of answer span, value ranges from 0 to 1.", - "format": "double", - "maximum": 1, - "minimum": 0 - }, - "offset": { - "type": "integer", - "description": "The answer span offset from the start of answer.", - "format": "int32" - }, - "length": { - "type": "integer", - "description": "The length of the answer span.", - "format": "int32" - } - } - }, - "AnswersOptions": { - "type": "object", - "description": "Parameters to query a knowledge base.", - "additionalProperties": false, - "properties": { - "qnaId": { - "type": "integer", - "description": "Exact QnA ID to fetch from the knowledge base, this field takes priority over question.", - "format": "int32" - }, - "question": { - "type": "string", - "description": "User question to query against the knowledge base." - }, - "top": { - "type": "integer", - "description": "Max number of answers to be returned for the question.", - "format": "int32" - }, - "userId": { - "type": "string", - "description": "Unique identifier for the user." - }, - "confidenceScoreThreshold": { - "type": "number", - "format": "double", - "x-ms-client-name": "confidenceThreshold", - "description": "Minimum threshold score for answers, value ranges from 0 to 1.", - "maximum": 1, - "minimum": 0 - }, - "context": { - "x-ms-client-name": "answerContext", - "description": "Context object with previous QnA's information.", - "$ref": "#/definitions/KnowledgeBaseAnswerContext" - }, - "rankerType": { - "type": "string", - "x-ms-client-name": "rankerKind", - "description": "Type of ranker to be used.", - "x-ms-enum": { - "name": "RankerKind", - "modelAsString": true, - "values": [ - { - "value": "QuestionOnly", - "description": "Question only ranker." - }, - { - "value": "Default", - "description": "Default ranker." - } - ] - }, - "enum": [ - "Default", - "QuestionOnly" - ] - }, - "filters": { - "description": "Filter QnAs based on given metadata list and knowledge base sources.", - "$ref": "#/definitions/QueryFilters" - }, - "answerSpanRequest": { - "x-ms-client-name": "shortAnswerOptions", - "description": "To configure Answer span prediction feature.", - "$ref": "#/definitions/ShortAnswerOptions" - }, - "includeUnstructuredSources": { - "type": "boolean", - "description": "(Optional) Flag to enable Query over Unstructured Sources." - } - } - }, - "KnowledgeBaseAnswerContext": { - "type": "object", - "description": "Context object with previous QnA's information.", - "additionalProperties": false, - "required": [ - "previousQnaId" - ], - "properties": { - "previousQnaId": { - "type": "integer", - "description": "Previous turn top answer result QnA ID.", - "format": "int32" - }, - "previousUserQuery": { - "type": "string", - "x-ms-client-name": "previousQuestion", - "description": "Previous user query." - } - } - }, - "QueryFilters": { - "type": "object", - "description": "filters over knowledge base.", - "additionalProperties": false, - "properties": { - "metadataFilter": { - "type": "object", - "$ref": "#/definitions/MetadataFilter" - }, - "sourceFilter": { - "type": "object", - "$ref": "#/definitions/SourceFilter" - }, - "logicalOperation": { - "type": "string", - "description": "Logical operation used to join metadata filter with source filter.", - "$ref": "#/definitions/LogicalOperationKind", - "default": "AND" - } - } - }, - "MetadataFilter": { - "type": "object", - "description": "Find QnAs that are associated with the given list of metadata.", - "additionalProperties": false, - "properties": { - "metadata": { - "type": "array", - "items": { - "$ref": "#/definitions/MetadataRecord" - } - }, - "logicalOperation": { - "type": "string", - "description": "Operation used to join metadata filters.", - "$ref": "#/definitions/LogicalOperationKind", - "default": "AND" - } - } - }, - "MetadataRecord": { - "type": "object", - "description": "Object to provide the key value pair for each metadata.", - "additionalProperties": false, - "required": [ - "key", - "value" - ], - "properties": { - "key": { - "type": "string", - "description": "Metadata Key from Metadata dictionary used in the QnA." - }, - "value": { - "type": "string", - "description": "Metadata Value from Metadata dictionary used in the QnA." - } - } - }, - "SourceFilter": { - "type": "array", - "description": "Find QnAs that are associated with any of the given list of sources in knowledge base.", - "items": { - "type": "string" - } - }, - "LogicalOperationKind": { - "type": "string", - "description": "Set to 'OR' or 'AND' for using corresponding logical operation.", - "x-ms-enum": { - "name": "LogicalOperationKind", - "modelAsString": true - }, - "enum": [ - "AND", - "OR" - ] - }, - "ShortAnswerOptions": { - "type": "object", - "description": "To configure Answer span prediction feature.", - "additionalProperties": false, - "required": [ - "enable" - ], - "properties": { - "enable": { - "type": "boolean", - "description": "Enable or disable Answer Span prediction.", - "enum": [ - true - ], - "x-ms-enum": { - "name": "enable", - "modelAsString": false - } - }, - "confidenceScoreThreshold": { - "type": "number", - "format": "double", - "x-ms-client-name": "confidenceThreshold", - "description": "Minimum threshold score required to include an answer span, value ranges from 0 to 1.", - "maximum": 1, - "minimum": 0 - }, - "topAnswersWithSpan": { - "type": "integer", - "x-ms-client-name": "top", - "description": "Number of Top answers to be considered for span prediction from 1 to 10.", - "format": "int32", - "maximum": 10, - "minimum": 1 - } - } - }, - "Sentiment": { - "type": "string", - "description": "Predicted sentiment.", - "enum": [ - "positive", - "neutral", - "negative", - "mixed" - ], - "x-ms-enum": { - "name": "TextSentiment", - "modelAsString": true, - "values": [ - { - "value": "positive", - "description": "Positive sentiment." - }, - { - "value": "neutral", - "description": "Neutral sentiment." - }, - { - "value": "negative", - "description": "Negative sentiment." - }, - { - "value": "mixed", - "description": "Mixed sentiment." - } - ] - } - }, - "SentimentConfidenceScores": { - "type": "object", - "required": [ - "positive", - "neutral", - "negative" - ], - "properties": { - "positive": { - "type": "number", - "format": "double", - "description": "Confidence score for positive sentiment" - }, - "neutral": { - "type": "number", - "format": "double", - "description": "Confidence score for neutral sentiment" - }, - "negative": { - "type": "number", - "format": "double", - "description": "Confidence score for negative sentiment" - } - }, - "description": "Represents the confidence scores between 0 and 1 across all sentiment classes: positive, neutral, negative." - }, - "AbstractiveSummarizationTaskParametersBase": { - "type": "object", - "description": "Supported parameters for an Abstractive Summarization task.", - "properties": { - "sentenceCount": { - "type": "integer", - "format": "int32", - "description": "It controls the approximate number of sentences in the output summaries." - }, - "stringIndexType": { - "$ref": "#/definitions/StringIndexType" - } - } - }, - "SummaryContext": { - "type": "object", - "description": "The context of the summary.", - "required": [ - "offset", - "length" - ], - "properties": { - "offset": { - "type": "integer", - "format": "int32", - "description": "Start position for the context. Use of different 'stringIndexType' values can affect the offset returned." - }, - "length": { - "type": "integer", - "format": "int32", - "description": "The length of the context. Use of different 'stringIndexType' values can affect the length returned." - } - } - } - }, - "parameters": { - "Endpoint": { - "name": "Endpoint", - "description": "Supported Cognitive Services endpoint (e.g., https://.api.cognitiveservices.azure.com).", - "x-ms-parameter-location": "client", - "required": true, - "type": "string", - "in": "path", - "x-ms-skip-url-encoding": true - }, - "ProjectNameQueryParameter": { - "name": "projectName", - "in": "query", - "required": true, - "type": "string", - "description": "The name of the project to use.", - "x-ms-parameter-location": "method" - }, - "ProjectNamePathParameter": { - "name": "projectName", - "in": "path", - "required": true, - "type": "string", - "maxLength": 100, - "description": "The name of the project to use.", - "x-ms-parameter-location": "method" - }, - "DeploymentNameQueryParameter": { - "name": "deploymentName", - "in": "query", - "required": true, - "type": "string", - "description": "The name of the specific deployment of the project to use.", - "x-ms-parameter-location": "method" - }, - "DeploymentNamePathParameter": { - "name": "deploymentName", - "in": "path", - "required": true, - "type": "string", - "description": "The name of the specific deployment of the project to use.", - "x-ms-parameter-location": "method" - }, - "ApiVersionParameter": { - "name": "api-version", - "in": "query", - "required": true, - "type": "string", - "description": "Client API version." - }, - "TopParameter": { - "name": "top", - "in": "query", - "description": "The maximum number of resources to return from the collection.", - "type": "integer", - "format": "int32", - "x-ms-parameter-location": "method" - }, - "SkipParameter": { - "name": "skip", - "in": "query", - "description": "An offset into the collection of the first resource to be returned.", - "type": "integer", - "format": "int32", - "x-ms-parameter-location": "method" - }, - "MaxPageSizeParameter": { - "name": "maxpagesize", - "in": "query", - "description": "The maximum number of resources to include in a single response.", - "type": "integer", - "format": "int32", - "x-ms-parameter-location": "method" - }, - "ShowStats": { - "name": "showStats", - "in": "query", - "description": "(Optional) if set to true, response will contain request and document level statistics.", - "type": "boolean", - "required": false, - "x-ms-parameter-location": "method" - }, - "JobId": { - "description": "Job ID", - "format": "uuid", - "in": "path", - "name": "jobId", - "required": true, - "type": "string", - "x-ms-parameter-location": "method" - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulAnalyzeTextJobsCancelRequest.json b/dev/cognitiveservices/data-plane/Language/examples/SuccessfulAnalyzeTextJobsCancelRequest.json deleted file mode 100644 index cab594cfd719..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulAnalyzeTextJobsCancelRequest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18" - }, - "responses": { - "202": { - "headers": { - "Operation-Location": "{Endpoint}/language/analyze-text/jobs/{jobId}?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulAnalyzeTextJobsMultipleTaskStatusRequest.json b/dev/cognitiveservices/data-plane/Language/examples/SuccessfulAnalyzeTextJobsMultipleTaskStatusRequest.json deleted file mode 100644 index 5823008ae035..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulAnalyzeTextJobsMultipleTaskStatusRequest.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "createdDateTime": "2020-10-01T15:00:45Z", - "displayName": "Extracting Location & US Region", - "expirationDateTime": "2020-10-03T15:01:03Z", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18", - "lastUpdatedDateTime": "2020-10-01T15:01:03Z", - "status": "succeeded", - "tasks": { - "completed": 2, - "failed": 0, - "inProgress": 0, - "total": 2, - "items": [ - { - "kind": "EntityRecognitionLROResults", - "taskName": "Recognize Entities", - "lastUpdateDateTime": "2020-10-01T15:01:03Z", - "status": "succeeded", - "results": { - "documents": [ - { - "entities": [ - { - "category": "Event", - "confidenceScore": 0.61, - "length": 4, - "offset": 18, - "text": "trip" - }, - { - "category": "Location", - "confidenceScore": 0.82, - "length": 7, - "offset": 26, - "subcategory": "GPE", - "text": "Seattle" - }, - { - "category": "DateTime", - "confidenceScore": 0.8, - "length": 9, - "offset": 34, - "subcategory": "DateRange", - "text": "last week" - } - ], - "id": "1", - "warnings": [] - }, - { - "entities": [ - { - "category": "Location", - "confidenceScore": 0.52, - "length": 3, - "offset": 14, - "subcategory": "GPE", - "text": "NYC" - }, - { - "category": "DateTime", - "confidenceScore": 0.8, - "length": 8, - "offset": 18, - "subcategory": "Date", - "text": "tomorrow" - } - ], - "id": "2", - "warnings": [] - } - ], - "errors": [], - "modelVersion": "2020-04-01" - } - }, - { - "kind": "CustomEntityRecognitionLROResults", - "taskName": "Recognize US Regions", - "lastUpdateDateTime": "2020-10-01T15:01:03Z", - "status": "succeeded", - "results": { - "documents": [ - { - "entities": [ - { - "category": "USRegion", - "confidenceScore": 0.85, - "length": 17, - "offset": 45, - "text": "Pacific Northwest" - } - ], - "id": "1", - "warnings": [] - }, - { - "entities": [ - { - "category": "USRegion", - "confidenceScore": 0.88, - "length": 10, - "offset": 63, - "text": "East Coast" - } - ], - "id": "2", - "warnings": [] - } - ], - "errors": [], - "projectName": "MyProject", - "deploymentName": "MyDeployment" - } - } - ] - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulAnalyzeTextJobsMultipleTaskSubmitRequest.json b/dev/cognitiveservices/data-plane/Language/examples/SuccessfulAnalyzeTextJobsMultipleTaskSubmitRequest.json deleted file mode 100644 index b01d275593b4..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulAnalyzeTextJobsMultipleTaskSubmitRequest.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "{Job ID}", - "body": { - "displayName": "Extracting Location & US Region", - "analysisInput": { - "documents": [ - { - "id": "1", - "language": "en", - "text": "I had a wonderful trip to Seattle last week." - }, - { - "id": "2", - "language": "en", - "text": "I'm flying to NYC tomorrow. See you there." - } - ] - }, - "tasks": [ - { - "kind": "EntityRecognition", - "taskName": "Recognize Entities", - "parameters": { - "modelVersion": "latest" - } - }, - { - "kind": "CustomEntityRecognition", - "taskName": "Recognize US Regions", - "parameters": { - "projectName": "MyProject", - "deploymentName": "MyDeployment" - } - } - ] - } - }, - "responses": { - "202": { - "headers": { - "Operation-Location": "{endpoint}/language/analyze-text/jobs/{jobId}" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulEntityLinkingRequest.json b/dev/cognitiveservices/data-plane/Language/examples/SuccessfulEntityLinkingRequest.json deleted file mode 100644 index 4c26cafd35d9..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulEntityLinkingRequest.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "body": { - "kind": "EntityLinking", - "parameters": { - "modelVersion": "latest" - }, - "analysisInput": { - "documents": [ - { - "id": "1", - "language": "en", - "text": "Microsoft was founded by Bill Gates and Paul Allen." - }, - { - "id": "2", - "language": "en", - "text": "Pike place market is my favorite Seattle attraction." - } - ] - } - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "kind": "EntityLinkingResults", - "results": { - "documents": [ - { - "entities": [ - { - "dataSource": "Wikipedia", - "id": "Bill Gates", - "language": "en", - "matches": [ - { - "confidenceScore": 0.52, - "length": 10, - "offset": 25, - "text": "Bill Gates" - } - ], - "name": "Bill Gates", - "url": "https://en.wikipedia.org/wiki/Bill_Gates" - }, - { - "dataSource": "Wikipedia", - "id": "Paul Allen", - "language": "en", - "matches": [ - { - "confidenceScore": 0.54, - "length": 10, - "offset": 40, - "text": "Paul Allen" - } - ], - "name": "Paul Allen", - "url": "https://en.wikipedia.org/wiki/Paul_Allen" - }, - { - "dataSource": "Wikipedia", - "id": "Microsoft", - "language": "en", - "matches": [ - { - "confidenceScore": 0.49, - "length": 9, - "offset": 0, - "text": "Microsoft" - } - ], - "name": "Microsoft", - "url": "https://en.wikipedia.org/wiki/Microsoft" - } - ], - "id": "1", - "warnings": [] - }, - { - "entities": [ - { - "dataSource": "Wikipedia", - "id": "Pike Place Market", - "language": "en", - "matches": [ - { - "confidenceScore": 0.86, - "length": 17, - "offset": 0, - "text": "Pike place market" - } - ], - "name": "Pike Place Market", - "url": "https://en.wikipedia.org/wiki/Pike_Place_Market" - }, - { - "dataSource": "Wikipedia", - "id": "Seattle", - "language": "en", - "matches": [ - { - "confidenceScore": 0.27, - "length": 7, - "offset": 33, - "text": "Seattle" - } - ], - "name": "Seattle", - "url": "https://en.wikipedia.org/wiki/Seattle" - } - ], - "id": "2", - "warnings": [] - } - ], - "errors": [], - "modelVersion": "2020-02-01" - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulEntityRecognitionRequest.json b/dev/cognitiveservices/data-plane/Language/examples/SuccessfulEntityRecognitionRequest.json deleted file mode 100644 index 3c446c0e6d09..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulEntityRecognitionRequest.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "body": { - "kind": "EntityRecognition", - "parameters": { - "modelVersion": "latest" - }, - "analysisInput": { - "documents": [ - { - "id": "1", - "language": "en", - "text": "Microsoft was founded by Bill Gates and Paul Allen." - }, - { - "id": "2", - "language": "en", - "text": "Pike place market is my favorite Seattle attraction." - } - ] - } - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "kind": "EntityRecognitionResults", - "results": { - "documents": [ - { - "entities": [ - { - "category": "Organization", - "confidenceScore": 0.84, - "length": 9, - "offset": 0, - "text": "Microsoft" - }, - { - "category": "Person", - "confidenceScore": 0.85, - "length": 10, - "offset": 25, - "text": "Bill Gates" - }, - { - "category": "Person", - "confidenceScore": 0.9, - "length": 10, - "offset": 40, - "text": "Paul Allen" - } - ], - "id": "1", - "warnings": [] - }, - { - "entities": [ - { - "category": "Location", - "confidenceScore": 0.55, - "length": 7, - "offset": 33, - "subcategory": "GPE", - "text": "Seattle" - } - ], - "id": "2", - "warnings": [] - } - ], - "errors": [], - "modelVersion": "2020-04-01" - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulKeyPhraseExtractionRequest.json b/dev/cognitiveservices/data-plane/Language/examples/SuccessfulKeyPhraseExtractionRequest.json deleted file mode 100644 index 59098ab951ab..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulKeyPhraseExtractionRequest.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "body": { - "kind": "KeyPhraseExtraction", - "parameters": { - "modelVersion": "latest" - }, - "analysisInput": { - "documents": [ - { - "id": "1", - "language": "en", - "text": "Microsoft was founded by Bill Gates and Paul Allen." - }, - { - "id": "2", - "language": "en", - "text": "Text Analytics is one of the Azure Cognitive Services." - }, - { - "id": "3", - "language": "en", - "text": "My cat might need to see a veterinarian." - } - ] - } - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "kind": "KeyPhraseExtractionResults", - "results": { - "documents": [ - { - "id": "1", - "keyPhrases": [ - "Bill Gates", - "Paul Allen", - "Microsoft" - ], - "warnings": [] - }, - { - "id": "2", - "keyPhrases": [ - "Azure Cognitive Services", - "Text Analytics" - ], - "warnings": [] - }, - { - "id": "3", - "keyPhrases": [ - "cat", - "veterinarian" - ], - "warnings": [] - } - ], - "errors": [], - "modelVersion": "2019-10-01" - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulLanguageDetectionRequest.json b/dev/cognitiveservices/data-plane/Language/examples/SuccessfulLanguageDetectionRequest.json deleted file mode 100644 index 3e96dca0095e..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulLanguageDetectionRequest.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "body": { - "kind": "LanguageDetection", - "parameters": { - "modelVersion": "latest" - }, - "analysisInput": { - "documents": [ - { - "id": "1", - "text": "Hello world" - }, - { - "id": "2", - "text": "Bonjour tout le monde" - }, - { - "id": "3", - "text": "Hola mundo" - } - ] - } - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "kind": "LanguageDetectionResults", - "results": { - "documents": [ - { - "detectedLanguage": { - "confidenceScore": 1, - "iso6391Name": "en", - "name": "English" - }, - "id": "1", - "warnings": [] - }, - { - "detectedLanguage": { - "confidenceScore": 1, - "iso6391Name": "fr", - "name": "French" - }, - "id": "2", - "warnings": [] - }, - { - "detectedLanguage": { - "confidenceScore": 1, - "iso6391Name": "es", - "name": "Spanish" - }, - "id": "3", - "warnings": [] - } - ], - "errors": [], - "modelVersion": "2020-04-01" - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulPiiEntityRecognitionRequest.json b/dev/cognitiveservices/data-plane/Language/examples/SuccessfulPiiEntityRecognitionRequest.json deleted file mode 100644 index db0fefd14d21..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulPiiEntityRecognitionRequest.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "body": { - "kind": "PiiEntityRecognition", - "parameters": { - "modelVersion": "latest" - }, - "analysisInput": { - "documents": [ - { - "id": "1", - "language": "en", - "text": "My SSN is 859-98-0987" - }, - { - "id": "2", - "language": "en", - "text": "Your ABA number - 111000025 - is the first 9 digits in the lower left hand corner of your personal check." - }, - { - "id": "3", - "language": "en", - "text": "Is 998.214.865-68 your Brazilian CPF number?" - } - ] - } - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "kind": "PiiEntityRecognitionResults", - "results": { - "documents": [ - { - "id": "1", - "redactedText": "My SSN is ***********", - "entities": [ - { - "category": "U.S. Social Security Number (SSN)", - "confidenceScore": 0.65, - "length": 11, - "offset": 28, - "text": "859-98-0987" - } - ], - "warnings": [] - }, - { - "id": "2", - "redactedText": "Your ABA number - ********* - is the first 9 digits in the lower left hand corner of your personal check.", - "entities": [ - { - "category": "ABA Routing Number", - "confidenceScore": 0.75, - "length": 9, - "offset": 18, - "text": "111000025" - } - ], - "warnings": [] - }, - { - "id": "3", - "redactedText": "Is ************** your Brazilian CPF number?", - "entities": [ - { - "category": "Brazil CPF Number", - "confidenceScore": 0.85, - "length": 14, - "offset": 3, - "text": "998.214.865-68" - } - ], - "warnings": [] - } - ], - "errors": [], - "modelVersion": "2019-10-01" - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulSentimentAnalysisRequest.json b/dev/cognitiveservices/data-plane/Language/examples/SuccessfulSentimentAnalysisRequest.json deleted file mode 100644 index 39c57ca40a75..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/SuccessfulSentimentAnalysisRequest.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "body": { - "kind": "SentimentAnalysis", - "parameters": { - "modelVersion": "latest" - }, - "analysisInput": { - "documents": [ - { - "id": "1", - "language": "en", - "text": "Great atmosphere. Close to plenty of restaurants, hotels, and transit! Staff are friendly and helpful." - } - ] - } - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "kind": "SentimentAnalysisResults", - "results": { - "documents": [ - { - "confidenceScores": { - "negative": 0, - "neutral": 0, - "positive": 1 - }, - "id": "1", - "sentences": [ - { - "targets": [ - { - "confidenceScores": { - "negative": 0, - "positive": 1 - }, - "length": 10, - "offset": 6, - "relations": [ - { - "ref": "#/documents/0/sentences/0/assessments/0", - "relationType": "assessment" - } - ], - "sentiment": "positive", - "text": "atmosphere" - } - ], - "confidenceScores": { - "negative": 0, - "neutral": 0, - "positive": 1 - }, - "length": 17, - "offset": 0, - "assessments": [ - { - "confidenceScores": { - "negative": 0, - "positive": 1 - }, - "isNegated": false, - "length": 5, - "offset": 0, - "sentiment": "positive", - "text": "great" - } - ], - "sentiment": "positive", - "text": "Great atmosphere." - }, - { - "targets": [ - { - "confidenceScores": { - "negative": 0.01, - "positive": 0.99 - }, - "length": 11, - "offset": 37, - "relations": [ - { - "ref": "#/documents/0/sentences/1/assessments/0", - "relationType": "assessment" - } - ], - "sentiment": "positive", - "text": "restaurants" - }, - { - "confidenceScores": { - "negative": 0.01, - "positive": 0.99 - }, - "length": 6, - "offset": 50, - "relations": [ - { - "ref": "#/documents/0/sentences/1/assessments/0", - "relationType": "assessment" - } - ], - "sentiment": "positive", - "text": "hotels" - } - ], - "confidenceScores": { - "negative": 0.01, - "neutral": 0.86, - "positive": 0.13 - }, - "length": 52, - "offset": 18, - "assessments": [ - { - "confidenceScores": { - "negative": 0.01, - "positive": 0.99 - }, - "isNegated": false, - "length": 15, - "offset": 18, - "sentiment": "positive", - "text": "Close to plenty" - } - ], - "sentiment": "neutral", - "text": "Close to plenty of restaurants, hotels, and transit!" - } - ], - "sentiment": "positive", - "warnings": [] - } - ], - "errors": [], - "modelVersion": "2020-04-01" - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulCancelTrainingJob.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulCancelTrainingJob.json deleted file mode 100644 index 88fb6767655e..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulCancelTrainingJob.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/train/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulCreateProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulCreateProject.json deleted file mode 100644 index 98007ecafe72..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulCreateProject.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/merge-patch+json", - "api-version": "2023-04-01", - "projectName": "myproject", - "body": { - "projectName": "myproject", - "language": "en", - "projectKind": "Conversation", - "description": "This is a sample conversation project.", - "multilingual": false - } - }, - "responses": { - "201": { - "headers": {}, - "body": { - "createdDateTime": "2022-04-18T13:53:03Z", - "lastModifiedDateTime": "2022-04-18T13:53:03Z", - "projectKind": "Conversation", - "projectName": "myproject", - "multilingual": false, - "description": "This is a sample conversation project.", - "language": "en" - } - }, - "200": { - "headers": {}, - "body": { - "createdDateTime": "2022-04-18T13:53:03Z", - "lastModifiedDateTime": "2022-04-18T13:53:03Z", - "lastTrainedDateTime": "2022-04-18T14:14:28Z", - "lastDeployedDateTime": "2022-04-18T14:49:01Z", - "projectKind": "Conversation", - "projectName": "myproject", - "multilingual": false, - "description": "This is a sample conversation project.", - "language": "en" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeleteDeployment.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeleteDeployment.json deleted file mode 100644 index 74ba10ef7f11..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeleteDeployment.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "deploymentName": "staging" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/deployments/staging/jobs/61ebb7ef-a207-40d2-82b9-5285440ae579_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeleteModel.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeleteModel.json deleted file mode 100644 index 7aaeb03f6752..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeleteModel.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "trainedModelLabel": "model2" - }, - "responses": { - "204": {} - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeleteProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeleteProject.json deleted file mode 100644 index 86397edb0efc..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeleteProject.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "myproject" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/global/deletion-jobs/129d3182-625d-496c-bcf9-43686e85160b_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeployProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeployProject.json deleted file mode 100644 index c115c10978e7..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulDeployProject.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "deploymentName": "production", - "body": { - "trainedModelLabel": "29886710a2ae49259d62cffca977db66" - } - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/deployments/production/jobs/66fa9a67-a561-42f1-8a13-f3a879b1a324_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulExportProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulExportProject.json deleted file mode 100644 index 4c84fa0276a7..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulExportProject.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "stringIndexType": "Utf16CodeUnit" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/export/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetDeployment.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetDeployment.json deleted file mode 100644 index e90d172ebf9c..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetDeployment.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "deploymentName": "staging" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "deploymentName": "staging", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49.4334381Z", - "lastDeployedDateTime": "2022-04-18T15:53:04Z", - "deploymentExpirationDate": "2023-10-28", - "modelTrainingConfigVersion": "2022-05-15-preview" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetDeploymentStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetDeploymentStatus.json deleted file mode 100644 index 6129720003e3..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetDeploymentStatus.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "deploymentName": "production", - "jobId": "66fa9a67-a561-42f1-8a13-f3a879b1a324_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "66fa9a67-a561-42f1-8a13-f3a879b1a324_637858368000000000", - "createdDateTime": "2022-04-18T15:52:48Z", - "lastUpdatedDateTime": "2022-04-18T15:53:04Z", - "expirationDateTime": "2022-04-25T15:52:48Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetExportStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetExportStatus.json deleted file mode 100644 index 8e3275eb0950..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetExportStatus.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "jobId": "c95efa2a-44e8-461e-8aa5-04b4677bfa84_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "resultUrl": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/export/jobs/c4946bfa-4fbf-493b-bfcf-2d232eb9de69_637858368000000000/result?api-version=2023-04-01", - "jobId": "c4946bfa-4fbf-493b-bfcf-2d232eb9de69_637858368000000000", - "createdDateTime": "2022-04-18T15:23:07Z", - "lastUpdatedDateTime": "2022-04-18T15:23:08Z", - "expirationDateTime": "2022-04-25T15:23:07Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetImportStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetImportStatus.json deleted file mode 100644 index 0554591835a2..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetImportStatus.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "jobId": "c95efa2a-44e8-461e-8aa5-04b4677bfa84_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "c95efa2a-44e8-461e-8aa5-04b4677bfa84_637858368000000000", - "createdDateTime": "2022-04-18T15:17:20Z", - "lastUpdatedDateTime": "2022-04-18T15:17:22Z", - "expirationDateTime": "2022-04-25T15:17:20Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetLoadSnapshotStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetLoadSnapshotStatus.json deleted file mode 100644 index dc21261da3bb..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetLoadSnapshotStatus.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "trainedModelLabel": "model1", - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000", - "createdDateTime": "2022-04-18T15:44:44Z", - "lastUpdatedDateTime": "2022-04-18T15:45:48Z", - "expirationDateTime": "2022-04-25T15:44:44Z", - "status": "running" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetModel.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetModel.json deleted file mode 100644 index 30d7b001de6a..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetModel.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "trainedModelLabel": "model1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "label": "model1", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49Z", - "lastTrainingDurationInSeconds": 186, - "modelExpirationDate": "2022-10-28", - "modelTrainingConfigVersion": "2022-05-01", - "hasSnapshot": true - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetModelEvaluation.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetModelEvaluation.json deleted file mode 100644 index 8dc4b6f88e33..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetModelEvaluation.json +++ /dev/null @@ -1,284 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "trainedModelLabel": "model1", - "stringIndexType": "Utf16CodeUnit", - "maxpagesize": 10 - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "text": "send the email", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [], - "predictedEntities": [] - }, - "intentsResult": { - "expectedIntent": "SendEmail", - "predictedIntent": "SendEmail" - } - }, - { - "text": "send a mail to daniel", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [ - { - "category": "ContactName", - "offset": 15, - "length": 6 - } - ], - "predictedEntities": [ - { - "category": "ContactName", - "offset": 15, - "length": 6 - } - ] - }, - "intentsResult": { - "expectedIntent": "SendEmail", - "predictedIntent": "SendEmail" - } - }, - { - "text": "i forgot to add an important part to that email to james . please set it up to edit", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [ - { - "category": "ContactName", - "offset": 51, - "length": 5 - } - ], - "predictedEntities": [ - { - "category": "Category", - "offset": 19, - "length": 9 - }, - { - "category": "ContactName", - "offset": 51, - "length": 5 - } - ] - }, - "intentsResult": { - "expectedIntent": "AddMore", - "predictedIntent": "AddMore" - } - }, - { - "text": "send email to a and tian", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [ - { - "category": "ContactName", - "offset": 14, - "length": 1 - }, - { - "category": "ContactName", - "offset": 20, - "length": 4 - } - ], - "predictedEntities": [ - { - "category": "ContactName", - "offset": 14, - "length": 1 - }, - { - "category": "ContactName", - "offset": 20, - "length": 4 - } - ] - }, - "intentsResult": { - "expectedIntent": "SendEmail", - "predictedIntent": "SendEmail" - } - }, - { - "text": "send thomas an email", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [ - { - "category": "ContactName", - "offset": 5, - "length": 6 - } - ], - "predictedEntities": [ - { - "category": "ContactName", - "offset": 5, - "length": 6 - } - ] - }, - "intentsResult": { - "expectedIntent": "SendEmail", - "predictedIntent": "SendEmail" - } - }, - { - "text": "i need to add more to the email message i am sending to vincent", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [ - { - "category": "ContactName", - "offset": 56, - "length": 7 - } - ], - "predictedEntities": [ - { - "category": "ContactName", - "offset": 56, - "length": 7 - } - ] - }, - "intentsResult": { - "expectedIntent": "AddMore", - "predictedIntent": "AddMore" - } - }, - { - "text": "send an email to lily roth and abc123@microsoft.com", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [ - { - "category": "ContactName", - "offset": 17, - "length": 9 - } - ], - "predictedEntities": [ - { - "category": "ContactName", - "offset": 17, - "length": 9 - } - ] - }, - "intentsResult": { - "expectedIntent": "SendEmail", - "predictedIntent": "SendEmail" - } - }, - { - "text": "i need to add something else to my email to cheryl", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [ - { - "category": "ContactName", - "offset": 44, - "length": 6 - } - ], - "predictedEntities": [ - { - "category": "ContactName", - "offset": 44, - "length": 6 - } - ] - }, - "intentsResult": { - "expectedIntent": "AddMore", - "predictedIntent": "AddMore" - } - }, - { - "text": "send an email to larry , joseph and billy larkson", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [ - { - "category": "ContactName", - "offset": 17, - "length": 5 - }, - { - "category": "ContactName", - "offset": 25, - "length": 6 - }, - { - "category": "ContactName", - "offset": 36, - "length": 13 - } - ], - "predictedEntities": [ - { - "category": "ContactName", - "offset": 17, - "length": 5 - }, - { - "category": "ContactName", - "offset": 25, - "length": 6 - }, - { - "category": "ContactName", - "offset": 36, - "length": 13 - } - ] - }, - "intentsResult": { - "expectedIntent": "SendEmail", - "predictedIntent": "SendEmail" - } - }, - { - "text": "send mail to dorothy", - "language": "en-us", - "entitiesResult": { - "expectedEntities": [ - { - "category": "ContactName", - "offset": 13, - "length": 7 - } - ], - "predictedEntities": [ - { - "category": "ContactName", - "offset": 13, - "length": 7 - } - ] - }, - "intentsResult": { - "expectedIntent": "SendEmail", - "predictedIntent": "SendEmail" - } - } - ], - "nextLink": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/models/model1/evaluation/result/?api-version=2023-04-01&top=2147483637&skip={maxpagesize}&maxpagesize={maxpagesize}" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetModelEvaluationSummary.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetModelEvaluationSummary.json deleted file mode 100644 index 8fb25eb99c9c..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetModelEvaluationSummary.json +++ /dev/null @@ -1,526 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "trainedModelLabel": "model1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "entitiesEvaluation": { - "confusionMatrix": { - "Attachment": { - "Attachment": { - "normalizedValue": 100.0, - "rawValue": 3.0 - } - }, - "Category": { - "Category": { - "normalizedValue": 91.666664, - "rawValue": 11.0 - }, - "$none": { - "normalizedValue": 8.333333, - "rawValue": 1.0 - } - }, - "ContactName": { - "ContactName": { - "normalizedValue": 91.666664, - "rawValue": 22.0 - }, - "SenderName": { - "normalizedValue": 4.1666665, - "rawValue": 1.0 - }, - "$none": { - "normalizedValue": 4.1666665, - "rawValue": 1.0 - } - }, - "Date": { - "Date": { - "normalizedValue": 100.0, - "rawValue": 2.0 - } - }, - "EmailSubject": { - "EmailSubject": { - "normalizedValue": 93.33333, - "rawValue": 9.333334 - }, - "$none": { - "normalizedValue": 6.6666665, - "rawValue": 0.6666667 - } - }, - "FromRelationshipName": { - "FromRelationshipName": { - "normalizedValue": 100.0, - "rawValue": 1.0 - } - }, - "Line": { - "Line": { - "normalizedValue": 100.0, - "rawValue": 2.0 - } - }, - "Message": { - "Message": { - "normalizedValue": 81.2063, - "rawValue": 6.496504 - }, - "EmailSubject": { - "normalizedValue": 7.43007, - "rawValue": 0.5944056 - }, - "$none": { - "normalizedValue": 9.120047, - "rawValue": 0.7296037 - }, - "Date": { - "normalizedValue": 2.2435899, - "rawValue": 0.17948718 - } - }, - "OrderReference": { - "OrderReference": { - "normalizedValue": 100.0, - "rawValue": 17.0 - } - }, - "PositionReference": { - "$none": { - "normalizedValue": 100.0, - "rawValue": 1.0 - } - }, - "RelationshipName": { - "RelationshipName": { - "normalizedValue": 66.666664, - "rawValue": 2.0 - }, - "$none": { - "normalizedValue": 33.333332, - "rawValue": 1.0 - } - }, - "SearchTexts": { - "SearchTexts": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "SenderName": { - "SenderName": { - "normalizedValue": 88.888885, - "rawValue": 8.0 - }, - "ContactName": { - "normalizedValue": 11.111111, - "rawValue": 1.0 - } - }, - "Time": { - "$none": { - "normalizedValue": 100.0, - "rawValue": 2.0 - } - }, - "$none": { - "$none": { - "normalizedValue": 99.739265, - "rawValue": 162.575 - }, - "Category": { - "normalizedValue": 0.2607362, - "rawValue": 0.425 - } - } - }, - "entities": { - "ContactName": { - "f1": 0.9361702799797058, - "precision": 0.95652174949646, - "recall": 0.9166666865348816, - "truePositiveCount": 22, - "trueNegativeCount": 0, - "falsePositiveCount": 1, - "falseNegativeCount": 2 - }, - "Category": { - "f1": 0.8799999952316284, - "precision": 0.8461538553237915, - "recall": 0.9166666865348816, - "truePositiveCount": 11, - "trueNegativeCount": 0, - "falsePositiveCount": 2, - "falseNegativeCount": 1 - }, - "SenderName": { - "f1": 0.8888888955116272, - "precision": 0.8888888955116272, - "recall": 0.8888888955116272, - "truePositiveCount": 8, - "trueNegativeCount": 0, - "falsePositiveCount": 1, - "falseNegativeCount": 1 - }, - "EmailSubject": { - "f1": 0.8181817531585693, - "precision": 0.75, - "recall": 0.8999999761581421, - "truePositiveCount": 9, - "trueNegativeCount": 0, - "falsePositiveCount": 3, - "falseNegativeCount": 1 - }, - "Message": { - "f1": 0.75, - "precision": 0.75, - "recall": 0.75, - "truePositiveCount": 6, - "trueNegativeCount": 0, - "falsePositiveCount": 2, - "falseNegativeCount": 2 - }, - "Date": { - "f1": 0.800000011920929, - "precision": 0.6666666865348816, - "recall": 1.0, - "truePositiveCount": 2, - "trueNegativeCount": 0, - "falsePositiveCount": 1, - "falseNegativeCount": 0 - }, - "OrderReference": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 17, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "SearchTexts": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "Attachment": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 3, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "RelationshipName": { - "f1": 0.800000011920929, - "precision": 1.0, - "recall": 0.6666666865348816, - "truePositiveCount": 2, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 1 - }, - "Line": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 2, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "Time": { - "f1": 0.0, - "precision": 0.0, - "recall": 0.0, - "truePositiveCount": 0, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 2 - }, - "FromRelationshipName": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 1, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "PositionReference": { - "f1": 0.0, - "precision": 0.0, - "recall": 0.0, - "truePositiveCount": 0, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 1 - } - }, - "microF1": 0.8923077, - "microPrecision": 0.8969072, - "microRecall": 0.8877551, - "macroF1": 0.7766601, - "macroPrecision": 0.7755879, - "macroRecall": 0.78849214 - }, - "intentsEvaluation": { - "confusionMatrix": { - "AddFlag": { - "AddFlag": { - "normalizedValue": 100.0, - "rawValue": 6.0 - } - }, - "AddMore": { - "AddMore": { - "normalizedValue": 100.0, - "rawValue": 17.0 - } - }, - "Cancel": { - "Cancel": { - "normalizedValue": 100.0, - "rawValue": 9.0 - } - }, - "CheckMessages": { - "CheckMessages": { - "normalizedValue": 100.0, - "rawValue": 9.0 - } - }, - "Confirm": { - "Confirm": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "Delete": { - "Delete": { - "normalizedValue": 100.0, - "rawValue": 5.0 - } - }, - "Forward": { - "Forward": { - "normalizedValue": 100.0, - "rawValue": 6.0 - } - }, - "None": { - "None": { - "normalizedValue": 100.0, - "rawValue": 1.0 - } - }, - "QueryLastText": { - "QueryLastText": { - "normalizedValue": 100.0, - "rawValue": 6.0 - } - }, - "ReadAloud": { - "ReadAloud": { - "normalizedValue": 100.0, - "rawValue": 16.0 - } - }, - "Reply": { - "Reply": { - "normalizedValue": 100.0, - "rawValue": 6.0 - } - }, - "SearchMessages": { - "SearchMessages": { - "normalizedValue": 100.0, - "rawValue": 9.0 - } - }, - "SendEmail": { - "SendEmail": { - "normalizedValue": 100.0, - "rawValue": 20.0 - } - }, - "ShowNext": { - "ShowNext": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "ShowPrevious": { - "ShowPrevious": { - "normalizedValue": 100.0, - "rawValue": 3.0 - } - } - }, - "intents": { - "AddMore": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 17, - "trueNegativeCount": 104, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "Cancel": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 9, - "trueNegativeCount": 112, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "SendEmail": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 20, - "trueNegativeCount": 101, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "CheckMessages": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 9, - "trueNegativeCount": 112, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "AddFlag": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 6, - "trueNegativeCount": 115, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "Reply": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 6, - "trueNegativeCount": 115, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "ReadAloud": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 16, - "trueNegativeCount": 105, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "QueryLastText": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 6, - "trueNegativeCount": 115, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "SearchMessages": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 9, - "trueNegativeCount": 112, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "Delete": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 5, - "trueNegativeCount": 116, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "Forward": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 6, - "trueNegativeCount": 115, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "Confirm": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 117, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "ShowNext": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 117, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "ShowPrevious": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 3, - "trueNegativeCount": 118, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "None": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 1, - "trueNegativeCount": 120, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - } - }, - "microF1": 1.0, - "microPrecision": 1.0, - "microRecall": 1.0, - "macroF1": 1.0, - "macroPrecision": 1.0, - "macroRecall": 1.0 - }, - "evaluationOptions": { - "kind": "percentage", - "trainingSplitPercentage": 80, - "testingSplitPercentage": 20 - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetProject.json deleted file mode 100644 index 50b53ae6415a..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetProject.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "myproject" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "createdDateTime": "2022-04-18T13:53:03Z", - "lastModifiedDateTime": "2022-04-18T13:53:03Z", - "lastTrainedDateTime": "2022-04-18T14:14:28Z", - "lastDeployedDateTime": "2022-04-18T14:49:01Z", - "projectKind": "Conversation", - "projectName": "myproject", - "multilingual": false, - "description": "This is a sample conversation project.", - "language": "en" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetProjectDeletionStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetProjectDeletionStatus.json deleted file mode 100644 index 73c8d1cf0647..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetProjectDeletionStatus.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "jobId": "129d3182-625d-496c-bcf9-43686e85160b_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "129d3182-625d-496c-bcf9-43686e85160b_637858368000000000", - "createdDateTime": "2022-04-18T14:02:34Z", - "lastUpdatedDateTime": "2022-04-18T14:02:34Z", - "expirationDateTime": "2022-04-25T14:02:34Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSupportedLanguages.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSupportedLanguages.json deleted file mode 100644 index ed6b0478cdd7..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSupportedLanguages.json +++ /dev/null @@ -1,398 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectKind": "Conversation" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "languageName": "English", - "languageCode": "en" - }, - { - "languageName": "English", - "languageCode": "en-us" - }, - { - "languageName": "English (UK)", - "languageCode": "en-gb" - }, - { - "languageName": "French", - "languageCode": "fr" - }, - { - "languageName": "Italian", - "languageCode": "it" - }, - { - "languageName": "Spanish", - "languageCode": "es" - }, - { - "languageName": "German", - "languageCode": "de" - }, - { - "languageName": "Portuguese (Brazil)", - "languageCode": "pt-br" - }, - { - "languageName": "Portuguese (Portugal)", - "languageCode": "pt-pt" - }, - { - "languageName": "Chinese (Simplified)", - "languageCode": "zh-hans" - }, - { - "languageName": "Japanese", - "languageCode": "ja" - }, - { - "languageName": "Korean", - "languageCode": "ko" - }, - { - "languageName": "Dutch", - "languageCode": "nl" - }, - { - "languageName": "Hindi", - "languageCode": "hi" - }, - { - "languageName": "Turkish", - "languageCode": "tr" - }, - { - "languageName": "Gujarati", - "languageCode": "gu" - }, - { - "languageName": "Marathi", - "languageCode": "mr" - }, - { - "languageName": "Tamil", - "languageCode": "ta" - }, - { - "languageName": "Telugu", - "languageCode": "te" - }, - { - "languageName": "Zulu", - "languageCode": "zu" - }, - { - "languageName": "Afrikaans", - "languageCode": "af" - }, - { - "languageName": "Amharic", - "languageCode": "am" - }, - { - "languageName": "Arabic", - "languageCode": "ar" - }, - { - "languageName": "Assamese", - "languageCode": "as" - }, - { - "languageName": "Azerbaijani", - "languageCode": "az" - }, - { - "languageName": "Belarusian", - "languageCode": "be" - }, - { - "languageName": "Bulgarian", - "languageCode": "bg" - }, - { - "languageName": "Breton", - "languageCode": "br" - }, - { - "languageName": "Bosnian", - "languageCode": "bs" - }, - { - "languageName": "Catalan", - "languageCode": "ca" - }, - { - "languageName": "Czech", - "languageCode": "cs" - }, - { - "languageName": "Welsh", - "languageCode": "cy" - }, - { - "languageName": "Danish", - "languageCode": "da" - }, - { - "languageName": "Greek", - "languageCode": "el" - }, - { - "languageName": "Esperanto", - "languageCode": "eo" - }, - { - "languageName": "Estonian", - "languageCode": "et" - }, - { - "languageName": "Basque", - "languageCode": "eu" - }, - { - "languageName": "Persian", - "languageCode": "fa" - }, - { - "languageName": "Finnish", - "languageCode": "fi" - }, - { - "languageName": "Western Frisian", - "languageCode": "fy" - }, - { - "languageName": "Irish", - "languageCode": "ga" - }, - { - "languageName": "Scottish Gaelic", - "languageCode": "gd" - }, - { - "languageName": "Galician", - "languageCode": "gl" - }, - { - "languageName": "Hausa", - "languageCode": "ha" - }, - { - "languageName": "Hebrew", - "languageCode": "he" - }, - { - "languageName": "Croatian", - "languageCode": "hr" - }, - { - "languageName": "Hungarian", - "languageCode": "hu" - }, - { - "languageName": "Armenian", - "languageCode": "hy" - }, - { - "languageName": "Indonesian", - "languageCode": "id" - }, - { - "languageName": "Javanese", - "languageCode": "jv" - }, - { - "languageName": "Georgian", - "languageCode": "ka" - }, - { - "languageName": "Kazakh", - "languageCode": "kk" - }, - { - "languageName": "Khmer", - "languageCode": "km" - }, - { - "languageName": "Kannada", - "languageCode": "kn" - }, - { - "languageName": "Kurdish (Kurmanji)", - "languageCode": "ku" - }, - { - "languageName": "Kyrgyz", - "languageCode": "ky" - }, - { - "languageName": "Latin", - "languageCode": "la" - }, - { - "languageName": "Lao", - "languageCode": "lo" - }, - { - "languageName": "Lithuanian", - "languageCode": "lt" - }, - { - "languageName": "Latvian", - "languageCode": "lv" - }, - { - "languageName": "Malagasy", - "languageCode": "mg" - }, - { - "languageName": "Macedonian", - "languageCode": "mk" - }, - { - "languageName": "Malayalam", - "languageCode": "ml" - }, - { - "languageName": "Mongolian", - "languageCode": "mn" - }, - { - "languageName": "Malay", - "languageCode": "ms" - }, - { - "languageName": "Burmese", - "languageCode": "my" - }, - { - "languageName": "Nepali", - "languageCode": "ne" - }, - { - "languageName": "Norwegian (Bokmal)", - "languageCode": "nb" - }, - { - "languageName": "Odia", - "languageCode": "or" - }, - { - "languageName": "Punjabi", - "languageCode": "pa" - }, - { - "languageName": "Polish", - "languageCode": "pl" - }, - { - "languageName": "Pashto", - "languageCode": "ps" - }, - { - "languageName": "Romanian", - "languageCode": "ro" - }, - { - "languageName": "Russian", - "languageCode": "ru" - }, - { - "languageName": "Sanskrit", - "languageCode": "sa" - }, - { - "languageName": "Sindhi", - "languageCode": "sd" - }, - { - "languageName": "Sinhala", - "languageCode": "si" - }, - { - "languageName": "Slovak", - "languageCode": "sk" - }, - { - "languageName": "Slovenian", - "languageCode": "sl" - }, - { - "languageName": "Somali", - "languageCode": "so" - }, - { - "languageName": "Albanian", - "languageCode": "sq" - }, - { - "languageName": "Serbian", - "languageCode": "sr" - }, - { - "languageName": "Sundanese", - "languageCode": "su" - }, - { - "languageName": "Swedish", - "languageCode": "sv" - }, - { - "languageName": "Swahili", - "languageCode": "sw" - }, - { - "languageName": "Thai", - "languageCode": "th" - }, - { - "languageName": "Filipino", - "languageCode": "tl" - }, - { - "languageName": "Uyghur", - "languageCode": "ug" - }, - { - "languageName": "Ukrainian", - "languageCode": "uk" - }, - { - "languageName": "Urdu", - "languageCode": "ur" - }, - { - "languageName": "Uzbek", - "languageCode": "uz" - }, - { - "languageName": "Vietnamese", - "languageCode": "vi" - }, - { - "languageName": "Xhosa", - "languageCode": "xh" - }, - { - "languageName": "Yiddish", - "languageCode": "yi" - }, - { - "languageName": "Chinese (Traditional)", - "languageCode": "zh-hant" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSupportedPrebuiltEntities.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSupportedPrebuiltEntities.json deleted file mode 100644 index 7e288d169478..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSupportedPrebuiltEntities.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "language": "en" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "category": "Person.Name", - "description": "Name of an individual", - "examples": "john, Sam, Lisa" - }, - { - "category": "General.Event", - "description": "Important events", - "examples": "World War two, Covid 19" - }, - { - "category": "General.Organization", - "description": "Companies and corporations", - "examples": "Microsoft, Amazon" - }, - { - "category": "Choice.Boolean", - "description": "boolean choice", - "examples": "yes, no, agreed" - }, - { - "category": "Quantity.Age", - "description": "Age of a person or thing", - "examples": "10-month-old, 19 years old, 58 year-old" - }, - { - "category": "Quantity.NumberRange", - "description": "a numeric interval", - "examples": "between 25 and 35, 25-35" - }, - { - "category": "Quantity.Number", - "description": "A cardinal number in numeric or text form", - "examples": "ten, forty two, 3.141, 10K" - }, - { - "category": "Quantity.Percentage", - "description": "A percentage, using the symbol % or the word \"percent\"", - "examples": "10%, 5.6 percent" - }, - { - "category": "Quantity.Ordinal", - "description": "An ordinal number in numeric or text form", - "examples": "first, second, tenth, 1st, 2nd, 10th" - }, - { - "category": "Quantity.Dimension", - "description": "Spacial dimensions, including length, distance, area, and volume", - "examples": "2 miles, 650 square kilometres, 9,350 feet" - }, - { - "category": "Quantity.Temperature", - "description": "A temperature in celsius or fahrenheit", - "examples": "32F, 34 degrees celsius, 2 deg C" - }, - { - "category": "Quantity.Currency", - "description": "Monetary amounts, including currency", - "examples": "1000.00 US dollars, £20.00, $ 67.5 B" - }, - { - "category": "DateTime", - "description": "exact date values", - "examples": "May 11th" - }, - { - "category": "Email", - "description": "Email addresses", - "examples": "user@example.net, user_name@example.com, user.Name12@example.net" - }, - { - "category": "Phone Number", - "description": "US phone numbers", - "examples": "123-456-7890, +1 123 456 789, (123)456-789" - }, - { - "category": "URL", - "description": "Websites URLs and links", - "examples": "www.example.com, http://example.net?name=my_name&age=10" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSupportedTrainingConfigVersions.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSupportedTrainingConfigVersions.json deleted file mode 100644 index 06780f7fa92b..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSupportedTrainingConfigVersions.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectKind": "Conversation" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "trainingConfigVersion": "2022-05-01", - "modelExpirationDate": "2022-10-28" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSwapDeploymentsStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSwapDeploymentsStatus.json deleted file mode 100644 index 0aac88c8d145..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetSwapDeploymentsStatus.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "jobId": "c36a8775-35b9-4cb5-a8db-665e7d91aafe_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "c36a8775-35b9-4cb5-a8db-665e7d91aafe_637858368000000000", - "createdDateTime": "2022-04-18T16:09:50Z", - "lastUpdatedDateTime": "2022-04-18T16:09:58Z", - "expirationDateTime": "2022-04-25T16:09:50Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetTrainStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetTrainStatus.json deleted file mode 100644 index 5f28035def5e..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulGetTrainStatus.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "result": { - "modelLabel": "model1", - "trainingConfigVersion": "2022-05-01", - "trainingMode": "standard", - "estimatedEndDateTime": "2022-04-18T15:47:58.8190649Z", - "trainingStatus": { - "percentComplete": 3, - "startDateTime": "2022-04-18T15:45:06.8190649Z", - "status": "running" - }, - "evaluationStatus": { - "percentComplete": 0, - "status": "notStarted" - } - }, - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000", - "createdDateTime": "2022-04-18T15:44:44Z", - "lastUpdatedDateTime": "2022-04-18T15:45:48Z", - "expirationDateTime": "2022-04-25T15:44:44Z", - "status": "running" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulImportProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulImportProject.json deleted file mode 100644 index 61d70d484c59..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulImportProject.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "body": { - "projectFileVersion": "2022-05-01", - "stringIndexType": "Utf16CodeUnit", - "metadata": { - "projectKind": "Conversation", - "settings": { - "confidenceThreshold": 0.7 - }, - "projectName": "EmailApp", - "multilingual": true, - "description": "Trying out CLU", - "language": "en-us" - }, - "assets": { - "projectKind": "Conversation", - "intents": [ - { - "category": "Read" - }, - { - "category": "Delete" - } - ], - "entities": [ - { - "category": "Sender" - }, - { - "category": "Number", - "regex": { - "expressions": [ - { - "regexKey": "UK Phone numbers", - "language": "en-us", - "regexPattern": "/^\\(?([0-9]{3})\\)?[-.\\s]?([0-9]{3})[-.\\s]?([0-9]{4})$/" - } - ] - } - } - ], - "utterances": [ - { - "text": "Open Blake's email", - "dataset": "Train", - "intent": "Read", - "entities": [ - { - "category": "Sender", - "offset": 5, - "length": 5 - } - ] - }, - { - "text": "Delete last email", - "language": "en-gb", - "dataset": "Test", - "intent": "Delete", - "entities": [] - } - ] - } - } - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/import/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListDeployments.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListDeployments.json deleted file mode 100644 index c968d5123219..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListDeployments.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp" - }, - "responses": { - "200": { - "body": { - "value": [ - { - "deploymentName": "production", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49.4334381Z", - "lastDeployedDateTime": "2022-04-18T16:03:51Z", - "deploymentExpirationDate": "2023-10-28", - "modelTrainingConfigVersion": "2022-05-01" - }, - { - "deploymentName": "staging", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49.4334381Z", - "lastDeployedDateTime": "2022-04-18T15:53:04Z", - "deploymentExpirationDate": "2023-10-28", - "modelTrainingConfigVersion": "2022-05-01" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListModels.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListModels.json deleted file mode 100644 index 0a9e351e22bb..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListModels.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "label": "model1", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49Z", - "lastTrainingDurationInSeconds": 186, - "modelExpirationDate": "2022-10-28", - "modelTrainingConfigVersion": "2022-05-01", - "hasSnapshot": true - }, - { - "label": "model2", - "modelId": "model2-20220418T052522-c63bd244dd9e4bf8adec1a7129968c99", - "lastTrainedDateTime": "2022-04-18T17:25:22Z", - "lastTrainingDurationInSeconds": 192, - "modelExpirationDate": "2022-10-28", - "modelTrainingConfigVersion": "2022-05-01", - "hasSnapshot": true - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListProjects.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListProjects.json deleted file mode 100644 index 8b52f2c1adce..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListProjects.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "createdDateTime": "2022-04-18T14:03:16Z", - "lastModifiedDateTime": "2022-04-18T14:03:16Z", - "projectKind": "Conversation", - "projectName": "myproject1", - "multilingual": false, - "description": "This is a sample conversation project.", - "language": "en" - }, - { - "createdDateTime": "2022-04-18T14:03:12Z", - "lastModifiedDateTime": "2022-04-18T14:03:12Z", - "projectKind": "Conversation", - "projectName": "myproject", - "multilingual": false, - "description": "This is a sample conversation project.", - "language": "en" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListTrainingJobs.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListTrainingJobs.json deleted file mode 100644 index 5772f4670098..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulListTrainingJobs.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "result": { - "modelLabel": "model1", - "trainingConfigVersion": "2022-05-01", - "trainingMode": "advanced", - "trainingStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T15:45:06.8190649Z", - "endDateTime": "2022-04-18T15:47:19.2639682Z", - "status": "succeeded" - }, - "evaluationStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T15:47:19.2734976Z", - "endDateTime": "2022-04-18T15:47:23.8378892Z", - "status": "succeeded" - } - }, - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000", - "createdDateTime": "2022-04-18T15:44:44Z", - "lastUpdatedDateTime": "2022-04-18T15:47:50Z", - "expirationDateTime": "2022-04-25T15:44:44Z", - "status": "succeeded" - }, - { - "result": { - "modelLabel": "model2", - "trainingConfigVersion": "2022-05-01", - "trainingMode": "standard", - "trainingStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T17:22:39.3663023Z", - "endDateTime": "2022-04-18T17:24:51.9440947Z", - "status": "succeeded" - }, - "evaluationStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T17:24:51.9571747Z", - "endDateTime": "2022-04-18T17:24:58.1427823Z", - "status": "succeeded" - } - }, - "jobId": "9145f93f-6f37-418c-8527-d2ded84cece0_637858368000000000", - "createdDateTime": "2022-04-18T17:22:11Z", - "lastUpdatedDateTime": "2022-04-18T17:25:23Z", - "expirationDateTime": "2022-04-25T17:22:11Z", - "status": "succeeded" - }, - { - "result": { - "modelLabel": "model2", - "trainingConfigVersion": "2022-05-01", - "trainingMode": "standard", - "trainingStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T17:44:41.388358Z", - "endDateTime": "2022-04-18T17:50:29.5675101Z", - "status": "succeeded" - }, - "evaluationStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T17:50:29.5808461Z", - "endDateTime": "2022-04-18T17:50:35.3482185Z", - "status": "succeeded" - } - }, - "jobId": "ee23c900-354d-4b6d-96e1-8197db2bd5f7_637858368000000000", - "createdDateTime": "2022-04-18T17:44:04Z", - "lastUpdatedDateTime": "2022-04-18T17:51:11Z", - "expirationDateTime": "2022-04-25T17:44:04Z", - "status": "succeeded" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulLoadSnapshot.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulLoadSnapshot.json deleted file mode 100644 index 9409332d34df..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulLoadSnapshot.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "trainedModelLabel": "model1" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/models/model1/load-snapshot/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulSwapDeployments.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulSwapDeployments.json deleted file mode 100644 index 5ac2aa6d1bea..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulSwapDeployments.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "body": { - "firstDeploymentName": "production", - "secondDeploymentName": "staging" - } - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/deployments/swap/jobs/c36a8775-35b9-4cb5-a8db-665e7d91aafe_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulTrainProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulTrainProject.json deleted file mode 100644 index a86428a5b7ea..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzeconversations-authoring/SuccessfulTrainProject.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "EmailApp", - "body": { - "modelLabel": "model1", - "trainingMode": "standard", - "trainingConfigVersion": "latest", - "evaluationOptions": { - "kind": "percentage", - "testingSplitPercentage": 20, - "trainingSplitPercentage": 80 - } - } - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/EmailApp/train/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzedocuments/SuccessfulAnalyzeTextJobsCancelRequest.json b/dev/cognitiveservices/data-plane/Language/examples/analyzedocuments/SuccessfulAnalyzeTextJobsCancelRequest.json deleted file mode 100644 index 74a03a34a42a..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzedocuments/SuccessfulAnalyzeTextJobsCancelRequest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-11-15-preview", - "Endpoint": "{Endpoint}", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18" - }, - "responses": { - "202": { - "headers": { - "Operation-Location": "{Endpoint}/language/analyze-documents/jobs/{jobId}?api-version=2023-11-15-preview" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzedocuments/SuccessfulPiiTaskResult.json b/dev/cognitiveservices/data-plane/Language/examples/analyzedocuments/SuccessfulPiiTaskResult.json deleted file mode 100644 index e49738947858..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzedocuments/SuccessfulPiiTaskResult.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-11-15-preview", - "Endpoint": "{Endpoint}", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "createdDateTime": "2020-10-01T15:00:45Z", - "displayName": "Redact PII Example", - "expirationDateTime": "2020-10-03T15:01:03Z", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18", - "lastUpdatedDateTime": "2020-10-01T15:01:03Z", - "status": "succeeded", - "tasks": { - "completed": 1, - "failed": 0, - "inProgress": 0, - "total": 1, - "items": [ - { - "kind": "PiiEntityRecognitionLROResults", - "taskName": "PII Redaction Task 1", - "lastUpdateDateTime": "2020-10-01T15:01:03Z", - "status": "succeeded", - "results": { - "documents": [ - { - "source": { - "location": "https://myblob.blob.core.windows.net/Container/document.txt", - "kind": "AzureBlob" - }, - "target": [ - { - "location": "https://myblob.blob.core.windows.net/Container/0af5def0-5855-41b2-9e75-0a559fb6f545_PiiEntityRecognition_1_document.txt", - "kind": "AzureBlob" - }, - { - "location": "https://myblob.blob.core.windows.net/Container/0af5def0-5855-41b2-9e75-0a559fb6f545_PiiEntityRecognition_1.json", - "kind": "AzureBlob" - } - ], - "id": "1", - "warnings": [] - } - ], - "errors": [], - "modelVersion": "2023-09-01" - } - } - ] - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzedocuments/SuccessfulPiiTaskSubmit.json b/dev/cognitiveservices/data-plane/Language/examples/analyzedocuments/SuccessfulPiiTaskSubmit.json deleted file mode 100644 index 8223a8a81c3d..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzedocuments/SuccessfulPiiTaskSubmit.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-11-15-preview", - "Endpoint": "{Endpoint}", - "jobId": "{Job ID}", - "body": { - "displayName": "Redact PII Example", - "analysisInput": { - "documents": [ - { - "id": "1", - "language": "en", - "source": { - "location": "https://myblob.blob.core.windows.net/Container/document.txt", - "kind": "AzureBlob", - "managedIdentityClientId": "edaa610c-d7e8-4ba2-823a-97d871d661fb" - }, - "target": { - "location": "https://myblob.blob.core.windows.net/Container", - "kind": "AzureBlob" - } - } - ] - }, - "tasks": [ - { - "kind": "PiiEntityRecognition", - "taskName": "Redact PII Task 1", - "parameters": { - "redactionCharacter": "-" - } - } - ] - } - }, - "responses": { - "202": { - "headers": { - "Operation-Location": "{endpoint}/language/analyze-documents/jobs/{jobId}?api-version=2023-11-15-preview" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulCancelTrainingJob.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulCancelTrainingJob.json deleted file mode 100644 index ee22e9fba1c8..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulCancelTrainingJob.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-text/projects/LoanAgreements/train/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulCreateProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulCreateProject.json deleted file mode 100644 index 0b7c0e4759e7..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulCreateProject.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/merge-patch+json", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "body": { - "projectName": "LoanAgreements", - "language": "en", - "projectKind": "CustomEntityRecognition", - "description": "This is a sample dataset provided by the Azure Language service team to help users get started with [Custom named entity recognition](https://aka.ms/ct-docs). The provided sample dataset contains 20 loan agreements drawn up between two entities.", - "multilingual": false, - "storageInputContainerName": "loanagreements" - } - }, - "responses": { - "201": { - "headers": {}, - "body": { - "createdDateTime": "2022-04-18T13:53:03Z", - "lastModifiedDateTime": "2022-04-18T13:53:03Z", - "projectKind": "CustomEntityRecognition", - "storageInputContainerName": "loanagreements", - "projectName": "LoanAgreements", - "multilingual": false, - "description": "This is a sample dataset provided by the Azure Language service team to help users get started with [Custom named entity recognition](https://aka.ms/ct-docs). The provided sample dataset contains 20 loan agreements drawn up between two entities.", - "language": "en" - } - }, - "200": { - "headers": {}, - "body": { - "createdDateTime": "2022-04-18T13:53:03Z", - "lastModifiedDateTime": "2022-04-18T13:53:03Z", - "lastTrainedDateTime": "2022-04-18T14:14:28Z", - "lastDeployedDateTime": "2022-04-18T14:49:01Z", - "projectKind": "CustomEntityRecognition", - "storageInputContainerName": "loanagreements", - "projectName": "LoanAgreements", - "multilingual": false, - "description": "This is a sample dataset provided by the Azure Language service team to help users get started with [Custom named entity recognition](https://aka.ms/ct-docs). The provided sample dataset contains 20 loan agreements drawn up between two entities.", - "language": "en" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeleteDeployment.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeleteDeployment.json deleted file mode 100644 index c7fe78e935dd..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeleteDeployment.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "deploymentName": "staging" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-text/projects/LoanAgreements/deployments/staging/jobs/61ebb7ef-a207-40d2-82b9-5285440ae579_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeleteModel.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeleteModel.json deleted file mode 100644 index 4878a4e330a9..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeleteModel.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "trainedModelLabel": "model2" - }, - "responses": { - "204": {} - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeleteProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeleteProject.json deleted file mode 100644 index 16b875ec4cf8..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeleteProject.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-text/projects/global/deletion-jobs/129d3182-625d-496c-bcf9-43686e85160b_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeployProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeployProject.json deleted file mode 100644 index d89e23562201..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulDeployProject.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "deploymentName": "production", - "body": { - "trainedModelLabel": "29886710a2ae49259d62cffca977db66" - } - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-text/projects/LoanAgreements/deployments/production/jobs/66fa9a67-a561-42f1-8a13-f3a879b1a324_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulExportProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulExportProject.json deleted file mode 100644 index 2b428b69c027..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulExportProject.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "stringIndexType": "Utf16CodeUnit" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-text/projects/LoanAgreements/export/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetDeployment.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetDeployment.json deleted file mode 100644 index aef1a2c13cc3..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetDeployment.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "deploymentName": "staging" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "deploymentName": "staging", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49.4334381Z", - "lastDeployedDateTime": "2022-04-18T15:53:04Z", - "deploymentExpirationDate": "2023-10-28", - "modelTrainingConfigVersion": "2022-05-15-preview" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetDeploymentStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetDeploymentStatus.json deleted file mode 100644 index dc9ac893ae0f..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetDeploymentStatus.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "deploymentName": "production", - "jobId": "66fa9a67-a561-42f1-8a13-f3a879b1a324_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "66fa9a67-a561-42f1-8a13-f3a879b1a324_637858368000000000", - "createdDateTime": "2022-04-18T15:52:48Z", - "lastUpdatedDateTime": "2022-04-18T15:53:04Z", - "expirationDateTime": "2022-04-25T15:52:48Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetExportStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetExportStatus.json deleted file mode 100644 index 56d2c50919bd..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetExportStatus.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "jobId": "c95efa2a-44e8-461e-8aa5-04b4677bfa84_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "resultUrl": "{Endpoint}/language/authoring/analyze-text/projects/LoanAgreements/export/jobs/c4946bfa-4fbf-493b-bfcf-2d232eb9de69_637858368000000000/result?api-version=2023-04-01", - "jobId": "c4946bfa-4fbf-493b-bfcf-2d232eb9de69_637858368000000000", - "createdDateTime": "2022-04-18T15:23:07Z", - "lastUpdatedDateTime": "2022-04-18T15:23:08Z", - "expirationDateTime": "2022-04-25T15:23:07Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetImportStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetImportStatus.json deleted file mode 100644 index aeef476cdd97..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetImportStatus.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "jobId": "c95efa2a-44e8-461e-8aa5-04b4677bfa84_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "c95efa2a-44e8-461e-8aa5-04b4677bfa84_637858368000000000", - "createdDateTime": "2022-04-18T15:17:20Z", - "lastUpdatedDateTime": "2022-04-18T15:17:22Z", - "expirationDateTime": "2022-04-25T15:17:20Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetLoadSnapshotStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetLoadSnapshotStatus.json deleted file mode 100644 index 5199596f42c2..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetLoadSnapshotStatus.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "trainedModelLabel": "model1", - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000", - "createdDateTime": "2022-04-18T15:44:44Z", - "lastUpdatedDateTime": "2022-04-18T15:45:48Z", - "expirationDateTime": "2022-04-25T15:44:44Z", - "status": "running" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetModel.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetModel.json deleted file mode 100644 index 94fffc44e6c1..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetModel.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "trainedModelLabel": "model1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "label": "model1", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49Z", - "lastTrainingDurationInSeconds": 186, - "modelExpirationDate": "2022-10-28", - "modelTrainingConfigVersion": "2022-05-15-preview", - "hasSnapshot": true - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetModelEvaluation.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetModelEvaluation.json deleted file mode 100644 index a3b7e0e9c428..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetModelEvaluation.json +++ /dev/null @@ -1,295 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "trainedModelLabel": "model2", - "stringIndexType": "Utf16CodeUnit", - "maxpagesize": 10 - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "location": "11.txt", - "language": "en-us", - "projectKind": "CustomEntityRecognition", - "customEntityRecognitionResult": { - "entities": [ - { - "expectedEntities": [ - { - "category": "Date", - "offset": 5, - "length": 9 - }, - { - "category": "BorrowerName", - "offset": 160, - "length": 13 - }, - { - "category": "BorrowerAddress", - "offset": 181, - "length": 34 - }, - { - "category": "BorrowerCity", - "offset": 225, - "length": 6 - }, - { - "category": "BorrowerState", - "offset": 242, - "length": 8 - }, - { - "category": "LenderName", - "offset": 271, - "length": 12 - }, - { - "category": "LenderAddress", - "offset": 310, - "length": 20 - }, - { - "category": "LenderCity", - "offset": 340, - "length": 8 - }, - { - "category": "LenderState", - "offset": 359, - "length": 11 - }, - { - "category": "LoanAmountWords", - "offset": 448, - "length": 52 - }, - { - "category": "LoanAmountNumbers", - "offset": 502, - "length": 10 - }, - { - "category": "Interest", - "offset": 588, - "length": 2 - } - ], - "predictedEntities": [ - { - "category": "Date", - "offset": 5, - "length": 9 - }, - { - "category": "BorrowerName", - "offset": 160, - "length": 13 - }, - { - "category": "BorrowerAddress", - "offset": 200, - "length": 15 - }, - { - "category": "BorrowerCity", - "offset": 225, - "length": 6 - }, - { - "category": "BorrowerState", - "offset": 242, - "length": 8 - }, - { - "category": "LenderName", - "offset": 271, - "length": 12 - }, - { - "category": "LenderAddress", - "offset": 310, - "length": 20 - }, - { - "category": "LenderCity", - "offset": 340, - "length": 8 - }, - { - "category": "LenderState", - "offset": 359, - "length": 11 - }, - { - "category": "LoanAmountWords", - "offset": 448, - "length": 52 - }, - { - "category": "LoanAmountNumbers", - "offset": 502, - "length": 10 - }, - { - "category": "Interest", - "offset": 588, - "length": 2 - } - ], - "regionOffset": 0, - "regionLength": 1780 - } - ] - } - }, - { - "location": "01.txt", - "language": "en-us", - "projectKind": "CustomEntityRecognition", - "customEntityRecognitionResult": { - "entities": [ - { - "expectedEntities": [ - { - "category": "Date", - "offset": 5, - "length": 9 - }, - { - "category": "BorrowerName", - "offset": 160, - "length": 13 - }, - { - "category": "BorrowerAddress", - "offset": 200, - "length": 13 - }, - { - "category": "BorrowerCity", - "offset": 223, - "length": 9 - }, - { - "category": "BorrowerState", - "offset": 243, - "length": 8 - }, - { - "category": "LenderName", - "offset": 273, - "length": 14 - }, - { - "category": "LenderAddress", - "offset": 314, - "length": 15 - }, - { - "category": "LenderCity", - "offset": 339, - "length": 10 - }, - { - "category": "LenderState", - "offset": 360, - "length": 8 - }, - { - "category": "LoanAmountWords", - "offset": 446, - "length": 66 - }, - { - "category": "LoanAmountNumbers", - "offset": 514, - "length": 11 - }, - { - "category": "Interest", - "offset": 601, - "length": 2 - } - ], - "predictedEntities": [ - { - "category": "Date", - "offset": 5, - "length": 9 - }, - { - "category": "BorrowerName", - "offset": 160, - "length": 13 - }, - { - "category": "BorrowerAddress", - "offset": 200, - "length": 13 - }, - { - "category": "BorrowerCity", - "offset": 223, - "length": 9 - }, - { - "category": "BorrowerState", - "offset": 243, - "length": 8 - }, - { - "category": "LenderName", - "offset": 273, - "length": 14 - }, - { - "category": "LenderAddress", - "offset": 314, - "length": 15 - }, - { - "category": "LenderCity", - "offset": 339, - "length": 10 - }, - { - "category": "LenderState", - "offset": 360, - "length": 8 - }, - { - "category": "LoanAmountWords", - "offset": 446, - "length": 66 - }, - { - "category": "LoanAmountNumbers", - "offset": 514, - "length": 11 - }, - { - "category": "Interest", - "offset": 601, - "length": 2 - } - ], - "regionOffset": 0, - "regionLength": 1793 - } - ] - } - } - ], - "nextLink": "{Endpoint}/language/authoring/analyze-text/projects/LoanAgreements/models/model2/evaluation/result/?api-version=2023-04-01&top=2147483645&skip={maxpagesize}&maxpagesize={maxpagesize}" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetModelEvaluationSummary.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetModelEvaluationSummary.json deleted file mode 100644 index 69d74725e758..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetModelEvaluationSummary.json +++ /dev/null @@ -1,232 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "trainedModelLabel": "model2" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "projectKind": "CustomEntityRecognition", - "customEntityRecognitionEvaluation": { - "confusionMatrix": { - "BorrowerAddress": { - "BorrowerAddress": { - "normalizedValue": 86.206894, - "rawValue": 3.4482758 - }, - "$none": { - "normalizedValue": 13.793103, - "rawValue": 0.55172414 - } - }, - "BorrowerCity": { - "BorrowerCity": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "BorrowerName": { - "BorrowerName": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "BorrowerState": { - "BorrowerState": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "Date": { - "Date": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "Interest": { - "Interest": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "LenderAddress": { - "LenderAddress": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "LenderCity": { - "LenderCity": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "LenderName": { - "LenderName": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "LenderState": { - "LenderState": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "LoanAmountNumbers": { - "LoanAmountNumbers": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "LoanAmountWords": { - "LoanAmountWords": { - "normalizedValue": 100.0, - "rawValue": 4.0 - } - }, - "$none": { - "$none": { - "normalizedValue": 99.81485, - "rawValue": 51.90372 - }, - "BorrowerAddress": { - "normalizedValue": 0.18315019, - "rawValue": 0.0952381 - }, - "Interest": { - "normalizedValue": 0.002005294, - "rawValue": 0.0010427529 - } - } - }, - "entities": { - "Date": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "BorrowerName": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "BorrowerAddress": { - "f1": 0.6666666865348816, - "precision": 0.6000000238418579, - "recall": 0.75, - "truePositiveCount": 3, - "trueNegativeCount": 0, - "falsePositiveCount": 2, - "falseNegativeCount": 1 - }, - "BorrowerCity": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "BorrowerState": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "LenderName": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "LenderAddress": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "LenderCity": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "LenderState": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "LoanAmountWords": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "LoanAmountNumbers": { - "f1": 1.0, - "precision": 1.0, - "recall": 1.0, - "truePositiveCount": 4, - "trueNegativeCount": 0, - "falsePositiveCount": 0, - "falseNegativeCount": 0 - }, - "Interest": { - "f1": 0.75, - "precision": 0.75, - "recall": 0.75, - "truePositiveCount": 3, - "trueNegativeCount": 0, - "falsePositiveCount": 1, - "falseNegativeCount": 1 - } - }, - "microF1": 0.94845366, - "microPrecision": 0.93877554, - "microRecall": 0.9583333, - "macroF1": 0.9513889, - "macroPrecision": 0.9458334, - "macroRecall": 0.9583333 - }, - "evaluationOptions": { - "kind": "percentage", - "trainingSplitPercentage": 80, - "testingSplitPercentage": 20 - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetProject.json deleted file mode 100644 index 3da80053e31e..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetProject.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "createdDateTime": "2022-04-18T13:53:03Z", - "lastModifiedDateTime": "2022-04-18T13:53:03Z", - "lastTrainedDateTime": "2022-04-18T14:14:28Z", - "lastDeployedDateTime": "2022-04-18T14:49:01Z", - "projectKind": "CustomEntityRecognition", - "storageInputContainerName": "loanagreements", - "projectName": "LoanAgreements", - "multilingual": false, - "description": "This is a sample dataset provided by the Azure Language service team to help users get started with [Custom named entity recognition](https://aka.ms/ct-docs). The provided sample dataset contains 20 loan agreements drawn up between two entities.", - "language": "en" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetProjectDeletionStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetProjectDeletionStatus.json deleted file mode 100644 index 73c8d1cf0647..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetProjectDeletionStatus.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "jobId": "129d3182-625d-496c-bcf9-43686e85160b_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "129d3182-625d-496c-bcf9-43686e85160b_637858368000000000", - "createdDateTime": "2022-04-18T14:02:34Z", - "lastUpdatedDateTime": "2022-04-18T14:02:34Z", - "expirationDateTime": "2022-04-25T14:02:34Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetSupportedLanguages.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetSupportedLanguages.json deleted file mode 100644 index 2cdfd865d82a..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetSupportedLanguages.json +++ /dev/null @@ -1,389 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "languageName": "English", - "languageCode": "en" - }, - { - "languageName": "English", - "languageCode": "en-us" - }, - { - "languageName": "French", - "languageCode": "fr" - }, - { - "languageName": "Italian", - "languageCode": "it" - }, - { - "languageName": "Spanish", - "languageCode": "es" - }, - { - "languageName": "German", - "languageCode": "de" - }, - { - "languageName": "Portuguese (Brazil)", - "languageCode": "pt-br" - }, - { - "languageName": "Portuguese (Portugal)", - "languageCode": "pt-pt" - }, - { - "languageName": "Afrikaans", - "languageCode": "af" - }, - { - "languageName": "Amharic", - "languageCode": "am" - }, - { - "languageName": "Arabic", - "languageCode": "ar" - }, - { - "languageName": "Assamese", - "languageCode": "as" - }, - { - "languageName": "Azerbaijani", - "languageCode": "az" - }, - { - "languageName": "Belarusian", - "languageCode": "be" - }, - { - "languageName": "Bulgarian", - "languageCode": "bg" - }, - { - "languageName": "Breton", - "languageCode": "br" - }, - { - "languageName": "Bosnian", - "languageCode": "bs" - }, - { - "languageName": "Catalan", - "languageCode": "ca" - }, - { - "languageName": "Czech", - "languageCode": "cs" - }, - { - "languageName": "Welsh", - "languageCode": "cy" - }, - { - "languageName": "Danish", - "languageCode": "da" - }, - { - "languageName": "Greek", - "languageCode": "el" - }, - { - "languageName": "Esperanto", - "languageCode": "eo" - }, - { - "languageName": "Estonian", - "languageCode": "et" - }, - { - "languageName": "Basque", - "languageCode": "eu" - }, - { - "languageName": "Persian", - "languageCode": "fa" - }, - { - "languageName": "Finnish", - "languageCode": "fi" - }, - { - "languageName": "Western Frisian", - "languageCode": "fy" - }, - { - "languageName": "Irish", - "languageCode": "ga" - }, - { - "languageName": "Scottish Gaelic", - "languageCode": "gd" - }, - { - "languageName": "Galician", - "languageCode": "gl" - }, - { - "languageName": "Gujarati", - "languageCode": "gu" - }, - { - "languageName": "Hausa", - "languageCode": "ha" - }, - { - "languageName": "Hebrew", - "languageCode": "he" - }, - { - "languageName": "Hindi", - "languageCode": "hi" - }, - { - "languageName": "Croatian", - "languageCode": "hr" - }, - { - "languageName": "Hungarian", - "languageCode": "hu" - }, - { - "languageName": "Armenian", - "languageCode": "hy" - }, - { - "languageName": "Indonesian", - "languageCode": "id" - }, - { - "languageName": "Japanese", - "languageCode": "ja" - }, - { - "languageName": "Javanese", - "languageCode": "jv" - }, - { - "languageName": "Georgian", - "languageCode": "ka" - }, - { - "languageName": "Kazakh", - "languageCode": "kk" - }, - { - "languageName": "Khmer", - "languageCode": "km" - }, - { - "languageName": "Kannada", - "languageCode": "kn" - }, - { - "languageName": "Korean", - "languageCode": "ko" - }, - { - "languageName": "Kurdish (Kurmanji)", - "languageCode": "ku" - }, - { - "languageName": "Kyrgyz", - "languageCode": "ky" - }, - { - "languageName": "Latin", - "languageCode": "la" - }, - { - "languageName": "Lao", - "languageCode": "lo" - }, - { - "languageName": "Lithuanian", - "languageCode": "lt" - }, - { - "languageName": "Latvian", - "languageCode": "lv" - }, - { - "languageName": "Malagasy", - "languageCode": "mg" - }, - { - "languageName": "Macedonian", - "languageCode": "mk" - }, - { - "languageName": "Malayalam", - "languageCode": "ml" - }, - { - "languageName": "Mongolian", - "languageCode": "mn" - }, - { - "languageName": "Marathi", - "languageCode": "mr" - }, - { - "languageName": "Malay", - "languageCode": "ms" - }, - { - "languageName": "Burmese", - "languageCode": "my" - }, - { - "languageName": "Nepali", - "languageCode": "ne" - }, - { - "languageName": "Dutch", - "languageCode": "nl" - }, - { - "languageName": "Norwegian (Bokmal)", - "languageCode": "nb" - }, - { - "languageName": "Odia", - "languageCode": "or" - }, - { - "languageName": "Punjabi", - "languageCode": "pa" - }, - { - "languageName": "Polish", - "languageCode": "pl" - }, - { - "languageName": "Pashto", - "languageCode": "ps" - }, - { - "languageName": "Romanian", - "languageCode": "ro" - }, - { - "languageName": "Russian", - "languageCode": "ru" - }, - { - "languageName": "Sanskrit", - "languageCode": "sa" - }, - { - "languageName": "Sindhi", - "languageCode": "sd" - }, - { - "languageName": "Sinhala", - "languageCode": "si" - }, - { - "languageName": "Slovak", - "languageCode": "sk" - }, - { - "languageName": "Slovenian", - "languageCode": "sl" - }, - { - "languageName": "Somali", - "languageCode": "so" - }, - { - "languageName": "Albanian", - "languageCode": "sq" - }, - { - "languageName": "Serbian", - "languageCode": "sr" - }, - { - "languageName": "Sundanese", - "languageCode": "su" - }, - { - "languageName": "Swedish", - "languageCode": "sv" - }, - { - "languageName": "Swahili", - "languageCode": "sw" - }, - { - "languageName": "Tamil", - "languageCode": "ta" - }, - { - "languageName": "Telugu", - "languageCode": "te" - }, - { - "languageName": "Thai", - "languageCode": "th" - }, - { - "languageName": "Filipino", - "languageCode": "tl" - }, - { - "languageName": "Turkish", - "languageCode": "tr" - }, - { - "languageName": "Uyghur", - "languageCode": "ug" - }, - { - "languageName": "Ukrainian", - "languageCode": "uk" - }, - { - "languageName": "Urdu", - "languageCode": "ur" - }, - { - "languageName": "Uzbek", - "languageCode": "uz" - }, - { - "languageName": "Vietnamese", - "languageCode": "vi" - }, - { - "languageName": "Xhosa", - "languageCode": "xh" - }, - { - "languageName": "Yiddish", - "languageCode": "yi" - }, - { - "languageName": "Chinese (Simplified)", - "languageCode": "zh-hans" - }, - { - "languageName": "Chinese (Traditional)", - "languageCode": "zh-hant" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetSupportedTrainingConfigVersions.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetSupportedTrainingConfigVersions.json deleted file mode 100644 index a60f9f0aade3..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetSupportedTrainingConfigVersions.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectKind": "CustomEntityRecognition" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "trainingConfigVersion": "2022-05-01", - "modelExpirationDate": "2022-10-28" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetSwapDeploymentsStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetSwapDeploymentsStatus.json deleted file mode 100644 index eef03f34b045..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetSwapDeploymentsStatus.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "jobId": "c36a8775-35b9-4cb5-a8db-665e7d91aafe_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "jobId": "c36a8775-35b9-4cb5-a8db-665e7d91aafe_637858368000000000", - "createdDateTime": "2022-04-18T16:09:50Z", - "lastUpdatedDateTime": "2022-04-18T16:09:58Z", - "expirationDateTime": "2022-04-25T16:09:50Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetTrainStatus.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetTrainStatus.json deleted file mode 100644 index 671b192b1951..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulGetTrainStatus.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "result": { - "modelLabel": "model1", - "trainingConfigVersion": "2022-05-01", - "estimatedEndDateTime": "2022-04-18T15:47:58.8190649Z", - "trainingStatus": { - "percentComplete": 3, - "startDateTime": "2022-04-18T15:45:06.8190649Z", - "status": "running" - }, - "evaluationStatus": { - "percentComplete": 0, - "status": "notStarted" - } - }, - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000", - "createdDateTime": "2022-04-18T15:44:44Z", - "lastUpdatedDateTime": "2022-04-18T15:45:48Z", - "expirationDateTime": "2022-04-25T15:44:44Z", - "status": "running" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulImportProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulImportProject.json deleted file mode 100644 index 1016782ada17..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulImportProject.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "body": { - "projectFileVersion": "2022-05-01", - "stringIndexType": "Utf16CodeUnit", - "metadata": { - "projectKind": "CustomEntityRecognition", - "storageInputContainerName": "loanagreements", - "settings": {}, - "projectName": "LoanAgreements", - "multilingual": false, - "description": "This is a sample dataset provided by the Azure Language service team to help users get started with [Custom named entity recognition](https://aka.ms/ct-docs). The provided sample dataset contains 20 loan agreements drawn up between two entities.", - "language": "en" - }, - "assets": { - "projectKind": "CustomEntityRecognition", - "entities": [ - { - "category": "Date" - }, - { - "category": "LenderName" - }, - { - "category": "LenderAddress" - } - ], - "documents": [ - { - "location": "01.txt", - "language": "en-us", - "entities": [ - { - "regionOffset": 0, - "regionLength": 1793, - "labels": [ - { - "category": "Date", - "offset": 5, - "length": 9 - }, - { - "category": "LenderName", - "offset": 273, - "length": 14 - }, - { - "category": "LenderAddress", - "offset": 314, - "length": 15 - } - ] - } - ] - }, - { - "location": "02.txt", - "language": "en-us", - "entities": [ - { - "regionOffset": 0, - "regionLength": 1804, - "labels": [ - { - "category": "Date", - "offset": 5, - "length": 10 - }, - { - "category": "LenderName", - "offset": 284, - "length": 10 - }, - { - "category": "LenderAddress", - "offset": 321, - "length": 20 - } - ] - } - ] - } - ] - } - } - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-text/projects/LoanAgreements/import/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2022-05-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListDeployments.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListDeployments.json deleted file mode 100644 index faf480e9b87a..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListDeployments.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements" - }, - "responses": { - "200": { - "body": { - "value": [ - { - "deploymentName": "production", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49.4334381Z", - "lastDeployedDateTime": "2022-04-18T16:03:51Z", - "deploymentExpirationDate": "2023-10-28", - "modelTrainingConfigVersion": "2022-05-01" - }, - { - "deploymentName": "staging", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49.4334381Z", - "lastDeployedDateTime": "2022-04-18T15:53:04Z", - "deploymentExpirationDate": "2023-10-28", - "modelTrainingConfigVersion": "2022-05-01" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListModels.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListModels.json deleted file mode 100644 index 8186c3f3d174..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListModels.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "label": "model1", - "modelId": "model1-20220418T034749-299f45b8114849538c1a750b21b05a94", - "lastTrainedDateTime": "2022-04-18T15:47:49Z", - "lastTrainingDurationInSeconds": 186, - "modelExpirationDate": "2022-10-28", - "modelTrainingConfigVersion": "2022-05-01", - "hasSnapshot": true - }, - { - "label": "model2", - "modelId": "model2-20220418T052522-c63bd244dd9e4bf8adec1a7129968c99", - "lastTrainedDateTime": "2022-04-18T17:25:22Z", - "lastTrainingDurationInSeconds": 192, - "modelExpirationDate": "2022-10-28", - "modelTrainingConfigVersion": "2022-05-01", - "hasSnapshot": true - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListProjects.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListProjects.json deleted file mode 100644 index 46244a7fb500..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListProjects.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "createdDateTime": "2022-04-18T13:53:03Z", - "lastModifiedDateTime": "2022-04-18T13:53:03Z", - "lastTrainedDateTime": "2022-04-18T14:14:28Z", - "lastDeployedDateTime": "2022-04-18T14:49:01Z", - "projectKind": "CustomEntityRecognition", - "storageInputContainerName": "loanagreements", - "projectName": "LoanAgreements", - "multilingual": false, - "description": "This is a sample dataset provided by the Azure Language service team to help users get started with [Custom named entity recognition](https://aka.ms/ct-docs). The provided sample dataset contains 20 loan agreements drawn up between two entities.", - "language": "en" - }, - { - "createdDateTime": "2022-04-18T14:03:12Z", - "lastModifiedDateTime": "2022-04-18T14:03:12Z", - "projectKind": "CustomMultiLabelClassification", - "storageInputContainerName": "loanagreements", - "projectName": "MoviesSummary", - "multilingual": false, - "description": "This is a sample dataset adapted from the CMU Movie Summary public dataset. This was prepared by Microsoft Azure Language Services product team to prepare this dataset to be used as a sample for getting started with Custom text classification. This sample dataset consists of 210 files each of them is a movie summary. Each movie can be classified into one or more of the following classes: \"Mystery\", \"Drama\", \"Thriller\", \"Comedy\", \"Action\".", - "language": "en" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListTrainingJobs.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListTrainingJobs.json deleted file mode 100644 index e9bf0ff70296..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulListTrainingJobs.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "result": { - "modelLabel": "model1", - "trainingConfigVersion": "2022-05-01", - "trainingStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T15:45:06.8190649Z", - "endDateTime": "2022-04-18T15:47:19.2639682Z", - "status": "succeeded" - }, - "evaluationStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T15:47:19.2734976Z", - "endDateTime": "2022-04-18T15:47:23.8378892Z", - "status": "succeeded" - } - }, - "jobId": "8ccf2ffe-e758-4d04-a44a-31512918c7e8_637858368000000000", - "createdDateTime": "2022-04-18T15:44:44Z", - "lastUpdatedDateTime": "2022-04-18T15:47:50Z", - "expirationDateTime": "2022-04-25T15:44:44Z", - "status": "succeeded" - }, - { - "result": { - "modelLabel": "model2", - "trainingConfigVersion": "2022-05-01", - "trainingStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T17:22:39.3663023Z", - "endDateTime": "2022-04-18T17:24:51.9440947Z", - "status": "succeeded" - }, - "evaluationStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T17:24:51.9571747Z", - "endDateTime": "2022-04-18T17:24:58.1427823Z", - "status": "succeeded" - } - }, - "jobId": "9145f93f-6f37-418c-8527-d2ded84cece0_637858368000000000", - "createdDateTime": "2022-04-18T17:22:11Z", - "lastUpdatedDateTime": "2022-04-18T17:25:23Z", - "expirationDateTime": "2022-04-25T17:22:11Z", - "status": "succeeded" - }, - { - "result": { - "modelLabel": "model2", - "trainingConfigVersion": "2022-05-01", - "trainingStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T17:44:41.388358Z", - "endDateTime": "2022-04-18T17:50:29.5675101Z", - "status": "succeeded" - }, - "evaluationStatus": { - "percentComplete": 100, - "startDateTime": "2022-04-18T17:50:29.5808461Z", - "endDateTime": "2022-04-18T17:50:35.3482185Z", - "status": "succeeded" - } - }, - "jobId": "ee23c900-354d-4b6d-96e1-8197db2bd5f7_637858368000000000", - "createdDateTime": "2022-04-18T17:44:04Z", - "lastUpdatedDateTime": "2022-04-18T17:51:11Z", - "expirationDateTime": "2022-04-25T17:44:04Z", - "status": "succeeded" - } - ], - "nextLink": null - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulLoadSnapshot.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulLoadSnapshot.json deleted file mode 100644 index f1cb63240640..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulLoadSnapshot.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "trainedModelLabel": "model1" - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-conversations/projects/LoanAgreements/models/model1/load-snapshot/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulSwapDeployments.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulSwapDeployments.json deleted file mode 100644 index a4d4408b4bba..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulSwapDeployments.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "body": { - "firstDeploymentName": "production", - "secondDeploymentName": "staging" - } - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-text/projects/LoanAgreements/deployments/swap/jobs/c36a8775-35b9-4cb5-a8db-665e7d91aafe_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulTrainProject.json b/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulTrainProject.json deleted file mode 100644 index ea8b27d1e04a..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/analyzetext-authoring/SuccessfulTrainProject.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "projectName": "LoanAgreements", - "body": { - "modelLabel": "model1", - "trainingConfigVersion": "latest", - "evaluationOptions": { - "kind": "percentage", - "testingSplitPercentage": 20, - "trainingSplitPercentage": 80 - } - } - }, - "responses": { - "202": { - "headers": { - "operation-location": "{Endpoint}/language/authoring/analyze-text/projects/LoanAgreements/train/jobs/4d37982f-fded-4c2c-afe3-15953b5919b6_637858368000000000?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversations.json b/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversations.json deleted file mode 100644 index bc8f8805bc81..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversations.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "body": { - "kind": "Conversation", - "analysisInput": { - "conversationItem": { - "id": "1", - "participantId": "1", - "text": "play In the air tonight from Phil Collins" - } - }, - "parameters": { - "projectName": "{project-name}", - "deploymentName": "{deployment-name}", - "stringIndexType": "TextElement_V8" - } - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "kind": "ConversationResult", - "result": { - "query": "play In the air tonight from Phil Collins", - "prediction": { - "topIntent": "PlayMusic", - "projectKind": "Conversation", - "intents": [ - { - "category": "PlayMusic", - "confidenceScore": 1 - }, - { - "category": "SearchCreativeWork", - "confidenceScore": 0 - }, - { - "category": "AddToPlaylist", - "confidenceScore": 0 - } - ], - "entities": [ - { - "category": "Media.Artist", - "text": "Phil Collins", - "offset": 29, - "length": 12, - "confidenceScore": 1 - } - ] - } - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversationsArbitration.json b/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversationsArbitration.json deleted file mode 100644 index 875e1e807b15..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversationsArbitration.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "body": { - "kind": "Conversation", - "analysisInput": { - "conversationItem": { - "participantId": "1", - "id": "1", - "modality": "text", - "language": "en-GB", - "text": "trains from London" - } - }, - "parameters": { - "projectName": "{project-name}", - "deploymentName": "{deployment-name}", - "verbose": true, - "isLoggingEnabled": false, - "stringIndexType": "TextElement_V8" - } - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "kind": "ConversationResult", - "result": { - "query": "trains from London", - "prediction": { - "topIntent": "Rail", - "projectKind": "Orchestration", - "intents": { - "Rail": { - "confidenceScore": 1, - "targetProjectKind": "Conversation", - "result": { - "query": "trains from London", - "prediction": { - "topIntent": "Timetable", - "projectKind": "Conversation", - "intents": [ - { - "category": "Timetable", - "confidenceScore": 0.99968535 - }, - { - "category": "Locomotive", - "confidenceScore": 0.000314623 - } - ], - "entities": [] - } - } - }, - "Tree": { - "confidenceScore": 0.2641529, - "targetProjectKind": "QuestionAnswering" - }, - "None": { - "confidenceScore": 0, - "targetProjectKind": "NonLinked" - } - } - } - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversationsArbitrationDirectTarget.json b/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversationsArbitrationDirectTarget.json deleted file mode 100644 index f23d13781902..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversationsArbitrationDirectTarget.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "body": { - "kind": "Conversation", - "analysisInput": { - "conversationItem": { - "text": "Ports and connectors", - "participantId": "1", - "id": "1" - } - }, - "parameters": { - "projectName": "prj1", - "deploymentName": "dep1", - "directTarget": "qnaProject", - "targetProjectParameters": { - "qnaProject": { - "targetProjectKind": "QuestionAnswering", - "callingOptions": { - "context": { - "previousUserQuery": "Meet Surface Pro 4", - "previousQnaId": 4 - }, - "top": 1, - "question": "App Service overview" - } - } - } - } - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "kind": "ConversationResult", - "result": { - "query": "Ports and connectors", - "prediction": { - "projectKind": "Orchestration", - "topIntent": "qnaTargetApp", - "intents": { - "qnaTargetApp": { - "targetProjectKind": "QuestionAnswering", - "confidenceScore": 1, - "result": { - "answers": [ - { - "questions": [ - "App Service overview" - ], - "answer": "The compute resources you use are determined by the *App Service plan* that you run your apps on.", - "confidenceScore": 0.7384000000000001, - "id": 1, - "source": "https://docs.microsoft.com/en-us/azure/app-service/overview", - "metadata": {}, - "dialog": { - "isContextOnly": false, - "prompts": [] - } - } - ] - } - } - } - } - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversationsJobsCancelRequest.json b/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversationsJobsCancelRequest.json deleted file mode 100644 index 705087d1ddf6..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulAnalyzeConversationsJobsCancelRequest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18" - }, - "responses": { - "202": { - "headers": { - "Operation-Location": "{Endpoint}/language/analyze-conversations/jobs/{jobId}?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarizationTaskResult.json b/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarizationTaskResult.json deleted file mode 100644 index 4f49bdcd04a8..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarizationTaskResult.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "3e9e8518-492f-47f9-abd1-9a7468231086" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "displayName": "Conversation Summarization Example", - "createdDateTime": "2022-04-01T15:00:45Z", - "expirationDateTime": "2022-04-02T15:00:45Z", - "jobId": "3e9e8518-492f-47f9-abd1-9a7468231086", - "lastUpdatedDateTime": "2022-04-01T15:00:49Z", - "status": "succeeded", - "tasks": { - "completed": 1, - "failed": 0, - "inProgress": 0, - "total": 1, - "items": [ - { - "kind": "ConversationalSummarizationResults", - "lastUpdateDateTime": "2022-04-01T15:00:49Z", - "taskName": "Conversation Summarization Task 1", - "status": "succeeded", - "results": { - "conversations": [ - { - "id": "1", - "summaries": [ - { - "aspect": "chapterTitle", - "text": "Bug Triage", - "contexts": [ - { - "conversationItemId": "2", - "offset": 4, - "length": 39 - } - ] - }, - { - "aspect": "narrative", - "text": "Speaker 3 believes there are 3 remaining bugs.", - "contexts": [ - { - "conversationItemId": "2", - "offset": 4, - "length": 39 - }, - { - "conversationItemId": "3", - "offset": 0, - "length": 7 - } - ] - } - ], - "warnings": [] - } - ], - "errors": [], - "modelVersion": "latest" - } - } - ] - }, - "nextLink": "/language/analyze-conversation/jobs/3e9e8518-492f-47f9-abd1-9a7468231086?$skip=10&$top=10" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarizationTaskStatusRequest.json b/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarizationTaskStatusRequest.json deleted file mode 100644 index fb4d61aacc88..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarizationTaskStatusRequest.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "displayName": "Analyze chat", - "createdDateTime": "2022-04-01T15:00:45Z", - "expirationDateTime": "2022-04-02T15:00:45Z", - "jobId": "3e9e8518-492f-47f9-abd1-9a7468231086", - "lastUpdatedDateTime": "2022-04-01T15:00:49Z", - "status": "succeeded", - "tasks": { - "completed": 1, - "failed": 0, - "inProgress": 0, - "total": 1, - "items": [ - { - "kind": "ConversationalSummarizationResults", - "lastUpdateDateTime": "2022-04-01T15:00:49Z", - "taskName": "analyze 1", - "status": "succeeded", - "results": { - "conversations": [ - { - "id": "20220101meeting", - "summaries": [ - { - "aspect": "issue", - "text": "Customer wants to cancel his subscription. Customer doesn't know how" - }, - { - "aspect": "resolution", - "text": "pii Auto-Fortify. \npii Gamertag.\npii E-mail.\nCanceled customer's subscription" - } - ], - "warnings": [], - "statistics": { - "transactionsCount": 1 - } - } - ], - "errors": [], - "modelVersion": "latest" - } - } - ] - }, - "nextLink": "/language/analyze-conversation/jobs/3e9e8518-492f-47f9-abd1-9a7468231086?$skip=10&$top=10" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarizationTaskSubmit.json b/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarizationTaskSubmit.json deleted file mode 100644 index 3f7e3e9682f5..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarizationTaskSubmit.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "{Job ID}", - "body": { - "displayName": "Conversation Summarization Example", - "analysisInput": { - "conversations": [ - { - "id": "1", - "language": "en", - "modality": "transcript", - "conversationItems": [ - { - "participantId": "speaker 1", - "id": "1", - "text": "Let's get started.", - "lexical": "", - "itn": "", - "maskedItn": "", - "conversationItemLevelTiming": { - "offset": 0, - "duration": 20000000 - } - }, - { - "participantId": "speaker 2", - "id": "2", - "text": "OK. How many remaining bugs do we have now?", - "lexical": "", - "itn": "", - "maskedItn": "", - "conversationItemLevelTiming": { - "offset": 20000000, - "duration": 50000000 - } - }, - { - "participantId": "speaker 3", - "id": "3", - "text": "Only 3.", - "lexical": "", - "itn": "", - "maskedItn": "", - "conversationItemLevelTiming": { - "offset": 50000000, - "duration": 60000000 - } - } - ] - } - ] - }, - "tasks": [ - { - "taskName": "Conversation Summarization Task 1", - "kind": "ConversationalSummarizationTask", - "parameters": { - "summaryAspects": [ - "chapterTitle", - "narrative" - ] - } - } - ] - } - }, - "responses": { - "202": { - "headers": { - "Operation-Location": "{Endpoint}/language/analyze-conversation/jobs/{jobId}?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarySubmit.json b/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarySubmit.json deleted file mode 100644 index 00a71497cd67..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/conversations/SuccessfulConversationSummarySubmit.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "{Job ID}", - "body": { - "displayName": "Analyze conversations from Contoso meeting", - "analysisInput": { - "conversations": [ - { - "id": "20220101meeting", - "language": "en", - "modality": "text", - "domain": "generic", - "conversationItems": [ - { - "participantId": "user", - "role": "customer", - "id": "1", - "text": "Hi!" - }, - { - "participantId": "agent", - "role": "agent", - "id": "2", - "text": "Hello, how can I help you?" - }, - { - "participantId": "user", - "role": "customer", - "id": "3", - "text": "I am having trouble issuing a return of a game on my xbox: call of duty" - } - ] - } - ] - }, - "tasks": [ - { - "taskName": "analyze 1", - "kind": "ConversationalSummarizationTask", - "parameters": { - "modelVersion": "latest", - "summaryAspects": [ - "issue", - "resolution" - ] - } - } - ] - } - }, - "responses": { - "202": { - "headers": { - "Operation-Location": "{Endpoint}/language/analyze-conversation/jobs/{jobId}?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/SuccessfulQueryKnowledgebases.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/SuccessfulQueryKnowledgebases.json deleted file mode 100644 index 35e2adf286f6..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/SuccessfulQueryKnowledgebases.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "deploymentName": "production", - "knowledgeBaseQueryOptions": { - "question": "how long it takes to charge surface?", - "top": 3, - "userId": "sd53lsY=", - "confidenceScoreThreshold": 0.2, - "context": { - "previousQnaId": 9, - "previousUserQuery": "Where are QnA Maker quickstarts?" - }, - "rankerType": "Default", - "filters": { - "metadataFilter": { - "metadata": [ - { - "key": "category", - "value": "api" - }, - { - "key": "editorial", - "value": "chitchat" - } - ], - "logicalOperation": "AND" - }, - "sourceFilter": [ - "filename1.pdf", - "https://www.wikipedia.org/microsoft" - ], - "logicalOperation": "AND" - }, - "answerSpanRequest": { - "enable": true, - "confidenceScoreThreshold": 0.2, - "topAnswersWithSpan": 1 - }, - "includeUnstructuredSources": true - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "answers": [ - { - "questions": [ - "Power and charging" - ], - "answer": "Power and charging**\n\nIt takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.\n\nYou can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface.", - "confidenceScore": 0.65, - "id": 20, - "source": "surface-pro-4-user-guide-EN.pdf", - "metadata": { - "category": "api", - "editorial": "chitchat" - }, - "dialog": { - "isContextOnly": false, - "prompts": [ - { - "displayOrder": 1, - "qnaId": 23, - "displayText": "prompt1" - }, - { - "displayOrder": 2, - "qnaId": 36, - "displayText": "prompt2" - } - ] - }, - "answerSpan": { - "text": "two to four hours", - "confidenceScore": 0.3, - "offset": 33, - "length": 50 - } - }, - { - "questions": [ - "Charge your Surface Pro 4" - ], - "answer": "**Charge your Surface Pro 4**\n\n1. Connect the two parts of the power cord.\n\n2. Connect the power cord securely to the charging port.\n\n3. Plug the power supply into an electrical outlet.", - "confidenceScore": 0.32, - "id": 13, - "source": "surface-pro-4-user-guide-EN.pdf" - } - ] - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/SuccessfulQueryText.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/SuccessfulQueryText.json deleted file mode 100644 index 7470afc8ebe5..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/SuccessfulQueryText.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "stringIndexType": "TextElements_v8", - "textQueryOptions": { - "question": "how long it takes to charge surface?", - "records": [ - { - "id": "1", - "text": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it." - }, - { - "id": "2", - "text": "You can use the USB port on your Surface Pro 4 power supply to charge other devices, like a phone, while your Surface charges. The USB port on the power supply is only for charging, not for data transfer. If you want to use a USB device, plug it into the USB port on your Surface." - } - ], - "language": "en" - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "answers": [ - { - "answer": "Power and charging. It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "confidenceScore": 0.93, - "id": "1", - "answerSpan": { - "text": "two to four hours", - "confidenceScore": 0, - "offset": 28, - "length": 45 - }, - "offset": 0, - "length": 224 - }, - { - "answer": "It takes two to four hours to charge the Surface Pro 4 battery fully from an empty state. It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "confidenceScore": 0.92, - "id": "1", - "answerSpan": { - "text": "two to four hours", - "confidenceScore": 0, - "offset": 8, - "length": 25 - }, - "offset": 20, - "length": 224 - }, - { - "answer": "It can take longer if you’re using your Surface for power-intensive activities like gaming or video streaming while you’re charging it.", - "confidenceScore": 0.05, - "id": "1", - "answerSpan": null, - "offset": 110, - "length": 244 - } - ] - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulCreateProject.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulCreateProject.json deleted file mode 100644 index 0379fa212ca0..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulCreateProject.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "body": { - "description": "proj1 is a test project.", - "language": "en", - "settings": { - "defaultAnswer": "No good match found for your question in the Knowledgebase." - }, - "multilingualResource": true - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "projectName": "proj1", - "description": "proj1 is a test project.", - "language": "en", - "settings": { - "defaultAnswer": "No good match found for your question in the Knowledgebase." - }, - "multilingualResource": true, - "createdDateTime": "2021-05-01T15:13:22Z", - "lastModifiedDateTime": "2021-05-01T15:13:22Z", - "lastDeployedDateTime": "2021-05-01T15:13:22Z" - } - }, - "201": { - "headers": {}, - "body": { - "projectName": "proj1", - "description": "proj1 is a test project.", - "language": "en", - "settings": { - "defaultAnswer": "No good match found for your question in the Knowledgebase." - }, - "multilingualResource": true, - "createdDateTime": "2021-05-01T15:13:22Z", - "lastModifiedDateTime": "2021-05-01T15:13:22Z", - "lastDeployedDateTime": "2021-05-01T15:13:22Z" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulDeleteProject.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulDeleteProject.json deleted file mode 100644 index 5f383a7c5843..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulDeleteProject.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1" - }, - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the analysis job.", - "headers": { - "Operation-Location": "https:///language/authoring/query-knowledgebases/projects/deletion-jobs/job1?api-version=2023-05-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetProject.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetProject.json deleted file mode 100644 index 6559ff5eecd7..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetProject.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "projectName": "proj1", - "description": "proj1 is a test project.", - "language": "en", - "settings": { - "defaultAnswer": "No good match found for your question in the Knowledgebase." - }, - "createdDateTime": "2021-05-01T15:13:22Z", - "lastModifiedDateTime": "2021-05-01T15:13:22Z", - "lastDeployedDateTime": "2021-05-01T15:13:22Z" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetQnas.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetQnas.json deleted file mode 100644 index a0c507000d46..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetQnas.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "id": 1, - "answer": "ans1", - "source": "source1", - "questions": [ - "question 1.1", - "question 1.2" - ], - "metadata": { - "k1": "v1", - "k2": "v2" - }, - "dialog": { - "isContextOnly": false, - "prompts": [ - { - "displayOrder": 1, - "qnaId": 11, - "displayText": "prompt 1.1" - }, - { - "displayOrder": 2, - "qnaId": 21, - "displayText": "prompt 1.2" - } - ] - }, - "lastUpdatedDateTime": "2021-05-01T17:21:14Z" - }, - { - "id": 2, - "answer": "ans2", - "source": "source2", - "questions": [ - "question 2.1", - "question 2.2" - ], - "lastUpdatedDateTime": "2021-05-01T17:21:14Z" - } - ] - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetSources.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetSources.json deleted file mode 100644 index 93151be618e3..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetSources.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "displayName": "source1", - "sourceUri": "https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/overview/overview", - "sourceKind": "url", - "lastUpdatedDateTime": "2021-05-01T15:13:22Z", - "source": "https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/overview/overview" - }, - { - "displayName": "source2", - "sourceUri": "https://download.microsoft.com/download/2/9/B/29B20383-302C-4517-A006-B0186F04BE28/surface-pro-4-user-guide-EN.pdf", - "sourceKind": "file", - "contentStructureKind": "unstructured", - "lastUpdatedDateTime": "2021-05-01T15:13:22Z", - "source": "surface-guide.pdf" - } - ] - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetSynonyms.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetSynonyms.json deleted file mode 100644 index 64179691eedc..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulGetSynonyms.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "alterations": [ - "qnamaker", - "qna maker" - ] - }, - { - "alterations": [ - "botframework", - "bot framework" - ] - } - ] - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectDeleteJobStatus.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectDeleteJobStatus.json deleted file mode 100644 index b32ee372970f..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectDeleteJobStatus.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "jobId": "job1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "errors": [], - "createdDateTime": "2021-05-01T17:21:14Z", - "expirationDateTime": "2021-05-01T17:21:14Z", - "jobId": "635c2741-15c4-4c2c-9f78-bfd30b6b2a4a", - "lastUpdatedDateTime": "2021-05-01T17:21:14Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectDeployJobStatus.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectDeployJobStatus.json deleted file mode 100644 index c3448c1ed6b6..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectDeployJobStatus.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "deploymentName": "production", - "jobId": "job1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "errors": [], - "createdDateTime": "2021-05-01T17:21:14Z", - "expirationDateTime": "2021-05-01T17:21:14Z", - "jobId": "635c2741-15c4-4c2c-9f78-bfd30b6b2a4a", - "lastUpdatedDateTime": "2021-05-01T17:21:14Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectExportJobStatus.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectExportJobStatus.json deleted file mode 100644 index faade3721442..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectExportJobStatus.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "jobId": "job1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "errors": [], - "createdDateTime": "2021-05-01T17:21:14Z", - "expirationDateTime": "2021-05-01T17:21:14Z", - "jobId": "635c2741-15c4-4c2c-9f78-bfd30b6b2a4a", - "lastUpdatedDateTime": "2021-05-01T17:21:14Z", - "status": "succeeded", - "resultUrl": "https:///language/authoring/query-knowledgebases/projects/proj1/export/jobs/job1/result?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectImportJobStatus.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectImportJobStatus.json deleted file mode 100644 index f77706e436b3..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectImportJobStatus.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "jobId": "job1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "errors": [], - "createdDateTime": "2021-05-01T17:21:14Z", - "expirationDateTime": "2021-05-01T17:21:14Z", - "jobId": "635c2741-15c4-4c2c-9f78-bfd30b6b2a4a", - "lastUpdatedDateTime": "2021-05-01T17:21:14Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectListDeployments.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectListDeployments.json deleted file mode 100644 index 8e1a7e2a6879..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectListDeployments.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "deploymentName": "production", - "lastDeployedDateTime": "2021-05-01T15:13:22Z" - } - ] - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectSubmitDeployJob.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectSubmitDeployJob.json deleted file mode 100644 index d2313483f1d9..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectSubmitDeployJob.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "deploymentName": "production" - }, - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the analysis job.", - "headers": { - "Operation-Location": "https:///language/authoring/query-knowledgebases/projects/proj1/deployments/production/jobs/job1?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectSubmitExportJob.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectSubmitExportJob.json deleted file mode 100644 index bf9a702a74e4..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectSubmitExportJob.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "body": { - "exportAssetTypes": [ - "qnas", - "synonyms" - ] - } - }, - "responses": { - "200": { - "headers": {}, - "body": { - "errors": [], - "createdDateTime": "2021-05-01T17:21:14Z", - "expirationDateTime": "2021-05-01T17:21:14Z", - "jobId": "635c2741-15c4-4c2c-9f78-bfd30b6b2a4a", - "lastUpdatedDateTime": "2021-05-01T17:21:14Z", - "status": "succeeded", - "resultUrl": "https:///language/authoring/query-knowledgebases/projects/proj1/export/jobs/job1/result?api-version=2023-04-01" - } - }, - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the analysis job.", - "headers": { - "Operation-Location": "https:///language/authoring/query-knowledgebases/projects/proj1/export/jobs/job1?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectSubmitImportJob.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectSubmitImportJob.json deleted file mode 100644 index 21e0c108fad1..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectSubmitImportJob.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "body": { - "assets": { - "synonyms": [ - { - "alterations": [ - "wa", - "washington", - "washington state" - ] - }, - { - "alterations": [ - "U.S", - "usa", - "united states of america" - ] - } - ], - "qnas": [ - { - "lastUpdatedDateTime": "2021-05-01T17:21:14Z", - "id": 1, - "answer": "ans1", - "source": "source1", - "questions": [ - "question 1.1", - "question 1.2" - ], - "metadata": { - "k1": "v1", - "k2": "v2" - }, - "dialog": { - "isContextOnly": false, - "prompts": [ - { - "displayOrder": 1, - "qnaId": 2, - "displayText": "prompt 1.1" - } - ] - } - }, - { - "lastUpdatedDateTime": "2021-05-01T17:21:14Z", - "id": 2, - "answer": "ans2", - "source": "source2", - "questions": [ - "question 2.1", - "question 2.2" - ] - } - ] - } - } - }, - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the analysis job.", - "headers": { - "Operation-Location": "https:///language/authoring/query-knowledgebases/projects/proj1/import/jobs/job1?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectsListProjects.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectsListProjects.json deleted file mode 100644 index 63c63c1a641d..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulProjectsListProjects.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "value": [ - { - "projectName": "proj1", - "description": "proj1 is a test project.", - "language": "en", - "multilingualResource": true, - "createdDateTime": "2021-05-01T15:13:22Z", - "lastModifiedDateTime": "2021-05-01T15:13:22Z", - "lastDeployedDateTime": "2021-05-01T15:13:22Z" - }, - { - "projectName": "proj2", - "description": "proj2 is a test project.", - "language": "fr", - "multilingualResource": true, - "createdDateTime": "2021-05-01T15:13:22Z", - "lastModifiedDateTime": "2021-05-01T15:13:22Z", - "lastDeployedDateTime": "2021-05-01T15:13:22Z" - } - ] - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateActiveLearningFeedback.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateActiveLearningFeedback.json deleted file mode 100644 index 5171bc538ad9..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateActiveLearningFeedback.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "body": { - "records": [ - { - "userId": "user1", - "userQuestion": "hi", - "qnaId": 1 - }, - { - "userId": "user1", - "userQuestion": "hello", - "qnaId": 2 - } - ] - } - }, - "responses": { - "204": {} - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateQnas.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateQnas.json deleted file mode 100644 index d2ce3b89bbf0..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateQnas.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "body": [ - { - "op": "add", - "value": { - "id": 3, - "answer": "ans3", - "source": "source1", - "questions": [ - "question 3.1", - "question 3.2" - ], - "metadata": { - "k1": "v1", - "k2": "v2" - }, - "dialog": { - "isContextOnly": false, - "prompts": [ - { - "displayOrder": 1, - "qnaId": 11, - "displayText": "prompt 3.1" - }, - { - "displayOrder": 2, - "qnaId": 21, - "displayText": "prompt 3.2" - } - ] - } - } - }, - { - "op": "replace", - "value": { - "id": 1, - "answer": "ans4", - "source": "source1", - "questions": [ - "question 4.1", - "question 4.2" - ] - } - }, - { - "op": "delete", - "value": { - "id": 2, - "answer": "ans2", - "source": "source1", - "questions": [ - "question 2.1", - "question 2.2" - ] - } - } - ] - }, - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the analysis job.", - "headers": { - "Operation-Location": "https:///language/authoring/query-knowledgebases/projects/proj1/qnas/jobs/job1?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateQnasJobStatus.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateQnasJobStatus.json deleted file mode 100644 index f77706e436b3..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateQnasJobStatus.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "jobId": "job1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "errors": [], - "createdDateTime": "2021-05-01T17:21:14Z", - "expirationDateTime": "2021-05-01T17:21:14Z", - "jobId": "635c2741-15c4-4c2c-9f78-bfd30b6b2a4a", - "lastUpdatedDateTime": "2021-05-01T17:21:14Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateSources.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateSources.json deleted file mode 100644 index 04f0f445f180..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateSources.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "body": [ - { - "op": "add", - "value": { - "displayName": "source3", - "sourceUri": "https://docs.microsoft.com/en-us/azure/cognitive-services/cognitive-services-support-options?context=/azure/cognitive-services/qnamaker/context/context", - "sourceKind": "url", - "source": "https://docs.microsoft.com/en-us/azure/cognitive-services/cognitive-services-support-options?context=/azure/cognitive-services/qnamaker/context/context" - } - }, - { - "op": "replace", - "value": { - "displayName": "source1", - "sourceUri": "https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/overview/overview", - "sourceKind": "url", - "refresh": true, - "source": "https://docs.microsoft.com/en-us/azure/cognitive-services/qnamaker/overview/overview" - } - }, - { - "op": "delete", - "value": { - "displayName": "source2", - "sourceUri": "https://download.microsoft.com/download/2/9/B/29B20383-302C-4517-A006-B0186F04BE28/surface-pro-4-user-guide-EN.pdf", - "sourceKind": "file", - "source": "surface-guide.pdf" - } - } - ] - }, - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the analysis job.", - "headers": { - "Operation-Location": "https:///language/authoring/query-knowledgebases/projects/proj1/sources/jobs/job1?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateSourcesJobStatus.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateSourcesJobStatus.json deleted file mode 100644 index f77706e436b3..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateSourcesJobStatus.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "jobId": "job1" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "errors": [], - "createdDateTime": "2021-05-01T17:21:14Z", - "expirationDateTime": "2021-05-01T17:21:14Z", - "jobId": "635c2741-15c4-4c2c-9f78-bfd30b6b2a4a", - "lastUpdatedDateTime": "2021-05-01T17:21:14Z", - "status": "succeeded" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateSynonyms.json b/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateSynonyms.json deleted file mode 100644 index ce35df49d490..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/questionanswering/authoring/SuccessfulUpdateSynonyms.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "parameters": { - "Endpoint": "{Endpoint}", - "Ocp-Apim-Subscription-Key": "{API key}", - "Content-Type": "application/json", - "api-version": "2023-04-01", - "projectName": "proj1", - "body": { - "value": [ - { - "alterations": [ - "qnamaker", - "qna maker" - ] - }, - { - "alterations": [ - "botframework", - "bot framework" - ] - } - ] - } - }, - "responses": { - "204": {} - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/text/SuccessfulAbstractiveSummarizationTaskResult.json b/dev/cognitiveservices/data-plane/Language/examples/text/SuccessfulAbstractiveSummarizationTaskResult.json deleted file mode 100644 index aeda986c5b03..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/text/SuccessfulAbstractiveSummarizationTaskResult.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18" - }, - "responses": { - "200": { - "headers": {}, - "body": { - "createdDateTime": "2020-10-01T15:00:45Z", - "displayName": "Document Abstractive Summarization Task Example", - "expirationDateTime": "2020-10-03T15:01:03Z", - "jobId": "c0f2a446-05d9-48fc-ba8f-3ef4af8d0b18", - "lastUpdatedDateTime": "2020-10-01T15:01:03Z", - "status": "succeeded", - "tasks": { - "completed": 1, - "failed": 0, - "inProgress": 0, - "total": 1, - "items": [ - { - "kind": "AbstractiveSummarizationLROResults", - "taskName": "Document Abstractive Summarization Task 1", - "lastUpdateDateTime": "2020-10-01T15:01:03Z", - "status": "succeeded", - "results": { - "documents": [ - { - "summaries": [ - { - "text": "Microsoft have been on a quest to advance AI beyond existing techniques.", - "contexts": [ - { - "offset": 0, - "length": 1629 - } - ] - } - ], - "id": "1", - "warnings": [] - } - ], - "errors": [], - "modelVersion": "latest" - } - } - ] - } - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/examples/text/SuccessfulAbstractiveSummarizationTaskSubmit.json b/dev/cognitiveservices/data-plane/Language/examples/text/SuccessfulAbstractiveSummarizationTaskSubmit.json deleted file mode 100644 index 1fec0f6e6ffd..000000000000 --- a/dev/cognitiveservices/data-plane/Language/examples/text/SuccessfulAbstractiveSummarizationTaskSubmit.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "parameters": { - "Ocp-Apim-Subscription-Key": "{API key}", - "api-version": "2023-04-01", - "Endpoint": "{Endpoint}", - "jobId": "{Job ID}", - "body": { - "displayName": "Document Abstractive Summarization Task Example", - "analysisInput": { - "documents": [ - { - "id": "1", - "language": "en", - "text": "At Microsoft, we have been on a quest to advance AI beyond existing techniques, by taking a more holistic, human-centric approach to learning and understanding. As Chief Technology Officer of Azure AI Cognitive Services, I have been working with a team of amazing scientists and engineers to turn this quest into a reality. In my role, I enjoy a unique perspective in viewing the relationship among three attributes of human cognition: monolingual text (X), audio or visual sensory signals, (Y) and multilingual (Z). At the intersection of all three, there is magic—what we call XYZ-code as illustrated in Figure 1—a joint representation to create more powerful AI that can speak, hear, see, and understand humans better. We believe XYZ-code will enable us to fulfill our long-term vision: cross-domain transfer learning, spanning modalities and languages. The goal is to have pre-trained models that can jointly learn representations to support a broad range of downstream AI tasks, much in the way humans do today. Over the past five years, we have achieved human performance on benchmarks in conversational speech recognition, machine translation, conversational question answering, machine reading comprehension, and image captioning. These five breakthroughs provided us with strong signals toward our more ambitious aspiration to produce a leap in AI capabilities, achieving multi-sensory and multilingual learning that is closer in line with how humans learn and understand. I believe the joint XYZ-code is a foundational component of this aspiration, if grounded with external knowledge sources in the downstream AI tasks." - } - ] - }, - "tasks": [ - { - "kind": "AbstractiveSummarization", - "taskName": "Document Abstractive Summarization Task 1", - "parameters": { - "sentenceCount": 1 - } - } - ] - } - }, - "responses": { - "202": { - "headers": { - "Operation-Location": "{endpoint}/language/analyze-text/jobs/{jobId}?api-version=2023-04-01" - } - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/questionanswering-authoring.json b/dev/cognitiveservices/data-plane/Language/questionanswering-authoring.json deleted file mode 100644 index 1345452cd8f2..000000000000 --- a/dev/cognitiveservices/data-plane/Language/questionanswering-authoring.json +++ /dev/null @@ -1,1804 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Microsoft Cognitive Language Service - Question Answering - Authoring", - "description": "The language service API is a suite of natural language processing (NLP) skills built with best-in-class Microsoft machine learning algorithms. The API can be used to analyze unstructured text for tasks such as sentiment analysis, key phrase extraction, language detection and question answering. Further documentation can be found in https://docs.microsoft.com/en-us/azure/cognitive-services/text-analytics/overview.", - "version": "2023-04-01" - }, - "securityDefinitions": { - "AADToken": { - "type": "oauth2", - "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", - "flow": "implicit", - "description": "These are the [Azure Active Directory OAuth2](https://docs.microsoft.com/azure/active-directory/develop/v1-overview) Flows. When paired with [Azure role-based access](https://docs.microsoft.com/azure/role-based-access-control/overview) control it can be used to control access to Azure Maps REST APIs. Azure role-based access controls are used to designate access to one or more Azure Maps resource account or sub-resources. Any user, group, or service principal can be granted access via a built-in role or a custom role composed of one or more permissions to Azure Maps REST APIs.\n\nTo implement scenarios, we recommend viewing [authentication concepts](https://aka.ms/amauth). In summary, this security definition provides a solution for modeling application(s) via objects capable of access control on specific APIs and scopes.\n\n#### Notes\n* This security definition **requires** the use of the `x-ms-client-id` header to indicate which Azure Maps resource the application is requesting access to. This can be acquired from the [Maps management API](https://aka.ms/amauthdetails).\n* \nThe `Authorization URL` is specific to the Azure public cloud instance. Sovereign clouds have unique Authorization URLs and Azure Active directory configurations. \n* \nThe Azure role-based access control is configured from the [Azure management plane](https://aka.ms/amrbac) via Azure portal, PowerShell, CLI, Azure SDKs, or REST APIs.\n* \nUsage of the [Azure Maps Web SDK](https://aka.ms/amaadmc) allows for configuration based setup of an application for multiple use cases.\n* Currently, Azure Active Directory [v1.0 or v2.0](https://docs.microsoft.com/azure/active-directory/develop/azure-ad-endpoint-comparison) supports Work, School, and Guests but does not support Personal accounts.", - "scopes": { - "https://cognitiveservices.azure.com/.default": "https://cognitiveservices.azure.com/.default" - } - }, - "apim_key": { - "type": "apiKey", - "description": "A subscription key for a Language service resource.", - "name": "Ocp-Apim-Subscription-Key", - "in": "header" - } - }, - "security": [ - { - "AADToken": [ - "https://cognitiveservices.azure.com/.default" - ] - }, - { - "apim_key": [] - } - ], - "x-ms-parameterized-host": { - "hostTemplate": "{Endpoint}/language", - "useSchemePrefix": false, - "parameters": [ - { - "$ref": "common.json#/parameters/Endpoint" - } - ] - }, - "paths": { - "/authoring/query-knowledgebases/projects": { - "get": { - "summary": "Gets all projects for a user.", - "operationId": "QuestionAnsweringProjects_ListProjects", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - }, - "responses": { - "200": { - "description": "The metadata of all projects.", - "schema": { - "$ref": "#/definitions/ProjectsMetadata" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulProjectsListProjects.json" - } - } - } - }, - "/authoring/query-knowledgebases/projects/{projectName}": { - "get": { - "summary": "Get the requested project metadata.", - "operationId": "QuestionAnsweringProjects_GetProjectDetails", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "The metadata for the specified project.", - "schema": { - "$ref": "#/definitions/ProjectMetadata" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulGetProject.json" - } - } - }, - "patch": { - "summary": "Create or update a project.", - "operationId": "QuestionAnsweringProjects_CreateProject", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "description": "Parameters needed to create the project.", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateProjectOptions" - } - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "The metadata of the updated project, if it already exists.", - "schema": { - "$ref": "#/definitions/ProjectMetadata" - } - }, - "201": { - "description": "The metadata of the created project, if it doesn't exist.", - "schema": { - "$ref": "#/definitions/ProjectMetadata" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulCreateProject.json" - } - } - }, - "delete": { - "summary": "Delete the project.", - "operationId": "QuestionAnsweringProjects_DeleteProject", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulDeleteProject.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/query-knowledgebases/projects/deletion-jobs/{jobId}": { - "get": { - "summary": "Gets the status of a Project delete job.", - "operationId": "QuestionAnsweringProjects_GetDeleteStatus", - "parameters": [ - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobIdParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Project delete job status.", - "schema": { - "$ref": "#/definitions/JobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulProjectDeleteJobStatus.json" - } - } - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/:export": { - "post": { - "summary": "Export project metadata and assets.", - "operationId": "QuestionAnsweringProjects_Export", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/ImportExportFormatParameter" - }, - { - "$ref": "#/parameters/AssetKindParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Export job status, project metadata, and assets.", - "schema": { - "$ref": "#/definitions/ExportJobState" - } - }, - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulProjectSubmitExportJob.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/export/jobs/{jobId}": { - "get": { - "summary": "Gets the status of an Export job, once job completes, returns the project metadata, and assets.", - "operationId": "QuestionAnsweringProjects_GetExportStatus", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobIdParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Export job status, project metadata, and assets.", - "schema": { - "$ref": "#/definitions/ExportJobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulProjectExportJobStatus.json" - } - } - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/:import": { - "post": { - "summary": "Import project assets.", - "operationId": "QuestionAnsweringProjects_Import", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "description": "Project assets the needs to be imported.", - "in": "body", - "name": "body", - "schema": { - "$ref": "#/definitions/ImportJobOptions" - } - }, - { - "$ref": "#/parameters/ImportExportFormatParameter" - }, - { - "$ref": "#/parameters/AssetKindParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulProjectSubmitImportJob.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/import/jobs/{jobId}": { - "get": { - "summary": "Gets the status of an Import job.", - "operationId": "QuestionAnsweringProjects_GetImportStatus", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobIdParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Import job status.", - "schema": { - "$ref": "#/definitions/JobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulProjectImportJobStatus.json" - } - } - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/deployments/{deploymentName}": { - "put": { - "summary": "Deploy project to production.", - "operationId": "QuestionAnsweringProjects_DeployProject", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulProjectSubmitDeployJob.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/deployments/{deploymentName}/jobs/{jobId}": { - "get": { - "summary": "Gets the status of a Deploy job.", - "operationId": "QuestionAnsweringProjects_GetDeployStatus", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobIdParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Deploy job state.", - "schema": { - "$ref": "#/definitions/JobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulProjectDeployJobStatus.json" - } - } - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/deployments": { - "get": { - "summary": "List all deployments of a project.", - "operationId": "QuestionAnsweringProjects_ListDeployments", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - }, - "responses": { - "200": { - "description": "List of deployments of a project.", - "schema": { - "$ref": "#/definitions/ProjectDeployments" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulProjectListDeployments.json" - } - } - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/synonyms": { - "get": { - "summary": "Gets all the synonyms of a project.", - "operationId": "QuestionAnsweringProjects_GetSynonyms", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - }, - "responses": { - "200": { - "description": "All the synonyms of a project.", - "schema": { - "$ref": "#/definitions/SynonymAssets" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulGetSynonyms.json" - } - } - }, - "put": { - "summary": "Updates all the synonyms of a project.", - "operationId": "QuestionAnsweringProjects_UpdateSynonyms", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "description": "All the synonyms of a project.", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/SynonymAssets" - } - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "204": { - "description": "Synonyms update successfully." - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulUpdateSynonyms.json" - } - } - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/sources": { - "get": { - "summary": "Gets all the sources of a project.", - "operationId": "QuestionAnsweringProjects_GetSources", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - }, - "responses": { - "200": { - "description": "All the sources of a project.", - "schema": { - "$ref": "#/definitions/QnaSources" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulGetSources.json" - } - } - }, - "patch": { - "summary": "Updates the sources of a project.", - "operationId": "QuestionAnsweringProjects_UpdateSources", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "description": "Update sources parameters of a project.", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/UpdateSourcesOptions" - } - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulUpdateSources.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/sources/jobs/{jobId}": { - "get": { - "summary": "Gets the status of update sources job.", - "operationId": "QuestionAnsweringProjects_GetUpdateSourcesStatus", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobIdParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Update sources job state.", - "schema": { - "$ref": "#/definitions/JobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulUpdateSourcesJobStatus.json" - } - } - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/qnas": { - "get": { - "summary": "Gets all the QnAs of a project.", - "operationId": "QuestionAnsweringProjects_GetQnas", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/SourceFilterParameter" - }, - { - "$ref": "common.json#/parameters/TopParameter" - }, - { - "$ref": "common.json#/parameters/SkipParameter" - }, - { - "$ref": "common.json#/parameters/MaxPageSizeParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "x-ms-pageable": { - "nextLinkName": "nextLink", - "itemName": "value" - }, - "responses": { - "200": { - "description": "All the QnAs of a project.", - "schema": { - "$ref": "#/definitions/QnaAssets" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulGetQnas.json" - } - } - }, - "patch": { - "summary": "Updates the QnAs of a project.", - "operationId": "QuestionAnsweringProjects_UpdateQnas", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "description": "Update QnAs parameters of a project.", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/UpdateQnaOptions" - } - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "202": { - "description": "A successful call results with an Operation-Location header used to check the status of the job.", - "headers": { - "Operation-Location": { - "type": "string" - } - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulUpdateQnas.json" - } - }, - "x-ms-long-running-operation": true - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/qnas/jobs/{jobId}": { - "get": { - "summary": "Gets the status of update QnAs job.", - "operationId": "QuestionAnsweringProjects_GetUpdateQnasStatus", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "$ref": "#/parameters/JobIdParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Update QnAs job state.", - "schema": { - "$ref": "#/definitions/JobState" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulUpdateQnasJobStatus.json" - } - } - } - }, - "/authoring/query-knowledgebases/projects/{projectName}/feedback": { - "post": { - "summary": "Update Active Learning feedback.", - "operationId": "QuestionAnsweringProjects_AddFeedback", - "parameters": [ - { - "$ref": "common.json#/parameters/ProjectNamePathParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - }, - { - "description": "Feedback for Active Learning.", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/ActiveLearningFeedback" - } - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "204": { - "description": "Feedback recorded successfully." - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/authoring/SuccessfulUpdateActiveLearningFeedback.json" - } - } - } - } - }, - "definitions": { - "ActiveLearningFeedback": { - "type": "object", - "description": "Feedback for Active Learning.", - "additionalProperties": false, - "properties": { - "records": { - "$ref": "#/definitions/FeedbackRecords" - } - } - }, - "FeedbackRecords": { - "type": "array", - "description": "A list of Feedback Records for Active Learning.", - "additionalProperties": false, - "items": { - "$ref": "#/definitions/FeedbackRecord" - } - }, - "FeedbackRecord": { - "type": "object", - "description": "Feedback Record for Active Learning.", - "additionalProperties": false, - "properties": { - "userId": { - "type": "string", - "description": "Unique identifier of the user." - }, - "userQuestion": { - "type": "string", - "description": "User suggested question for the QnA." - }, - "qnaId": { - "type": "integer", - "description": "Unique ID of the QnA.", - "format": "int32" - } - } - }, - "ProjectsMetadata": { - "type": "object", - "description": "Collection of projects metadata and global settings.", - "additionalProperties": false, - "properties": { - "value": { - "type": "array", - "items": { - "$ref": "#/definitions/ProjectMetadata" - } - }, - "nextLink": { - "type": "string" - } - } - }, - "ProjectMetadata": { - "type": "object", - "description": "Represents the project.", - "additionalProperties": false, - "properties": { - "projectName": { - "type": "string", - "description": "Name of the project." - }, - "description": { - "type": "string", - "description": "Description of the project." - }, - "language": { - "$ref": "common.json#/definitions/Language" - }, - "multilingualResource": { - "type": "boolean", - "description": "Resource enabled for multiple languages across projects or not." - }, - "settings": { - "$ref": "#/definitions/ProjectSettings" - }, - "createdDateTime": { - "type": "string", - "description": "Project creation date-time.", - "format": "date-time" - }, - "lastModifiedDateTime": { - "type": "string", - "description": "Represents the project last modified date-time.", - "format": "date-time" - }, - "lastDeployedDateTime": { - "type": "string", - "description": "Represents the project last deployment date-time.", - "format": "date-time" - } - } - }, - "CreateProjectOptions": { - "type": "object", - "description": "Parameters needed to create the project.", - "additionalProperties": false, - "required": [ - "language" - ], - "properties": { - "description": { - "type": "string", - "description": "Description of the project." - }, - "language": { - "$ref": "common.json#/definitions/Language" - }, - "multilingualResource": { - "type": "boolean", - "description": "Set to true to enable creating knowledgebases in different languages for the same resource." - }, - "settings": { - "$ref": "#/definitions/ProjectSettings" - } - } - }, - "ProjectSettings": { - "type": "object", - "description": "Configurable settings of the Project.", - "additionalProperties": false, - "properties": { - "defaultAnswer": { - "type": "string", - "description": "Default Answer response when no good match is found in the knowledge base." - } - } - }, - "ImportJobOptions": { - "type": "object", - "description": "Project assets the needs to be imported.", - "additionalProperties": false, - "properties": { - "metadata": { - "$ref": "#/definitions/CreateProjectOptions" - }, - "assets": { - "$ref": "#/definitions/Assets" - }, - "fileUri": { - "type": "string", - "description": "Import data File URI." - } - } - }, - "JobMetadata": { - "description": "Represents the job metadata.", - "properties": { - "createdDateTime": { - "format": "date-time", - "type": "string" - }, - "expirationDateTime": { - "format": "date-time", - "type": "string" - }, - "jobId": { - "type": "string" - }, - "lastUpdatedDateTime": { - "format": "date-time", - "type": "string" - }, - "status": { - "$ref": "#/definitions/JobStatus" - } - }, - "required": [ - "jobId", - "lastUpdatedDateTime", - "createdDateTime", - "status" - ], - "type": "object" - }, - "JobStatus": { - "description": "Job Status.", - "enum": [ - "notStarted", - "running", - "succeeded", - "failed", - "cancelled", - "cancelling", - "partiallyCompleted" - ], - "type": "string", - "x-ms-enum": { - "modelAsString": true, - "name": "jobStatus" - } - }, - "JobState": { - "description": "Job state represents the job metadata and any errors.", - "allOf": [ - { - "$ref": "#/definitions/JobMetadata" - }, - { - "$ref": "#/definitions/Errors" - } - ] - }, - "Errors": { - "description": "Collection of Error types.", - "type": "object", - "properties": { - "errors": { - "type": "array", - "items": { - "$ref": "common.json#/definitions/Error" - } - } - } - }, - "ExportJobState": { - "description": "Export job status, project metadata, and assets.", - "allOf": [ - { - "$ref": "#/definitions/JobState" - }, - { - "$ref": "#/definitions/ExportJobResultUrl" - } - ] - }, - "ExportJobResultUrl": { - "type": "object", - "description": "URL to download the result of the Export Job.", - "required": [ - "resultUrl" - ], - "properties": { - "resultUrl": { - "type": "string", - "description": "URL to download the result of the Export Job." - } - } - }, - "Assets": { - "type": "object", - "description": "All assets for this project.", - "additionalProperties": false, - "properties": { - "synonyms": { - "$ref": "#/definitions/SynonymAsset" - }, - "qnas": { - "$ref": "#/definitions/ImportQnaAsset" - } - } - }, - "SynonymAssets": { - "type": "object", - "description": "All synonym assets for this project.", - "additionalProperties": false, - "properties": { - "value": { - "$ref": "#/definitions/SynonymAsset" - }, - "nextLink": { - "type": "string" - } - } - }, - "SynonymAsset": { - "type": "array", - "description": "Collection of synonyms.", - "additionalProperties": false, - "maxLength": 10000, - "items": { - "$ref": "#/definitions/WordAlterations" - } - }, - "WordAlterations": { - "type": "object", - "description": "Collection of word alterations.", - "additionalProperties": false, - "required": [ - "alterations" - ], - "properties": { - "alterations": { - "type": "array", - "description": "Collection of word alterations.", - "maxLength": 20, - "items": { - "type": "string" - } - } - } - }, - "QnaAssets": { - "type": "object", - "description": "All QnA assets for the project.", - "additionalProperties": false, - "properties": { - "value": { - "$ref": "#/definitions/QnaAsset" - }, - "nextLink": { - "type": "string" - } - } - }, - "ImportQnaAsset": { - "type": "array", - "description": "List of QnA records to import.", - "additionalProperties": false, - "items": { - "$ref": "#/definitions/ImportQnaRecord" - } - }, - "QnaAsset": { - "type": "array", - "description": "List of QnA records.", - "additionalProperties": false, - "items": { - "$ref": "#/definitions/RetrieveQnaRecord" - } - }, - "QnaRecord": { - "type": "object", - "description": "QnA record.", - "additionalProperties": false, - "properties": { - "id": { - "type": "integer", - "description": "Unique ID for the QnA.", - "format": "int32" - }, - "answer": { - "type": "string", - "description": "Answer text.", - "maxLength": 25000, - "minLength": 1 - }, - "source": { - "type": "string", - "description": "Source from which QnA was indexed e.g. https://docs.microsoft.com/en-us/azure/cognitive-services/QnAMaker/FAQs .", - "maxLength": 300 - }, - "questions": { - "type": "array", - "description": "List of questions associated with the answer.", - "maxLength": 100, - "minLength": 1, - "items": { - "type": "string" - } - }, - "metadata": { - "type": "object", - "description": "Metadata associated with the answer, useful to categorize or filter question answers.", - "additionalProperties": { - "type": "string" - } - }, - "dialog": { - "description": "Context of a QnA.", - "$ref": "#/definitions/QnaDialog" - }, - "activeLearningSuggestions": { - "type": "array", - "description": "List of Active Learning suggestions for the QnA.", - "items": { - "$ref": "#/definitions/SuggestedQuestionsCluster" - } - } - } - }, - "SuggestedQuestionsCluster": { - "type": "object", - "description": "Active Learning suggested questions cluster details.", - "additionalProperties": false, - "properties": { - "clusterHead": { - "type": "string", - "description": "Question chosen as the head of suggested questions cluster by Active Learning clustering algorithm." - }, - "suggestedQuestions": { - "type": "array", - "description": "List of all suggested questions for the QnA.", - "items": { - "$ref": "#/definitions/SuggestedQuestion" - } - } - } - }, - "SuggestedQuestion": { - "type": "object", - "description": "Question suggested by the Active Learning feature.", - "additionalProperties": false, - "properties": { - "question": { - "type": "string", - "description": "Question suggested by the Active Learning feature." - }, - "userSuggestedCount": { - "type": "integer", - "description": "The number of times the question was suggested explicitly by the user.", - "format": "int32" - }, - "autoSuggestedCount": { - "type": "integer", - "description": "The number of times the question was suggested automatically by the Active Learning algorithm.", - "format": "int32" - } - } - }, - "ImportQnaRecord": { - "description": "Import QnA Record.", - "allOf": [ - { - "$ref": "#/definitions/RetrieveQnaRecord" - }, - { - "$ref": "#/definitions/SourceDisplayNameParameter" - } - ] - }, - "SourceDisplayNameParameter": { - "type": "object", - "properties": { - "sourceDisplayName": { - "description": "Friendly name of the Source.", - "type": "string", - "maxLength": 200, - "minLength": 1 - } - } - }, - "RetrieveQnaRecord": { - "description": "QnA Record with last updated date-time.", - "allOf": [ - { - "$ref": "#/definitions/QnaRecord" - }, - { - "$ref": "#/definitions/LastUpdatedDateTimeParameter" - } - ] - }, - "LastUpdatedDateTimeParameter": { - "type": "object", - "description": "Last updated date-time parameter.", - "properties": { - "lastUpdatedDateTime": { - "type": "string", - "format": "date-time", - "description": "Date-time when the QnA was last updated." - } - } - }, - "QnaDialog": { - "type": "object", - "description": "Dialog associated with QnA Record.", - "properties": { - "isContextOnly": { - "type": "boolean", - "description": "To mark if a prompt is relevant only with a previous question or not. If true, do not include this QnA as answer for queries without context; otherwise, ignores context and includes this QnA in answers." - }, - "prompts": { - "type": "array", - "description": "List of prompts associated with the answer.", - "maxItems": 20, - "items": { - "$ref": "#/definitions/QnaPrompt" - } - } - } - }, - "QnaPrompt": { - "type": "object", - "description": "Prompt for an answer.", - "properties": { - "displayOrder": { - "type": "integer", - "description": "Index of the prompt. It is used for ordering of the prompts.", - "format": "int32" - }, - "qnaId": { - "type": "integer", - "description": "ID of the QnA corresponding to the prompt.", - "format": "int32" - }, - "qna": { - "description": "QnA record. Either QnAId or QnA record needs to be present in a Prompt.", - "$ref": "#/definitions/QnaRecord" - }, - "displayText": { - "type": "string", - "description": "Text displayed to represent a follow up question prompt.", - "maxLength": 200 - } - } - }, - "UpdateOperationKind": { - "type": "string", - "description": "Update operation type for assets.", - "enum": [ - "add", - "delete", - "replace" - ], - "x-ms-enum": { - "modelAsString": true, - "name": "updateOperationKind" - } - }, - "UpdateSourcesOptions": { - "type": "array", - "description": "A list of sources to be updated.", - "additionalProperties": false, - "items": { - "$ref": "#/definitions/UpdateSourceRecord" - } - }, - "UpdateQnaOptions": { - "type": "array", - "description": "A list of QnAs to be updated.", - "additionalProperties": false, - "items": { - "$ref": "#/definitions/UpdateQnaRecord" - } - }, - "UpdateQnaRecord": { - "type": "object", - "description": "QnA record to be updated.", - "additionalProperties": false, - "required": [ - "op", - "value" - ], - "properties": { - "op": { - "type": "string", - "$ref": "#/definitions/UpdateOperationKind" - }, - "value": { - "$ref": "#/definitions/QnaRecord" - } - } - }, - "UpdateSourceRecord": { - "type": "object", - "description": "Source to be updated.", - "additionalProperties": false, - "required": [ - "op", - "value" - ], - "properties": { - "op": { - "type": "string", - "$ref": "#/definitions/UpdateOperationKind" - }, - "value": { - "$ref": "#/definitions/UpdateQnaSourceRecord" - } - } - }, - "QnaSources": { - "type": "object", - "description": "All QnA sources for the project.", - "additionalProperties": false, - "properties": { - "value": { - "$ref": "#/definitions/QnaSourcesMetadata" - }, - "nextLink": { - "type": "string" - } - } - }, - "QnaSourcesMetadata": { - "type": "array", - "description": "Custom sources from which QnAs were extracted or explicitly added to the project.", - "additionalProperties": false, - "items": { - "$ref": "#/definitions/QnaSourceRecord" - } - }, - "QnaSourceRecord": { - "description": "Custom source record with last updated date-time.", - "allOf": [ - { - "$ref": "#/definitions/QnaSourceMetadata" - }, - { - "$ref": "#/definitions/LastUpdatedDateTimeParameter" - } - ] - }, - "UpdateQnaSourceRecord": { - "description": "Update source record.", - "allOf": [ - { - "$ref": "#/definitions/QnaSourceMetadata" - }, - { - "$ref": "#/definitions/RefreshSourceOptions" - } - ] - }, - "RefreshSourceOptions": { - "type": "object", - "description": "Boolean flag used to refresh data from the Source.", - "properties": { - "refresh": { - "type": "boolean", - "description": "Boolean flag used to refresh data from the Source." - } - } - }, - "QnaSourceMetadata": { - "type": "object", - "description": "Input source file or url details.", - "additionalProperties": false, - "required": [ - "sourceUri", - "sourceKind", - "source" - ], - "properties": { - "displayName": { - "type": "string", - "description": "Friendly name of the Source.", - "maxLength": 200, - "minLength": 1 - }, - "source": { - "type": "string", - "description": "Unique source identifier. Name of the file if it's a 'file' source; otherwise, the complete URL if it's a 'url' source." - }, - "sourceUri": { - "type": "string", - "description": "URI location for the file or url.", - "format": "uri" - }, - "sourceKind": { - "type": "string", - "$ref": "#/definitions/QnaSourceKind", - "default": "url" - }, - "contentStructureKind": { - "type": "string", - "$ref": "#/definitions/SourceContentStructureKind", - "default": "Unstructured" - } - } - }, - "QnaSourceKind": { - "type": "string", - "description": "Supported source types.", - "x-ms-enum": { - "name": "sourceKind", - "modelAsString": true - }, - "enum": [ - "file", - "url" - ] - }, - "SourceContentStructureKind": { - "type": "string", - "description": "Content structure type for sources.", - "enum": [ - "unstructured" - ], - "x-ms-enum": { - "modelAsString": true, - "name": "sourceContentStructureKind" - } - }, - "ProjectDeployments": { - "type": "object", - "description": "All deployments of a project.", - "additionalProperties": false, - "properties": { - "value": { - "$ref": "#/definitions/ProjectDeploymentsList" - }, - "nextLink": { - "type": "string" - } - } - }, - "ProjectDeploymentsList": { - "type": "array", - "description": "List of deployments of a project.", - "additionalProperties": false, - "items": { - "$ref": "#/definitions/ProjectDeployment" - } - }, - "ProjectDeployment": { - "type": "object", - "description": "Project deployment details.", - "additionalProperties": false, - "properties": { - "deploymentName": { - "type": "string", - "description": "Name of the deployment." - }, - "lastDeployedDateTime": { - "type": "string", - "description": "Represents the project last deployment date-time.", - "format": "date-time" - } - } - } - }, - "parameters": { - "JobIdParameter": { - "name": "jobId", - "in": "path", - "description": "Job ID.", - "required": true, - "type": "string", - "x-ms-parameter-location": "method" - }, - "AssetKindParameter": { - "name": "assetKind", - "type": "string", - "in": "query", - "description": "Kind of the asset of the project.", - "enum": [ - "qnas", - "synonyms" - ], - "x-ms-parameter-location": "method" - }, - "ImportExportFormatParameter": { - "name": "format", - "type": "string", - "in": "query", - "description": "Knowledge base Import or Export format.", - "default": "json", - "enum": [ - "json", - "tsv", - "excel" - ], - "x-ms-enum": { - "name": "format", - "modelAsString": true, - "values": [ - { - "value": "json", - "description": "Export or Import QnA assets in JSON format." - }, - { - "value": "tsv", - "description": "Export or Import knowledge base replica including all assets and metadata in Excel format." - }, - { - "value": "excel", - "description": "Export or Import knowledge base replica including all assets and metadata in Tsv format." - } - ] - }, - "x-ms-parameter-location": "method" - }, - "SourceFilterParameter": { - "name": "source", - "in": "query", - "description": "Source of the QnA.", - "type": "string", - "x-ms-parameter-location": "method" - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/questionanswering.json b/dev/cognitiveservices/data-plane/Language/questionanswering.json deleted file mode 100644 index 4fd80da03469..000000000000 --- a/dev/cognitiveservices/data-plane/Language/questionanswering.json +++ /dev/null @@ -1,601 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "Microsoft Cognitive Language Service - Question Answering", - "description": "The language service API is a suite of natural language processing (NLP) skills built with best-in-class Microsoft machine learning algorithms. The API can be used to analyze unstructured text for tasks such as sentiment analysis, key phrase extraction, language detection and question answering. Further documentation can be found in https://docs.microsoft.com/en-us/azure/cognitive-services/text-analytics/overview.", - "version": "2023-04-01" - }, - "securityDefinitions": { - "AADToken": { - "type": "oauth2", - "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", - "flow": "implicit", - "description": "These are the [Azure Active Directory OAuth2](https://docs.microsoft.com/azure/active-directory/develop/v1-overview) Flows. When paired with [Azure role-based access](https://docs.microsoft.com/azure/role-based-access-control/overview) control it can be used to control access to Azure Maps REST APIs. Azure role-based access controls are used to designate access to one or more Azure Maps resource account or sub-resources. Any user, group, or service principal can be granted access via a built-in role or a custom role composed of one or more permissions to Azure Maps REST APIs.\n\nTo implement scenarios, we recommend viewing [authentication concepts](https://aka.ms/amauth). In summary, this security definition provides a solution for modeling application(s) via objects capable of access control on specific APIs and scopes.\n\n#### Notes\n* This security definition **requires** the use of the `x-ms-client-id` header to indicate which Azure Maps resource the application is requesting access to. This can be acquired from the [Maps management API](https://aka.ms/amauthdetails).\n* \nThe `Authorization URL` is specific to the Azure public cloud instance. Sovereign clouds have unique Authorization URLs and Azure Active directory configurations. \n* \nThe Azure role-based access control is configured from the [Azure management plane](https://aka.ms/amrbac) via Azure portal, PowerShell, CLI, Azure SDKs, or REST APIs.\n* \nUsage of the [Azure Maps Web SDK](https://aka.ms/amaadmc) allows for configuration based setup of an application for multiple use cases.\n* Currently, Azure Active Directory [v1.0 or v2.0](https://docs.microsoft.com/azure/active-directory/develop/azure-ad-endpoint-comparison) supports Work, School, and Guests but does not support Personal accounts.", - "scopes": { - "https://cognitiveservices.azure.com/.default": "https://cognitiveservices.azure.com/.default" - } - }, - "apim_key": { - "type": "apiKey", - "description": "A subscription key for a Language service resource.", - "name": "Ocp-Apim-Subscription-Key", - "in": "header" - } - }, - "security": [ - { - "AADToken": [ - "https://cognitiveservices.azure.com/.default" - ] - }, - { - "apim_key": [] - } - ], - "x-ms-parameterized-host": { - "hostTemplate": "{Endpoint}/language", - "useSchemePrefix": false, - "parameters": [ - { - "$ref": "common.json#/parameters/Endpoint" - } - ] - }, - "paths": { - "/:query-knowledgebases": { - "post": { - "summary": "Answers the specified question using your knowledge base.", - "operationId": "QuestionAnswering_GetAnswers", - "parameters": [ - { - "$ref": "#/parameters/AnswersOptions" - }, - { - "$ref": "common.json#/parameters/ProjectNameQueryParameter" - }, - { - "$ref": "common.json#/parameters/DeploymentNameQueryParameter" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "A successful response to get answers from knowledge base.", - "schema": { - "$ref": "#/definitions/AnswersResult" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/SuccessfulQueryKnowledgebases.json" - } - } - } - }, - "/:query-text": { - "post": { - "summary": "Answers the specified question using the provided text in the body.", - "operationId": "QuestionAnswering_GetAnswersFromText", - "parameters": [ - { - "$ref": "#/parameters/AnswersFromTextOptions" - }, - { - "$ref": "common.json#/parameters/ApiVersionParameter" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "A successful response to get answers from input text.", - "schema": { - "$ref": "#/definitions/AnswersFromTextResult" - } - }, - "default": { - "description": "Error response.", - "schema": { - "$ref": "common.json#/definitions/ErrorResponse" - } - } - }, - "x-ms-examples": { - "Successful query": { - "$ref": "./examples/questionanswering/SuccessfulQueryText.json" - } - } - } - } - }, - "definitions": { - "AnswersOptions": { - "type": "object", - "description": "Parameters to query a knowledge base.", - "additionalProperties": false, - "properties": { - "qnaId": { - "type": "integer", - "description": "Exact QnA ID to fetch from the knowledge base, this field takes priority over question.", - "format": "int32" - }, - "question": { - "type": "string", - "description": "User question to query against the knowledge base." - }, - "top": { - "type": "integer", - "description": "Max number of answers to be returned for the question.", - "format": "int32" - }, - "userId": { - "type": "string", - "description": "Unique identifier for the user." - }, - "confidenceScoreThreshold": { - "type": "number", - "format": "double", - "x-ms-client-name": "confidenceThreshold", - "description": "Minimum threshold score for answers, value ranges from 0 to 1.", - "maximum": 1, - "minimum": 0 - }, - "context": { - "x-ms-client-name": "answerContext", - "description": "Context object with previous QnA's information.", - "$ref": "#/definitions/KnowledgeBaseAnswerContext" - }, - "rankerType": { - "type": "string", - "x-ms-client-name": "rankerKind", - "description": "Type of ranker to be used.", - "x-ms-enum": { - "name": "RankerKind", - "modelAsString": true, - "values": [ - { - "value": "QuestionOnly", - "description": "Question only ranker." - }, - { - "value": "Default", - "description": "Default ranker." - } - ] - }, - "enum": [ - "Default", - "QuestionOnly" - ] - }, - "filters": { - "description": "Filter QnAs based on given metadata list and knowledge base sources.", - "$ref": "#/definitions/QueryFilters" - }, - "answerSpanRequest": { - "x-ms-client-name": "shortAnswerOptions", - "description": "To configure Answer span prediction feature.", - "$ref": "#/definitions/ShortAnswerOptions" - }, - "includeUnstructuredSources": { - "type": "boolean", - "description": "(Optional) Flag to enable Query over Unstructured Sources." - } - } - }, - "ShortAnswerOptions": { - "type": "object", - "description": "To configure Answer span prediction feature.", - "additionalProperties": false, - "required": [ - "enable" - ], - "properties": { - "enable": { - "type": "boolean", - "description": "Enable or disable Answer Span prediction.", - "enum": [ - true - ], - "x-ms-enum": { - "name": "enable", - "modelAsString": false - } - }, - "confidenceScoreThreshold": { - "type": "number", - "format": "double", - "x-ms-client-name": "confidenceThreshold", - "description": "Minimum threshold score required to include an answer span, value ranges from 0 to 1.", - "maximum": 1, - "minimum": 0 - }, - "topAnswersWithSpan": { - "type": "integer", - "x-ms-client-name": "top", - "description": "Number of Top answers to be considered for span prediction from 1 to 10.", - "format": "int32", - "maximum": 10, - "minimum": 1 - } - } - }, - "KnowledgeBaseAnswerContext": { - "type": "object", - "description": "Context object with previous QnA's information.", - "additionalProperties": false, - "required": [ - "previousQnaId" - ], - "properties": { - "previousQnaId": { - "type": "integer", - "description": "Previous turn top answer result QnA ID.", - "format": "int32" - }, - "previousUserQuery": { - "type": "string", - "x-ms-client-name": "previousQuestion", - "description": "Previous user query." - } - } - }, - "AnswersResult": { - "type": "object", - "description": "Represents List of Question Answers.", - "additionalProperties": false, - "properties": { - "answers": { - "type": "array", - "description": "Represents Answer Result list.", - "items": { - "$ref": "#/definitions/KnowledgeBaseAnswer" - } - } - } - }, - "KnowledgeBaseAnswer": { - "type": "object", - "description": "Represents knowledge base answer.", - "additionalProperties": false, - "properties": { - "questions": { - "type": "array", - "description": "List of questions associated with the answer.", - "items": { - "type": "string" - } - }, - "answer": { - "type": "string", - "description": "Answer text." - }, - "confidenceScore": { - "type": "number", - "format": "double", - "x-ms-client-name": "confidence", - "description": "Answer confidence score, value ranges from 0 to 1.", - "maximum": 1, - "minimum": 0 - }, - "id": { - "type": "integer", - "x-ms-client-name": "qnaId", - "description": "ID of the QnA result.", - "format": "int32" - }, - "source": { - "type": "string", - "description": "Source of QnA result." - }, - "metadata": { - "type": "object", - "description": "Metadata associated with the answer, useful to categorize or filter question answers.", - "additionalProperties": { - "type": "string" - } - }, - "dialog": { - "type": "object", - "$ref": "#/definitions/KnowledgeBaseAnswerDialog" - }, - "answerSpan": { - "type": "object", - "x-ms-client-name": "shortAnswer", - "description": "Answer span object of QnA with respect to user's question.", - "$ref": "#/definitions/AnswerSpan" - } - } - }, - "KnowledgeBaseAnswerDialog": { - "type": "object", - "description": "Dialog associated with Answer.", - "properties": { - "isContextOnly": { - "type": "boolean", - "description": "To mark if a prompt is relevant only with a previous question or not. If true, do not include this QnA as search result for queries without context; otherwise, if false, ignores context and includes this QnA in search result." - }, - "prompts": { - "type": "array", - "description": "List of prompts associated with the answer.", - "maxItems": 20, - "items": { - "$ref": "#/definitions/KnowledgeBaseAnswerPrompt" - } - } - } - }, - "KnowledgeBaseAnswerPrompt": { - "type": "object", - "description": "Prompt for an answer.", - "properties": { - "displayOrder": { - "type": "integer", - "description": "Index of the prompt - used in ordering of the prompts.", - "format": "int32" - }, - "qnaId": { - "type": "integer", - "description": "QnA ID corresponding to the prompt.", - "format": "int32" - }, - "displayText": { - "type": "string", - "description": "Text displayed to represent a follow up question prompt.", - "maxLength": 200 - } - } - }, - "QueryFilters": { - "type": "object", - "description": "filters over knowledge base.", - "additionalProperties": false, - "properties": { - "metadataFilter": { - "type": "object", - "$ref": "#/definitions/MetadataFilter" - }, - "sourceFilter": { - "type": "object", - "$ref": "#/definitions/SourceFilter" - }, - "logicalOperation": { - "type": "string", - "description": "Logical operation used to join metadata filter with source filter.", - "$ref": "#/definitions/LogicalOperationKind", - "default": "AND" - } - } - }, - "MetadataFilter": { - "type": "object", - "description": "Find QnAs that are associated with the given list of metadata.", - "additionalProperties": false, - "properties": { - "metadata": { - "type": "array", - "items": { - "$ref": "#/definitions/MetadataRecord" - } - }, - "logicalOperation": { - "type": "string", - "description": "Operation used to join metadata filters.", - "$ref": "#/definitions/LogicalOperationKind", - "default": "AND" - } - } - }, - "MetadataRecord": { - "type": "object", - "description": "Object to provide the key value pair for each metadata.", - "additionalProperties": false, - "required": [ - "key", - "value" - ], - "properties": { - "key": { - "type": "string", - "description": "Metadata Key from Metadata dictionary used in the QnA." - }, - "value": { - "type": "string", - "description": "Metadata Value from Metadata dictionary used in the QnA." - } - } - }, - "LogicalOperationKind": { - "type": "string", - "description": "Set to 'OR' or 'AND' for using corresponding logical operation.", - "x-ms-enum": { - "name": "LogicalOperationKind", - "modelAsString": true - }, - "enum": [ - "AND", - "OR" - ] - }, - "SourceFilter": { - "type": "array", - "description": "Find QnAs that are associated with any of the given list of sources in knowledge base.", - "items": { - "type": "string" - } - }, - "AnswerSpan": { - "type": "object", - "description": "Answer span object of QnA.", - "additionalProperties": false, - "properties": { - "text": { - "type": "string", - "description": "Predicted text of answer span." - }, - "confidenceScore": { - "type": "number", - "x-ms-client-name": "confidence", - "description": "Predicted score of answer span, value ranges from 0 to 1.", - "format": "double", - "maximum": 1, - "minimum": 0 - }, - "offset": { - "type": "integer", - "description": "The answer span offset from the start of answer.", - "format": "int32" - }, - "length": { - "type": "integer", - "description": "The length of the answer span.", - "format": "int32" - } - } - }, - "AnswersFromTextOptions": { - "type": "object", - "description": "The question and text record parameters to answer.", - "required": [ - "question", - "records" - ], - "additionalProperties": false, - "properties": { - "question": { - "type": "string", - "description": "User question to query against the given text records." - }, - "records": { - "type": "array", - "x-ms-client-name": "textDocuments", - "description": "Text records to be searched for given question.", - "items": { - "$ref": "#/definitions/TextDocument" - } - }, - "language": { - "$ref": "common.json#/definitions/Language" - }, - "stringIndexType": { - "$ref": "common.json#/definitions/StringIndexType" - } - } - }, - "AnswersFromTextResult": { - "type": "object", - "description": "Represents the answer results.", - "additionalProperties": false, - "properties": { - "answers": { - "type": "array", - "description": "Represents the answer results.", - "items": { - "$ref": "#/definitions/TextAnswer" - } - } - } - }, - "TextAnswer": { - "type": "object", - "description": "Represents answer result.", - "additionalProperties": false, - "properties": { - "answer": { - "type": "string", - "description": "Answer." - }, - "confidenceScore": { - "type": "number", - "x-ms-client-name": "confidence", - "description": "answer confidence score, value ranges from 0 to 1.", - "format": "double", - "maximum": 1, - "minimum": 0 - }, - "id": { - "type": "string", - "description": "record ID." - }, - "answerSpan": { - "type": "object", - "x-ms-client-name": "shortAnswer", - "description": "Answer span object with respect to user's question.", - "$ref": "#/definitions/AnswerSpan" - }, - "offset": { - "type": "integer", - "description": "The sentence offset from the start of the document.", - "format": "int32" - }, - "length": { - "type": "integer", - "description": "The length of the sentence.", - "format": "int32" - } - } - }, - "TextDocument": { - "type": "object", - "description": "Represent input text record to be queried.", - "additionalProperties": false, - "required": [ - "id", - "text" - ], - "properties": { - "id": { - "type": "string", - "description": "Unique identifier for the text record." - }, - "text": { - "type": "string", - "description": "Text contents of the record." - } - } - } - }, - "parameters": { - "AnswersOptions": { - "name": "knowledgeBaseQueryOptions", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/AnswersOptions" - }, - "x-nullable": true, - "description": "Post body of the request.", - "x-ms-parameter-location": "method" - }, - "AnswersFromTextOptions": { - "name": "textQueryOptions", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/AnswersFromTextOptions" - }, - "x-nullable": true, - "description": "Post body of the request.", - "x-ms-parameter-location": "method" - } - } -} diff --git a/dev/cognitiveservices/data-plane/Language/readme.csharp.md b/dev/cognitiveservices/data-plane/Language/readme.csharp.md deleted file mode 100644 index f0c6a178371a..000000000000 --- a/dev/cognitiveservices/data-plane/Language/readme.csharp.md +++ /dev/null @@ -1,18 +0,0 @@ -# Cognitive Services Language SDK for .NET - -This is the AutoRest configuration file the Cognitive Services Language SDK for .NET. - -> see https://aka.ms/autorest - -## Common C# Settings - -```yaml $(csharp) -csharp: - clear-output-folder: true - license-header: MICROSOFT_MIT_NO_VERSION - -directive: - from: swagger-document - where: $.parameters.Endpoint - transform: $.format = "url" -``` diff --git a/dev/cognitiveservices/data-plane/Language/readme.md b/dev/cognitiveservices/data-plane/Language/readme.md deleted file mode 100644 index b0aa3455b3e3..000000000000 --- a/dev/cognitiveservices/data-plane/Language/readme.md +++ /dev/null @@ -1,49 +0,0 @@ -# Cognitive Services Language SDK - -This is the AutoRest configuration file the Cognitive Services Language SDK. - -> see https://aka.ms/autorest - -## Releases - -The current release of Language is 2023-11-11-preview. - - - -```yaml -tag: release_2023-11-15-preview -add-credentials: true -clear-output-folder: true -openapi-type: data-plane -directive: - - suppress: LongRunningResponseStatusCode - reason: The validation tools do not properly recognize 202 as a supported response code. -``` - -### Release - -``` yaml $(tag) == 'release_2023-11-15-preview' -input-file: - - analyzedocuments.json - - analyzetext.json - - analyzetext-authoring.json - - analyzeconversations.json - - analyzeconversations-authoring.json - - questionanswering.json - - questionanswering-authoring.json -title: - Microsoft Cognitive Language Service -modelerfour: - lenient-model-deduplication: true -``` - -## Swagger to SDK - -This section describes what SDK should be generated by the automatic system. -This is not used by Autorest itself. - -``` yaml $(swagger-to-sdk) -swagger-to-sdk: - - repo: azure-sdk-for-net - - repo: azure-sdk-for-python -``` diff --git a/eng/common/TestResources/New-TestResources.ps1 b/eng/common/TestResources/New-TestResources.ps1 index 078991250e64..c106b904ef48 100755 --- a/eng/common/TestResources/New-TestResources.ps1 +++ b/eng/common/TestResources/New-TestResources.ps1 @@ -129,6 +129,11 @@ param ( $wellKnownTMETenants = @('70a036f6-8e4d-4615-bad6-149c02e7720d') +# People keep passing this legacy parameter. Throw an error to save them future keystrokes +if ($NewTestResourcesRemainingArguments -like '*UserAuth*') { + throw "The -UserAuth parameter is deprecated and is now the default behavior" +} + if (!$ServicePrincipalAuth) { # Clear secrets if not using Service Principal auth. This prevents secrets # from being passed to pre- and post-scripts. @@ -353,15 +358,12 @@ try { # Make sure the provisioner OID is set so we can pass it through to the deployment. if (!$ProvisionerApplicationId -and !$ProvisionerApplicationOid) { if ($context.Account.Type -eq 'User') { - # Support corp tenant and TME tenant user id lookups - $user = Get-AzADUser -Mail $context.Account.Id - if ($null -eq $user -or !$user.Id) { - $user = Get-AzADUser -UserPrincipalName $context.Account.Id - } - if ($null -eq $user -or !$user.Id) { + # HomeAccountId format is '.' + $userAccountId = (Get-AzContext).Account.ExtendedProperties.HomeAccountId.Split('.')[0] + if ($null -eq $userAccountId) { throw "Failed to find entra object ID for the current user" } - $ProvisionerApplicationOid = $user.Id + $ProvisionerApplicationOid = $userAccountId } elseif ($context.Account.Type -eq 'ServicePrincipal') { $sp = Get-AzADServicePrincipal -ApplicationId $context.Account.Id $ProvisionerApplicationOid = $sp.Id @@ -431,17 +433,14 @@ try { Write-Warning "The specified TestApplicationId '$TestApplicationId' will be ignored when -ServicePrincipalAutth is not set." } - # Support corp tenant and TME tenant user id lookups - $userAccount = (Get-AzADUser -Mail (Get-AzContext).Account.Id) - if ($null -eq $userAccount -or !$userAccount.Id) { - $userAccount = (Get-AzADUser -UserPrincipalName (Get-AzContext).Account) - } - if ($null -eq $userAccount -or !$userAccount.Id) { + $userAccountName = (Get-AzContext).Account.Id + # HomeAccountId format is '.' + $userAccountId = (Get-AzContext).Account.ExtendedProperties.HomeAccountId.Split('.')[0] + if ($null -eq $userAccountId) { throw "Failed to find entra object ID for the current user" } - $TestApplicationOid = $userAccount.Id + $TestApplicationOid = $userAccountId $TestApplicationId = $testApplicationOid - $userAccountName = $userAccount.UserPrincipalName Log "User authentication with user '$userAccountName' ('$TestApplicationId') will be used." } # If user has specified -ServicePrincipalAuth diff --git a/eng/common/TestResources/TestResources-Helpers.ps1 b/eng/common/TestResources/TestResources-Helpers.ps1 index cbe047ebc5f1..ce16b7767907 100644 --- a/eng/common/TestResources/TestResources-Helpers.ps1 +++ b/eng/common/TestResources/TestResources-Helpers.ps1 @@ -264,9 +264,10 @@ function SetDeploymentOutputs( ) { $deploymentEnvironmentVariables = $environmentVariables.Clone() $deploymentOutputs = BuildDeploymentOutputs $serviceName $azContext $deployment $deploymentEnvironmentVariables + $isBicep = $templateFile.originalFilePath -and $templateFile.originalFilePath.EndsWith(".bicep") - if ($OutFile) { - if ($IsWindows -and $Language -eq 'dotnet') { + # Azure SDK for .NET on Windows uses DPAPI-encrypted, JSON-encoded environment variables. + if ($OutFile -and $IsWindows -and $Language -eq 'dotnet') { $outputFile = "$($templateFile.originalFilePath).env" $environmentText = $deploymentOutputs | ConvertTo-Json; @@ -276,29 +277,29 @@ function SetDeploymentOutputs( Set-Content $outputFile -Value $protectedBytes -AsByteStream -Force Write-Host "Test environment settings`n$environmentText`nstored into encrypted $outputFile" + } + # Any Bicep template in a repo that has opted into .env files. + elseif ($OutFile -and $isBicep) { + $bicepTemplateFile = $templateFile.originalFilePath + + # Make sure the file would not write secrets to .env file. + if (!(LintBicepFile $bicepTemplateFile)) { + Write-Error "$bicepTemplateFile may write secrets. No file written." } - elseif ($templateFile.originalFilePath -and $templateFile.originalFilePath.EndsWith(".bicep")) { - $bicepTemplateFile = $templateFile.originalFilePath + $outputFile = $bicepTemplateFile | Split-Path | Join-Path -ChildPath '.env' - # Make sure the file would not write secrets to .env file. - if (!(LintBicepFile $bicepTemplateFile)) { - Write-Error "$bicepTemplateFile may write secrets. No file written." + # Make sure the file would be ignored. + git check-ignore -- "$outputFile" > $null + if ($?) { + $environmentText = foreach ($kv in $deploymentOutputs.GetEnumerator()) { + "$($kv.Key)=`"$($kv.Value)`"" } - $outputFile = $bicepTemplateFile | Split-Path | Join-Path -ChildPath '.env' - # Make sure the file would be ignored. - git check-ignore -- "$outputFile" > $null - if ($?) { - $environmentText = foreach ($kv in $deploymentOutputs.GetEnumerator()) { - "$($kv.Key)=`"$($kv.Value)`"" - } - - Set-Content $outputFile -Value $environmentText -Force - Write-Host "Test environment settings`n$environmentText`nstored in $outputFile" - } - else { - Write-Error "$outputFile is not ignored by .gitignore. No file written." - } + Set-Content $outputFile -Value $environmentText -Force + Write-Host "Test environment settings`n$environmentText`nstored in $outputFile" + } + else { + Write-Error "$outputFile is not ignored by .gitignore. No file written." } } else { diff --git a/eng/common/mcp/README.md b/eng/common/mcp/README.md index 4a7c28e46b55..4e72c026b5f0 100644 --- a/eng/common/mcp/README.md +++ b/eng/common/mcp/README.md @@ -24,15 +24,13 @@ The script will install the latest version of the azsdk cli executable from [too Azure SDK MCP server code is in [azure-sdk-tools/tools/azsdk-cli/Azure.Sdk.Tools.Cli](https://github.com/Azure/azure-sdk-tools/tree/main/tools/azsdk-cli/Azure.Sdk.Tools.Cli). -Azure SDK MCP servers should support [stdio and sse transports](https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse). +Azure SDK MCP servers [**MUST** support stdio and **SHOULD** support streamable HTTP using latest OAuth 2.1 Best Current Practices (BCPs)](https://modelcontextprotocol.io/docs/concepts/transports). -When running in copilot the default is stdio mode, but SSE is useful to support for external debugging. +When running in copilot the default is stdio mode. ### Developing MCP servers in C# See the [C# MCP SDK](https://github.com/modelcontextprotocol/csharp-sdk) -Add an [SSE transport](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/AspNetCoreSseServer) - TODO: Add the azsdk-cli project to pull in MCP server dependencies from the repo diff --git a/eng/common/mcp/azure-sdk-mcp.ps1 b/eng/common/mcp/azure-sdk-mcp.ps1 index b56fb4e96a28..331155516005 100755 --- a/eng/common/mcp/azure-sdk-mcp.ps1 +++ b/eng/common/mcp/azure-sdk-mcp.ps1 @@ -1,4 +1,7 @@ -#!/bin/env pwsh +#!/usr/bin/env pwsh + +#Requires -Version 7.0 +#Requires -PSEdition Core param( [string]$FileName = 'Azure.Sdk.Tools.Cli', @@ -9,21 +12,13 @@ param( [string]$RunDirectory = (Resolve-Path (Join-Path $PSScriptRoot .. .. ..)), [switch]$Run, [switch]$UpdateVsCodeConfig, - [switch]$Clean + [switch]$UpdatePathInProfile ) $ErrorActionPreference = "Stop" - -if (-not $InstallDirectory) -{ - $homeDir = if ($env:HOME) { $env:HOME } else { $env:USERPROFILE } - $InstallDirectory = (Join-Path $homeDir ".azure-sdk-mcp" "azsdk") -} . (Join-Path $PSScriptRoot '..' 'scripts' 'Helpers' 'AzSdkTool-Helpers.ps1') -if ($Clean) { - Clear-Directory -Path $InstallDirectory -} +$toolInstallDirectory = $InstallDirectory ? $InstallDirectory : (Get-CommonInstallDirectory) if ($UpdateVsCodeConfig) { $vscodeConfigPath = Join-Path $PSScriptRoot ".." ".." ".." ".vscode" "mcp.json" @@ -54,13 +49,33 @@ if ($UpdateVsCodeConfig) { $vscodeConfig | ConvertTo-Json -Depth 10 | Set-Content -Path $vscodeConfigPath -Force } -$exe = Install-Standalone-Tool ` +# Install to a temp directory first so we don't dump out all the other +# release zip contents to one of the users bin directories. +$tmp = $env:TEMP ? $env:TEMP : [System.IO.Path]::GetTempPath() +$guid = [System.Guid]::NewGuid() +$tempInstallDirectory = Join-Path $tmp "azsdk-install-$($guid)" +$tempExe = Install-Standalone-Tool ` -Version $Version ` -FileName $FileName ` -Package $Package ` - -Directory $InstallDirectory ` + -Directory $tempInstallDirectory ` -Repository $Repository +if (-not (Test-Path $toolInstallDirectory)) { + New-Item -ItemType Directory -Path $toolInstallDirectory -Force | Out-Null +} +$exeName = Split-Path $tempExe -Leaf +$exeDestination = Join-Path $toolInstallDirectory $exeName +Copy-Item -Path $tempExe -Destination $exeDestination -Force + +Write-Host "Package $package is installed at $exeDestination" +if (!$UpdatePathInProfile) { + Write-Warning "To add the tool to PATH for new shell sessions, re-run with -UpdatePathInProfile to modify the shell profile file." +} else { + Add-InstallDirectoryToPathInProfile -InstallDirectory $toolInstallDirectory + Write-Warning "'$exeName' will be available in PATH for new shell sessions." +} + if ($Run) { - Start-Process -WorkingDirectory $RunDirectory -FilePath $exe -ArgumentList 'start' -NoNewWindow -Wait + Start-Process -WorkingDirectory $RunDirectory -FilePath $exeDestination -ArgumentList 'start' -NoNewWindow -Wait } diff --git a/eng/common/pipelines/templates/archetype-typespec-emitter.yml b/eng/common/pipelines/templates/archetype-typespec-emitter.yml index a92ad9d9935e..c359c3b1ae12 100644 --- a/eng/common/pipelines/templates/archetype-typespec-emitter.yml +++ b/eng/common/pipelines/templates/archetype-typespec-emitter.yml @@ -248,11 +248,7 @@ extends: jobs: - job: Initialize steps: - - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml - parameters: - Paths: - - "/*" - - "!SessionRecords" + - checkout: self - task: UseNode@1 displayName: 'Install Node.js' @@ -325,15 +321,12 @@ extends: matrixArtifactsPath: $(Pipeline.Workspace)/matrix_artifacts AzureSdkRepoName: $[format('azure-sdk/{0}', split(variables['Build.Repository.Name'], '/')[1])] steps: - - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml - parameters: - Paths: - - "/*" - - "!SessionRecords" - Repositories: - - Name: $(AzureSdkRepoName) - Commitish: $(branchName) - WorkingDirectory: $(System.DefaultWorkingDirectory) + - checkout: self + - pwsh: | + git remote add azure-sdk https://github.com/$(AzureSdkRepoName).git + git fetch azure-sdk $(branchName) + git switch $(branchName) + displayName: 'Checkout PR branch $(branchName)' - task: UseNode@1 displayName: 'Install Node.js' diff --git a/eng/common/pipelines/templates/jobs/npm-publish.yml b/eng/common/pipelines/templates/jobs/npm-publish.yml index 9909ace96e9f..ce51a7b583e9 100644 --- a/eng/common/pipelines/templates/jobs/npm-publish.yml +++ b/eng/common/pipelines/templates/jobs/npm-publish.yml @@ -99,6 +99,7 @@ jobs: $packageTags = npm view $packageJson.name "dist-tags" -json -silent | ConvertFrom-Json if ($LASTEXITCODE -ne 0 -or !$packageTags) { Write-Warning "Failed to retrieve dist-tags for $packageJson.name. It is possible the package hasn't been indexed yet so ignoring." + $global:LASTEXITCODE = 0 continue } diff --git a/eng/common/pipelines/templates/jobs/perf.yml b/eng/common/pipelines/templates/jobs/perf.yml index bd53833282c2..67c4accf3800 100644 --- a/eng/common/pipelines/templates/jobs/perf.yml +++ b/eng/common/pipelines/templates/jobs/perf.yml @@ -112,7 +112,30 @@ jobs: - template: /eng/common/pipelines/templates/steps/verify-agent-os.yml parameters: AgentImage: $(OSVmImage) - + + # Copied from eng/pipelines/templates/steps/install-dotnet.yml, but changed workingDirectory + - task: UseDotNet@2 # About UseDotNet@2 task: https://learn.microsoft.com/azure/devops/pipelines/tasks/reference/use-dotnet-v2?view=azure-pipelines + displayName: "Use .NET SDK from global.json" + retryCountOnTaskFailure: 3 + inputs: + useGlobalJson: true + workingDirectory: azure-sdk-tools + + # Copied from eng/pipelines/templates/steps/install-dotnet.yml, but changed workingDirectory + - task: UseDotNet@2 + displayName: "Use .NET SDK 8.0.x" + retryCountOnTaskFailure: 3 + inputs: + # We must install sdk, not just runtime, as it is required by some of our tools, like test-proxy. + # Specifically, test-proxy requires asp.net core runtime, which is installed only when sdk option + # is selected, per: https://github.com/microsoft/azure-pipelines-tasks/issues/14405 + packageType: sdk + version: 8.0.x + # performMultiLevelLookup comes into play when given .NET executable target runtime is different + # than the installed .NET SDK. Without this, such runtime would not be found. + performMultiLevelLookup: true + workingDirectory: azure-sdk-tools + - ${{ parameters.InstallLanguageSteps }} - template: /eng/common/TestResources/deploy-test-resources.yml diff --git a/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 b/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 index 4fa981196139..8d74ece07dd9 100644 --- a/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 +++ b/eng/common/scripts/Helpers/AzSdkTool-Helpers.ps1 @@ -192,3 +192,61 @@ function Install-Standalone-Tool ( return $executable_path } + +function Get-CommonInstallDirectory { + $installDirectory = Join-Path $HOME "bin" + if (-not (Test-Path $installDirectory)) { + New-Item -ItemType Directory -Path $installDirectory -Force | Out-Null + } + + # Update PATH in current session + if (-not ($env:PATH -like "*$InstallDirectory*")) { + $env:PATH += ";$InstallDirectory" + } + + return $installDirectory +} + +function Add-InstallDirectoryToPathInProfile( + [Parameter()] + [string]$InstallDirectory = (Get-CommonInstallDirectory) +) { + $powershellProfilePath = $PROFILE + $bashrcPath = Join-Path $HOME ".bashrc" + $zshrcPath = Join-Path $HOME ".zshrc" + $markerComment = " # azsdk install path" + $pathCommand = "" + $configFile = "" + + if ($IsWindows) { # expect powershell for windows, cmd.exe path update is not currently supported + $configFile = $powershellProfilePath + $pathCommand = "if (-not (`$env:PATH -like `'*$InstallDirectory*`')) { `$env:PATH += ';$InstallDirectory`' }" + $markerComment + } + elseif ($IsLinux) { # handle bash or zsh shells for linux + if (Test-Path $zshrcPath) { + $configFile = $zshrcPath + } + else { + $configFile = $bashrcPath + } + $pathCommand = "export PATH=`"`$PATH:$InstallDirectory`"" + $markerComment + } + elseif ($IsMacOS) { # mac os should use zsh by default + $configFile = $zshrcPath + $pathCommand = "export PATH=`"`$PATH:$InstallDirectory`"" + $markerComment + } + else { + throw "Unsupported platform" + } + + if (-not (Test-Path $configFile)) { + New-Item -ItemType File -Path $configFile -Force | Out-Null + } + + $configContent = Get-Content $configFile -Raw + + if (!$configContent -or !$configContent.Contains($markerComment)) { + Write-Host "Adding installation to PATH in shell profile at '$configFile'" + Add-Content -Path $configFile -Value ([Environment]::NewLine + $pathCommand) + } +} diff --git a/eng/common/spelling/Invoke-Cspell.ps1 b/eng/common/spelling/Invoke-Cspell.ps1 old mode 100644 new mode 100755 index 4e5baa0bdf33..df4d2e587348 --- a/eng/common/spelling/Invoke-Cspell.ps1 +++ b/eng/common/spelling/Invoke-Cspell.ps1 @@ -1,3 +1,5 @@ +#!/usr/bin/env pwsh + <# .SYNOPSIS Invokes cspell using dependencies defined in adjacent ./package*.json diff --git a/eng/common/spelling/README.md b/eng/common/spelling/README.md new file mode 100644 index 000000000000..06e711c90a26 --- /dev/null +++ b/eng/common/spelling/README.md @@ -0,0 +1,84 @@ +# Spelling Check Scripts + +This directory contains a script to run cspell (Code Spell Checker) on the repository using the dependencies defined in the adjacent `package*.json` files. + +## Adding Legitimate Words + +If the spell checker flags legitimate words as misspelled, you can add them to the dictionary configuration file located at `.vscode/cspell.json`. + +### Where to Add Words + +There are two main places to add legitimate words. Maintain alphabetical order when adding words to keep the dictionary organized: + +1. **Root-level words array**: Add words to the `"words"` array at the root level of the configuration file. This is the preferred location for project-specific terms, technical vocabulary, and commonly used words. + +2. **Baseline dictionary**: Add words to the `"baseline"` dictionary under `"dictionaryDefinitions"`. This is typically used for words that were already present in the codebase when the spell checker was first introduced. + + +### Example + +To add new words, edit `.vscode/cspell.json` and add them to the `"words"` array: + +```json +{ + "words": [ + "myprojectname", + "customterm", + "technicalword" + ] +} +``` + +### Guidelines + +- Use lowercase for words +- Consider whether the word is truly legitimate or if it might be a typo + +## Available Scripts + +### PowerShell Version (Windows) +- **File**: `Invoke-Cspell.ps1` +- **Usage**: For Windows PowerShell environments + +## Usage Examples + +```powershell +# Check all files (default) +./eng/common/spelling/Invoke-Cspell.ps1 + +# Check specific files +./eng/common/spelling/Invoke-Cspell.ps1 -ScanGlobs 'sdk/*/*/PublicAPI/**/*.md' + +# Check multiple globs (powershell invocation only) +./eng/common/spelling/Invoke-Cspell.ps1 -ScanGlobs @('sdk/storage/**', 'sdk/keyvault/**') + +# Check single file +./eng/common/spelling/Invoke-Cspell.ps1 -ScanGlobs './README.md' +``` + +## Parameters + +- **Job Type**: The cspell command to run (default: `lint`) +- **Scan Globs**: File patterns to check for spelling +- **Config Path**: Location of the cspell.json configuration file +- **Spell Check Root**: Root directory for relative paths +- **Package Cache**: Working directory for npm dependencies +- **Leave Cache**: Option to preserve the npm package cache + +## Requirements + +- Node.js and npm must be installed +- The `.vscode/cspell.json` configuration file must exist +- `jq` command-line JSON processor (for bash version) + +## How It Works + +1. Creates a temporary working directory for npm packages +2. Copies `package.json` and `package-lock.json` to the working directory +3. Installs npm dependencies using `npm ci` +4. Modifies the cspell configuration to include specified file globs +5. Runs cspell with the modified configuration +6. Restores the original configuration +7. Cleans up temporary files + +The scripts ensure that a LICENSE file (or temporary file) is always included in the scan to meet cspell's requirements for the "files" array. diff --git a/eng/common/testproxy/dotnet-devcert.crt b/eng/common/testproxy/dotnet-devcert.crt index 764cf84c6c0d..931b6e739722 100644 --- a/eng/common/testproxy/dotnet-devcert.crt +++ b/eng/common/testproxy/dotnet-devcert.crt @@ -1,21 +1,21 @@ -----BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIUUGsnTw0cjASpYrNVxo7S41oMGtUwDQYJKoZIhvcNAQEL -BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDcyODA2NTAzOFoXDTI1MDcy -ODA2NTAzOFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAs9w2heE1VvnZcD3aR3jLgI/tiwjJScf+ljOMor9lxcIA -80NvcnfH3T7eH4Gsk0xcQ+J94FpFC5sDcsumcuEV1W6flvqj9k8vASoBpvpkoUnA -tr6aChLEp5hpwz59NAhdLrzd1eU+PNtB2CS0Blb6OEKUsVvoscmP5BwOWe+fuAdW -rQisHYrYzT9K5oWQA+AntrRJPbow7LCGQtT1viIlqxKaI8mTor+aRB0AmrAMJlPZ -4scmrpNs7+ertRVTz+uZ6HWHN5CJsVKnFKgTTuYYustRV2Oz9tF4W0+j1fBKCkYM -WQdga3md/9t51mG0naezJNDsMlT45IFOadqOozqsswIDAQABo4GwMIGtMA8GA1Ud +MIIDZzCCAk+gAwIBAgIUXUCvBB5U6rYQyAEu5rApzjp3RQYwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDcyMzE3NTUyM1oXDTI2MDcy +MzE3NTUyM1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA67HYsBPB865zJS8TDwUBgFSKqWgJ7dGTQh3aTlvamFED +5/kHnL7oTr3x/6Hylnajf0v4vGWmSok0/3SAcbpr/9l19/7zbpA1LLGzu8G909o5 +38Wl5sNbGvQIMK91KxbHRXlVMKoYTIL38cNdZvhzfb9m9Tew6vPmz4ABMPiwYS9T +R19lAPwmQYwce00NkKaQE5+6pzsPhnG/o/Ww9rBE370fidXn8jhqLSOEk+hbp3ju +KlxeSrVHAqlvTzlvSTZGRyxioRLDEMFT3ka1cyLo6HP3U7lj76mlJBibahE+ylL+ +z594fzHnfYPQaN5g13G9H2oxTg+VwwNtL1U737FNiwIDAQABo4GwMIGtMA8GA1Ud EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGmMBYGA1UdJQEB/wQMMAoGCCsGAQUF BwMBMBcGA1UdEQEB/wQNMAuCCWxvY2FsaG9zdDA6BgorBgEEAYI3VAEBBCwMKkFT UC5ORVQgQ29yZSBIVFRQUyBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTAdBgNVHQ4E -FgQUhYGoJsA6zisErkL5EcbKAO3LoQ8wDQYJKoZIhvcNAQELBQADggEBAHOBgMVZ -a27y41edktHaLC4kayKHe3bLBtiXUd1vjWEZaPrcvQH6FItJdB5Z3xbi/4LA6QuU -dZ33z44Lt4AzOiyvR7AGojytM2UYimtBBOFvi9CZuyrMC6ls4SLy6c/Rq1ATmyet -QfkT1a7awC1cJUn7jeuHfly7hnYqrw+67HFY7Ajugv+Uwu6wCessKloWM127AZj/ -V0Gw0rblOmX+uX7fvH3BrYOkrNiHQdzCYZ1P1WU7eWm55JgBfaCrheNZ4L5TVeB2 -uTZPnlsBKQ23qgZ0dtcJzL0scC7zSuYI+qm1QNNyz13K+1ORXPQGQJXULlt7UeHB -ZYN060pO6IO4Ti8= +FgQUg5xH/y5mh7lGgF+y+1sJC7ZMN9owDQYJKoZIhvcNAQELBQADggEBAK/gRk/L +5q4/xXXYW77WawygxpGmTgBLDHkiJViMnJ4VDk6q97/DMABIqCp3sv3FNP9sOssG +ypgmX4jYbyrLNwfvdtpVaiHAHvrTfAtrfHgG0EVM1DRUJ1e0/SRfYf1QTdVxEZv+ +3IM+0roYa8EtQq8BxSsAZz+zhNMoav8nXQVhR7+v5NI5vzT0TVncfXIYYYfLhllb +wcmh9iQuXMifj7WohOFE1XK4O6Bats/6V85ZSGDl3npEpYcgBwyxNQE5hKn/lG5b +3DDkpCTeoMZxAHLo39RzAy0WJTF1KPHQ2EzUa+MfoTNWNrhYSW3IqI3xfPzevSDx +BTBk4gS9MSt2Jj8= -----END CERTIFICATE----- diff --git a/eng/common/testproxy/dotnet-devcert.pfx b/eng/common/testproxy/dotnet-devcert.pfx new file mode 100644 index 000000000000..93a5617aea60 Binary files /dev/null and b/eng/common/testproxy/dotnet-devcert.pfx differ diff --git a/eng/common/testproxy/target_version.txt b/eng/common/testproxy/target_version.txt index 977b480f4fd3..f296dfcc218b 100644 --- a/eng/common/testproxy/target_version.txt +++ b/eng/common/testproxy/target_version.txt @@ -1 +1 @@ -1.0.0-dev.20250501.1 +1.0.0-dev.20250805.1 diff --git a/eng/pipelines/spec-gen-sdk.yml b/eng/pipelines/spec-gen-sdk.yml index 0b5b5e849d6e..d324b28067ea 100644 --- a/eng/pipelines/spec-gen-sdk.yml +++ b/eng/pipelines/spec-gen-sdk.yml @@ -25,11 +25,11 @@ parameters: - 'stable' default: 'beta' displayName: 'SDK release type' - - name: SkipPullRequestCreation + - name: CreatePullRequest type: boolean default: false - displayName: 'Skip SDK pull request creation' - + displayName: 'Create SDK pull request' + trigger: none extends: @@ -42,4 +42,4 @@ extends: ConfigPath: ${{ parameters.ConfigPath }} ApiVersion: ${{ parameters.ApiVersion }} SdkReleaseType: ${{ parameters.SdkReleaseType }} - SkipPullRequestCreation: ${{ parameters.SkipPullRequestCreation }} + CreatePullRequest: ${{ parameters.CreatePullRequest }} diff --git a/eng/pipelines/swagger-api-doc-preview.yml b/eng/pipelines/swagger-api-doc-preview.yml new file mode 100644 index 000000000000..8d8fbae17881 --- /dev/null +++ b/eng/pipelines/swagger-api-doc-preview.yml @@ -0,0 +1,235 @@ +trigger: none +pr: + paths: + include: + # Trigger for files that will result in a doc preview build + - specification/** + + # Smoke test on changed files + - eng/pipelines/swagger-api-doc-preview.yml + - eng/pipelines/templates/steps/set-sha-check.yml + - .github/shared/src/doc-preview.js + - .github/shared/cmd/api-doc-preview.js + +jobs: + - job: SwaggerApiDocPreview + + pool: + name: $(LINUXPOOL) + vmImage: $(LINUXVMIMAGE) + + variables: + - template: /eng/pipelines/templates/variables/globals.yml + - template: /eng/pipelines/templates/variables/image.yml + + - name: BranchName + value: preview/$(Build.Repository.Name)/pr/$(System.PullRequest.PullRequestNumber)/build/$(Build.BuildId)/attempt/$(System.JobAttempt) + + - name: CurrentBuildUrl + value: $(System.CollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId) + + - name: StatusName + value: 'Swagger ApiDocPreview' + + steps: + - template: /eng/pipelines/templates/steps/set-sha-check.yml + parameters: + State: pending + TargetUrl: $(CurrentBuildUrl) + Description: 'Starting docs build' + Context: $(StatusName) + + - checkout: self + # Fetch depth required to get list of changed files + fetchDepth: 2 + + - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml + parameters: + SkipCheckoutNone: true + TokenToUseForAuth: $(azuresdk-github-pat) + # Path does not need to be set because sparse-checkout.yml already + # checks out files in the repo root + Repositories: + - Name: MicrosoftDocs/AzureRestPreview + Commitish: main + WorkingDirectory: AzureRestPreview + + - template: /eng/pipelines/templates/steps/npm-install.yml + parameters: + WorkingDirectory: .github/shared + + - script: cmd/api-doc-preview.js --output ../../AzureRestPreview + displayName: Generate Swagger API documentation preview + workingDirectory: .github/shared + + - template: /eng/common/pipelines/templates/steps/git-push-changes.yml + parameters: + WorkingDirectory: AzureRestPreview + BaseRepoOwner: MicrosoftDocs + TargetRepoOwner: MicrosoftDocs + TargetRepoName: AzureRestPreview + BaseRepoBranch: $(BranchName) + CommitMsg: | + Update API Doc Preview + Build: $(System.CollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId) + PR: $(System.PullRequest.SourceRepositoryURI)/pull/$(System.PullRequest.PullRequestId) + + - task: AzureCLI@2 + displayName: Start docs build + condition: and(succeeded(), eq(variables['HasChanges'], 'true')) + inputs: + azureSubscription: msdocs-apidrop-connection + scriptType: pscore + scriptLocation: inlineScript + inlineScript: | + $buildStartRaw = az pipelines build queue ` + --organization "https://dev.azure.com/apidrop/" ` + --project "Content CI" ` + --definition-id "8157" ` + --variables 'params={"target_repo":{"url":"https://github.com/MicrosoftDocs/AzureRestPreview","branch":"$(BranchName)"}, "source_of_truth": "code"}' + $buildStartRaw | Set-Content buildstart.json + $buildStart = $buildStartRaw | ConvertFrom-Json + Write-Host "Build started at https://dev.azure.com/apidrop/Content%20CI/_build/results?buildId=$($buildStart.id)" + + - template: /eng/pipelines/templates/steps/set-sha-check.yml + parameters: + Condition: and(succeeded(), eq(variables['HasChanges'], 'true')) + State: pending + TargetUrl: $(CurrentBuildUrl) + Description: 'Waiting for docs build to finish' + Context: $(StatusName) + + - task: AzureCLI@2 + displayName: Wait for docs build to finish + condition: and(succeeded(), eq(variables['HasChanges'], 'true')) + # Retry on failure to handle transient issues or builds that run longer + # than the token used by az CLI is valid + retryCountOnTaskFailure: 3 + inputs: + azureSubscription: msdocs-apidrop-connection + scriptType: pscore + scriptLocation: inlineScript + inlineScript: | + $buildStart = Get-Content buildstart.json | ConvertFrom-Json + + Write-Host "Waiting for build to finish: https://dev.azure.com/apidrop/Content%20CI/_build/results?buildId=$($buildStart.id)" + + # Timeout in 10 minutes to avoid infinite waiting + $start = Get-Date + while (((Get-Date) - $start).TotalMinutes -lt 10) { + $runStatusRaw = az pipelines runs show ` + --organization "https://dev.azure.com/apidrop/" ` + --project "Content CI" ` + --id "$($buildStart.id)" + + if ($LASTEXITCODE) { + Write-Host "Failed to get run status" + Write-Host "Exit code: $LASTEXITCODE" + Write-Host "Output: $runStatusRaw" + exit 1 + } + + $runStatus = $runStatusRaw | ConvertFrom-Json + Write-Host "Run status: $($runStatus.status)" + + if ($runStatus.status -eq "completed") { + break; + } + Start-Sleep -Seconds 10 + } + + Write-Host "Build completed with status: $($runStatus.result)" + Write-Host "Build logs: https://dev.azure.com/apidrop/Content%20CI/_build/results?buildId=$($buildStart.id)" + + Write-Host "Downloading artifact..." + $artifactDownloadRaw = az pipelines runs artifact download ` + --organization "https://dev.azure.com/apidrop/" ` + --project "Content CI" ` + --run-id "$($buildStart.id)" ` + --artifact-name "report" ` + --path "./" + + if ($LASTEXITCODE) { + Write-Host "Failed to download artifact" + Write-Host "Exit code: $LASTEXITCODE" + Write-Host "Output: $artifactDownloadRaw" + exit 1 + } + + Write-Host "Artifact downloaded successfully" + + - pwsh: | + # Get build ID from buildstart.json (set during the "Start docs build" step) + $buildStart = Get-Content buildstart.json | ConvertFrom-Json + $buildLink = "https://dev.azure.com/apidrop/Content%20CI/_build/results?buildId=$($buildStart.id)" + + if (!(Test-Path -Path "./report.json")) { + Write-Host "Report file not found. Docs build may have failed or not produced the expected artifact." + Write-Host "##vso[task.setvariable variable=CheckUrl]${buildLink}" + Write-Host "##vso[task.setvariable variable=CheckDescription]Docs build failed with no output artifacts (click to see pipeline logs)" + Write-Host "##vso[task.setvariable variable=CheckState]failure" + exit 0 + } + + # Read the report.json file downloaded from the docs build artifact + $reportRaw = Get-Content -Path "./report.json" -Raw + $report = $reportRaw | ConvertFrom-Json -AsHashtable + + if ($report.status -ne "Succeeded") { + Write-Host "Docs build failed with status: $($report.status)" + Write-Host "Report:" + Write-Host $reportRaw + + Write-Host "##vso[task.setvariable variable=CheckUrl]${buildLink}" + Write-Host "##vso[task.setvariable variable=CheckDescription]Docs build failed (click to see pipeline logs)" + Write-Host "##vso[task.setvariable variable=CheckState]failure" + + # Docs build failed, but this job should not fail unless it + # encounters an unexpected error. The check status will be set in + # the next task. + exit 0 + } + + Write-Host "Docs build succeeded with status: $($report.status)" + + $docsPreviewUrl = "https://review.learn.microsoft.com/en-us/rest/api/azure-rest-preview/?branch=$([System.Web.HttpUtility]::UrlEncode($report.branch))&view=azure-rest-preview" + Write-Host "##vso[task.setvariable variable=CheckUrl]$docsPreviewUrl" + Write-Host "##vso[task.setvariable variable=CheckDescription]Docs build succeeded (click to see preview)" + Write-Host "##vso[task.setvariable variable=CheckState]success" + Write-Host "Docs preview URL: $docsPreviewUrl" + + exit 0 + displayName: Interpret docs build results + condition: and(succeeded(), eq(variables['HasChanges'], 'true')) + + # Sets check status from docs build using $(CheckUrl), $(CheckState), and + # $(CheckDescription) variables set by api-doc-preview-interpret.js. + - template: /eng/pipelines/templates/steps/set-sha-check.yml + parameters: + DisplayName: Set PR status from docs build + Condition: and(succeeded(), eq(variables['HasChanges'], 'true')) + State: $(CheckState) + TargetUrl: $(CheckUrl) + Description: $(CheckDescription) + Context: $(StatusName) + + - template: /eng/pipelines/templates/steps/set-sha-check.yml + parameters: + DisplayName: Set PR status for no-op + Condition: and(succeeded(), ne(variables['HasChanges'], 'true')) + State: success + TargetUrl: $(CurrentBuildUrl) + Description: No files changed require docs build + Context: $(StatusName) + + # In the event of a failure in this job (not the docs build job), set the + # PR status to failed and link to the current build. + - template: /eng/pipelines/templates/steps/set-sha-check.yml + parameters: + DisplayName: Set PR status for job failure + # Only run if a previous step in the job failed + Condition: failed() + State: failure + TargetUrl: $(CurrentBuildUrl) + Description: 'Orchestration build failed (click to see logs)' + Context: $(StatusName) diff --git a/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml b/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml index 764f9e421e0b..9fd98f9ed3da 100644 --- a/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml +++ b/eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml @@ -22,7 +22,7 @@ parameters: type: string default: '' displayName: 'SDK release type' - - name: SkipPullRequestCreation + - name: CreatePullRequest type: boolean default: false - name: SpecBatchTypes @@ -171,6 +171,11 @@ stages: sdk_gen_info="$sdk_gen_info API Version: ${{ parameters.ApiVersion }}, SDK Release Type: ${{ parameters.SdkReleaseType }}," fi sdk_gen_info="$sdk_gen_info and CommitSHA: '$updatedSpecRepoCommit' in SpecRepo: '${{ parameters.SpecRepoUrl }}'" + + if [ "$(Build.Reason)" = "Manual" ]; then + sdk_gen_info="$sdk_gen_info Pipeline run: $(System.CollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)" + sdk_gen_info="$sdk_gen_info Refer to https://eng.ms/docs/products/azure-developer-experience/develop/sdk-release/sdk-release-prerequisites to prepare for SDK release." + fi echo "##vso[task.setvariable variable=GeneratedSDKInformation]$sdk_gen_info" echo "$sdk_gen_info" @@ -200,7 +205,7 @@ stages: ArtifactPath: $(StagedArtifactsFolder) CustomCondition: and(succeededOrFailed(), ne(variables['StagedArtifactsFolder'], '')) - - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.SkipPullRequestCreation, false), ne(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if and(eq(variables['System.TeamProject'], 'internal'), eq(parameters.CreatePullRequest, true), ne(variables['Build.Reason'], 'PullRequest')) }}: - template: /eng/common/pipelines/templates/steps/git-push-changes.yml parameters: BaseRepoBranch: $(PrBranch)-$(Build.BuildId) diff --git a/eng/pipelines/templates/steps/npm-install.yml b/eng/pipelines/templates/steps/npm-install.yml index c64de780bc12..399d58b558fa 100644 --- a/eng/pipelines/templates/steps/npm-install.yml +++ b/eng/pipelines/templates/steps/npm-install.yml @@ -2,6 +2,9 @@ parameters: - name: NodeVersion type: string default: $(NodeVersion) + - name: WorkingDirectory + type: string + default: $(Build.SourcesDirectory) steps: - template: /eng/pipelines/templates/steps/use-node-version.yml @@ -10,7 +13,9 @@ steps: - script: npm ci displayName: npm ci + workingDirectory: ${{ parameters.WorkingDirectory }} - script: npm ls -a || true displayName: npm ls -a condition: succeededOrFailed() + workingDirectory: ${{ parameters.WorkingDirectory }} diff --git a/eng/pipelines/templates/steps/set-sha-check.yml b/eng/pipelines/templates/steps/set-sha-check.yml new file mode 100644 index 000000000000..ae5845922de0 --- /dev/null +++ b/eng/pipelines/templates/steps/set-sha-check.yml @@ -0,0 +1,64 @@ +# Create a "status" check for a SHA (inferred from PR by default) in a GitHub +# repository. By default this uses the azure-sdk account. This might not work +# for required checks where a source must be configured. + +parameters: + - name: Sha + type: string + default: $(System.PullRequest.SourceCommitId) + + - name: RepositoryName + type: string + default: $(Build.Repository.Name) + + - name: State + type: string + default: 'pending' + # Valid values: + # - error + # - failure + # - pending + # - success + + - name: TargetUrl + type: string + default: '' + + - name: Description + type: string + default: '' + + - name: Context + type: string + default: default context + + - name: Condition + type: string + default: succeeded() + + - name: DisplayName + type: string + default: 'Set PR status' + + - name: GitHubToken + type: string + default: $(azuresdk-github-pat) + +steps: + - bash: | + echo "Repository Name: ${{ parameters.RepositoryName }}" + echo "Commit ID: ${{ parameters.Sha }}" + echo "State: ${{ parameters.State }}" + echo "Target URL: ${{ parameters.TargetUrl }}" + echo "Description: ${{ parameters.Description }}" + echo "Context: ${{ parameters.Context }}" + + gh api repos/${{ parameters.RepositoryName }}/statuses/${{ parameters.Sha }} \ + -f state='${{ parameters.State }}' \ + -f target_url='${{ parameters.TargetUrl }}' \ + -f description='${{ parameters.Description }}' \ + -f context='${{ parameters.Context }}' + displayName: ${{ parameters.DisplayName }} + condition: ${{ parameters.Condition }} + env: + GH_TOKEN: ${{ parameters.GitHubToken }} diff --git a/eng/scripts/Create-APIView.ps1 b/eng/scripts/Create-APIView.ps1 index e855e93765e6..999129e68946 100644 --- a/eng/scripts/Create-APIView.ps1 +++ b/eng/scripts/Create-APIView.ps1 @@ -464,7 +464,7 @@ function New-TypeSpecAPIViewTokens { LogGroupEnd foreach ($typeSpecProject in $typeSpecProjects) { # Skip Baseline APIView Token for new projects - if (!(Test-Path -Path $typeSpecProject)) { + if (!(Test-Path -Path (Join-Path $typeSpecProject "tspconfig.yaml"))) { Write-Host "TypeSpec project $typeSpecProject is not found in pull request target branch. API review will not have a baseline revision." } else { diff --git a/eng/scripts/TypeSpec-Requirement.ps1 b/eng/scripts/TypeSpec-Requirement.ps1 index 4b2628348cbb..ab7682e11742 100644 --- a/eng/scripts/TypeSpec-Requirement.ps1 +++ b/eng/scripts/TypeSpec-Requirement.ps1 @@ -169,7 +169,7 @@ else { if ($responseStatus -eq 200) { LogInfo " Branch 'main' contains path '$servicePath/stable', so spec already exists and is not required to use TypeSpec" - $notice = "Brownfield services will soon be required to convert from OpenAPI to TypeSpec. See https://aka.ms/azsdk/typespec." + $notice = "Your service description will soon be required to convert from OpenAPI to TypeSpec. See https://aka.ms/azsdk/typespec." LogNoticeForFile $file $notice if ($env:GITHUB_OUTPUT) { diff --git a/eng/tools/.prettierrc.yaml b/eng/tools/.prettierrc.yaml index a851a86e34a8..69cc825aaf04 100644 --- a/eng/tools/.prettierrc.yaml +++ b/eng/tools/.prettierrc.yaml @@ -1,4 +1,7 @@ -# .prettierrc.yaml +# Keep in sync with .github/.prettierrc.yaml + +plugins: + - prettier-plugin-organize-imports # Aligned with microsoft/typespec printWidth: 100 diff --git a/eng/tools/lint-diff/README.md b/eng/tools/lint-diff/README.md index 283a6f62127c..67556c42f4d4 100644 --- a/eng/tools/lint-diff/README.md +++ b/eng/tools/lint-diff/README.md @@ -1,31 +1,51 @@ -# TODO: REVERIFY THESE DOCS BEFORE MERGE - # LintDiff LintDiff looks at files changed in a commit, runs `autorest` linting over those files, and looks for "new" errors in the results. -Altered files examined include `readme.md` files and swagger `.json` files. In -the case of `readme.md` files the tool will examine tags and other files that -could be impacted by the change (e.g. `readme.md` files in folders higher in the -directory structure). +Interesting changed files are swagger `.json` files and `readme.md` files. +Changes to swagger JSON files are mapped to `readme.md` files and tags which +include the swagger file. The entire tree of swagger dependencies is traversed +so changes to a file will result in scanning of readme.md and swagger files +which depend on the changed file. + +## Correlation + +LintDiff attempts to correlate errors found to files with the same base name +and JSON path to the error site. Array position matters, so if an object's +position in an array changes, the error will not correlate and may be reported. + +## Prerequisites + +- Node.JS 20 or later ## Run Locally -1. Setup `before` - Clone the specs repo at the _base commit_ for the changes you want to test. (generally cloned to main and in a temp location like `/tmp/rest-api-specs-before`) -1. Setup `after` - Clone the specs repo at the commit for the changes you want to test. -1. Build a list of changed files from `after` generally by running `git diff --name-only > changed-files.txt` -1. From the root of the repo run `npm i` to install dependencies. -1. Run `npm exec --no -- lint-diff --before --after --changed-files ` +1. Setup `before` folder state - Clone the specs repo at the _base commit_ for the changes you want to test. (generally cloned to main and in a temp location like `/tmp/rest-api-specs-before`) +1. Setup `after` folder state - Clone the specs repo at the commit for the changes you want to test in a different folder. +1. Build a list of changed files from `after` generally by running `git diff --name-only > changed-files.txt` in the `after` folder. +1. From the root of the repo run `npm ci` to install dependencies. +1. Run `npm exec --no -- lint-diff --before --after --changed-files ` ### Example In most common cases, changes to LintDiff are in the current state of the repo and you'll need to set up a `before` to run against. +At the root of the repo with changes you want to test, run the following commands: + ```bash +# Clone the specs repo into a temporary location at the base commit git clone https://github.com/Azure/azure-rest-api-specs.git /tmp/rest-api-specs-before + +# Diff the current repo (after state) against the base commit git diff --name-only main > changed-files.txt -npm i +npm ci npm exec --no -- lint-diff --before /tmp/rest-api-specs-before --after . --changed-files changed-files.txt ``` + +## Output + +Output is written by default to `lint-diff.md` in the current working +directory. The file contains a table of errors. LintDiff itself also +formats error output to the console. diff --git a/eng/tools/lint-diff/package.json b/eng/tools/lint-diff/package.json index 14767c5d302c..38914549a80b 100644 --- a/eng/tools/lint-diff/package.json +++ b/eng/tools/lint-diff/package.json @@ -19,14 +19,16 @@ "node": ">= 20.0.0" }, "dependencies": { - "@apidevtools/json-schema-ref-parser": "^14.1.0", + "@apidevtools/json-schema-ref-parser": "^14.1.1", "@azure-tools/specs-shared": "file:../../../.github/shared", - "@microsoft.azure/openapi-validator": "^2.2.4", + "@microsoft.azure/openapi-validator": "2.2.4", + "@microsoft.azure/openapi-validator-core": "1.0.6", + "@microsoft.azure/openapi-validator-rulesets": "2.1.7", "autorest": "^3.7.2", "axios": "^1.8.3", "change-case": "^5.4.4", "deep-eql": "^5.0.2", - "marked": "^16.0.0" + "marked": "^16.1.1" }, "devDependencies": { "@types/deep-eql": "^4.0.2", @@ -35,6 +37,7 @@ "execa": "^9.5.2", "memfs": "^4.17.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.2" } diff --git a/eng/tools/lint-diff/src/correlateResults.ts b/eng/tools/lint-diff/src/correlateResults.ts index 3d327e75c08b..f69ed1eb5ece 100644 --- a/eng/tools/lint-diff/src/correlateResults.ts +++ b/eng/tools/lint-diff/src/correlateResults.ts @@ -1,8 +1,8 @@ +import { Readme } from "@azure-tools/specs-shared/readme"; import { basename, join, relative } from "path"; -import { relativizePath, pathExists, isFailure, isWarning } from "./util.js"; import { AutorestRunResult, BeforeAfter, LintDiffViolation, Source } from "./lintdiff-types.js"; import { getDefaultTag } from "./markdown-utils.js"; -import { Readme } from "@azure-tools/specs-shared/readme"; +import { isFailure, isWarning, pathExists, relativizePath } from "./util.js"; export async function correlateRuns( beforePath: string, diff --git a/eng/tools/lint-diff/src/generateReport.ts b/eng/tools/lint-diff/src/generateReport.ts index 62f0019650e1..bb2fab806137 100644 --- a/eng/tools/lint-diff/src/generateReport.ts +++ b/eng/tools/lint-diff/src/generateReport.ts @@ -1,16 +1,21 @@ +import { kebabCase } from "change-case"; import { writeFile } from "node:fs/promises"; import { relative } from "node:path"; -import { kebabCase } from "change-case"; -import { getRelatedArmRpcFromDoc } from "./markdown-utils.js"; -import { getPathToDependency, getDependencyVersion, relativizePath } from "./util.js"; import { getViolations } from "./correlateResults.js"; -import { isFailure, isWarning } from "./util.js"; import { - AutorestRunResult, AutoRestMessage, + AutorestRunResult, BeforeAfter, LintDiffViolation, } from "./lintdiff-types.js"; +import { getRelatedArmRpcFromDoc } from "./markdown-utils.js"; +import { + getDependencyVersion, + getPathToDependency, + isFailure, + isWarning, + relativizePath, +} from "./util.js"; const LIMIT_50_MESSAGE = "Only 50 items are listed, please refer to log for more details."; @@ -44,11 +49,18 @@ export async function generateLintDiffReport( const afterPath = getPath(after); const beforePath = before ? getPath(before) : ""; - outputMarkdown += `| ${afterName} | [${afterName}](${getFileLink(githubRepoPath, compareSha, afterPath)}) | [${beforeName}](${getFileLink(githubRepoPath, baseBranch, beforePath)}) |\n`; + outputMarkdown += `| ${afterName} | [${afterName}](${getFileLink(githubRepoPath, compareSha, afterPath)}) | [${beforeName}](${getFileLink(githubRepoPath, baseBranch, beforePath)}) ${getAutoRestFailedMessage(before)}|\n`; } outputMarkdown += `\n\n`; + for (const [, { before }] of runCorrelations.entries()) { + if (before && before.error) { + outputMarkdown += `> [!WARNING]\n`; + outputMarkdown += `> Autorest failed checking before state of ${relative(before.rootPath, before.readme.path)} ${before.tag}\n\n`; + } + } + const [newViolations, existingViolations] = getViolations(runCorrelations, affectedSwaggers); for (const newItem of newViolations) { @@ -278,3 +290,10 @@ export function getPath(result: AutorestRunResult) { const readmePathRelative = relative(rootPath, readme.path); return tag ? `${readmePathRelative}#tag-${tag}` : readmePathRelative; } + +export function getAutoRestFailedMessage(result: AutorestRunResult | null): string { + if (result?.error) { + return "Autorest Failed"; + } + return ""; +} diff --git a/eng/tools/lint-diff/src/lint-diff.ts b/eng/tools/lint-diff/src/lint-diff.ts index a597dc21eff1..9b575ff9e72f 100644 --- a/eng/tools/lint-diff/src/lint-diff.ts +++ b/eng/tools/lint-diff/src/lint-diff.ts @@ -1,11 +1,11 @@ +import { SpecModelError } from "@azure-tools/specs-shared/spec-model-error"; +import { writeFile } from "node:fs/promises"; import { parseArgs, ParseArgsConfig } from "node:util"; -import { pathExists, getDependencyVersion, getPathToDependency } from "./util.js"; -import { getRunList } from "./processChanges.js"; -import { runChecks, getAutorestErrors } from "./runChecks.js"; import { correlateRuns } from "./correlateResults.js"; import { generateAutoRestErrorReport, generateLintDiffReport } from "./generateReport.js"; -import { writeFile } from "node:fs/promises"; -import { SpecModelError } from "@azure-tools/specs-shared/spec-model-error"; +import { getRunList } from "./processChanges.js"; +import { getAutorestErrors, runChecks } from "./runChecks.js"; +import { getDependencyVersion, getPathToDependency, pathExists } from "./util.js"; function usage() { console.log("TODO: Write up usage"); @@ -120,10 +120,8 @@ async function runLintDiff( ); } catch (error) { if (error instanceof SpecModelError) { - console.log("\n\n"); - console.log("❌ Error building Spec Model from changed file list:"); + console.log("\n❌ Error building Spec Model from changed file list:"); console.log(`${error}`); - console.log("Ensure input files and references are valid."); process.exitCode = 1; return; @@ -146,7 +144,10 @@ async function runLintDiff( // It may be possible to run these in parallel as they're running against // different directories. + console.log("Running checks on before state..."); const beforeChecks = await runChecks(beforePath, beforeList); + + console.log("Running checks on after state..."); const afterChecks = await runChecks(afterPath, afterList); // If afterChecks has AutoRest errors, fail the run. diff --git a/eng/tools/lint-diff/src/markdown-utils.ts b/eng/tools/lint-diff/src/markdown-utils.ts index 20d2a5e54d35..b9a2d94d757a 100644 --- a/eng/tools/lint-diff/src/markdown-utils.ts +++ b/eng/tools/lint-diff/src/markdown-utils.ts @@ -1,7 +1,7 @@ -import { marked } from "marked"; -import { kebabCase } from "change-case"; -import axios from "axios"; import { Readme } from "@azure-tools/specs-shared/readme"; +import axios from "axios"; +import { kebabCase } from "change-case"; +import { marked } from "marked"; export enum MarkdownType { Arm = "arm", diff --git a/eng/tools/lint-diff/src/processChanges.ts b/eng/tools/lint-diff/src/processChanges.ts index c57bec9648b2..ea2400474d46 100644 --- a/eng/tools/lint-diff/src/processChanges.ts +++ b/eng/tools/lint-diff/src/processChanges.ts @@ -1,13 +1,13 @@ -import { join, relative, resolve, sep } from "path"; -import { readFile } from "fs/promises"; -import { pathExists } from "./util.js"; -import { specification, readme, swagger } from "@azure-tools/specs-shared/changed-files"; +import { readme, swagger } from "@azure-tools/specs-shared/changed-files"; import { SpecModel } from "@azure-tools/specs-shared/spec-model"; -import { ReadmeAffectedTags } from "./lintdiff-types.js"; import deepEqual from "deep-eql"; +import { readFile } from "fs/promises"; +import { join, relative, resolve, sep } from "path"; +import { ReadmeAffectedTags } from "./lintdiff-types.js"; +import { pathExists } from "./util.js"; -import { deduplicateTags } from "./markdown-utils.js"; import $RefParser from "@apidevtools/json-schema-ref-parser"; +import { deduplicateTags } from "./markdown-utils.js"; export async function getRunList( beforePath: string, @@ -19,12 +19,9 @@ export async function getRunList( // Read changed files, exclude any files that should be ignored const ignoreFilesWith = ["/examples/", "/quickstart-templates/", "/scenarios/"]; - const changedSpecFiles = (await readFileList(changedFilesPath)).filter((file) => { - // File is in specification/ folder - if (!specification(file)) { - return false; - } + // Changed files should already be filtered to the top-level "specification" folder (see lintdiff-code.yaml) + const changedSpecFiles = (await readFileList(changedFilesPath)).filter((file) => { // File is not ignored for (const ignore of ignoreFilesWith) { if (file.includes(ignore)) { diff --git a/eng/tools/lint-diff/src/runChecks.ts b/eng/tools/lint-diff/src/runChecks.ts index 887ec894c124..ec7cc6807197 100644 --- a/eng/tools/lint-diff/src/runChecks.ts +++ b/eng/tools/lint-diff/src/runChecks.ts @@ -1,11 +1,10 @@ -import { join } from "path"; -import { execNpmExec, isExecError, ExecError } from "@azure-tools/specs-shared/exec"; +import { ExecError, execNpmExec, isExecError } from "@azure-tools/specs-shared/exec"; import { debugLogger } from "@azure-tools/specs-shared/logger"; +import { join } from "path"; -import { getPathToDependency, isFailure } from "./util.js"; -import { AutoRestMessage, AutorestRunResult } from "./lintdiff-types.js"; -import { ReadmeAffectedTags } from "./lintdiff-types.js"; +import { AutoRestMessage, AutorestRunResult, ReadmeAffectedTags } from "./lintdiff-types.js"; import { getOpenapiType } from "./markdown-utils.js"; +import { getPathToDependency, isFailure } from "./util.js"; const MAX_EXEC_BUFFER = 64 * 1024 * 1024; diff --git a/eng/tools/lint-diff/test/correlateResults.test.ts b/eng/tools/lint-diff/test/correlateResults.test.ts index 628cbf7f6951..7f06982fe783 100644 --- a/eng/tools/lint-diff/test/correlateResults.test.ts +++ b/eng/tools/lint-diff/test/correlateResults.test.ts @@ -1,23 +1,23 @@ -import { test, describe, expect } from "vitest"; +import { describe, expect, test } from "vitest"; +import { Readme } from "@azure-tools/specs-shared/readme"; +import { resolve } from "path"; import { - AutorestRunResult, - LintDiffViolation, - Source, - BeforeAfter, -} from "../src/lintdiff-types.js"; -import { + arrayIsEqual, correlateRuns, - getViolations, getLintDiffViolations, - arrayIsEqual, getNewItems, + getViolations, isSameSources, } from "../src/correlateResults.js"; +import { + AutorestRunResult, + BeforeAfter, + LintDiffViolation, + Source, +} from "../src/lintdiff-types.js"; import { relativizePath } from "../src/util.js"; import { isWindows } from "./test-util.js"; -import { Readme } from "@azure-tools/specs-shared/readme"; -import { resolve } from "path"; const __dirname = new URL(".", import.meta.url).pathname; diff --git a/eng/tools/lint-diff/test/generateReport.test.ts b/eng/tools/lint-diff/test/generateReport.test.ts index 3ed6750bf938..cb0f56a32394 100644 --- a/eng/tools/lint-diff/test/generateReport.test.ts +++ b/eng/tools/lint-diff/test/generateReport.test.ts @@ -1,8 +1,9 @@ -import { beforeEach, test, describe, expect, vi } from "vitest"; +import { beforeEach, describe, expect, test, vi } from "vitest"; import { compareLintDiffViolations, generateAutoRestErrorReport, generateLintDiffReport, + getAutoRestFailedMessage, getDocUrl, getFile, getFileLink, @@ -11,11 +12,11 @@ import { iconFor, } from "../src/generateReport.js"; import { - Source, - LintDiffViolation, - BeforeAfter, - AutorestRunResult, AutoRestMessage, + AutorestRunResult, + BeforeAfter, + LintDiffViolation, + Source, } from "../src/lintdiff-types.js"; import { isWindows } from "./test-util.js"; @@ -28,8 +29,8 @@ vi.mock("node:fs/promises", async () => { }; }); -import { readFile } from "fs/promises"; import { Readme } from "@azure-tools/specs-shared/readme"; +import { readFile } from "fs/promises"; vi.mock("../src/util.js", async () => { const original = await vi.importActual("../src/util.js"); @@ -51,6 +52,36 @@ describe("iconFor", () => { }); }); +describe("getAutoRestFailedMessage", () => { + test("returns empty string when result is null", () => { + expect(getAutoRestFailedMessage(null)).toEqual(""); + }); + + test("returns empty string when result has no error", () => { + const result: AutorestRunResult = { + error: null, + rootPath: "", + readme: new Readme("file.md"), + tag: "default", + stdout: "", + stderr: "", + }; + expect(getAutoRestFailedMessage(result)).toEqual(""); + }); + + test("returns 'Autorest Failed' when result has an error", () => { + const result: AutorestRunResult = { + error: new Error("Autorest failed"), + rootPath: "", + readme: new Readme("file.md"), + tag: "default", + stdout: "", + stderr: "", + }; + expect(getAutoRestFailedMessage(result)).toEqual("Autorest Failed"); + }); +}); + describe("getLine", () => { test("returns the line number", () => { const violation = { @@ -465,6 +496,65 @@ describe("generateLintDiffReport", () => { `); }); + test.skipIf(isWindows())( + "passes and displays warning if before has errors", + async ({ expect }) => { + const afterViolation = { + extensionName: "@microsoft.azure/openapi-validator", + level: "warning", + code: "SomeCode", + message: "A warning occurred", + source: [], + details: {}, + }; + + const beforeResult = { + error: new Error("Autorest failed"), + stdout: "", + stderr: "", + rootPath: "", + readme: new Readme("file1.md"), + tag: "", + } as AutorestRunResult; + const afterResult = { + error: null, + stdout: JSON.stringify(afterViolation), + stderr: "", + rootPath: "", + readme: new Readme("file1.md"), + tag: "", + } as AutorestRunResult; + + const runCorrelations = new Map([ + ["file1.md", { before: beforeResult, after: afterResult }], + ]); + + const outFile = "test-output-fatal.md"; + const actual = await generateLintDiffReport( + runCorrelations, + new Set([ + "specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json", + ]), + outFile, + "baseBranch", + "compareSha", + "repo/path", + ); + expect(actual).toBe(true); + expect(await readFile(outFile, { encoding: "utf-8" })).toMatchInlineSnapshot(` + "| Compared specs ([v1.0.0](https://www.npmjs.com/package/@microsoft.azure/openapi-validator/v/1.0.0)) | new version | base version | + | --- | --- | --- | + | default | [default](https://github.com/repo/path/blob/compareSha/file1.md) | [default](https://github.com/repo/path/blob/baseBranch/file1.md) Autorest Failed| + + + > [!WARNING] + > Autorest failed checking before state of file1.md + + " + `); + }, + ); + test.skipIf(isWindows())( "passes if new violations do not include an error (warnings only)", async ({ expect }) => { diff --git a/eng/tools/lint-diff/test/lint-diff.test.ts b/eng/tools/lint-diff/test/lint-diff.test.ts index 8a11bd01418f..c1c3ceca88c1 100644 --- a/eng/tools/lint-diff/test/lint-diff.test.ts +++ b/eng/tools/lint-diff/test/lint-diff.test.ts @@ -1,5 +1,5 @@ import { execa } from "execa"; -import { test, describe, expect } from "vitest"; +import { describe, expect, test } from "vitest"; // TODO: Actual tests describe("e2e", () => { diff --git a/eng/tools/lint-diff/test/markdown-utils.test.ts b/eng/tools/lint-diff/test/markdown-utils.test.ts index e133e72a563f..0e296f7d34da 100644 --- a/eng/tools/lint-diff/test/markdown-utils.test.ts +++ b/eng/tools/lint-diff/test/markdown-utils.test.ts @@ -1,17 +1,17 @@ -import { beforeEach, test, describe, vi, Mock, expect } from "vitest"; import { readFile } from "fs/promises"; import { join } from "node:path"; +import { Mock, beforeEach, describe, expect, test, vi } from "vitest"; import axios from "axios"; +import { Readme } from "@azure-tools/specs-shared/readme"; import { deduplicateTags, - getDocRawUrl, getDefaultTag, + getDocRawUrl, getOpenapiType, getRelatedArmRpcFromDoc, } from "../src/markdown-utils.js"; -import { Readme } from "@azure-tools/specs-shared/readme"; vi.mock("axios"); diff --git a/eng/tools/lint-diff/test/processChanges.fs.test.ts b/eng/tools/lint-diff/test/processChanges.fs.test.ts index f588d66984ae..67426b4399ef 100644 --- a/eng/tools/lint-diff/test/processChanges.fs.test.ts +++ b/eng/tools/lint-diff/test/processChanges.fs.test.ts @@ -1,5 +1,5 @@ -import { afterEach, vi, test, describe, expect } from "vitest"; import { vol } from "memfs"; +import { afterEach, describe, expect, test, vi } from "vitest"; import { readFileList } from "../src/processChanges.js"; diff --git a/eng/tools/lint-diff/test/processChanges.test.ts b/eng/tools/lint-diff/test/processChanges.test.ts index 294f4c3c5de1..7224e4978994 100644 --- a/eng/tools/lint-diff/test/processChanges.test.ts +++ b/eng/tools/lint-diff/test/processChanges.test.ts @@ -1,17 +1,17 @@ -import { test, describe, expect } from "vitest"; +import { describe, expect, test } from "vitest"; +import { ReadmeAffectedTags } from "../src/lintdiff-types.js"; import { + buildState, getAffectedServices, + getChangedSwaggers, getService, reconcileChangedFilesAndTags, - getChangedSwaggers, - buildState, } from "../src/processChanges.js"; -import { ReadmeAffectedTags } from "../src/lintdiff-types.js"; -import { isWindows } from "./test-util.js"; import { Readme } from "@azure-tools/specs-shared/readme"; import { resolve } from "node:path"; +import { isWindows } from "./test-util.js"; describe("getAffectedServices", () => { test.skipIf(isWindows())("returns single service with multiple files", async () => { diff --git a/eng/tools/lint-diff/test/runChecks.test.ts b/eng/tools/lint-diff/test/runChecks.test.ts index c46ae153ddfc..5dd16b881dfc 100644 --- a/eng/tools/lint-diff/test/runChecks.test.ts +++ b/eng/tools/lint-diff/test/runChecks.test.ts @@ -1,4 +1,4 @@ -import { vi, test, describe, beforeEach, expect, Mock } from "vitest"; +import { beforeEach, describe, expect, Mock, test, vi } from "vitest"; vi.mock(import("@azure-tools/specs-shared/exec"), async (importOriginal) => { const actual = await importOriginal(); @@ -24,11 +24,10 @@ vi.mock(import("../src/markdown-utils.js"), async (importOriginal) => { }; }); -import { runChecks, getAutorestErrors } from "../src/runChecks.js"; -import { AutorestRunResult } from "../src/lintdiff-types.js"; import { execNpmExec } from "@azure-tools/specs-shared/exec"; -import { ReadmeAffectedTags } from "../src/lintdiff-types.js"; import { Readme } from "@azure-tools/specs-shared/readme"; +import { AutorestRunResult, ReadmeAffectedTags } from "../src/lintdiff-types.js"; +import { getAutorestErrors, runChecks } from "../src/runChecks.js"; describe("runChecks", () => { beforeEach(() => { diff --git a/eng/tools/lint-diff/test/util.test.ts b/eng/tools/lint-diff/test/util.test.ts index 80e6fde73fff..8cda2857a072 100644 --- a/eng/tools/lint-diff/test/util.test.ts +++ b/eng/tools/lint-diff/test/util.test.ts @@ -1,7 +1,7 @@ -import { test, describe, vi, expect } from "vitest"; import { vol } from "memfs"; -import { pathExists, isFailure, isWarning } from "../src/util.js"; import { beforeEach } from "node:test"; +import { describe, expect, test, vi } from "vitest"; +import { isFailure, isWarning, pathExists } from "../src/util.js"; vi.mock("fs/promises", () => { const memfs = require("memfs"); diff --git a/eng/tools/oav-runner/package.json b/eng/tools/oav-runner/package.json index 0f9a07d527da..8b335c53ef5a 100644 --- a/eng/tools/oav-runner/package.json +++ b/eng/tools/oav-runner/package.json @@ -17,12 +17,13 @@ "dependencies": { "@azure-tools/specs-shared": "file:../../../.github/shared", "js-yaml": "^4.1.0", - "oav": "^3.5.1", + "oav": "^3.6.4", "simple-git": "^3.27.0" }, "devDependencies": { "@types/node": "^20.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, diff --git a/eng/tools/oav-runner/src/cli.ts b/eng/tools/oav-runner/src/cli.ts index a8a3f74e59a7..b61575c8ad59 100644 --- a/eng/tools/oav-runner/src/cli.ts +++ b/eng/tools/oav-runner/src/cli.ts @@ -1,30 +1,17 @@ #!/usr/bin/env node -import { checkSpecs, checkExamples } from "./runner.js"; import { outputAnnotatedErrors, outputErrorSummary, outputSuccessSummary, ReportableOavError, } from "./formatting.js"; +import { checkExamples, checkSpecs } from "./runner.js"; -import { resolve } from "path"; -import { parseArgs, ParseArgsConfig } from "node:util"; +import { getRootFolder } from "@azure-tools/specs-shared/simple-git"; import fs from "node:fs/promises"; -import { simpleGit } from "simple-git"; - -export async function getRootFolder(inputPath: string): Promise { - try { - const gitRoot = await simpleGit(inputPath).revparse("--show-toplevel"); - return resolve(gitRoot.trim()); - } catch (error) { - console.error( - `Error: Unable to determine the root folder of the git repository.`, - `Please ensure you are running this command within a git repository OR providing a targeted directory that is within a git repo.`, - ); - process.exit(1); - } -} +import { parseArgs, ParseArgsConfig } from "node:util"; +import { resolve } from "path"; export async function main() { const config: ParseArgsConfig = { diff --git a/eng/tools/oav-runner/src/runner.ts b/eng/tools/oav-runner/src/runner.ts index a9c4e191a57f..392a8e69b830 100644 --- a/eng/tools/oav-runner/src/runner.ts +++ b/eng/tools/oav-runner/src/runner.ts @@ -1,19 +1,19 @@ #!/usr/bin/env node +import * as fs from "fs"; import * as oav from "oav"; import * as path from "path"; -import * as fs from "fs"; +import { example, getChangedFiles, swagger } from "@azure-tools/specs-shared/changed-files"; //getChangedFiles, import { Swagger } from "@azure-tools/specs-shared/swagger"; -import { includesFolder } from "@azure-tools/specs-shared/path"; -import { getChangedFiles } from "@azure-tools/specs-shared/changed-files"; //getChangedFiles, import { ReportableOavError } from "./formatting.js"; export async function preCheckFiltering( rootDirectory: string, fileList?: string[], ): Promise { - const changedFiles = fileList ?? (await getChangedFiles({ cwd: rootDirectory })); + const changedFiles = + fileList ?? (await getChangedFiles({ cwd: rootDirectory, paths: ["specification"] })); const swaggerFiles = await processFilesToSpecificationList(rootDirectory, changedFiles); @@ -114,9 +114,20 @@ export async function checkSpecs( async function getFiles(rootDirectory: string, directory: string): Promise { const target = path.join(rootDirectory, directory); - const items = await fs.promises.readdir(target, { - withFileTypes: true, - }); + + let items: fs.Dirent[]; + try { + items = await fs.promises.readdir(target, { + withFileTypes: true, + }); + } catch (error) { + if ((error as NodeJS.ErrnoException)?.code === "ENOENT") { + console.log(`Skipping deleted directory '${target}'`); + return []; + } else { + throw error; + } + } return items .filter((d) => d.isFile() && d.name.endsWith(".json")) @@ -125,24 +136,6 @@ async function getFiles(rootDirectory: string, directory: string): Promise d.includes("specification" + path.sep)); } -function example(file: string): boolean { - return ( - typeof file === "string" && - file.toLowerCase().endsWith(".json") && - includesFolder(file, "examples") - ); -} - -function swagger(file: string): boolean { - return ( - typeof file === "string" && - file.toLowerCase().endsWith(".json") && - (includesFolder(file, "data-plane") || includesFolder(file, "resource-manager")) && - includesFolder(file, "specification") && - !includesFolder(file, "examples") - ); -} - export async function processFilesToSpecificationList( rootDirectory: string, files: string[], @@ -154,10 +147,6 @@ export async function processFilesToSpecificationList( // files from get-changed-files are relative to the root of the repo, // though that context is passed into this from cli arguments. for (const file of files) { - if (!file.startsWith("specification/")) { - continue; - } - const absoluteFilePath = path.join(rootDirectory, file); // if the file is an example, we need to find the swagger file that references it diff --git a/eng/tools/oav-runner/test/cli.test.ts b/eng/tools/oav-runner/test/cli.test.ts deleted file mode 100644 index 34ac9b9d93d9..000000000000 --- a/eng/tools/oav-runner/test/cli.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, it, expect, vi } from "vitest"; -import { getRootFolder } from "../src/cli.js"; -import path from "path"; - -const REPOROOT = path.resolve(__dirname, "..", "..", "..", ".."); - -describe("invocation directory checks", () => { - it("Should return the same path when invoked from the root of a git repo.", async () => { - const result = await getRootFolder(REPOROOT); - expect(result).toBe(REPOROOT); - }); - - it("Should return a higher path when invoked from a path deep in a git repo.", async () => { - const result = await getRootFolder(path.join(REPOROOT, "eng", "tools", "oav-runner")); - expect(result).toBe(REPOROOT); - }); - - it("Should exit with error when invoked outside of a git directory.", async () => { - const pathOutsideRepo = path.resolve(path.join(REPOROOT, "..")); - - const exitMock = vi - .spyOn(process, "exit") - .mockImplementation((code?: string | number | null | undefined) => { - throw new Error(`Exit ${code}`); - }); - - await expect(getRootFolder(pathOutsideRepo)).rejects.toThrow("Exit 1"); - - exitMock.mockRestore(); - }); -}); diff --git a/eng/tools/oav-runner/test/runner.test.ts b/eng/tools/oav-runner/test/runner.test.ts index a01e211babfe..cab717cf6ec1 100644 --- a/eng/tools/oav-runner/test/runner.test.ts +++ b/eng/tools/oav-runner/test/runner.test.ts @@ -1,6 +1,6 @@ -import { describe, it, expect } from "vitest"; -import { processFilesToSpecificationList } from "../src/runner.js"; import path from "path"; +import { describe, expect, it } from "vitest"; +import { processFilesToSpecificationList } from "../src/runner.js"; const ROOT = path.resolve(__dirname, "..", "test", "fixtures"); @@ -59,11 +59,19 @@ describe("file processing", () => { it("should handle deleted files without error", async () => { const changedFiles = [ - "specification/serviceB/data-plane/service.B/stable/2025-06-01/serviceBspec.json", - // non-existent file. Should not throw and quietly omit + // existing spec + "specification/serviceA/resource-manager/service.A/stable/2025-06-01/serviceAspec.json", + // existing example + "specification/serviceB/data-plane/service.B/stable/2025-06-01/examples/CreateResource.json", + // non-existent spec. Should not throw and quietly omit "specification/serviceB/data-plane/service.B/stable/2025-06-01/serviceBspecDeleted.json", + // non-existent example. Should not throw and quietly omit + "specification/serviceB/data-plane/service.B/stable/2025-06-01/examples/CreateResourceDeleted.json", + // non-existent example and containing version folder. Should not throw and quietly omit + "specification/serviceB/data-plane/service.B/stable/2025-06-01-deleted/examples/CreateResource.json", ]; const expected = [ + "specification/serviceA/resource-manager/service.A/stable/2025-06-01/serviceAspec.json", "specification/serviceB/data-plane/service.B/stable/2025-06-01/serviceBspec.json", ]; diff --git a/eng/tools/openapi-diff-runner/package.json b/eng/tools/openapi-diff-runner/package.json index b7fd0e0a19d4..e12cb0555d0a 100644 --- a/eng/tools/openapi-diff-runner/package.json +++ b/eng/tools/openapi-diff-runner/package.json @@ -25,6 +25,7 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" } diff --git a/eng/tools/openapi-diff-runner/src/command-helpers.ts b/eng/tools/openapi-diff-runner/src/command-helpers.ts index d3d45c17d78e..f6cd01d0bff5 100644 --- a/eng/tools/openapi-diff-runner/src/command-helpers.ts +++ b/eng/tools/openapi-diff-runner/src/command-helpers.ts @@ -1,17 +1,17 @@ +import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; +import { getChangedFilesStatuses, swagger } from "@azure-tools/specs-shared/changed-files"; +import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import path from "node:path"; -import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from "node:fs"; +import { logError, LogLevel, logMessage, setOutput } from "./log.js"; import { + BreakingChangeReviewRequiredLabel, BreakingChangesCheckType, Context, - BreakingChangeReviewRequiredLabel, VersioningReviewRequiredLabel, } from "./types/breaking-change.js"; import { ResultMessageRecord } from "./types/message.js"; import { createOadMessageProcessor } from "./utils/oad-message-processor.js"; import { createPullRequestProperties } from "./utils/pull-request.js"; -import { getChangedFilesStatuses, swagger } from "@azure-tools/specs-shared/changed-files"; -import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; -import { logError, LogLevel, logMessage, setOutput } from "./log.js"; /** * Interface for parsed CLI arguments @@ -74,6 +74,7 @@ function getBreakingChangeCheckName(runType: BreakingChangesCheckType): string { /** * Output the breaking change labels as GitHub Actions environment variables. * This function checks the BreakingChangeLabelsToBeAdded set and sets the appropriate outputs. + * The doc describing breaking change review rules can be found here: https://aka.ms/brch-dev */ export function outputBreakingChangeLabelVariables(): void { // Output the breaking change labels as GitHub Actions environment variables @@ -89,19 +90,24 @@ export function outputBreakingChangeLabelVariables(): void { logMessage("'BreakingChangeReviewRequired' label needs to be added."); setOutput("breakingChangeReviewLabelName", BreakingChangeReviewRequiredLabel); setOutput("breakingChangeReviewLabelValue", "true"); + logMessage("'VersioningReviewRequired' label needs to be deleted."); + setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); + setOutput("versioningReviewLabelValue", "false"); } else { logMessage("'BreakingChangeReviewRequired' label needs to be deleted."); setOutput("breakingChangeReviewLabelName", BreakingChangeReviewRequiredLabel); setOutput("breakingChangeReviewLabelValue", "false"); - } - if (BreakingChangeLabelsToBeAdded.has(VersioningReviewRequiredLabel)) { - logMessage("'VersioningReviewRequired' label needs to be added."); - setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); - setOutput("versioningReviewLabelValue", "true"); - } else { - logMessage("'VersioningReviewRequired' label needs to be deleted."); - setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); - setOutput("versioningReviewLabelValue", "false"); + + // versioning review label is only set if the breaking change review label is not set + if (BreakingChangeLabelsToBeAdded.has(VersioningReviewRequiredLabel)) { + logMessage("'VersioningReviewRequired' label needs to be added."); + setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); + setOutput("versioningReviewLabelValue", "true"); + } else { + logMessage("'VersioningReviewRequired' label needs to be deleted."); + setOutput("versioningReviewLabelName", VersioningReviewRequiredLabel); + setOutput("versioningReviewLabelValue", "false"); + } } } } @@ -133,6 +139,7 @@ export async function getSwaggerDiffs( baseCommitish: options.baseCommitish, cwd: options.cwd, headCommitish: options.headCommitish, + paths: ["specification"], }); // Filter each array to only include Swagger files using the swagger filter from changed-files.js diff --git a/eng/tools/openapi-diff-runner/src/commands.ts b/eng/tools/openapi-diff-runner/src/commands.ts index c974d53638f8..0dea61b5a09a 100644 --- a/eng/tools/openapi-diff-runner/src/commands.ts +++ b/eng/tools/openapi-diff-runner/src/commands.ts @@ -3,30 +3,31 @@ * i.e. it is invoked by files with depth 0 and invokes files with depth 2. */ -import { RawMessageRecord, ResultMessageRecord } from "./types/message.js"; +import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; import { existsSync } from "node:fs"; import * as path from "node:path"; -import { createOadTrace, setOadBaseBranch, generateOadMarkdown } from "./types/oad-types.js"; -import { - createBreakingChangeDetectionContext, - checkBreakingChangeOnSameVersion, - checkCrossVersionBreakingChange, -} from "./detect-breaking-change.js"; -import { Context } from "./types/breaking-change.js"; import { - getSwaggerDiffs, changeBaseBranch, - logFullOadMessagesList, - createDummySwagger, cleanDummySwagger, - isSameVersionBreakingType, + createDummySwagger, getCreatedDummySwaggerCount, + getSwaggerDiffs, + isSameVersionBreakingType, + logFullOadMessagesList, outputBreakingChangeLabelVariables, } from "./command-helpers.js"; +import { + checkBreakingChangeOnSameVersion, + checkCrossVersionBreakingChange, + createBreakingChangeDetectionContext, +} from "./detect-breaking-change.js"; import { generateBreakingChangeResultSummary } from "./generate-report.js"; import { LOG_PREFIX, logMessage } from "./log.js"; +import { Context } from "./types/breaking-change.js"; +import { RawMessageRecord, ResultMessageRecord } from "./types/message.js"; +import { createOadTrace, generateOadMarkdown, setOadBaseBranch } from "./types/oad-types.js"; +import { checkPrTargetsProductionBranch } from "./utils/common-utils.js"; import { appendMarkdownToLog } from "./utils/oad-message-processor.js"; -import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; /** * The function validateBreakingChange() is executed with type SameVersion or CrossVersion @@ -170,15 +171,21 @@ export async function validateBreakingChange(context: Context): Promise ({ msgs, runtimeErrors, oadViolationsCnt, errorCnt } = await checkCrossVersionBreakingChange(detectionContext)); } - const comparedSpecsTableContent = generateOadMarkdown(detectionContext.oadTracer); + const comparedSpecsTableContent = await generateOadMarkdown(detectionContext.oadTracer); // Log the markdown content to the pipeline log file if (comparedSpecsTableContent) { appendMarkdownToLog(context.oadMessageProcessorContext, comparedSpecsTableContent); } - // process breaking change labels - outputBreakingChangeLabelVariables(); + // output breaking change label variables only when the PR targets a production branch + logMessage( + `Evaluate breaking change labels: targetRepo: ${context.targetRepo}, ` + + `targetBranch: ${context.prInfo!.targetBranch}`, + ); + if (checkPrTargetsProductionBranch(context.targetRepo, context.prInfo!.targetBranch)) { + outputBreakingChangeLabelVariables(); + } // If exitCode is already defined and non-zero, we do not interfere with its value here. if (process.exitCode === undefined || process.exitCode === 0) { @@ -195,17 +202,14 @@ export async function validateBreakingChange(context: Context): Promise // - If there are messages from OAD (openapi-diff) denoting violations, but none // of them resulted in adding any breaking changes labels. // This is why we do not include 'oadViolationsCnt' in this formula at all. - // Instead, we rely on 'labelsAddedCount'. - // See https://github.com/Azure/azure-sdk-tools/issues/6396 // - If there are errors, but they are only warning-level. This happens when comparing // to previous preview version. In such cases, these errors are not included in the 'errorCnt' at all. - //process.exitCode = labelsAddedCount > 0 || errorCnt > 0 ? 1 : 0; process.exitCode = errorCnt > 0 ? 1 : 0; } logMessage( `${LOG_PREFIX}validateBreakingChange: prUrl: ${context.prUrl}, ` + - `comparisonType: ${context.runType}, labelsAddedCount: , ` + + `comparisonType: ${context.runType},` + `errorCnt: ${errorCnt}, oadViolationsCnt: ${oadViolationsCnt}, ` + `process.exitCode: ${process.exitCode}`, ); diff --git a/eng/tools/openapi-diff-runner/src/detect-breaking-change.ts b/eng/tools/openapi-diff-runner/src/detect-breaking-change.ts index f5619e303c1f..b56dee2903c9 100644 --- a/eng/tools/openapi-diff-runner/src/detect-breaking-change.ts +++ b/eng/tools/openapi-diff-runner/src/detect-breaking-change.ts @@ -17,6 +17,12 @@ );pth 2, * i.e. it is invoked by files with depth 1 and invokes files with depth 3. */ +import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; +import { SpecModel } from "@azure-tools/specs-shared/spec-model"; +import { appendFileSync, existsSync } from "node:fs"; +import * as path from "node:path"; +import { logError, LogLevel, logMessage } from "./log.js"; +import { runOad } from "./run-oad.js"; import { ApiVersionLifecycleStage, BreakingChangesCheckType, @@ -24,6 +30,8 @@ import { logFileName, } from "./types/breaking-change.js"; import { RawMessageRecord, ResultMessageRecord } from "./types/message.js"; +import { addOadTrace, OadMessage, OadTraceData } from "./types/oad-types.js"; +import { applyRules } from "./utils/apply-rules.js"; import { blobHref, branchHref, @@ -32,16 +40,12 @@ import { processOadRuntimeErrorMessage, specIsPreview, } from "./utils/common-utils.js"; -import { appendFileSync } from "node:fs"; -import * as path from "node:path"; -import { applyRules } from "./utils/apply-rules.js"; -import { OadMessage, OadTraceData, addOadTrace } from "./types/oad-types.js"; -import { runOad } from "./run-oad.js"; import { processAndAppendOadMessages } from "./utils/oad-message-processor.js"; -import { getExistedVersionOperations, getPrecedingSwaggers } from "./utils/spec.js"; -import { logError, LogLevel, logMessage } from "./log.js"; -import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; -import { SpecModel } from "@azure-tools/specs-shared/spec-model"; +import { + deduplicateSwaggers, + getExistedVersionOperations, + getPrecedingSwaggers, +} from "./utils/spec.js"; // We want to display some lines as we improved AutoRest v2 error output since March 2024 to provide multi-line error messages, e.g.: // https://github.com/Azure/autorest/pull/4934 @@ -49,9 +53,6 @@ import { SpecModel } from "@azure-tools/specs-shared/spec-model"; // The value here is an arbitrary high number to limit the stack trace in case a bug would cause it to be excessively long. const stackTraceMaxLength = 500; -// Module-level cache for SpecModel instances -const specModelCache = new Map(); - /** * Context for breaking change detection operations */ @@ -168,6 +169,7 @@ export async function checkCrossVersionBreakingChange( .concat(detectionContext.newVersionChangedSwaggers) .concat(detectionContext.existingVersionSwaggers.filter(isInDevFolder))) { logMessage(`Processing swaggerPath: ${swaggerPath}`, LogLevel.Group); + // use the detectionContext.context.localSpecRepoPath to resolve the absolute path as it's the merge commit working directory const absoluteSwaggerPath = path.resolve( detectionContext.context.localSpecRepoPath, @@ -178,11 +180,20 @@ export async function checkCrossVersionBreakingChange( detectionContext.context.prInfo!.tempRepoFolder, swaggerPath, ); - const availableSwaggers = await specModel.getSwaggers(); + + // If the specModel is not found, it means the swaggerPath is a new RP + if (!specModel) { + continue; + } + + const originalReturnedSwaggers = await specModel.getSwaggers(); + const availableSwaggers = deduplicateSwaggers(originalReturnedSwaggers); logMessage( `checkCrossVersionBreakingChange: swaggerPath: ${swaggerPath}, availableSwaggers.length: ${availableSwaggers?.length}`, ); - const previousVersions = await getPrecedingSwaggers(swaggerPath, availableSwaggers); + + // use absoluteSwaggerPath as it will need to it will fall back to use the version info in the swagger content + const previousVersions = await getPrecedingSwaggers(absoluteSwaggerPath, availableSwaggers); logMessage( `checkCrossVersionBreakingChange: previousVersions: ${JSON.stringify(previousVersions)}`, ); @@ -351,35 +362,49 @@ export function isInDevFolder(swaggerPath: string) { } /** + * Find the path to the closest readme.md file based on the input file path, + * searching upward from the file's directory up to the 'resource-manager' or 'data-plane' sub path. * Example: * input: specification/network/resource-manager/Microsoft.Network/stable/2019-11-01/network.json - * returns: specification/network/resource-manager + * returns: path to the directory containing the closest readme.md file, up to specification/network/resource-manager */ export function getReadmeFolder(swaggerFile: string) { const segments = swaggerFile.split(/\\|\//); - if (segments && segments.length >= 3) { - // Handle dev folder conversion - if (segments[0] === "dev") { - segments[0] = "specification"; - } + if (!segments || segments.length < 3) { + return undefined; + } - // Look for "resource-manager" or "data-plane" in the path - const resourceManagerIndex = segments.findIndex((segment) => segment === "resource-manager"); - if (resourceManagerIndex !== -1) { - return segments.slice(0, resourceManagerIndex + 1).join("/"); - } + // Handle dev folder conversion + if (segments[0] === "dev") { + segments[0] = "specification"; + } + + // Find the boundary index (resource-manager or data-plane) + const resourceManagerIndex = segments.findIndex((segment) => segment === "resource-manager"); + const dataPlaneIndex = segments.findIndex((segment) => segment === "data-plane"); + const boundaryIndex = resourceManagerIndex !== -1 ? resourceManagerIndex : dataPlaneIndex; + + // Determine search range: from file's parent directory up to boundary (or root if no boundary found) + const startIndex = segments.length - 2; // Start from parent directory of the file + const minIndex = boundaryIndex !== -1 ? boundaryIndex : 2; // Stop at boundary or at least keep first 3 segments + + // Search upward for readme.md + for (let i = startIndex; i >= minIndex; i--) { + const currentPath = segments.slice(0, i + 1).join(path.sep); + const readmePath = path.join(currentPath, "readme.md"); - const dataPlaneIndex = segments.findIndex((segment) => segment === "data-plane"); - if (dataPlaneIndex !== -1) { - return segments.slice(0, dataPlaneIndex + 1).join("/"); + if (existsSync(readmePath)) { + return currentPath; } + } - // Default: return first 3 segments - return segments.slice(0, 3).join("/"); + // Fallback: return up to boundary if found, otherwise first 3 segments + if (boundaryIndex !== -1) { + return segments.slice(0, boundaryIndex + 1).join(path.sep); } - return undefined; + return segments.slice(0, 3).join(path.sep); } /** @@ -387,26 +412,55 @@ export function getReadmeFolder(swaggerFile: string) { * Uses caching to avoid redundant SpecModel initialization for the same folder. * @param specRepoFolder - The root folder of the spec repository * @param swaggerPath - Path to the swagger file - * @returns SpecModel instance for the folder containing the swagger file + * @returns SpecModel instance for the folder containing the swagger file or undefined if the folder does not exist */ -export function getSpecModel(specRepoFolder: string, swaggerPath: string): SpecModel { +export function getSpecModel(specRepoFolder: string, swaggerPath: string): SpecModel | undefined { const folder = getReadmeFolder(swaggerPath); if (!folder) { throw new Error(`Could not determine readme folder for swagger path: ${swaggerPath}`); } - const fullFolderPath = path.join(specRepoFolder, folder); - logMessage(`getSpecModel: folder: ${fullFolderPath}, swaggerPath: ${swaggerPath}`); + let isNewRp = false; + let fullFolderPath = path.join(specRepoFolder, folder); + if (!existsSync(fullFolderPath)) { + isNewRp = true; + } + + // If the initial folder doesn't exist, search upward for a folder with readme.md + while ( + isNewRp && + !fullFolderPath.endsWith("resource-manager") && + !fullFolderPath.endsWith("data-plane") + ) { + const parent = path.dirname(fullFolderPath); + + // Prevent infinite loop if we reach the root + if (parent === fullFolderPath) { + break; + } - // Check if we already have a SpecModel for this folder - if (specModelCache.has(fullFolderPath)) { - return specModelCache.get(fullFolderPath)!; + fullFolderPath = parent; + const readmePath = path.join(fullFolderPath, "readme.md"); + + // If we find a readme.md, use this folder + if (existsSync(readmePath)) { + isNewRp = false; + break; + } + } + + // Return if the readme folder is not found which means it's a new RP + if (isNewRp) { + logMessage( + `getSpecModel: this is a new RP as ${fullFolderPath} folder does not exist in the base branch of spec repo.`, + ); + return undefined; } + logMessage(`getSpecModel: folder: ${fullFolderPath}, swaggerPath: ${swaggerPath}`); // Create new SpecModel and cache it const specModel = new SpecModel(fullFolderPath); - specModelCache.set(fullFolderPath, specModel); return specModel; } @@ -419,6 +473,9 @@ export async function checkAPIsBeingMovedToANewSpec( const absoluteSwaggerPath = path.resolve(context.localSpecRepoPath, swaggerPath); logMessage(`checkAPIsBeingMovedToANewSpec: absoluteSwaggerPath: ${absoluteSwaggerPath}`); const specModel = getSpecModel(context.localSpecRepoPath, swaggerPath); + if (!specModel) { + return; + } const swaggersFromOriginalClonedRepo = await specModel.getSwaggers(); const targetSwagger = swaggersFromOriginalClonedRepo.find((s) => s.path === absoluteSwaggerPath); if (!targetSwagger) { @@ -428,7 +485,9 @@ export async function checkAPIsBeingMovedToANewSpec( return; } const targetOperations = await targetSwagger.getOperations(); - const movedApis = await getExistedVersionOperations(swaggerPath, availableSwaggers, [ + + // use absoluteSwaggerPath as it will need to it will fall back to use the version info in the swagger content + const movedApis = await getExistedVersionOperations(absoluteSwaggerPath, availableSwaggers, [ ...targetOperations.values(), ]); logMessage( diff --git a/eng/tools/openapi-diff-runner/src/generate-report.ts b/eng/tools/openapi-diff-runner/src/generate-report.ts index 502f3904b5af..eaa4213aa4f6 100644 --- a/eng/tools/openapi-diff-runner/src/generate-report.ts +++ b/eng/tools/openapi-diff-runner/src/generate-report.ts @@ -1,3 +1,5 @@ +import { addToSummary, logMessage, logWarning } from "./log.js"; +import { ApiVersionLifecycleStage, Context } from "./types/breaking-change.js"; import { BrChMsgRecord, getKey, @@ -5,14 +7,12 @@ import { RawMessageRecord, ResultMessageRecord, } from "./types/message.js"; -import { ApiVersionLifecycleStage, Context } from "./types/breaking-change.js"; import { BreakingChangeMdReport, createBreakingChangeMdReport, reportToString, sortBreakingChangeMdReports, } from "./utils/markdown-report.js"; -import { addToSummary, logMessage, logWarning } from "./log.js"; // Per the GitHub documentation [1], the length limit of a check pane is 65535 characters. // While not immediately obvious, it looks like the 65535 limit applies to the total length of the text and summary, @@ -251,8 +251,8 @@ function getSummaryData( return ( summaryTitle + summaryDataSuppressionAndDetailsText + - `> [!IMPORTANT]\n` + - `> Browse to the job logs to see the details.\n` + `\n\n> [!IMPORTANT]\n` + + `> Browse to the job logs to see the details.\n\n` ); } diff --git a/eng/tools/openapi-diff-runner/src/index.ts b/eng/tools/openapi-diff-runner/src/index.ts index d42c933ef086..3b22994fa212 100644 --- a/eng/tools/openapi-diff-runner/src/index.ts +++ b/eng/tools/openapi-diff-runner/src/index.ts @@ -1,17 +1,17 @@ -import { exit } from "node:process"; -import { parseArgs, type ParseArgsConfig } from "node:util"; +import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; +import { existsSync, mkdirSync } from "node:fs"; import path from "node:path"; +import { exit } from "node:process"; import { fileURLToPath } from "node:url"; -import { existsSync, mkdirSync } from "node:fs"; -import { validateBreakingChange } from "./commands.js"; +import { parseArgs, type ParseArgsConfig } from "node:util"; import { buildPrInfo, createContextFromParsedArgs, type ParsedCliArguments, } from "./command-helpers.js"; +import { validateBreakingChange } from "./commands.js"; import { logError, logMessage } from "./log.js"; import { BreakingChangesCheckType } from "./types/breaking-change.js"; -import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; const __filename: string = fileURLToPath(import.meta.url); const __dirname: string = path.dirname(__filename); @@ -173,5 +173,11 @@ export async function main() { await buildPrInfo(context); let statusCode = 0; statusCode = await validateBreakingChange(context); + + if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) { + logMessage( + `See validation report summary at: ${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`, + ); + } exit(statusCode); } diff --git a/eng/tools/openapi-diff-runner/src/run-oad.ts b/eng/tools/openapi-diff-runner/src/run-oad.ts index b4d19e390080..7cdf896fd6ba 100644 --- a/eng/tools/openapi-diff-runner/src/run-oad.ts +++ b/eng/tools/openapi-diff-runner/src/run-oad.ts @@ -5,8 +5,8 @@ * i.e. it is invoked by files with depth 2. */ import * as oad from "@azure/oad"; -import { OadMessage } from "./types/oad-types.js"; import { logMessage, logMessageSafe } from "./log.js"; +import { OadMessage } from "./types/oad-types.js"; /** * The runOad() function is a wrapper around the "@azure/oad" library whose source is https://github.com/Azure/openapi-diff. diff --git a/eng/tools/openapi-diff-runner/src/types/breaking-change.ts b/eng/tools/openapi-diff-runner/src/types/breaking-change.ts index 409b53e16a01..da3be82f8ded 100644 --- a/eng/tools/openapi-diff-runner/src/types/breaking-change.ts +++ b/eng/tools/openapi-diff-runner/src/types/breaking-change.ts @@ -1,11 +1,11 @@ -import { OadMessageProcessorContext } from "../utils/oad-message-processor.js"; -import { PullRequestProperties } from "../utils/pull-request.js"; import { - VERSIONING_APPROVALS, BREAKING_CHANGE_APPROVALS, - REVIEW_REQUIRED_LABELS, BREAKING_CHANGES_CHECK_TYPES, + REVIEW_REQUIRED_LABELS, + VERSIONING_APPROVALS, } from "@azure-tools/specs-shared/breaking-change"; +import { OadMessageProcessorContext } from "../utils/oad-message-processor.js"; +import { PullRequestProperties } from "../utils/pull-request.js"; /** * This file contains types used by the OpenAPI specification breaking change checks diff --git a/eng/tools/openapi-diff-runner/src/types/oad-types.ts b/eng/tools/openapi-diff-runner/src/types/oad-types.ts index 4c670a7af3b5..ad0ce6ef606f 100644 --- a/eng/tools/openapi-diff-runner/src/types/oad-types.ts +++ b/eng/tools/openapi-diff-runner/src/types/oad-types.ts @@ -8,14 +8,16 @@ * - "[Breaking Change][PR Workflow] Use more granular labels for Breaking Changes approvals" * https://github.com/Azure/azure-sdk-tools/issues/6374 */ -import { basename } from "node:path"; -import { getVersionFromInputFile, specificBranchHref } from "../utils/common-utils.js"; -import { MessageLevel } from "./message.js"; -import { sourceBranchHref } from "../utils/common-utils.js"; -import { ApiVersionLifecycleStage, Context } from "./breaking-change.js"; import { readFileSync } from "node:fs"; +import { basename, dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import { dirname, join } from "node:path"; +import { + getVersionFromInputFile, + sourceBranchHref, + specificBranchHref, +} from "../utils/common-utils.js"; +import { ApiVersionLifecycleStage, Context } from "./breaking-change.js"; +import { MessageLevel } from "./message.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -92,7 +94,7 @@ export const setOadBaseBranch = (traceData: OadTraceData, branchName: string): O /** * Generates markdown content from OAD trace data */ -export const generateOadMarkdown = (traceData: OadTraceData): string => { +export const generateOadMarkdown = async (traceData: OadTraceData): Promise => { const oadVersion = packageJson.dependencies?.["@azure/oad"]?.replace(/[\^~]/, "") || "unknown"; if (traceData.traces.length === 0) { return ""; @@ -106,13 +108,13 @@ export const generateOadMarkdown = (traceData: OadTraceData): string => { for (const value of traceData.traces) { // Compose each column for clarity const newFileName = basename(value.new); - const newVersion = getVersionFromInputFile(value.new, true); + const newVersion = await getVersionFromInputFile(value.new, true); // Truncate commit hash to first 8 characters for better readability const shortCommit = traceData.context.headCommit.substring(0, 8); const newCommitLink = `[${shortCommit}](${sourceBranchHref(traceData.context.sourceRepo, traceData.context.headCommit, value.new)})`; - const oldVersion = getVersionFromInputFile(value.old, true); + const oldVersion = await getVersionFromInputFile(value.old, true); const oldCommitLink = `[${traceData.baseBranch}](${specificBranchHref(traceData.context.targetRepo, value.old, traceData.baseBranch)})`; // Add a row to the markdown table with proper spacing diff --git a/eng/tools/openapi-diff-runner/src/utils/apply-rules.ts b/eng/tools/openapi-diff-runner/src/utils/apply-rules.ts index ad19637cf1d9..cf44c6e61445 100644 --- a/eng/tools/openapi-diff-runner/src/utils/apply-rules.ts +++ b/eng/tools/openapi-diff-runner/src/utils/apply-rules.ts @@ -4,6 +4,7 @@ * In the "breakingChanges directory invocation depth" this file has depth 3 * i.e. it is invoked by files with depth 2. */ +import { BreakingChangeLabelsToBeAdded } from "../command-helpers.js"; import { ApiVersionLifecycleStage, BreakingChangesCheckType, @@ -11,16 +12,15 @@ import { VersioningReviewRequiredLabel, } from "../types/breaking-change.js"; import { OadMessage } from "../types/oad-types.js"; -import { BreakingChangeLabelsToBeAdded } from "../command-helpers.js"; +import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; +import { logMessage, logWarning } from "../log.js"; import { OadMessageRule, fallbackLabel, fallbackRule as fallbackOadMessageRule, oadMessagesRuleMap, } from "./oad-rule-map.js"; -import { logMessage, logWarning } from "../log.js"; -import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; /** * The function applyRules() applies oadMessagesRuleMap to OAD messages returned by runOad(). diff --git a/eng/tools/openapi-diff-runner/src/utils/common-utils.ts b/eng/tools/openapi-diff-runner/src/utils/common-utils.ts index d917e23eff3b..68c6ec903d29 100644 --- a/eng/tools/openapi-diff-runner/src/utils/common-utils.ts +++ b/eng/tools/openapi-diff-runner/src/utils/common-utils.ts @@ -1,5 +1,6 @@ -import { FilePosition } from "../types/message.js"; +import { readFile } from "node:fs/promises"; import { Context } from "../types/breaking-change.js"; +import { FilePosition } from "../types/message.js"; export function blobHref(repo: string, sha: string, file: string): string { return `https://github.com/${repo}/blob/${sha}/${file}`; @@ -81,7 +82,10 @@ export function specificBranchHref( ); } -export function getVersionFromInputFile(filePath: string, withPreview = false): string { +export async function getVersionFromInputFile( + filePath: string, + withPreview = false, +): Promise { const apiVersionRegex = /^\d{4}-\d{2}-\d{2}(|-preview|-privatepreview|-alpha|-beta|-rc)$/; // Normalize path separators to forward slashes for consistent processing @@ -111,16 +115,19 @@ export function getVersionFromInputFile(filePath: string, withPreview = false): } } - // If no regex match found, return the immediate folder name (parent directory of the file) - if (segments && segments.length > 1) { - // Get the folder name that contains the file (last segment before filename) - const folderName = segments[segments.length - 2]; - if (folderName) { - return folderName; - } + let version = ""; + // If no regex match found, try to read version from file content + try { + const fileContent = await readFile(filePath, "utf8"); + const parsedContent = JSON.parse(fileContent); + version = parsedContent?.info?.version; + } catch (error) { + throw new Error(`Failed to read version from file:${filePath}, cause: ${error}`); } - - return ""; + if (!version) { + throw new Error(`Version not found in file: ${filePath}`); + } + return version; } export function getArgumentValue(args: string[], flag: string, defaultValue: string): string { @@ -226,3 +233,20 @@ export function convertRawErrorToUnifiedMsg( }; return JSON.stringify(result); } + +/** + * Check if the PR targets a production branch. + * The production branches are defined at https://aka.ms/azsdk/pr-brch-deep + * @param targetRepo The repository name, e.g. "azure-rest-api-specs" or "azure-rest-api-specs-pr". + * @param targetBranch The target branch of the PR. + * @returns {boolean} True if the PR targets a production branch, false otherwise. + */ +export function checkPrTargetsProductionBranch(targetRepo: string, targetBranch: string): boolean { + const targetsPublicProductionBranch = + targetRepo.endsWith("azure-rest-api-specs") && targetBranch === "main"; + + const targetsPrivateProductionBranch = + targetRepo.endsWith("azure-rest-api-specs-pr") && targetBranch === "RPSaaSMaster"; + + return targetsPublicProductionBranch || targetsPrivateProductionBranch; +} diff --git a/eng/tools/openapi-diff-runner/src/utils/markdown-report.ts b/eng/tools/openapi-diff-runner/src/utils/markdown-report.ts index 8679b3c13eeb..2680f39a5c78 100644 --- a/eng/tools/openapi-diff-runner/src/utils/markdown-report.ts +++ b/eng/tools/openapi-diff-runner/src/utils/markdown-report.ts @@ -1,11 +1,11 @@ +import { LogLevel, logMessage } from "../log.js"; +import { BrChMsgRecord, MessageLevel, ResultMessageRecord, getKey } from "../types/message.js"; import { createBreakingChangeMdRows, - getMdTableHeader, getDeficitRow, + getMdTableHeader, rowToString, } from "./markdown-report-row.js"; -import { BrChMsgRecord, MessageLevel, ResultMessageRecord, getKey } from "../types/message.js"; -import { logMessage, LogLevel } from "../log.js"; /** * Represents a markdown report for breaking change violations diff --git a/eng/tools/openapi-diff-runner/src/utils/oad-message-processor.ts b/eng/tools/openapi-diff-runner/src/utils/oad-message-processor.ts index cad206cc9ae6..0e753463307e 100644 --- a/eng/tools/openapi-diff-runner/src/utils/oad-message-processor.ts +++ b/eng/tools/openapi-diff-runner/src/utils/oad-message-processor.ts @@ -1,11 +1,10 @@ -import path from "node:path"; import fs from "node:fs"; -import { OadMessage } from "../types/oad-types.js"; +import path from "node:path"; +import { logMessage, logMessageSafe } from "../log.js"; +import { Context, logFileName } from "../types/breaking-change.js"; import { JsonPath, MessageLevel, ResultMessageRecord } from "../types/message.js"; +import { OadMessage } from "../types/oad-types.js"; import { sourceBranchHref, specificBranchHref } from "./common-utils.js"; -import { logFileName } from "../types/breaking-change.js"; -import { logMessage, logMessageSafe } from "../log.js"; -import { Context } from "../types/breaking-change.js"; /** * Context for OAD message processing operations diff --git a/eng/tools/openapi-diff-runner/src/utils/oad-rule-map.ts b/eng/tools/openapi-diff-runner/src/utils/oad-rule-map.ts index d1ddd9caa14d..0d3492943ade 100644 --- a/eng/tools/openapi-diff-runner/src/utils/oad-rule-map.ts +++ b/eng/tools/openapi-diff-runner/src/utils/oad-rule-map.ts @@ -1,6 +1,6 @@ +import { BreakingChangesCheckType, ReviewRequiredLabel } from "../types/breaking-change.js"; import { MessageLevel } from "../types/message.js"; import { OadRuleCode } from "../types/oad-types.js"; -import { BreakingChangesCheckType, ReviewRequiredLabel } from "../types/breaking-change.js"; /** * This oadMessagesRuleMap is applied by applyRules() function, invoked by BreakingChangeDetector, diff --git a/eng/tools/openapi-diff-runner/src/utils/pull-request.ts b/eng/tools/openapi-diff-runner/src/utils/pull-request.ts index 8dc6fb85423c..b02821c5611b 100644 --- a/eng/tools/openapi-diff-runner/src/utils/pull-request.ts +++ b/eng/tools/openapi-diff-runner/src/utils/pull-request.ts @@ -1,8 +1,8 @@ import { existsSync, mkdirSync } from "node:fs"; import path from "node:path"; import { simpleGit } from "simple-git"; -import { Context } from "../types/breaking-change.js"; import { logError, logMessage } from "../log.js"; +import { Context } from "../types/breaking-change.js"; /** * Properties of Pull Request in Azure DevOps CI. diff --git a/eng/tools/openapi-diff-runner/src/utils/spec.ts b/eng/tools/openapi-diff-runner/src/utils/spec.ts index 39266344e2bb..499c9e3fb471 100644 --- a/eng/tools/openapi-diff-runner/src/utils/spec.ts +++ b/eng/tools/openapi-diff-runner/src/utils/spec.ts @@ -1,3 +1,4 @@ +import { mapAsync } from "@azure-tools/specs-shared/array"; import { basename } from "node:path"; import { logError } from "../log.js"; import { ApiVersionLifecycleStage } from "../types/breaking-change.js"; @@ -28,16 +29,22 @@ async function getPrecedingSwaggerByType( return undefined; } - const currentVersion = getVersionFromInputFile(targetSwaggerPath); + const currentVersion = await getVersionFromInputFile(targetSwaggerPath); const fileName = getBaseNameForSwagger(targetSwaggerPath, currentVersion); // Load version info for all swaggers to enable filtering - const swaggersWithVersions = availableSwaggers.map((swagger) => ({ - swagger, - version: getVersionFromInputFile(swagger.path), - fileName: getBaseNameForSwagger(swagger.path, getVersionFromInputFile(swagger.path)), - versionKind: swagger.versionKind, - })); + const swaggersWithVersions = await mapAsync(availableSwaggers, async (swagger) => { + const version = await getVersionFromInputFile(swagger.path); + const versionWithPreview = await getVersionFromInputFile(swagger.path, true); + return { + swagger, + version, + fileName: getBaseNameForSwagger(swagger.path, version), + versionKind: versionWithPreview.includes("preview") + ? ApiVersionLifecycleStage.PREVIEW + : ApiVersionLifecycleStage.STABLE, + }; + }); const versionsOfType = swaggersWithVersions.filter( (item) => @@ -70,15 +77,20 @@ export async function getExistedVersionOperations( return result; } - const currentVersion = getVersionFromInputFile(targetSwaggerPath); + const currentVersion = await getVersionFromInputFile(targetSwaggerPath); const fileName = getBaseNameForSwagger(targetSwaggerPath, currentVersion); // Load version info for all swaggers to enable filtering - const swaggersWithVersions = availableSwaggers.map((swagger) => ({ - swagger, - version: getVersionFromInputFile(swagger.path), - fileName: getBaseNameForSwagger(swagger.path, getVersionFromInputFile(swagger.path)), - })); + const swaggersWithVersions = await Promise.all( + availableSwaggers.map(async (swagger) => { + const version = await getVersionFromInputFile(swagger.path); + return { + swagger, + version, + fileName: getBaseNameForSwagger(swagger.path, version), + }; + }), + ); // Get all current and previous versions of the swaggers with different fileNames const previousVersionSwaggers = swaggersWithVersions @@ -155,3 +167,13 @@ export function getBaseNameForSwagger(filePath: string, version: string = ""): s } return basename(filePath); } + +/** + * Deduplicate swagger objects in an array by their path. + * @param swaggers The array of swagger objects to deduplicate. + * @returns A new array with duplicate swagger objects removed. + */ +export function deduplicateSwaggers(swaggers: Swagger[]): Swagger[] { + // Deduplicate by path + return [...new Map(swaggers.map((s) => [s.path, s])).values()]; +} diff --git a/eng/tools/openapi-diff-runner/test/command-helpers.test.ts b/eng/tools/openapi-diff-runner/test/command-helpers.test.ts index da40bb8c4819..2a433b9d9cba 100644 --- a/eng/tools/openapi-diff-runner/test/command-helpers.test.ts +++ b/eng/tools/openapi-diff-runner/test/command-helpers.test.ts @@ -1,30 +1,29 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from "node:fs"; -import path from "node:path"; +import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; +import { getChangedFilesStatuses } from "@azure-tools/specs-shared/changed-files"; +import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { - createContextFromParsedArgs, BreakingChangeLabelsToBeAdded, - getSwaggerDiffs, buildPrInfo, changeBaseBranch, - logFullOadMessagesList, - createDummySwagger, cleanDummySwagger, - isSameVersionBreakingType, + createContextFromParsedArgs, + createDummySwagger, getCreatedDummySwaggerCount, + getSwaggerDiffs, + isSameVersionBreakingType, + logFullOadMessagesList, outputBreakingChangeLabelVariables, type ParsedCliArguments, } from "../src/command-helpers.js"; +import { LogLevel } from "../src/log.js"; import { - Context, BreakingChangeReviewRequiredLabel, + Context, VersioningReviewRequiredLabel, } from "../src/types/breaking-change.js"; import { ResultMessageRecord } from "../src/types/message.js"; -import { getChangedFilesStatuses } from "@azure-tools/specs-shared/changed-files"; -import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; -import { LogLevel } from "../src/log.js"; // Test constants const TEST_CONSTANTS = { @@ -186,7 +185,6 @@ function setupCommonMocks() { // Mock dependencies vi.mock("node:fs"); -vi.mock("node:path"); vi.mock("node:url"); vi.mock("../src/utils/common-utils.js"); vi.mock("../src/utils/oad-message-processor.js"); @@ -206,7 +204,6 @@ describe("command-helpers", () => { const mockReadFileSync = vi.mocked(readFileSync); const mockWriteFileSync = vi.mocked(writeFileSync); const mockRmSync = vi.mocked(rmSync); - const mockPath = vi.mocked(path); const mockFileURLToPath = vi.mocked(fileURLToPath); const mockGetChangedFilesStatuses = vi.mocked(getChangedFilesStatuses); @@ -214,9 +211,6 @@ describe("command-helpers", () => { vi.clearAllMocks(); // Setup default mocks - mockPath.resolve.mockImplementation((...paths) => paths.join("/")); - mockPath.join.mockImplementation((...paths) => paths.join("/")); - mockPath.dirname.mockImplementation((p) => p.split("/").slice(0, -1).join("/")); mockFileURLToPath.mockReturnValue("/path/to/file.js"); // Mock console methods to avoid test output noise @@ -370,6 +364,7 @@ describe("command-helpers", () => { baseCommitish: TEST_CONSTANTS.BRANCHES.MAIN, cwd: TEST_CONSTANTS.PATHS.TEST_PATH, headCommitish: TEST_CONSTANTS.COMMITS.HEAD, + paths: ["specification"], }); }); @@ -451,6 +446,7 @@ describe("command-helpers", () => { baseCommitish: undefined, cwd: undefined, headCommitish: undefined, + paths: ["specification"], }); }); @@ -896,7 +892,7 @@ describe("command-helpers", () => { "versioningReviewLabelName", VersioningReviewRequiredLabel, ); - expect(setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", "true"); + expect(setOutput).toHaveBeenCalledWith("versioningReviewLabelValue", "false"); }); it("should handle labels set with non-review labels", async () => { diff --git a/eng/tools/openapi-diff-runner/test/detect-breaking-change.test.ts b/eng/tools/openapi-diff-runner/test/detect-breaking-change.test.ts index f2d0cb9ef405..418e22eca6bb 100644 --- a/eng/tools/openapi-diff-runner/test/detect-breaking-change.test.ts +++ b/eng/tools/openapi-diff-runner/test/detect-breaking-change.test.ts @@ -1,13 +1,16 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { SpecModel } from "@azure-tools/specs-shared/spec-model"; +import { existsSync } from "node:fs"; +import * as path from "node:path"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { - getReadmeFolder, - isInDevFolder, checkBreakingChangeOnSameVersion, doBreakingChangeDetection, + getReadmeFolder, + getSpecModel, + isInDevFolder, type BreakingChangeDetectionContext, } from "../src/detect-breaking-change.js"; -import { Context, ApiVersionLifecycleStage } from "../src/types/breaking-change.js"; -import { SpecModel } from "@azure-tools/specs-shared/spec-model"; +import { ApiVersionLifecycleStage, Context } from "../src/types/breaking-change.js"; import { getExistedVersionOperations, getPrecedingSwaggers } from "../src/utils/spec.js"; vi.mock("@azure-tools/specs-shared/spec-model", () => ({ @@ -17,14 +20,23 @@ vi.mock("@azure-tools/specs-shared/spec-model", () => ({ vi.mock("../src/utils/spec.js", () => ({ getExistedVersionOperations: vi.fn(), getPrecedingSwaggers: vi.fn(), + deduplicateSwaggers: vi.fn(), })); -// Mock getSpecModel and other functions since they're not automatically picked up by vi.mock +vi.mock("node:fs", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + appendFileSync: vi.fn(), + existsSync: vi.fn(), + }; +}); + +// Mock specific functions but keep getSpecModel as real implementation vi.mock("../src/detect-breaking-change.js", async () => { const original = await vi.importActual("../src/detect-breaking-change.js"); return { ...original, - getSpecModel: vi.fn(), createBreakingChangeDetectionContext: vi .fn() .mockImplementation( @@ -79,138 +91,239 @@ vi.mock("../src/utils/apply-rules.js", () => ({ applyRules: vi.fn().mockReturnValue([]), })); -vi.mock("node:fs", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - appendFileSync: vi.fn(), - readFileSync: vi - .fn() - .mockReturnValue('{"name": "@azure-tools/openapi-diff-runner", "version": "1.0.0"}'), - }; -}); - describe("detect-breaking-change", () => { let mockContext: Context; let mockDetectionContext: BreakingChangeDetectionContext; let detectionModule: any; - // Helper function to create mock SpecModel - const createMockSpecModel = (folder = "/mock/folder", swaggers: any[] = []) => ({ - getSwaggers: vi.fn().mockResolvedValue(swaggers), - folder, - logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, - readmes: [] as any[], - }); + // Test constants + const TEST_CONSTANTS = { + PATHS: { + network: + "specification/network/resource-manager/Microsoft.Network/stable/2019-11-01/network.json", + storage: + "specification/storage/resource-manager/Microsoft.Storage/stable/2021-04-01/storage.json", + networkStable: + "specification/network/resource-manager/Microsoft.Network/stable/2021-05-01/network.json", + }, + FOLDERS: { + tempRepo: "/test/working/dir", + specRepo: "/test/repo", + mockFolder: "/mock/folder", + // Cross-platform test repo path (avoids leading slash issues on Windows) + testRepoPath: path.resolve("path", "to", "repo"), + }, + OPERATIONS: { + operation1: { id: "operation1", path: "/api/test1", httpMethod: "GET" }, + operation2: { id: "operation2", path: "/api/test2", httpMethod: "POST" }, + operation3: { id: "operation3", path: "/api/test3", httpMethod: "DELETE" }, + }, + }; - // Helper function to create mock operations - const createMockOperations = () => - new Map([ - ["operation1", { id: "operation1", path: "/api/test1", httpMethod: "GET" }], - ["operation2", { id: "operation2", path: "/api/test2", httpMethod: "POST" }], - ]); - - // Helper function to create mock operations array for getExistedVersionOperations - const createMockOperationsArray = () => - new Map([ - [ - "/test/swagger1.json", + // Test data factories + const TestFixtures = { + createMockSpecModel: (folder = TEST_CONSTANTS.FOLDERS.mockFolder, swaggers: any[] = []) => ({ + getSwaggers: vi.fn().mockResolvedValue(swaggers || []), + folder, + logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, + readmes: [] as any[], + }), + + createSpyManager: () => { + const spies: Array = []; + return { + add: (spy: any) => { + spies.push(spy); + return spy; + }, + restoreAll: () => { + spies.forEach((spy) => spy.mockRestore()); + spies.length = 0; + }, + }; + }, + + createMockOperations: () => + new Map([ + ["operation1", TEST_CONSTANTS.OPERATIONS.operation1], + ["operation2", TEST_CONSTANTS.OPERATIONS.operation2], + ]), + + createMockOperationsArray: () => + new Map([ [ - { id: "operation1", path: "/api/test1", httpMethod: "GET" }, - { id: "operation2", path: "/api/test2", httpMethod: "POST" }, + "/test/swagger1.json", + [TEST_CONSTANTS.OPERATIONS.operation1, TEST_CONSTANTS.OPERATIONS.operation2], ], - ], - ["/test/swagger2.json", [{ id: "operation3", path: "/api/test3", httpMethod: "DELETE" }]], - ]); - - // Helper function to create test context - const createTestContext = (overrides = {}) => - ({ - localSpecRepoPath: "/path/to/spec/repo", - ...overrides, - }) as Context; - - // Helper function to manage spies with automatic cleanup - const createSpyManager = () => { - const spies: any[] = []; - return { - add: (spy: any) => { - spies.push(spy); - return spy; - }, - restoreAll: () => spies.forEach((spy) => spy.mockRestore?.()), - }; + ["/test/swagger2.json", [TEST_CONSTANTS.OPERATIONS.operation3]], + ]), + + createMockContext: (overrides = {}) => + ({ + prInfo: { + targetBranch: "main", + sourceBranch: "feature-branch", + baseBranch: "main", + currentBranch: "feature-branch", + tempRepoFolder: TEST_CONSTANTS.FOLDERS.tempRepo, + checkout: vi.fn(), + }, + localSpecRepoPath: TEST_CONSTANTS.FOLDERS.specRepo, + workingFolder: "/test/working", + logFileFolder: "/test/logs", + swaggerDirs: ["specification"], + baseBranch: "main", + headCommit: "abc123", + runType: "SAME_VERSION" as any, + checkName: "test-check", + targetRepo: "Azure/azure-rest-api-specs", + sourceRepo: "user/azure-rest-api-specs", + prNumber: "123", + prSourceBranch: "feature-branch", + prTargetBranch: "main", + oadMessageProcessorContext: { + logFilePath: "/test/logs/openapi-diff-runner.log", + prUrl: "https://github.com/Azure/azure-rest-api-specs/pull/123", + messageCache: [], + }, + prUrl: "https://github.com/Azure/azure-rest-api-specs/pull/123", + ...overrides, + }) as Context, + + createMockDetectionContext: (contextOverrides = {}, overrides = {}) => { + const context = TestFixtures.createMockContext(contextOverrides); + return { + context, + existingVersionSwaggers: ["existing1.json", "existing2.json"], + newVersionSwaggers: ["new1.json", "new2.json"], + newVersionChangedSwaggers: ["changed1.json", "changed2.json"], + msgs: [], + runtimeErrors: [], + oadTracer: { traces: [], baseBranch: "main", context }, + tempTagName: "oad-default-tag", + ...overrides, + } as BreakingChangeDetectionContext; + }, + + createTestContext: (overrides = {}) => + ({ + localSpecRepoPath: path.resolve("path", "to", "spec", "repo"), + ...overrides, + }) as Context, + + createMockSwagger: (pathOverride?: string, operationsOverride?: Map) => ({ + path: pathOverride || `${TEST_CONSTANTS.FOLDERS.tempRepo}/${TEST_CONSTANTS.PATHS.storage}`, + getOperations: vi + .fn() + .mockResolvedValue(operationsOverride || TestFixtures.createMockOperations()), + }), }; - // Common test paths - const TEST_PATHS = { - network: - "specification/network/resource-manager/Microsoft.Network/stable/2019-11-01/network.json", - storage: - "specification/storage/resource-manager/Microsoft.Storage/stable/2021-04-01/storage.json", - networkStable: - "specification/network/resource-manager/Microsoft.Network/stable/2021-05-01/network.json", + // Mock setup utilities + const MockSetup = { + setupDefaultMocks: () => { + vi.mocked(existsSync).mockReturnValue(true); + vi.mocked(getExistedVersionOperations).mockResolvedValue(new Map()); + vi.mocked(getPrecedingSwaggers).mockResolvedValue({ + stable: "/test/previous-stable.json", + preview: "/test/previous-preview.json", + }); + }, + + setupSpecModelMock: (mockInstance?: any) => { + if (mockInstance) { + // Use the provided instance + vi.mocked(SpecModel).mockImplementation(() => mockInstance as unknown as SpecModel); + return mockInstance; + } else { + // Create different instances based on folder path + vi.mocked(SpecModel).mockImplementation((folder: string) => { + return TestFixtures.createMockSpecModel(folder) as unknown as SpecModel; + }); + return null; // No specific instance to return + } + }, + + resetAllMocks: () => { + vi.clearAllMocks(); + vi.resetAllMocks(); + }, }; beforeEach(async () => { - vi.clearAllMocks(); + MockSetup.resetAllMocks(); detectionModule = await import("../src/detect-breaking-change.js"); - mockContext = { - prInfo: { - tempRepoFolder: "/test/working/dir", - baseBranch: "main", - checkout: vi.fn(), - }, - localSpecRepoPath: "/test/repo", - baseBranch: "main", - oadMessageProcessorContext: {}, - } as any; - - // Create mock detection context manually instead of using the function - // since we're testing that function itself - mockDetectionContext = { - context: mockContext, - existingVersionSwaggers: ["existing1.json", "existing2.json"], - newVersionSwaggers: ["new1.json", "new2.json"], - newVersionChangedSwaggers: ["changed1.json", "changed2.json"], - msgs: [], - runtimeErrors: [], - oadTracer: { traces: [], baseBranch: "main", context: mockContext }, - tempTagName: "oad-default-tag", - }; + mockContext = TestFixtures.createMockContext(); + mockDetectionContext = TestFixtures.createMockDetectionContext(); + + MockSetup.setupDefaultMocks(); }); afterEach(() => { - vi.resetAllMocks(); + vi.restoreAllMocks(); }); describe("getReadmeFolder", () => { - it("should extract folder path up to resource-manager", () => { - const testPath = - "specification/network/resource-manager/Microsoft.Network/stable/2019-11-01/network.json"; + it("should return first readme.md found when searching upward", () => { + const testPath = TEST_CONSTANTS.PATHS.network; + + // Mock existsSync to return true only for the resource-manager level + vi.mocked(existsSync).mockImplementation((pathArg: any) => { + return pathArg + .toString() + .endsWith(path.join("specification", "network", "resource-manager", "readme.md")); + }); + const result = getReadmeFolder(testPath); - expect(result).toBe("specification/network/resource-manager"); + expect(result).toBe(path.join("specification", "network", "resource-manager")); }); - it("should extract folder path up to data-plane", () => { + it("should find readme.md at data-plane level", () => { const testPath = "specification/cognitiveservices/data-plane/TextAnalytics/preview/v3.1/textanalytics.json"; + + vi.mocked(existsSync).mockImplementation((pathArg: any) => { + return pathArg + .toString() + .endsWith(path.join("specification", "cognitiveservices", "data-plane", "readme.md")); + }); + const result = getReadmeFolder(testPath); - expect(result).toBe("specification/cognitiveservices/data-plane"); + expect(result).toBe(path.join("specification", "cognitiveservices", "data-plane")); }); - it("should return first 3 segments as fallback", () => { - const testPath = "specification/someservice/other/file.json"; + it("should fall back to boundary when no readme.md found", () => { + const testPath = TEST_CONSTANTS.PATHS.network; + + // No readme.md files exist + vi.mocked(existsSync).mockReturnValue(false); + const result = getReadmeFolder(testPath); - expect(result).toBe("specification/someservice/other"); + expect(result).toBe(path.join("specification", "network", "resource-manager")); + }); + + it("should fall back to first 3 segments when no boundary found", () => { + const testPath = "specification/someservice/other/deep/path/file.json"; + + vi.mocked(existsSync).mockReturnValue(false); + + const result = getReadmeFolder(testPath); + expect(result).toBe(path.join("specification", "someservice", "other")); }); it("should handle dev folder conversion", () => { const testPath = "dev/network/resource-manager/Microsoft.Network/stable/2019-11-01/network.json"; + + vi.mocked(existsSync).mockImplementation((pathArg: any) => { + return pathArg + .toString() + .endsWith(path.join("specification", "network", "resource-manager", "readme.md")); + }); + const result = getReadmeFolder(testPath); - expect(result).toBe("specification/network/resource-manager"); + expect(result).toBe(path.join("specification", "network", "resource-manager")); }); it("should return undefined for short paths", () => { @@ -219,11 +332,35 @@ describe("detect-breaking-change", () => { expect(result).toBeUndefined(); }); - it("should handle paths with backslashes", () => { + it("should handle backslashes in paths", () => { const testPath = "specification\\network\\resource-manager\\Microsoft.Network\\stable\\2019-11-01\\network.json"; + + vi.mocked(existsSync).mockImplementation((pathArg: any) => { + return pathArg + .toString() + .endsWith(path.join("specification", "network", "resource-manager", "readme.md")); + }); + const result = getReadmeFolder(testPath); - expect(result).toBe("specification/network/resource-manager"); + expect(result).toBe(path.join("specification", "network", "resource-manager")); + }); + + it("should find readme.md within boundary search range", () => { + const testPath = + "specification/network/resource-manager/Microsoft.Network/stable/2019-11-01/network.json"; + + // Simulate readme.md at Microsoft.Network level (within search range) + vi.mocked(existsSync).mockImplementation((pathArg: any) => { + const pathStr = pathArg.toString(); + return pathStr.includes(path.join("Microsoft.Network", "readme.md")); + }); + + // Should find readme.md at Microsoft.Network level since it's within the search range + const result = getReadmeFolder(testPath); + expect(result).toBe( + path.join("specification", "network", "resource-manager", "Microsoft.Network"), + ); }); }); @@ -241,76 +378,270 @@ describe("detect-breaking-change", () => { describe("getSpecModel", () => { beforeEach(() => { - vi.clearAllMocks(); - const mockSpecModelInstance = createMockSpecModel(); - vi.mocked(SpecModel).mockImplementation(() => mockSpecModelInstance as unknown as SpecModel); + MockSetup.resetAllMocks(); }); - it("should create and cache SpecModel for new folder", async () => { - const spyManager = createSpyManager(); - const getSpecModelSpy = spyManager.add(vi.spyOn(detectionModule, "getSpecModel")); - const getReadmeFolderSpy = spyManager.add(vi.spyOn(detectionModule, "getReadmeFolder")); + // Helper function to create consistent folder existence mocks + const createFolderExistenceMock = (existingFolders: string[], readmeFolders: string[] = []) => { + return vi.mocked(existsSync).mockImplementation((pathArg: any) => { + const pathStr = pathArg.toString().replace(/\\/g, "/"); // Normalize to forward slashes for comparison - getReadmeFolderSpy.mockReturnValue("specification/network/resource-manager"); + // Check for readme.md files (used by getReadmeFolder) + if (pathStr.endsWith("readme.md")) { + return readmeFolders.some((folder) => pathStr.includes(`${folder}/readme.md`)); + } - const repoFolder = "/path/to/repo"; - const result1 = detectionModule.getSpecModel(repoFolder, TEST_PATHS.network); - const result2 = detectionModule.getSpecModel(repoFolder, TEST_PATHS.network); + // Check for folder existence (used by getSpecModel) - normalize both paths for comparison + return existingFolders.some((folder) => pathStr.endsWith(folder.replace(/\\/g, "/"))); + }); + }; - expect(getSpecModelSpy).toHaveBeenCalledTimes(2); - expect(getSpecModelSpy).toHaveBeenCalledWith(repoFolder, TEST_PATHS.network); - expect(result1).toBe(result2); + it("should create SpecModel when folder exists", () => { + const mockSpecModelInstance = TestFixtures.createMockSpecModel(); + MockSetup.setupSpecModelMock(mockSpecModelInstance); - spyManager.restoreAll(); + createFolderExistenceMock( + [path.join("specification", "network", "resource-manager")], + ["resource-manager"], + ); + + const result = getSpecModel( + TEST_CONSTANTS.FOLDERS.testRepoPath, + TEST_CONSTANTS.PATHS.network, + ); + + expect(result).toBeDefined(); + expect(vi.mocked(SpecModel)).toHaveBeenCalledWith( + path.join( + TEST_CONSTANTS.FOLDERS.testRepoPath, + "specification", + "network", + "resource-manager", + ), + ); }); - it("should create different SpecModels for different folders", async () => { - const spyManager = createSpyManager(); - const mockSpecModel1 = createMockSpecModel( - "/path/to/repo/specification/network/resource-manager", + it("should search upward and find parent folder when initial folder doesn't exist", () => { + const mockSpecModelInstance = TestFixtures.createMockSpecModel(); + MockSetup.setupSpecModelMock(mockSpecModelInstance); + + // Microsoft.Network folder doesn't exist, but resource-manager does with readme.md + createFolderExistenceMock( + [path.join("specification", "network", "resource-manager")], + ["resource-manager"], + ); + + const result = getSpecModel( + TEST_CONSTANTS.FOLDERS.testRepoPath, + TEST_CONSTANTS.PATHS.network, ); - const mockSpecModel2 = createMockSpecModel( - "/path/to/repo/specification/storage/resource-manager", + + expect(result).toBeDefined(); + expect(vi.mocked(SpecModel)).toHaveBeenCalledWith( + path.join( + TEST_CONSTANTS.FOLDERS.testRepoPath, + "specification", + "network", + "resource-manager", + ), ); + }); + + it("should handle data-plane boundary correctly", () => { + const testPath = path.join( + "specification", + "cognitiveservices", + "data-plane", + "TextAnalytics", + "preview", + "v3.1", + "textanalytics.json", + ); + const mockSpecModelInstance = TestFixtures.createMockSpecModel(); + MockSetup.setupSpecModelMock(mockSpecModelInstance); + + // TextAnalytics folder doesn't exist, but data-plane does with readme.md + createFolderExistenceMock( + [path.join("specification", "cognitiveservices", "data-plane")], + ["data-plane"], + ); + + const result = getSpecModel(TEST_CONSTANTS.FOLDERS.testRepoPath, testPath); - vi.mocked(SpecModel) - .mockImplementationOnce(() => mockSpecModel1 as unknown as SpecModel) - .mockImplementationOnce(() => mockSpecModel2 as unknown as SpecModel); + expect(result).toBeDefined(); + expect(vi.mocked(SpecModel)).toHaveBeenCalledWith( + path.join( + TEST_CONSTANTS.FOLDERS.testRepoPath, + "specification", + "cognitiveservices", + "data-plane", + ), + ); + }); + + it("should return undefined when no valid folder with readme.md is found", () => { + const mockSpecModelInstance = TestFixtures.createMockSpecModel(); + MockSetup.setupSpecModelMock(mockSpecModelInstance); + + // No folders exist, not even boundary folders + createFolderExistenceMock([], []); + + const result = getSpecModel( + TEST_CONSTANTS.FOLDERS.testRepoPath, + TEST_CONSTANTS.PATHS.network, + ); + + expect(result).toBeUndefined(); + expect(vi.mocked(SpecModel)).not.toHaveBeenCalled(); + }); - const getReadmeFolderSpy = spyManager.add(vi.spyOn(detectionModule, "getReadmeFolder")); - getReadmeFolderSpy.mockImplementation((path: string) => { - if (path.includes("network")) return "specification/network/resource-manager"; - if (path.includes("storage")) return "specification/storage/resource-manager"; - return undefined; + it("should use boundary folder when getReadmeFolder returns boundary folder", () => { + const mockSpecModelInstance = TestFixtures.createMockSpecModel(); + MockSetup.setupSpecModelMock(mockSpecModelInstance); + + vi.mocked(existsSync).mockImplementation((pathArg: any) => { + const pathStr = pathArg.toString().replace(/\\/g, "/"); // Normalize path separators + + // getReadmeFolder finds readme.md at resource-manager level (boundary fallback) + if (pathStr.includes("resource-manager/readme.md")) return true; + + // resource-manager folder exists (this is what getReadmeFolder returned) + if (pathStr.endsWith("specification/network/resource-manager")) return true; + + return false; + }); + + const result = getSpecModel( + TEST_CONSTANTS.FOLDERS.testRepoPath, + TEST_CONSTANTS.PATHS.network, + ); + + // Should create SpecModel for the boundary folder since it exists + expect(result).toBeDefined(); + expect(vi.mocked(SpecModel)).toHaveBeenCalledWith( + path.join( + TEST_CONSTANTS.FOLDERS.testRepoPath, + "specification", + "network", + "resource-manager", + ), + ); + }); + + it("should find readme.md in intermediate folder during upward search", () => { + const testPath = path.join( + "specification", + "network", + "resource-manager", + "Microsoft.Network", + "stable", + "2019-11-01", + "network.json", + ); + const mockSpecModelInstance = TestFixtures.createMockSpecModel(); + MockSetup.setupSpecModelMock(mockSpecModelInstance); + + // getReadmeFolder returns Microsoft.Network level, but that folder doesn't exist + // However, resource-manager level has readme.md during upward search + vi.mocked(existsSync).mockImplementation((pathArg: any) => { + const pathStr = pathArg.toString().replace(/\\/g, "/"); // Normalize path separators + + // getReadmeFolder finds Microsoft.Network level + if (pathStr.includes("Microsoft.Network/readme.md")) return true; + + // Microsoft.Network folder doesn't exist + if (pathStr.endsWith("specification/network/resource-manager/Microsoft.Network")) + return false; + + // resource-manager folder exists and has readme.md + if (pathStr.endsWith("specification/network/resource-manager")) return true; + if (pathStr.endsWith("specification/network/resource-manager/readme.md")) return true; + + return false; }); - const getSpecModelSpy = spyManager.add(vi.spyOn(detectionModule, "getSpecModel")); - getSpecModelSpy - .mockReturnValueOnce(mockSpecModel1 as unknown as SpecModel) - .mockReturnValueOnce(mockSpecModel2 as unknown as SpecModel); + const result = getSpecModel(TEST_CONSTANTS.FOLDERS.testRepoPath, testPath); + + expect(result).toBeDefined(); + // Should use resource-manager folder because that's where readme.md was found during upward search + expect(vi.mocked(SpecModel)).toHaveBeenCalledWith( + path.join( + TEST_CONSTANTS.FOLDERS.testRepoPath, + "specification", + "network", + "resource-manager", + ), + ); + }); + + it("should create different SpecModels for different folders", () => { + MockSetup.setupSpecModelMock(); - const repoFolder = "/path/to/repo"; - const result1 = detectionModule.getSpecModel(repoFolder, TEST_PATHS.network); - const result2 = detectionModule.getSpecModel(repoFolder, TEST_PATHS.storage); + // Both services have their folders and readme.md files + createFolderExistenceMock( + [ + path.join("specification", "network", "resource-manager"), + path.join("specification", "storage", "resource-manager"), + ], + ["resource-manager"], + ); + + const result1 = getSpecModel( + TEST_CONSTANTS.FOLDERS.testRepoPath, + TEST_CONSTANTS.PATHS.network, + ); + const result2 = getSpecModel( + TEST_CONSTANTS.FOLDERS.testRepoPath, + TEST_CONSTANTS.PATHS.storage, + ); expect(result1).not.toBe(result2); - expect(result1.folder).toBe("/path/to/repo/specification/network/resource-manager"); - expect(result2.folder).toBe("/path/to/repo/specification/storage/resource-manager"); - expect(getSpecModelSpy).toHaveBeenCalledTimes(2); + expect(result1).toBeDefined(); + expect(result2).toBeDefined(); + }); - spyManager.restoreAll(); + it("should handle dev folder conversion in upward search", () => { + const testPath = path.join( + "dev", + "network", + "resource-manager", + "Microsoft.Network", + "stable", + "2019-11-01", + "network.json", + ); + const mockSpecModelInstance = TestFixtures.createMockSpecModel(); + MockSetup.setupSpecModelMock(mockSpecModelInstance); + + // After dev->specification conversion, resource-manager folder exists with readme.md + createFolderExistenceMock( + [path.join("specification", "network", "resource-manager")], + ["resource-manager"], + ); + + const result = getSpecModel(TEST_CONSTANTS.FOLDERS.testRepoPath, testPath); + + expect(result).toBeDefined(); + expect(vi.mocked(SpecModel)).toHaveBeenCalledWith( + path.join( + TEST_CONSTANTS.FOLDERS.testRepoPath, + "specification", + "network", + "resource-manager", + ), + ); }); }); describe("checkAPIsBeingMovedToANewSpec", () => { beforeEach(() => { - vi.clearAllMocks(); + MockSetup.resetAllMocks(); }); it("should process moved APIs when found", async () => { - const spyManager = createSpyManager(); - const mockOperationsArray = createMockOperationsArray(); - const mockTargetOperations = createMockOperations(); + const spyManager = TestFixtures.createSpyManager(); + const mockOperationsArray = TestFixtures.createMockOperationsArray(); + const mockTargetOperations = TestFixtures.createMockOperations(); const checkAPIsBeingMovedToANewSpecSpy = spyManager.add( vi.spyOn(detectionModule, "checkAPIsBeingMovedToANewSpec"), @@ -324,21 +655,21 @@ describe("detect-breaking-change", () => { ); vi.mocked(getExistedVersionOperations).mockResolvedValue(mockOperationsArray); - const testContext = createTestContext(); + const testContext = TestFixtures.createTestContext(); await detectionModule.checkAPIsBeingMovedToANewSpec( testContext, - TEST_PATHS.networkStable, + TEST_CONSTANTS.PATHS.networkStable, [], ); expect(checkAPIsBeingMovedToANewSpecSpy).toHaveBeenCalledWith( testContext, - TEST_PATHS.networkStable, + TEST_CONSTANTS.PATHS.networkStable, [], ); expect(vi.mocked(getExistedVersionOperations)).toHaveBeenCalledWith( - TEST_PATHS.networkStable, + TEST_CONSTANTS.PATHS.networkStable, [], [...mockTargetOperations.values()], ); @@ -347,10 +678,10 @@ describe("detect-breaking-change", () => { }); it("should handle empty moved APIs", async () => { - const spyManager = createSpyManager(); + const spyManager = TestFixtures.createSpyManager(); vi.clearAllMocks(); - const mockTargetOperations = createMockOperations(); + const mockTargetOperations = TestFixtures.createMockOperations(); const checkAPIsBeingMovedToANewSpecSpy = spyManager.add( vi.spyOn(detectionModule, "checkAPIsBeingMovedToANewSpec"), @@ -364,17 +695,21 @@ describe("detect-breaking-change", () => { ); vi.mocked(getExistedVersionOperations).mockResolvedValue(new Map()); - const testContext = createTestContext(); + const testContext = TestFixtures.createTestContext(); - await detectionModule.checkAPIsBeingMovedToANewSpec(testContext, TEST_PATHS.storage, []); + await detectionModule.checkAPIsBeingMovedToANewSpec( + testContext, + TEST_CONSTANTS.PATHS.storage, + [], + ); expect(checkAPIsBeingMovedToANewSpecSpy).toHaveBeenCalledWith( testContext, - TEST_PATHS.storage, + TEST_CONSTANTS.PATHS.storage, [], ); expect(vi.mocked(getExistedVersionOperations)).toHaveBeenCalledWith( - TEST_PATHS.storage, + TEST_CONSTANTS.PATHS.storage, [], [...mockTargetOperations.values()], ); @@ -387,7 +722,7 @@ describe("detect-breaking-change", () => { let mockSpecModelInstance: any; beforeEach(async () => { - mockSpecModelInstance = createMockSpecModel("/mock/folder", [ + mockSpecModelInstance = TestFixtures.createMockSpecModel("/mock/folder", [ { path: "/test/swagger1.json" }, { path: "/test/swagger2.json" }, ]); @@ -404,7 +739,7 @@ describe("detect-breaking-change", () => { const getSpecModelSpy = vi.spyOn(detectionModule, "getSpecModel"); getSpecModelSpy.mockReturnValue(mockSpecModelInstance); - mockDetectionContext.newVersionSwaggers = [TEST_PATHS.networkStable]; + mockDetectionContext.newVersionSwaggers = [TEST_CONSTANTS.PATHS.networkStable]; mockDetectionContext.newVersionChangedSwaggers = []; mockDetectionContext.existingVersionSwaggers = []; @@ -417,53 +752,67 @@ describe("detect-breaking-change", () => { expect(result.errorCnt).toBeDefined(); }); - it("should process swaggers with no previous versions", async () => { + it("should process swaggers with previous versions", async () => { + // Complete mock reset to avoid interference from other tests vi.clearAllMocks(); + vi.resetAllMocks(); + + // Ensure existsSync returns true for this test so getSpecModel doesn't return undefined + vi.mocked(existsSync).mockReturnValue(true); const mockTargetOperations = new Map([ ["operation1", { id: "operation1", path: "/api/test1", httpMethod: "GET" }], ]); const mockTargetSwagger = { - path: "/test/path/to/swagger.json", + path: "/test/working/dir/specification/storage/resource-manager/Microsoft.Storage/stable/2021-04-01/storage.json", getOperations: vi.fn().mockResolvedValue(mockTargetOperations), }; - const mockSpecModel = createMockSpecModel("/mock/folder", [mockTargetSwagger]); - const detectionModule = await import("../src/detect-breaking-change.js"); - const mockGetSpecModel = vi.spyOn(detectionModule, "getSpecModel"); - mockGetSpecModel.mockReturnValue(mockSpecModel as unknown as SpecModel); + // Create a proper mock SpecModel instance that actually works + const mockSpecModelInstance = { + getSwaggers: vi.fn().mockResolvedValue([mockTargetSwagger]), + folder: "/test/working/dir/specification/storage/resource-manager", + logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, + readmes: [] as any[], + }; - vi.mocked(getPrecedingSwaggers).mockImplementation(async () => { - await detectionModule.checkAPIsBeingMovedToANewSpec(mockContext, "test-path", []); - return { stable: undefined, preview: undefined }; + // Mock SpecModel constructor directly + vi.mocked(SpecModel).mockImplementation(() => mockSpecModelInstance as unknown as SpecModel); + + // Mock getExistedVersionOperations to return a proper Map + vi.mocked(getExistedVersionOperations).mockResolvedValue(new Map()); + + // Mock getPrecedingSwaggers to return some previous versions to avoid checkAPIsBeingMovedToANewSpec call + vi.mocked(getPrecedingSwaggers).mockResolvedValue({ + stable: "/test/previous-stable.json", + preview: undefined, }); - const checkAPIsBeingMovedToANewSpecSpy = vi.spyOn( - detectionModule, - "checkAPIsBeingMovedToANewSpec", - ); - checkAPIsBeingMovedToANewSpecSpy.mockImplementation( - async (_context, swaggerPath, _availableSwaggers) => { - await getExistedVersionOperations(swaggerPath, [], [...mockTargetOperations.values()]); - }, - ); + // Import the module + const detectionModule = await import("../src/detect-breaking-change.js"); - mockDetectionContext.newVersionSwaggers = [TEST_PATHS.storage]; + mockDetectionContext.newVersionSwaggers = [TEST_CONSTANTS.PATHS.storage]; mockDetectionContext.newVersionChangedSwaggers = []; mockDetectionContext.existingVersionSwaggers = []; mockDetectionContext.context = { ...mockContext, - localSpecRepoPath: "/path/to/repo", + localSpecRepoPath: TEST_CONSTANTS.FOLDERS.testRepoPath, + prInfo: { + ...mockContext.prInfo, + tempRepoFolder: "/test/working/dir", + }, } as Context; const result = await detectionModule.checkCrossVersionBreakingChange(mockDetectionContext); expect(result).toBeDefined(); - expect(checkAPIsBeingMovedToANewSpecSpy).toHaveBeenCalled(); + // For this simplified test, just verify that the function completes successfully + expect(result.msgs).toBeDefined(); + expect(result.runtimeErrors).toBeDefined(); - mockGetSpecModel.mockRestore(); - checkAPIsBeingMovedToANewSpecSpy.mockRestore(); + // Verify that SpecModel was called to create the specModel + expect(vi.mocked(SpecModel)).toHaveBeenCalled(); }); }); @@ -506,7 +855,10 @@ describe("detect-breaking-change", () => { describe("checkBreakingChangeOnSameVersion", () => { beforeEach(() => { - mockDetectionContext.existingVersionSwaggers = [TEST_PATHS.networkStable, TEST_PATHS.storage]; + mockDetectionContext.existingVersionSwaggers = [ + TEST_CONSTANTS.PATHS.networkStable, + TEST_CONSTANTS.PATHS.storage, + ]; mockDetectionContext.msgs = []; mockDetectionContext.runtimeErrors = []; }); @@ -533,7 +885,10 @@ describe("detect-breaking-change", () => { }); it("should accumulate violations and errors from multiple swaggers", async () => { - mockDetectionContext.existingVersionSwaggers = [TEST_PATHS.networkStable, TEST_PATHS.storage]; + mockDetectionContext.existingVersionSwaggers = [ + TEST_CONSTANTS.PATHS.networkStable, + TEST_CONSTANTS.PATHS.storage, + ]; const result = await checkBreakingChangeOnSameVersion(mockDetectionContext); diff --git a/eng/tools/openapi-diff-runner/test/generate-report.test.ts b/eng/tools/openapi-diff-runner/test/generate-report.test.ts index f4eb8b50e5c1..c177f2cd07f0 100644 --- a/eng/tools/openapi-diff-runner/test/generate-report.test.ts +++ b/eng/tools/openapi-diff-runner/test/generate-report.test.ts @@ -1,15 +1,15 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { generateBreakingChangeResultSummary } from "../src/generate-report.js"; +import { addToSummary, logMessage } from "../src/log.js"; import { Context } from "../src/types/breaking-change.js"; import { RawMessageRecord, ResultMessageRecord } from "../src/types/message.js"; -import { addToSummary, logMessage } from "../src/log.js"; import { BreakingChangeMdReport, createBreakingChangeMdReport, reportToString, sortBreakingChangeMdReports, } from "../src/utils/markdown-report.js"; -import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; // Mock dependencies vi.mock("../src/log.js"); diff --git a/eng/tools/openapi-diff-runner/test/types/oad-types.test.ts b/eng/tools/openapi-diff-runner/test/types/oad-types.test.ts index 5bd816e9db6f..2246d3623230 100644 --- a/eng/tools/openapi-diff-runner/test/types/oad-types.test.ts +++ b/eng/tools/openapi-diff-runner/test/types/oad-types.test.ts @@ -1,12 +1,12 @@ +import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; import { describe, expect, it } from "vitest"; +import { Context } from "../../src/types/breaking-change.js"; import { - createOadTrace, addOadTrace, + createOadTrace, generateOadMarkdown, setOadBaseBranch, } from "../../src/types/oad-types.js"; -import { Context } from "../../src/types/breaking-change.js"; -import { BREAKING_CHANGES_CHECK_TYPES } from "@azure-tools/specs-shared/breaking-change"; const mockContext: Context = { runType: BREAKING_CHANGES_CHECK_TYPES.SAME_VERSION, @@ -55,14 +55,14 @@ describe("OAD Trace Functions", () => { expect(updatedTrace.baseBranch).toBe("feature-branch"); }); - it("should generate empty markdown when no traces", () => { + it("should generate empty markdown when no traces", async () => { const traceData = createOadTrace(mockContext); - const markdown = generateOadMarkdown(traceData); + const markdown = await generateOadMarkdown(traceData); expect(markdown).toBe(""); }); - it("should generate markdown table when traces exist", () => { + it("should generate markdown table when traces exist", async () => { let traceData = createOadTrace(mockContext); traceData = addOadTrace( traceData, @@ -70,7 +70,7 @@ describe("OAD Trace Functions", () => { "specification/storage/resource-manager/Microsoft.Storage/stable/2021-09-01/storage.json", ); - const markdown = generateOadMarkdown(traceData); + const markdown = await generateOadMarkdown(traceData); expect(markdown).toContain("| Compared specs"); expect(markdown).toContain("storage.json"); diff --git a/eng/tools/openapi-diff-runner/test/utils/apply-rules.test.ts b/eng/tools/openapi-diff-runner/test/utils/apply-rules.test.ts index c4e89504180b..068112462db5 100644 --- a/eng/tools/openapi-diff-runner/test/utils/apply-rules.test.ts +++ b/eng/tools/openapi-diff-runner/test/utils/apply-rules.test.ts @@ -1,9 +1,9 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { applyRules } from "../../src/utils/apply-rules.js"; -import { OadMessage } from "../../src/types/oad-types.js"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { BreakingChangeLabelsToBeAdded } from "../../src/command-helpers.js"; import { logMessage, logWarning } from "../../src/log.js"; import { ApiVersionLifecycleStage } from "../../src/types/breaking-change.js"; +import { OadMessage } from "../../src/types/oad-types.js"; +import { applyRules } from "../../src/utils/apply-rules.js"; // Mock the command-helpers module vi.mock("../../src/command-helpers.js", () => ({ diff --git a/eng/tools/openapi-diff-runner/test/utils/common-utils.test.ts b/eng/tools/openapi-diff-runner/test/utils/common-utils.test.ts index 690e22eabe6b..ea5366e707b2 100644 --- a/eng/tools/openapi-diff-runner/test/utils/common-utils.test.ts +++ b/eng/tools/openapi-diff-runner/test/utils/common-utils.test.ts @@ -1,21 +1,32 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { readFile } from "node:fs/promises"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { Context } from "../../src/types/breaking-change.js"; import { blobHref, - targetHref, branchHref, + checkPrTargetsProductionBranch, + convertRawErrorToUnifiedMsg, + cutoffMsg, + getArgumentValue, getGithubStyleFilePath, getRelativeSwaggerPathToRepo, - sourceBranchHref, - targetBranchHref, - specificBranchHref, getVersionFromInputFile, - getArgumentValue, - cutoffMsg, processOadRuntimeErrorMessage, + sourceBranchHref, + specificBranchHref, specIsPreview, - convertRawErrorToUnifiedMsg, + targetBranchHref, + targetHref, } from "../../src/utils/common-utils.js"; -import { Context } from "../../src/types/breaking-change.js"; + +// Mock node:fs/promises module for async file operations +vi.mock("node:fs/promises", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + readFile: vi.fn(), + }; +}); describe("common-utils", () => { let originalEnv: NodeJS.ProcessEnv; @@ -225,29 +236,117 @@ describe("common-utils", () => { }); describe("getVersionFromInputFile", () => { - it("should extract version from data-plane path", () => { - const result = getVersionFromInputFile(TEST_CONSTANTS.DATA_PLANE_PATH); + beforeEach(() => { + vi.mocked(readFile).mockReset(); + }); + + it("should extract version from data-plane path", async () => { + const result = await getVersionFromInputFile(TEST_CONSTANTS.DATA_PLANE_PATH); expect(result).toBe(TEST_CONSTANTS.VERSION); }); - it("should extract version with preview from data-plane path", () => { - const result = getVersionFromInputFile(TEST_CONSTANTS.DATA_PLANE_PREVIEW_PATH, true); + it("should extract version with preview from data-plane path", async () => { + const result = await getVersionFromInputFile(TEST_CONSTANTS.DATA_PLANE_PREVIEW_PATH, true); expect(result).toBe(TEST_CONSTANTS.PREVIEW_VERSION); }); - it("should extract version from resource-manager path", () => { - const result = getVersionFromInputFile(TEST_CONSTANTS.RESOURCE_MANAGER_PATH); + it("should extract version from resource-manager path", async () => { + const result = await getVersionFromInputFile(TEST_CONSTANTS.RESOURCE_MANAGER_PATH); expect(result).toBe(TEST_CONSTANTS.VERSION); }); - it("should return empty string when no version found in path", () => { - const result = getVersionFromInputFile("test.json"); - expect(result).toBe(""); + it("should throw error when no version found in path", async () => { + const mockFileContent = JSON.stringify({ + info: { + title: "Test API", + }, + }); + + vi.mocked(readFile).mockResolvedValue(mockFileContent); + + await expect(getVersionFromInputFile("test.json")).rejects.toThrow( + "Version not found in file: test.json", + ); }); - it("should return folder name when no valid API version found", () => { - const result = getVersionFromInputFile("invalid/path.json"); - expect(result).toBe("invalid"); + it("should throw error when no valid API version found", async () => { + const mockFileContent = JSON.stringify({ + info: { + title: "Test API", + }, + }); + + vi.mocked(readFile).mockResolvedValue(mockFileContent); + + await expect(getVersionFromInputFile("invalid/path.json")).rejects.toThrow( + "Version not found in file: invalid/path.json", + ); + }); + + it("should extract version from file content when path regex fails", async () => { + const filePath = "some/custom/path/spec.json"; + const mockFileContent = JSON.stringify({ + info: { + version: "2023-05-01", + }, + }); + + vi.mocked(readFile).mockResolvedValue(mockFileContent); + + const result = await getVersionFromInputFile(filePath); + expect(result).toBe("2023-05-01"); + expect(vi.mocked(readFile)).toHaveBeenCalledWith(filePath, "utf8"); + }); + + it("should extract preview version from file content when includePreview is true", async () => { + const filePath = "some/custom/path/spec.json"; + const mockFileContent = JSON.stringify({ + info: { + version: "2023-05-01-preview", + }, + }); + + vi.mocked(readFile).mockResolvedValue(mockFileContent); + + const result = await getVersionFromInputFile(filePath, true); + expect(result).toBe("2023-05-01-preview"); + expect(vi.mocked(readFile)).toHaveBeenCalledWith(filePath, "utf8"); + }); + + it("should throw error when file content has no version", async () => { + const filePath = "some/custom/path/spec.json"; + const mockFileContent = JSON.stringify({ + info: { + title: "Test API", + }, + }); + + vi.mocked(readFile).mockResolvedValue(mockFileContent); + + await expect(getVersionFromInputFile(filePath)).rejects.toThrow( + "Version not found in file: some/custom/path/spec.json", + ); + }); + + it("should throw error when file content is invalid JSON", async () => { + const filePath = "some/custom/path/spec.json"; + const mockFileContent = "invalid json content"; + + vi.mocked(readFile).mockResolvedValue(mockFileContent); + + await expect(getVersionFromInputFile(filePath)).rejects.toThrow( + "Failed to read version from file:some/custom/path/spec.json", + ); + }); + + it("should throw error when file read fails", async () => { + const filePath = "some/custom/path/spec.json"; + + vi.mocked(readFile).mockRejectedValue(new Error("File read error")); + + await expect(getVersionFromInputFile(filePath)).rejects.toThrow( + "Failed to read version from file:some/custom/path/spec.json", + ); }); }); @@ -539,4 +638,95 @@ describe("common-utils", () => { expect(parsed.extra.details).toBe(errorMsg); }); }); + + describe("checkPrTargetsProductionBranch", () => { + it("should return true for public production branch (azure-rest-api-specs + main)", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs", "main"); + expect(result).toBe(true); + }); + + it("should return true for public production branch with extended repo name", () => { + const result = checkPrTargetsProductionBranch("owner/azure-rest-api-specs", "main"); + expect(result).toBe(true); + }); + + it("should return true for private production branch (azure-rest-api-specs-pr + RPSaaSMaster)", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs-pr", "RPSaaSMaster"); + expect(result).toBe(true); + }); + + it("should return true for private production branch with extended repo name", () => { + const result = checkPrTargetsProductionBranch( + "owner/azure-rest-api-specs-pr", + "RPSaaSMaster", + ); + expect(result).toBe(true); + }); + + it("should return false for public repo with non-main branch", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs", "feature-branch"); + expect(result).toBe(false); + }); + + it("should return false for public repo with develop branch", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs", "develop"); + expect(result).toBe(false); + }); + + it("should return false for private repo with main branch instead of RPSaaSMaster", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs-pr", "main"); + expect(result).toBe(false); + }); + + it("should return false for private repo with non-RPSaaSMaster branch", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs-pr", "feature-branch"); + expect(result).toBe(false); + }); + + it("should return false for unrelated repository with main branch", () => { + const result = checkPrTargetsProductionBranch("some-other-repo", "main"); + expect(result).toBe(false); + }); + + it("should return false for unrelated repository with RPSaaSMaster branch", () => { + const result = checkPrTargetsProductionBranch("some-other-repo", "RPSaaSMaster"); + expect(result).toBe(false); + }); + + it("should return false for empty repository name", () => { + const result = checkPrTargetsProductionBranch("", "main"); + expect(result).toBe(false); + }); + + it("should return false for empty branch name", () => { + const result = checkPrTargetsProductionBranch("azure-rest-api-specs", ""); + expect(result).toBe(false); + }); + + it("should return false for both empty parameters", () => { + const result = checkPrTargetsProductionBranch("", ""); + expect(result).toBe(false); + }); + + it("should be case sensitive for branch names", () => { + const result1 = checkPrTargetsProductionBranch("azure-rest-api-specs", "Main"); + const result2 = checkPrTargetsProductionBranch("azure-rest-api-specs-pr", "rpsaasmaster"); + + expect(result1).toBe(false); + expect(result2).toBe(false); + }); + + it("should handle partial repo name matches correctly", () => { + const result1 = checkPrTargetsProductionBranch("azure-rest-api", "main"); + const result2 = checkPrTargetsProductionBranch("rest-api-specs", "main"); + const result3 = checkPrTargetsProductionBranch( + "azure-rest-api-specs-private", + "RPSaaSMaster", + ); + + expect(result1).toBe(false); + expect(result2).toBe(false); + expect(result3).toBe(false); + }); + }); }); diff --git a/eng/tools/openapi-diff-runner/test/utils/markdown-report-row.test.ts b/eng/tools/openapi-diff-runner/test/utils/markdown-report-row.test.ts index b826902f7b32..b86de5356579 100644 --- a/eng/tools/openapi-diff-runner/test/utils/markdown-report-row.test.ts +++ b/eng/tools/openapi-diff-runner/test/utils/markdown-report-row.test.ts @@ -1,12 +1,12 @@ -import { describe, it, expect } from "vitest"; +import { describe, expect, it } from "vitest"; +import { BrChMsgRecord, ResultMessageRecord } from "../../src/types/message.js"; import { + BreakingChangeMdRow, createBreakingChangeMdRows, - getMdTableHeader, getDeficitRow, + getMdTableHeader, rowToString, - BreakingChangeMdRow, } from "../../src/utils/markdown-report-row.js"; -import { BrChMsgRecord, ResultMessageRecord } from "../../src/types/message.js"; describe("markdown-report-row", () => { // Test constants diff --git a/eng/tools/openapi-diff-runner/test/utils/markdown-report.test.ts b/eng/tools/openapi-diff-runner/test/utils/markdown-report.test.ts index ab38015646d9..acad8c8735e5 100644 --- a/eng/tools/openapi-diff-runner/test/utils/markdown-report.test.ts +++ b/eng/tools/openapi-diff-runner/test/utils/markdown-report.test.ts @@ -1,14 +1,14 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { LogLevel, logMessage } from "../../src/log.js"; +import { BrChMsgRecord, ResultMessageRecord } from "../../src/types/message.js"; import { + BreakingChangeMdReport, createBreakingChangeMdReport, - sortBreakingChangeMdReports, getReportLength, - reportToString, getRowCount, - BreakingChangeMdReport, + reportToString, + sortBreakingChangeMdReports, } from "../../src/utils/markdown-report.js"; -import { BrChMsgRecord, ResultMessageRecord } from "../../src/types/message.js"; -import { logMessage, LogLevel } from "../../src/log.js"; // Mock the log module vi.mock("../../src/log.js", () => ({ diff --git a/eng/tools/openapi-diff-runner/test/utils/oad-message-processor.test.ts b/eng/tools/openapi-diff-runner/test/utils/oad-message-processor.test.ts index 2948239cce39..28006758bac0 100644 --- a/eng/tools/openapi-diff-runner/test/utils/oad-message-processor.test.ts +++ b/eng/tools/openapi-diff-runner/test/utils/oad-message-processor.test.ts @@ -1,21 +1,21 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import fs from "node:fs"; import path from "node:path"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { logMessage, logMessageSafe } from "../../src/log.js"; +import { ApiVersionLifecycleStage, Context } from "../../src/types/breaking-change.js"; +import { MessageLevel } from "../../src/types/message.js"; +import { OadMessage } from "../../src/types/oad-types.js"; import { - convertOadMessagesToResultMessageRecords, - createOadMessageProcessor, - createMessageKey, - appendToLogFile, appendMarkdownToLog, - processAndAppendOadMessages, + appendToLogFile, clearMessageCache, + convertOadMessagesToResultMessageRecords, + createMessageKey, + createOadMessageProcessor, getMessageCacheSize, OadMessageProcessorContext, + processAndAppendOadMessages, } from "../../src/utils/oad-message-processor.js"; -import { OadMessage } from "../../src/types/oad-types.js"; -import { MessageLevel } from "../../src/types/message.js"; -import { logMessage, logMessageSafe } from "../../src/log.js"; -import { ApiVersionLifecycleStage, Context } from "../../src/types/breaking-change.js"; // Test constants const TEST_CONSTANTS = { diff --git a/eng/tools/openapi-diff-runner/test/utils/pull-request.test.ts b/eng/tools/openapi-diff-runner/test/utils/pull-request.test.ts index 0851a28b694f..26b99c5736a3 100644 --- a/eng/tools/openapi-diff-runner/test/utils/pull-request.test.ts +++ b/eng/tools/openapi-diff-runner/test/utils/pull-request.test.ts @@ -1,9 +1,9 @@ -import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"; import { existsSync, mkdirSync } from "node:fs"; import path from "node:path"; import { simpleGit } from "simple-git"; -import { createPullRequestProperties } from "../../src/utils/pull-request.js"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { Context } from "../../src/types/breaking-change.js"; +import { createPullRequestProperties } from "../../src/utils/pull-request.js"; // Test constants const TEST_CONSTANTS = { diff --git a/eng/tools/openapi-diff-runner/test/utils/spec.test.ts b/eng/tools/openapi-diff-runner/test/utils/spec.test.ts index 40fd0c8c2eba..7258ce23cac7 100644 --- a/eng/tools/openapi-diff-runner/test/utils/spec.test.ts +++ b/eng/tools/openapi-diff-runner/test/utils/spec.test.ts @@ -1,11 +1,12 @@ import { describe, expect, it } from "vitest"; +import { ApiVersionLifecycleStage } from "../../src/types/breaking-change.js"; import { - getPrecedingSwaggers, + deduplicateSwaggers, + getBaseNameForSwagger, getExistedVersionOperations, + getPrecedingSwaggers, type Swagger, - getBaseNameForSwagger, } from "../../src/utils/spec.js"; -import { ApiVersionLifecycleStage } from "../../src/types/breaking-change.js"; // Type definitions for tests (extending the base interfaces if needed) interface MockSwagger extends Swagger { @@ -102,11 +103,9 @@ describe("Helper functions for version analysis", () => { createMockSwagger(TEST_PATHS.stable2020_07_02, ApiVersionLifecycleStage.STABLE), ]; - const result = await getPrecedingSwaggers(TEST_PATHS.nonexistent, mockSwaggers); - - expectResultStructure(result); - expect(result.stable).toBeUndefined(); - expect(result.preview).toBeUndefined(); + await expect(getPrecedingSwaggers(TEST_PATHS.nonexistent, mockSwaggers)).rejects.toThrow( + `Failed to read version from file:${TEST_PATHS.nonexistent}`, + ); }); it("should handle null or undefined availableSwaggers array", async () => { @@ -266,4 +265,81 @@ describe("Helper functions for version analysis", () => { expect(result).toBe("test.json"); }); }); + + describe("deduplicateSwaggers", () => { + it("should return empty array for empty input", () => { + const result = deduplicateSwaggers([]); + expect(result).toEqual([]); + }); + + it("should return the same array when no duplicates exist", () => { + const mockSwaggers: MockSwagger[] = [ + createMockSwagger(TEST_PATHS.stable2020_07_02), + createMockSwagger(TEST_PATHS.stable2020_08_04), + createMockSwagger(TEST_PATHS.preview2020_07_02), + ]; + + const result = deduplicateSwaggers(mockSwaggers); + + expect(result).toHaveLength(3); + expect(result[0].path).toBe(TEST_PATHS.stable2020_07_02); + expect(result[1].path).toBe(TEST_PATHS.stable2020_08_04); + expect(result[2].path).toBe(TEST_PATHS.preview2020_07_02); + }); + + it("should remove duplicate swaggers with same path", () => { + const mockSwaggers: MockSwagger[] = [ + createMockSwagger(TEST_PATHS.stable2020_07_02), + createMockSwagger(TEST_PATHS.stable2020_08_04), + createMockSwagger(TEST_PATHS.stable2020_07_02), // duplicate + createMockSwagger(TEST_PATHS.preview2020_07_02), + createMockSwagger(TEST_PATHS.stable2020_08_04), // duplicate + ]; + + const result = deduplicateSwaggers(mockSwaggers); + + expect(result).toHaveLength(3); + expect(result.map((s) => s.path)).toEqual([ + TEST_PATHS.stable2020_07_02, + TEST_PATHS.stable2020_08_04, + TEST_PATHS.preview2020_07_02, + ]); + }); + + it("should handle single swagger", () => { + const mockSwaggers: MockSwagger[] = [createMockSwagger(TEST_PATHS.stable2020_07_02)]; + + const result = deduplicateSwaggers(mockSwaggers); + + expect(result).toHaveLength(1); + expect(result[0].path).toBe(TEST_PATHS.stable2020_07_02); + }); + + it("should handle all duplicates scenario", () => { + const mockSwaggers: MockSwagger[] = [ + createMockSwagger(TEST_PATHS.stable2020_07_02), + createMockSwagger(TEST_PATHS.stable2020_07_02), + createMockSwagger(TEST_PATHS.stable2020_07_02), + ]; + + const result = deduplicateSwaggers(mockSwaggers); + + expect(result).toHaveLength(1); + expect(result[0].path).toBe(TEST_PATHS.stable2020_07_02); + }); + + it("should not mutate the original array", () => { + const mockSwaggers: MockSwagger[] = [ + createMockSwagger(TEST_PATHS.stable2020_07_02), + createMockSwagger(TEST_PATHS.stable2020_07_02), + ]; + const originalLength = mockSwaggers.length; + + const result = deduplicateSwaggers(mockSwaggers); + + expect(mockSwaggers).toHaveLength(originalLength); // Original unchanged + expect(result).not.toBe(mockSwaggers); // Different array instance + expect(result).toHaveLength(1); + }); + }); }); diff --git a/eng/tools/package.json b/eng/tools/package.json index 981da6b3bb5e..e7bceb3ca520 100644 --- a/eng/tools/package.json +++ b/eng/tools/package.json @@ -10,7 +10,8 @@ "@azure-tools/tsp-client-tests": "file:tsp-client-tests", "@azure-tools/typespec-migration-validation": "file:typespec-migration-validation", "@azure-tools/typespec-requirement": "file:typespec-requirement", - "@azure-tools/typespec-validation": "file:typespec-validation" + "@azure-tools/typespec-validation": "file:typespec-validation", + "@azure-tools/summarize-impact": "file:summarize-impact" }, "scripts": { "build": "tsc --build", diff --git a/eng/tools/sdk-suppressions/package.json b/eng/tools/sdk-suppressions/package.json index 2d27648ca1d9..7c16b065fbbb 100644 --- a/eng/tools/sdk-suppressions/package.json +++ b/eng/tools/sdk-suppressions/package.json @@ -32,6 +32,7 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" } diff --git a/eng/tools/sdk-suppressions/src/common.ts b/eng/tools/sdk-suppressions/src/common.ts index 8fb57b0d62eb..12818c77bf1b 100644 --- a/eng/tools/sdk-suppressions/src/common.ts +++ b/eng/tools/sdk-suppressions/src/common.ts @@ -1,12 +1,12 @@ -import { parse as yamlParse } from "yaml"; import { getChangedFiles } from "@azure-tools/specs-shared/changed-files"; +import { parse as yamlParse } from "yaml"; /** * @returns {string[]} * @description get the changed files in the current PR */ export async function getSDKSuppressionsChangedFiles() { - const changedFiles = await getChangedFiles(); + const changedFiles = await getChangedFiles({ paths: ["specification"] }); const sdkSuppressionsFiles = changedFiles.filter((file) => file.endsWith("sdk-suppressions.yaml"), ); diff --git a/eng/tools/sdk-suppressions/src/sdkSuppressions.ts b/eng/tools/sdk-suppressions/src/sdkSuppressions.ts index b67e3444c934..bdb6b05fc803 100644 --- a/eng/tools/sdk-suppressions/src/sdkSuppressions.ts +++ b/eng/tools/sdk-suppressions/src/sdkSuppressions.ts @@ -4,8 +4,8 @@ * - https://microsoftapc-my.sharepoint.com/:w:/g/personal/raychen_microsoft_com/EbOAA9SkhQhGlgxtf7mc0kUB-25bFue0EFbXKXS3TFLTQA */ -import { Ajv } from "ajv"; import { SdkName, sdkLabels } from "@azure-tools/specs-shared/sdk-types"; +import { Ajv } from "ajv"; export const sdkSuppressionsFileName = "sdk-suppressions.yaml"; diff --git a/eng/tools/sdk-suppressions/src/updateSdkSuppressionsLabel.ts b/eng/tools/sdk-suppressions/src/updateSdkSuppressionsLabel.ts index 434049359f8a..9f5c14db693d 100644 --- a/eng/tools/sdk-suppressions/src/updateSdkSuppressionsLabel.ts +++ b/eng/tools/sdk-suppressions/src/updateSdkSuppressionsLabel.ts @@ -1,16 +1,16 @@ -import _ from "lodash"; +import { sdkLabels, SdkName } from "@azure-tools/specs-shared/sdk-types"; import debug from "debug"; import { writeFileSync } from "fs"; +import _ from "lodash"; import { simpleGit } from "simple-git"; -import { sdkLabels, SdkName } from "@azure-tools/specs-shared/sdk-types"; +import { getSDKSuppressionsChangedFiles, parseYamlContent } from "./common.js"; import { - SdkSuppressionsYml, - SdkSuppressionsSection, - sdkSuppressionsFileName, SdkPackageSuppressionsEntry, + sdkSuppressionsFileName, + SdkSuppressionsSection, + SdkSuppressionsYml, validateSdkSuppressionsFile, } from "./sdkSuppressions.js"; -import { getSDKSuppressionsChangedFiles, parseYamlContent } from "./common.js"; // Enable simple-git debug logging to improve console output debug.enable("simple-git"); diff --git a/eng/tools/sdk-suppressions/test/updateSdkSuppressionsLabel.test.ts b/eng/tools/sdk-suppressions/test/updateSdkSuppressionsLabel.test.ts index 802e7ba6982f..1f9f563037ca 100644 --- a/eng/tools/sdk-suppressions/test/updateSdkSuppressionsLabel.test.ts +++ b/eng/tools/sdk-suppressions/test/updateSdkSuppressionsLabel.test.ts @@ -1,10 +1,10 @@ -import { vi, expect, test } from "vitest"; +import { expect, test, vi } from "vitest"; +import { validateSdkSuppressionsFile } from "../src/sdkSuppressions.js"; import { filterSuppressionList, getSdkNamesWithChangedSuppressions, processLabels, } from "../src/updateSdkSuppressionsLabel.js"; -import { validateSdkSuppressionsFile } from "../src/sdkSuppressions.js"; vi.mock("process", () => ({ exit: vi.fn(), diff --git a/eng/tools/spec-gen-sdk-runner/package.json b/eng/tools/spec-gen-sdk-runner/package.json index af028550ce48..66b659207754 100644 --- a/eng/tools/spec-gen-sdk-runner/package.json +++ b/eng/tools/spec-gen-sdk-runner/package.json @@ -26,8 +26,9 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "eslint": "^9.21.0", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "typescript-eslint": "^8.26.0", "vitest": "^3.0.7" diff --git a/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts b/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts index e9b7ee06cc5e..fc42f69a1b4b 100644 --- a/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts +++ b/eng/tools/spec-gen-sdk-runner/src/command-helpers.ts @@ -1,14 +1,8 @@ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import { - findReadmeFiles, - getArgumentValue, - getAllTypeSpecPaths, - objectToMap, - SpecConfigs, -} from "./utils.js"; import { LogIssueType, LogLevel, logMessage, setVsoVariable, vsoLogIssue } from "./log.js"; +import { groupSpecConfigPaths } from "./spec-helpers.js"; import { APIViewRequestData, SdkName, @@ -17,7 +11,13 @@ import { SpecGenSdkRequiredSettings, VsoLogs, } from "./types.js"; -import { groupSpecConfigPaths } from "./spec-helpers.js"; +import { + findReadmeFiles, + getAllTypeSpecPaths, + getArgumentValue, + objectToMap, + SpecConfigs, +} from "./utils.js"; /** * Load execution-report.json. @@ -280,6 +280,7 @@ export function getBreakingChangeInfo(executionReport: any): boolean { * @param result - The spec-gen-sdk execution result. * @param hasBreakingChange - A flag indicating whether there are breaking changes. * @param hasManagementPlaneSpecs - A flag indicating whether there are management plane specs. + * @param hasTypeSpecProjects - A flag indicating whether there are TypeSpec projects. * @param stagedArtifactsFolder - The staged artifacts folder. * @param apiViewRequestData - The API view request data. * @param sdkGenerationExecuted - A flag indicating whether the SDK generation was executed. @@ -290,6 +291,7 @@ export function generateArtifact( result: string, hasBreakingChange: boolean, hasManagementPlaneSpecs: boolean, + hasTypeSpecProjects: boolean, stagedArtifactsFolder: string, apiViewRequestData: APIViewRequestData[], sdkGenerationExecuted: boolean = true, @@ -309,6 +311,7 @@ export function generateArtifact( if (sdkGenerationExecuted) { isSpecGenSdkCheckRequired = getRequiredSettingValue( hasManagementPlaneSpecs, + hasTypeSpecProjects, commandInput.sdkLanguage as SdkName, ); } @@ -317,6 +320,7 @@ export function generateArtifact( const artifactInfo: SpecGenSdkArtifactInfo = { language: commandInput.sdkLanguage, result, + headSha: commandInput.specCommitSha, prNumber: commandInput.prNumber, labelAction: hasBreakingChange, isSpecGenSdkCheckRequired, @@ -358,13 +362,19 @@ export function getServiceFolderPath(specConfigPath: string): string { /** * Get the required setting value for the SDK check based on the spec PR types. * @param hasManagementPlaneSpecs - A flag indicating whether there are management plane specs. + * @param hasTypeSpecProjects - A flag indicating whether there are TypeSpec projects. * @param sdkName - The SDK name. * @returns boolean indicating whether the SDK check is required. */ export function getRequiredSettingValue( hasManagementPlaneSpecs: boolean, + hasTypeSpecProjects: boolean, sdkName: SdkName, ): boolean { + // If the SDK is azure-sdk-for-net, return false if there are no TypeSpec projects. + if (sdkName === "azure-sdk-for-net" && !hasTypeSpecProjects) { + return false; + } if (hasManagementPlaneSpecs) { return SpecGenSdkRequiredSettings[sdkName].managementPlane; } else { diff --git a/eng/tools/spec-gen-sdk-runner/src/commands.ts b/eng/tools/spec-gen-sdk-runner/src/commands.ts index 5bcb0a27df60..07ebe91feb45 100644 --- a/eng/tools/spec-gen-sdk-runner/src/commands.ts +++ b/eng/tools/spec-gen-sdk-runner/src/commands.ts @@ -1,9 +1,5 @@ import fs from "node:fs"; import path from "node:path"; -import { runSpecGenSdkCommand, resetGitRepo, SpecConfigs } from "./utils.js"; -import { LogLevel, logMessage, vsoAddAttachment, vsoLogIssue } from "./log.js"; -import { APIViewRequestData, SpecGenSdkCmdInput } from "./types.js"; -import { detectChangedSpecConfigFiles } from "./spec-helpers.js"; import { generateArtifact, getBreakingChangeInfo, @@ -15,6 +11,10 @@ import { prepareSpecGenSdkCommand, setPipelineVariables, } from "./command-helpers.js"; +import { LogLevel, logMessage, vsoAddAttachment, vsoLogIssue } from "./log.js"; +import { detectChangedSpecConfigFiles } from "./spec-helpers.js"; +import { APIViewRequestData, SpecGenSdkCmdInput } from "./types.js"; +import { resetGitRepo, runSpecGenSdkCommand, SpecConfigs } from "./utils.js"; /** * Generate SDK for a single spec. @@ -87,6 +87,7 @@ export async function generateSdkForSpecPr(): Promise { let executionReport; let changedSpecPathText = ""; let hasManagementPlaneSpecs = false; + let hasTypeSpecProjects = false; let overallRunHasBreakingChange = false; let currentRunHasBreakingChange = false; let sdkGenerationExecuted = true; @@ -147,6 +148,9 @@ export async function generateSdkForSpecPr(): Promise { // Read the execution report to aggreate the generation results executionReport = getExecutionReport(commandInput); currentExecutionResult = executionReport.executionResult; + if (executionReport.generateFromTypeSpec) { + hasTypeSpecProjects = true; + } if (executionReport.stagedArtifactsFolder) { stagedArtifactsFolder = executionReport.stagedArtifactsFolder; @@ -181,6 +185,7 @@ export async function generateSdkForSpecPr(): Promise { overallExecutionResult, overallRunHasBreakingChange, hasManagementPlaneSpecs, + hasTypeSpecProjects, stagedArtifactsFolder, apiViewRequestData, sdkGenerationExecuted, diff --git a/eng/tools/spec-gen-sdk-runner/src/index.ts b/eng/tools/spec-gen-sdk-runner/src/index.ts index a66fe5eaa8fe..a14f53c8a409 100644 --- a/eng/tools/spec-gen-sdk-runner/src/index.ts +++ b/eng/tools/spec-gen-sdk-runner/src/index.ts @@ -1,12 +1,12 @@ -import { exit } from "node:process"; -import path from "node:path"; import { existsSync, mkdirSync } from "node:fs"; -import { getArgumentValue } from "./utils.js"; +import path from "node:path"; +import { exit } from "node:process"; import { generateSdkForBatchSpecs, generateSdkForSingleSpec, generateSdkForSpecPr, } from "./commands.js"; +import { getArgumentValue } from "./utils.js"; export async function main() { // Get the arguments passed to the script diff --git a/eng/tools/spec-gen-sdk-runner/src/spec-helpers.ts b/eng/tools/spec-gen-sdk-runner/src/spec-helpers.ts index f79c7ce07248..7763aef2f9bc 100644 --- a/eng/tools/spec-gen-sdk-runner/src/spec-helpers.ts +++ b/eng/tools/spec-gen-sdk-runner/src/spec-helpers.ts @@ -1,18 +1,18 @@ import path from "node:path"; +import { logMessage } from "./log.js"; +import { SpecGenSdkCmdInput } from "./types.js"; import { + createCombinedSpecs, getChangedFiles, + getLastPathSegment, + groupPathsByService, searchRelatedParentFolders, - searchSharedLibrary, searchRelatedTypeSpecProjectBySharedLibrary, - groupPathsByService, - createCombinedSpecs, - type SpecResults, + searchSharedLibrary, type ChangedSpecs, type SpecConfigs, - getLastPathSegment, + type SpecResults, } from "./utils.js"; -import { logMessage } from "./log.js"; -import { SpecGenSdkCmdInput } from "./types.js"; export const readmeMdRegex = /^readme.md$/; export const typespecProjectRegex = /^tspconfig.yaml$/; diff --git a/eng/tools/spec-gen-sdk-runner/src/types.ts b/eng/tools/spec-gen-sdk-runner/src/types.ts index e50d63947b77..7cdbee04afa6 100644 --- a/eng/tools/spec-gen-sdk-runner/src/types.ts +++ b/eng/tools/spec-gen-sdk-runner/src/types.ts @@ -44,6 +44,7 @@ export type VsoLogs = Map< export interface SpecGenSdkArtifactInfo { language: string; result: string; + headSha: string; prNumber?: string; labelAction?: boolean; isSpecGenSdkCheckRequired: boolean; diff --git a/eng/tools/spec-gen-sdk-runner/src/utils.ts b/eng/tools/spec-gen-sdk-runner/src/utils.ts index 32d01a70384b..55ac719e30c9 100644 --- a/eng/tools/spec-gen-sdk-runner/src/utils.ts +++ b/eng/tools/spec-gen-sdk-runner/src/utils.ts @@ -1,8 +1,8 @@ -import { spawn, spawnSync, exec } from "node:child_process"; -import path from "node:path"; +import { exec, spawn, spawnSync } from "node:child_process"; import fs from "node:fs"; -import { LogLevel, logMessage } from "./log.js"; +import path from "node:path"; import { promisify } from "node:util"; +import { LogLevel, logMessage } from "./log.js"; type Dirent = fs.Dirent; diff --git a/eng/tools/spec-gen-sdk-runner/test/command-helpers.test.ts b/eng/tools/spec-gen-sdk-runner/test/command-helpers.test.ts index 9da7002439cd..be8adf2a1321 100644 --- a/eng/tools/spec-gen-sdk-runner/test/command-helpers.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/command-helpers.test.ts @@ -1,22 +1,22 @@ -import { describe, test, expect, vi, beforeEach } from "vitest"; -import * as log from "../src/log.js"; -import * as utils from "../src/utils.js"; -import * as specHelpers from "../src/spec-helpers.js"; -import { fileURLToPath } from "node:url"; import fs from "node:fs"; import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { beforeEach, describe, expect, test, vi } from "vitest"; import { + generateArtifact, getBreakingChangeInfo, getRequiredSettingValue, getSpecPaths, logIssuesToPipeline, parseArguments, prepareSpecGenSdkCommand, - generateArtifact, setPipelineVariables, } from "../src/command-helpers.js"; +import * as log from "../src/log.js"; import { LogLevel } from "../src/log.js"; +import * as specHelpers from "../src/spec-helpers.js"; import { APIViewRequestData } from "../src/types.js"; +import * as utils from "../src/utils.js"; // Get the absolute path to the repo root const currentFilePath = fileURLToPath(import.meta.url); @@ -421,13 +421,15 @@ describe("commands.ts", () => { runMode: "", localSpecRepoPath: "", localSdkRepoPath: "", + prNumber: "123", sdkRepoName: "", - specCommitSha: "", + specCommitSha: "abc123", specRepoHttpsUrl: "", }; const mockResult = "succeeded"; const mockhasBreakingChange = false; const mockhasManagementPlaneSpecs = false; + const mockhasTypeSpecProjects = false; const mockStagedArtifactsFolder = "mockStagedArtifactsFolder"; const mockApiViewRequestData: APIViewRequestData[] = []; const result = generateArtifact( @@ -435,6 +437,7 @@ describe("commands.ts", () => { mockResult, mockhasBreakingChange, mockhasManagementPlaneSpecs, + mockhasTypeSpecProjects, mockStagedArtifactsFolder, mockApiViewRequestData, ); @@ -455,6 +458,8 @@ describe("commands.ts", () => { { language: "azure-sdk-for-js", result: "succeeded", + headSha: "abc123", + prNumber: "123", labelAction: false, isSpecGenSdkCheckRequired: false, apiViewRequestData: [], @@ -505,6 +510,7 @@ describe("commands.ts", () => { const mockResult = "failed"; const mockhasBreakingChange = false; const mockhasManagementPlaneSpecs = false; + const mockhasTypeSpecProjects = false; const mockStagedArtifactsFolder = ""; const mockApiViewRequestData: APIViewRequestData[] = []; const result = generateArtifact( @@ -512,6 +518,7 @@ describe("commands.ts", () => { mockResult, mockhasBreakingChange, mockhasManagementPlaneSpecs, + mockhasTypeSpecProjects, mockStagedArtifactsFolder, mockApiViewRequestData, ); @@ -551,6 +558,7 @@ describe("commands.ts", () => { // Using true for hasManagementPlaneSpecs, which would normally make isSpecGenSdkCheckRequired=true // for Go SDK (as tested in the getRequiredSettingValue tests) const mockhasManagementPlaneSpecs = true; + const mockhasTypeSpecProjects = true; const mockStagedArtifactsFolder = "mockStagedArtifactsFolder"; const mockApiViewRequestData: APIViewRequestData[] = []; @@ -560,6 +568,7 @@ describe("commands.ts", () => { mockResult, mockhasBreakingChange, mockhasManagementPlaneSpecs, + mockhasTypeSpecProjects, mockStagedArtifactsFolder, mockApiViewRequestData, false, // sdkGenerationExecuted = false @@ -580,6 +589,7 @@ describe("commands.ts", () => { { language: "azure-sdk-for-go", result: "succeeded", + headSha: "", labelAction: false, isSpecGenSdkCheckRequired: false, // This should be false when sdkGenerationExecuted is false apiViewRequestData: [], @@ -593,23 +603,45 @@ describe("commands.ts", () => { describe("getRequiredSettingValue", () => { test("should return managementPlane setting when hasManagementPlaneSpecs is true", () => { - const result = getRequiredSettingValue(true, "azure-sdk-for-go"); + const result = getRequiredSettingValue(true, true, "azure-sdk-for-go"); // Based on the constants in types.ts, Go SDK requires check for management plane expect(result).toBe(true); - const result2 = getRequiredSettingValue(true, "azure-sdk-for-net"); - // Based on the constants in types.ts, .NET SDK does not require check for management plane + const result2 = getRequiredSettingValue(true, true, "azure-sdk-for-net"); + // .NET SDK set (managementPlane: false) expect(result2).toBe(false); }); test("should return dataPlane setting when hasManagementPlaneSpecs is false", () => { - const result = getRequiredSettingValue(false, "azure-sdk-for-go"); + const result = getRequiredSettingValue(false, true, "azure-sdk-for-go"); // Based on the constants in types.ts, Go SDK requires check for data plane expect(result).toBe(true); - const result2 = getRequiredSettingValue(false, "azure-sdk-for-js"); + const result2 = getRequiredSettingValue(false, true, "azure-sdk-for-js"); // Based on the constants in types.ts, JS SDK does not require check for data plane expect(result2).toBe(false); + + const result3 = getRequiredSettingValue(false, true, "azure-sdk-for-net"); + // .NET SDK set (dataplane: false) + expect(result3).toBe(false); + }); + + test("should return false for azure-sdk-for-net when hasTypeSpecProjects is false", () => { + // Test the special case for .NET SDK without TypeSpec projects + const result = getRequiredSettingValue(true, false, "azure-sdk-for-net"); + expect(result).toBe(false); + + const result2 = getRequiredSettingValue(false, false, "azure-sdk-for-net"); + expect(result2).toBe(false); + }); + + test("should follow normal rules for other SDKs when hasTypeSpecProjects is false", () => { + // Other SDKs should follow normal rules regardless of hasTypeSpecProjects + const result = getRequiredSettingValue(true, false, "azure-sdk-for-go"); + expect(result).toBe(true); + + const result2 = getRequiredSettingValue(false, false, "azure-sdk-for-python"); + expect(result2).toBe(true); }); }); }); diff --git a/eng/tools/spec-gen-sdk-runner/test/commands.test.ts b/eng/tools/spec-gen-sdk-runner/test/commands.test.ts index a10c890d766a..0b55917251dc 100644 --- a/eng/tools/spec-gen-sdk-runner/test/commands.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/commands.test.ts @@ -1,16 +1,16 @@ -import { describe, test, expect, vi, beforeEach, type Mock } from "vitest"; -import * as utils from "../src/utils.js"; +import fs from "node:fs"; +import path from "node:path"; +import { beforeEach, describe, expect, test, vi, type Mock } from "vitest"; +import * as commandHelpers from "../src/command-helpers.js"; import { generateSdkForBatchSpecs, generateSdkForSingleSpec, generateSdkForSpecPr, } from "../src/commands.js"; -import * as commandHelpers from "../src/command-helpers.js"; import * as log from "../src/log.js"; -import * as changeFiles from "../src/spec-helpers.js"; -import fs from "node:fs"; -import path from "node:path"; import { LogLevel } from "../src/log.js"; +import * as changeFiles from "../src/spec-helpers.js"; +import * as utils from "../src/utils.js"; function getNormalizedFsCalls(mockFn: Mock): unknown[][] { return mockFn.mock.calls.map((args: unknown[]) => { @@ -241,6 +241,7 @@ describe("generateSdkForSpecPr", () => { "succeeded", // overallExecutionResult false, // overallRunHasBreakingChange true, // hasManagementPlaneSpecs + false, // hasTypeSpecProjects "", // stagedArtifactsFolder [], // apiViewRequestData true, // sdkGenerationExecuted @@ -283,6 +284,7 @@ describe("generateSdkForSpecPr", () => { "succeeded", // overallExecutionResult should be set to "succeeded" false, // overallRunHasBreakingChange false, // hasManagementPlaneSpecs + false, // hasTypeSpecProjects "", // stagedArtifactsFolder [], // apiViewRequestData false, // sdkGenerationExecuted should be set to false @@ -321,6 +323,7 @@ describe("generateSdkForSpecPr", () => { "", // overallExecutionResult is empty because no spec was actually processed false, // overallRunHasBreakingChange false, // hasManagementPlaneSpecs + false, // hasTypeSpecProjects "", // stagedArtifactsFolder [], // apiViewRequestData true, // sdkGenerationExecuted is true because there were some changed specs but they had no valid config diff --git a/eng/tools/spec-gen-sdk-runner/test/log.test.ts b/eng/tools/spec-gen-sdk-runner/test/log.test.ts index 60c3fb569262..527e13bb95c8 100644 --- a/eng/tools/spec-gen-sdk-runner/test/log.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/log.test.ts @@ -1,11 +1,11 @@ -import { describe, test, expect, vi, beforeEach, afterEach } from "vitest"; +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { - logMessage, - LogLevel, LogIssueType, + LogLevel, + logMessage, + setVsoVariable, vsoAddAttachment, vsoLogIssue, - setVsoVariable, } from "../src/log.js"; const logSpy = vi.spyOn(console, "log").mockImplementation(() => { diff --git a/eng/tools/spec-gen-sdk-runner/test/spec-helpers.test.ts b/eng/tools/spec-gen-sdk-runner/test/spec-helpers.test.ts index 144aed93b518..696e3132ca4c 100644 --- a/eng/tools/spec-gen-sdk-runner/test/spec-helpers.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/spec-helpers.test.ts @@ -1,17 +1,17 @@ -import { describe, test, expect, vi, beforeEach } from "vitest"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { beforeEach, describe, expect, test, vi } from "vitest"; import { detectChangedSpecConfigFiles, groupSpecConfigPaths, processTypeSpecProjectsV2FolderStructure, } from "../src/spec-helpers.js"; import { SpecGenSdkCmdInput } from "../src/types.js"; -import { fileURLToPath } from "node:url"; -import path from "node:path"; import { type ChangedSpecs, type SpecConfigs, - normalizePath, getChangedFiles, + normalizePath, } from "../src/utils.js"; vi.mock("../src/utils.js", async () => { diff --git a/eng/tools/spec-gen-sdk-runner/test/utils/createCombinedSpecs.test.ts b/eng/tools/spec-gen-sdk-runner/test/utils/createCombinedSpecs.test.ts index 591a997b5109..c6d72817c6cc 100644 --- a/eng/tools/spec-gen-sdk-runner/test/utils/createCombinedSpecs.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/utils/createCombinedSpecs.test.ts @@ -1,6 +1,6 @@ -import { describe, test, expect } from "vitest"; -import { createCombinedSpecs, type SpecResults } from "../../src/utils.js"; import path from "node:path"; +import { describe, expect, test } from "vitest"; +import { createCombinedSpecs, type SpecResults } from "../../src/utils.js"; describe("createCombinedSpecs", () => { test("combines specs from readme and typespec paths", () => { diff --git a/eng/tools/spec-gen-sdk-runner/test/utils/extractServiceName.test.ts b/eng/tools/spec-gen-sdk-runner/test/utils/extractServiceName.test.ts index c4119e911a58..ca8ccdbf22ce 100644 --- a/eng/tools/spec-gen-sdk-runner/test/utils/extractServiceName.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/utils/extractServiceName.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { extractServiceName } from "../../src/utils"; describe("extractServiceName", () => { diff --git a/eng/tools/spec-gen-sdk-runner/test/utils/findParentWithFile.test.ts b/eng/tools/spec-gen-sdk-runner/test/utils/findParentWithFile.test.ts index d1a00509a538..4a59be236a73 100644 --- a/eng/tools/spec-gen-sdk-runner/test/utils/findParentWithFile.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/utils/findParentWithFile.test.ts @@ -1,8 +1,8 @@ -import { describe, test, expect } from "vitest"; -import { findParentWithFile } from "../../src/utils.js"; -import { fileURLToPath } from "node:url"; import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, test } from "vitest"; import { typespecProjectRegex } from "../../src/spec-helpers.js"; +import { findParentWithFile } from "../../src/utils.js"; describe("findParentWithFile", () => { // Get the absolute path to the repo root diff --git a/eng/tools/spec-gen-sdk-runner/test/utils/getLastPathSegment.test.ts b/eng/tools/spec-gen-sdk-runner/test/utils/getLastPathSegment.test.ts index 3f40b940182b..ac37454c4913 100644 --- a/eng/tools/spec-gen-sdk-runner/test/utils/getLastPathSegment.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/utils/getLastPathSegment.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { getLastPathSegment } from "../../src/utils.js"; describe("getLastPathSegment", () => { diff --git a/eng/tools/spec-gen-sdk-runner/test/utils/groupPathsByService.test.ts b/eng/tools/spec-gen-sdk-runner/test/utils/groupPathsByService.test.ts index b91c582bbb71..a5c0802b0e0c 100644 --- a/eng/tools/spec-gen-sdk-runner/test/utils/groupPathsByService.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/utils/groupPathsByService.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from "vitest"; +import { describe, expect, test } from "vitest"; import { groupPathsByService } from "../../src/utils.js"; describe("groupPathsByService", () => { diff --git a/eng/tools/spec-gen-sdk-runner/test/utils/searchRelatedParentFolders.test.ts b/eng/tools/spec-gen-sdk-runner/test/utils/searchRelatedParentFolders.test.ts index 7a87eb8b3196..52b10464a230 100644 --- a/eng/tools/spec-gen-sdk-runner/test/utils/searchRelatedParentFolders.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/utils/searchRelatedParentFolders.test.ts @@ -1,8 +1,8 @@ -import { describe, test, expect } from "vitest"; -import { searchRelatedParentFolders } from "../../src/utils.js"; -import { fileURLToPath } from "node:url"; import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, test } from "vitest"; import { readmeMdRegex, typespecProjectRegex } from "../../src/spec-helpers.js"; +import { searchRelatedParentFolders } from "../../src/utils.js"; describe("searchRelatedParentFolders", () => { // Get the absolute path to the repo root diff --git a/eng/tools/spec-gen-sdk-runner/test/utils/searchRelatedTypeSpecProjectBySharedLibrary.test.ts b/eng/tools/spec-gen-sdk-runner/test/utils/searchRelatedTypeSpecProjectBySharedLibrary.test.ts index 227bb3521592..0bc1e91fd752 100644 --- a/eng/tools/spec-gen-sdk-runner/test/utils/searchRelatedTypeSpecProjectBySharedLibrary.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/utils/searchRelatedTypeSpecProjectBySharedLibrary.test.ts @@ -1,7 +1,7 @@ -import { describe, test, expect } from "vitest"; -import { searchRelatedTypeSpecProjectBySharedLibrary } from "../../src/utils.js"; -import { fileURLToPath } from "node:url"; import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, test } from "vitest"; +import { searchRelatedTypeSpecProjectBySharedLibrary } from "../../src/utils.js"; describe("searchRelatedTypeSpecProjectBySharedLibrary", () => { // Get the absolute path to the repo root diff --git a/eng/tools/spec-gen-sdk-runner/test/utils/searchSharedLibrary.test.ts b/eng/tools/spec-gen-sdk-runner/test/utils/searchSharedLibrary.test.ts index 5cd5cd908fa4..eacf838b7713 100644 --- a/eng/tools/spec-gen-sdk-runner/test/utils/searchSharedLibrary.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/utils/searchSharedLibrary.test.ts @@ -1,8 +1,8 @@ -import { describe, test, expect } from "vitest"; -import { searchSharedLibrary } from "../../src/utils.js"; -import { fileURLToPath } from "node:url"; import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, test } from "vitest"; import { typespecProjectSharedLibraryRegex } from "../../src/spec-helpers.js"; +import { searchSharedLibrary } from "../../src/utils.js"; describe("searchSharedLibrary", () => { // Get the absolute path to the repo root diff --git a/eng/tools/spec-gen-sdk-runner/test/utils/utils.test.ts b/eng/tools/spec-gen-sdk-runner/test/utils/utils.test.ts index 4dec6f2a9ded..13044a155ce2 100644 --- a/eng/tools/spec-gen-sdk-runner/test/utils/utils.test.ts +++ b/eng/tools/spec-gen-sdk-runner/test/utils/utils.test.ts @@ -1,15 +1,15 @@ -import { describe, test, expect, beforeEach, vi } from "vitest"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { beforeEach, describe, expect, test, vi } from "vitest"; import { findFilesRecursive, findReadmeFiles, - getRelativePathFromSpecification, getArgumentValue, + getRelativePathFromSpecification, mapToObject, - objectToMap, normalizePath, + objectToMap, } from "../../src/utils.js"; -import { fileURLToPath } from "node:url"; -import path from "node:path"; // Get the absolute path to the repo root const currentFilePath = fileURLToPath(import.meta.url); diff --git a/eng/tools/summarize-impact/README.md b/eng/tools/summarize-impact/README.md new file mode 100644 index 000000000000..674eddddae5e --- /dev/null +++ b/eng/tools/summarize-impact/README.md @@ -0,0 +1,24 @@ +# `Summarize Impact` + +This tool models the PR and produces an artifact that is consumed by the `summarize-checks` check. + +The schema of the artifact looks like: + +```typescript +export type ImpactAssessment = { + prType: string[]; + resourceManagerRequired: boolean; + suppressionReviewRequired: boolean; + versioningReviewRequired: boolean; + breakingChangeReviewRequired: boolean; + isNewApiVersion: boolean; + rpaasExceptionRequired: boolean; + rpaasRpNotInPrivateRepo: boolean; + rpaasChange: boolean; + newRP: boolean; + rpaasRPMissing: boolean; + typeSpecChanged: boolean; + isDraft: boolean; + labelContext: LabelContext; +}; +``` diff --git a/eng/tools/summarize-impact/cmd/summarize-impact.js b/eng/tools/summarize-impact/cmd/summarize-impact.js new file mode 100755 index 000000000000..abdd7016b6cf --- /dev/null +++ b/eng/tools/summarize-impact/cmd/summarize-impact.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +import { main } from "../dist/src/cli.js"; + +await main(); diff --git a/eng/tools/summarize-impact/package.json b/eng/tools/summarize-impact/package.json new file mode 100644 index 000000000000..cb5b023ebf9d --- /dev/null +++ b/eng/tools/summarize-impact/package.json @@ -0,0 +1,41 @@ +{ + "name": "@azure-tools/summarize-impact", + "private": true, + "type": "module", + "main": "dist/src/main.js", + "bin": { + "summarize-impact": "cmd/summarize-impact.js" + }, + "scripts": { + "build": "tsc --build", + "format": "prettier . --ignore-path ../.prettierignore --write", + "format:check": "prettier . --ignore-path ../.prettierignore --check", + "format:check:ci": "prettier . --ignore-path ../.prettierignore --check --log-level debug", + "test": "vitest --run", + "test:ci": "vitest run --coverage --reporter=verbose" + }, + "dependencies": { + "@azure-tools/openapi-tools-common": "^1.2.2", + "@azure-tools/specs-shared": "file:../../../.github/shared", + "@azure/openapi-markdown": "0.9.4", + "@octokit/rest": "^22.0.0", + "commonmark": "0.31.2", + "glob": "^11.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.20", + "simple-git": "^3.27.0" + }, + "devDependencies": { + "@types/commonmark": "0.27.10", + "@types/glob": "^9.0.0", + "@types/lodash": "^4.14.161", + "@types/node": "^20.0.0", + "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", + "typescript": "~5.8.2", + "vitest": "^3.0.7" + }, + "engines": { + "node": ">=20.0.0" + } +} diff --git a/eng/tools/summarize-impact/src/ImpactAssessment.ts b/eng/tools/summarize-impact/src/ImpactAssessment.ts new file mode 100644 index 000000000000..e6c331ad052c --- /dev/null +++ b/eng/tools/summarize-impact/src/ImpactAssessment.ts @@ -0,0 +1,14 @@ +export type ImpactAssessment = { + resourceManagerRequired: boolean; + dataPlaneRequired: boolean; + suppressionReviewRequired: boolean; + isNewApiVersion: boolean; + rpaasExceptionRequired: boolean; + rpaasRpNotInPrivateRepo: boolean; + rpaasChange: boolean; + newRP: boolean; + rpaasRPMissing: boolean; + typeSpecChanged: boolean; + isDraft: boolean; + targetBranch: string; +}; diff --git a/eng/tools/summarize-impact/src/PRContext.ts b/eng/tools/summarize-impact/src/PRContext.ts new file mode 100644 index 000000000000..3ad0b676b5fc --- /dev/null +++ b/eng/tools/summarize-impact/src/PRContext.ts @@ -0,0 +1,335 @@ +import * as fs from "fs"; +import { dirname, join } from "path"; + +import { parseMarkdown } from "@azure-tools/openapi-tools-common"; +import * as amd from "@azure/openapi-markdown"; + +import { example, readme, swagger, typespec } from "@azure-tools/specs-shared/changed-files"; +import { includesFolder } from "@azure-tools/specs-shared/path"; +import { Readme } from "@azure-tools/specs-shared/readme"; +import { SpecModel } from "@azure-tools/specs-shared/spec-model"; + +import { DiffResult, ReadmeTag, TagConfigDiff, TagDiff } from "./diff-types.js"; +import { LabelContext } from "./labelling-types.js"; + +export type FileListInfo = { + additions: string[]; + modifications: string[]; + deletions: string[]; + renames: { from: string; to: string }[]; + total: number; +}; + +export type PRContextOptions = { + fileList?: FileListInfo; + sourceBranch: string; + targetBranch: string; + sha: string; + repo: string; + owner: string; + prNumber: string; + isDraft: boolean; +}; + +export class PRContext { + // we are starting with checking out before and after to different directories + // sourceDirectory corresponds to "after". EG the PR context is the "after" state of the PR. + // The "before" state is the git root directory without the changes aka targetDirectory + sourceDirectory: string; + targetDirectory: string; + sourceSpecModel?: SpecModel; + targetSpecModel?: SpecModel; + fileList?: FileListInfo; + sourceBranch: string; + targetBranch: string; + sha: string; + repo: string; + owner: string; + prNumber: string; + isDraft: boolean; + labelContext: LabelContext; + + constructor( + sourceDirectory: string, + targetDirectory: string, + labelContext: LabelContext, + options: PRContextOptions, + ) { + this.sourceDirectory = sourceDirectory; + this.targetDirectory = targetDirectory; + this.sourceSpecModel = new SpecModel(sourceDirectory); + this.targetSpecModel = new SpecModel(targetDirectory); + this.labelContext = labelContext; + this.sourceBranch = options.sourceBranch; + this.targetBranch = options.targetBranch; + this.sha = options.sha; + this.repo = options.repo; + this.prNumber = options.prNumber; + this.fileList = options.fileList; + this.isDraft = options.isDraft; + this.owner = options.owner; + } + + getChangedFiles(): string[] { + if (!this.fileList) { + return []; + } + + const changedFiles: string[] = (this.fileList.additions || []) + .concat(this.fileList.modifications || []) + .concat(this.fileList.deletions || []) + .concat(this.fileList.renames.map((r) => r.to) || []); + return changedFiles; + } + + // todo get rid of async here not necessary + // todo store the results + getTypeSpecDiffs(): DiffResult { + if (!this.fileList) { + return { + additions: [], + deletions: [], + changes: [], + }; + } + + const additions = this.fileList.additions.filter((file) => typespec(file)); + const deletions = this.fileList.deletions.filter((file) => typespec(file)); + const changes = this.fileList.modifications.filter((file) => typespec(file)); + + return { + additions, + deletions, + changes, + }; + } + + getSwaggerDiffs(): DiffResult { + if (!this.fileList) { + return { + additions: [], + deletions: [], + changes: [], + }; + } + + const additions = this.fileList.additions.filter((file) => swagger(file)); + const deletions = this.fileList.deletions.filter((file) => swagger(file)); + const changes = this.fileList.modifications.filter((file) => swagger(file)); + + return { + additions, + deletions, + changes, + }; + } + + getExampleDiffs(): DiffResult { + if (!this.fileList) { + return { + additions: [], + deletions: [], + changes: [], + }; + } + + const additions = this.fileList.additions.filter((file) => example(file)); + const deletions = this.fileList.deletions.filter((file) => example(file)); + const changes = this.fileList.modifications.filter((file) => example(file)); + + return { + additions, + deletions, + changes, + }; + } + + async getTagsFromReadme(readmePath: string): Promise { + const tags = await new Readme(readmePath).getTags(); + return [...tags.values()].map((tag) => tag.name); + } + + async getPossibleParentConfigurations(): Promise { + console.log("ENTER definition getPossibleParentConfigurations"); + const changedFiles = await this.getChangedFiles(); + console.log(`Detect changes in the PR:\n${JSON.stringify(changedFiles, null, 2)}`); + const readmes = changedFiles.filter((f) => readme(f)); + + const visitedFolder = new Set(); + changedFiles + .filter((f) => [".md", ".json", ".yaml", ".yml"].some((p) => f.endsWith(p))) + .forEach((f) => { + let dir = dirname(f); + if (visitedFolder.has(dir)) { + return; + } + while (includesFolder(dir, "specification")) { + if (visitedFolder.has(dir)) { + break; + } + visitedFolder.add(dir); + const possibleReadme = join(dir, "readme.md"); + if (fs.existsSync(possibleReadme)) { + if (!readmes.includes(possibleReadme)) { + readmes.push(possibleReadme); + } + break; + } + dir = dirname(dir); + } + }); + console.log("RETURN definition getPossibleParentConfigurations"); + return readmes; + } + + getAllTags(readMeContent: string): string[] { + // todo: we should refactor this to use the spec model, but I haven't had the the time to explicitly + // diff what oad does does here, so I'm leaving it alone for now until I can build some strong unit tests around this. + const cmd = parseMarkdown(readMeContent); + const allTags = new amd.ReadMeManipulator( + { error: (_msg: string) => {} }, + new amd.ReadMeBuilder(), + ).getAllTags(cmd); + return [...allTags]; + } + + async getInputFiles(readMeContent: string, tag: string) { + // todo: we should refactor this to use spec model, but I haven't had time to isolate exactly what + // openapi-markdown is doing here, so I'm just going to use the same logic for now + const cmd = parseMarkdown(readMeContent); + return amd.getInputFilesForTag(cmd.markDown, tag); + } + + async getChangingTags(): Promise { + // we are retrieving all the readme changes, no matter if they're additions, deletions, etc + // Additionally, we're also retrieving all the readme files that may be affected by the changes in the PR, which means + // climbing up the directory tree until we find a readme.md file if necessary. + const allAffectedReadmes: string[] = await this.getPossibleParentConfigurations(); + console.log(`all affected readme are:`); + console.log(JSON.stringify(allAffectedReadmes, null, 2)); + const Diffs: TagDiff[] = []; + for (const readme of allAffectedReadmes) { + const oldReadme = join(this.targetDirectory, readme); + const newReadme = join(this.sourceDirectory, readme); + // As the readme may be not existing , need to check if it's existing. + // we are checking target and source individually, because the readme may not exist in the target branch + const oldTags: string[] = fs.existsSync(oldReadme) + ? [...(await new Readme(oldReadme).getTags()).keys()] + : []; + const newTags: string[] = fs.existsSync(newReadme) + ? [...(await new Readme(newReadme).getTags()).keys()] + : []; + const intersect = oldTags + .filter((t) => newTags.includes(t)) + .filter((tag) => tag !== "all-api-versions"); + const insertions = newTags.filter((t) => !oldTags.includes(t)); + const deletions = oldTags.filter((t) => !newTags.includes(t)); + const differences: TagConfigDiff[] = []; + + // todo: we need to ensure we get ALL effected swaggers by their relationships, not just the swagger files that are directly changed in the PR. + // right now I'm just going to filter to the ones that are directly changed in the PR. + // this is a temporary solution, we need to ensure we get all of the swagger files + // that are affected by the changes in the readme. SpecModel will be useful for this + // we want to get all of the swagger files that are affected by the changes in the readme. + // we can do that using the specmodel + // const allAffectedInputFiles = await this.getRealAffectedSwagger(readme) + // talk to Mike and ask him how we could get all affected swagger files from a readme path. + // I want to say that readme(readme).specModel.getAffectedSwaggerFiles will work? + const allAffectedInputFiles = await (await this.getChangedFiles()).filter((f) => swagger(f)); + console.log(`all affected swagger files in ${readme} are:`); + console.log(JSON.stringify(allAffectedInputFiles, null, 2)); + const getChangedInputFiles = async (tag: string) => { + const readmeContent = await fs.promises.readFile(newReadme, "utf-8"); + const inputFiles = await this.getInputFiles(readmeContent, tag); + if (inputFiles) { + const changedInputFiles = (inputFiles as string[]).filter((f) => + allAffectedInputFiles.some((a) => a.endsWith(f)), + ); + return changedInputFiles; + } + return []; + }; + const changes: string[] = []; + for (const tag of intersect) { + const tagDiff: TagConfigDiff = { name: tag }; + const changedInputFiles = await getChangedInputFiles(tag); + if (changedInputFiles.length) { + console.log("found changed input files under tag:" + tag); + tagDiff.changedInputFiles = changedInputFiles; + changes.push(tag); + } + } + Diffs.push({ readme, insertions, deletions, changes, differences }); + } + return Diffs; + } + + // this function is based upon LocalDirContext.getReadmeDiffs() and CommonPRContext.getReadmeDiffs() which are + // very different from each other (because why not). This implementation is based MOSTLY on CommonPRContext + async getReadmeDiffs(): Promise> { + // this gets all of the readme diffs + // const readmeAdditions = this.fileList?.additions.filter(file => readme(file)) || []; + // const readmeChanges = this.fileList?.modifications.filter(file => readme(file)) + // const readmeDeletions = this.fileList?.deletions.filter(file => readme(file)); + //const changedFiles: DiffFileResult | undefined = await this.localPRContext?.getChangingFiles(); + + const changedFiles = await this.fileList; + const tagDiffs = (await this.getChangingTags()) || []; + + const readmeTagDiffs = tagDiffs + ?.filter((tagDiff: TagDiff) => readme(tagDiff.readme)) + .map((tagDiff: TagDiff) => { + return { + readme: tagDiff.readme, + tags: { + changes: tagDiff.changes, + deletions: tagDiff.deletions, + additions: tagDiff.insertions, + }, + } as ReadmeTag; + }); + + const readmeTagDiffsInAddedReadmeFiles: ReadmeTag[] = readmeTagDiffs.filter( + (readmeTag: ReadmeTag): boolean => + Boolean(changedFiles?.additions.includes(readmeTag.readme)), + ); + const readmeTagDiffsInDeletedReadmeFiles: ReadmeTag[] = readmeTagDiffs.filter( + (readmeTag: ReadmeTag): boolean => + Boolean(changedFiles?.deletions.includes(readmeTag.readme)), + ); + const readmeTagDiffsInChangedReadmeFiles: ReadmeTag[] = readmeTagDiffs.filter( + (readmeTag: ReadmeTag): boolean => { + // The README file that contains the API version tags that have been diffed in given readmeTag (i.e. readmeTag.tags) + // has been modified. + const readmeModified = changedFiles?.modifications.includes(readmeTag.readme); + + // The README was not modified, added or deleted; just the specs belonging to the API version tags in the README were modified. + // In such case we assume the README is 'changed' in the sense the specs belonging to the API version tags in the README were modified. + // The README file itself wasn't modified. + // We assume here the README is present in the repository - otherwise it would show up as 'deleted' or would not + // appear as readmeTag.readme in any of the readmeTagDiffs. + const readmeUnchanged = + !changedFiles?.additions.includes(readmeTag.readme) && + !changedFiles?.deletions.includes(readmeTag.readme); + + return readmeModified || readmeUnchanged; + }, + ); + + const result = { + additions: readmeTagDiffsInAddedReadmeFiles, + deletions: readmeTagDiffsInDeletedReadmeFiles, + changes: readmeTagDiffsInChangedReadmeFiles, + }; + + console.log( + `RETURN definition CommonPRContext.getReadmeDiffs. ` + + `changedFiles: ${JSON.stringify(changedFiles)}, ` + + `tagDiffs: ${JSON.stringify(tagDiffs)}, ` + + `readmeTagDiffs: ${JSON.stringify(readmeTagDiffs)}, ` + + `this.readmeDiffs: ${JSON.stringify(result)}.`, + ); + + return result; + } +} diff --git a/eng/tools/summarize-impact/src/cli.ts b/eng/tools/summarize-impact/src/cli.ts new file mode 100644 index 000000000000..c8394fb3efc9 --- /dev/null +++ b/eng/tools/summarize-impact/src/cli.ts @@ -0,0 +1,129 @@ +import { getChangedFilesStatuses } from "@azure-tools/specs-shared/changed-files"; +import { setOutput } from "@azure-tools/specs-shared/error-reporting"; +import { evaluateImpact, getRPaaSFolderList } from "./impact.js"; + +import { getRootFolder } from "@azure-tools/specs-shared/simple-git"; +import { Octokit } from "@octokit/rest"; +import { writeFile } from "fs/promises"; +import { parseArgs, ParseArgsConfig } from "node:util"; +import { resolve } from "path"; +import { LabelContext } from "./labelling-types.js"; +import { PRContext } from "./PRContext.js"; + +export async function main() { + const config: ParseArgsConfig = { + options: { + // the target branch checked out + targetDirectory: { + type: "string", + multiple: false, + }, + // the pr + sourceDirectory: { + type: "string", + multiple: false, + default: process.cwd(), + }, + fileList: { + type: "string", + short: "f", + multiple: false, + default: undefined, + }, + number: { + type: "string", + short: "n", + multiple: false, + }, + sourceBranch: { + type: "string", + multiple: false, + }, + targetBranch: { + type: "string", + multiple: false, + }, + sha: { + type: "string", + short: "s", + multiple: false, + }, + repo: { + type: "string", + short: "r", + multiple: false, + }, + owner: { + type: "string", + short: "o", + multiple: false, + }, + isDraft: { + type: "boolean", + multiple: false, + default: false, + }, + }, + allowPositionals: true, + }; + + const { values: opts } = parseArgs(config); + + // todo: refactor these opts + const sourceDirectory = opts.sourceDirectory as string; + const targetDirectory = opts.targetDirectory as string; + const sourceGitRoot = await getRootFolder(sourceDirectory); + const targetGitRoot = await getRootFolder(targetDirectory); + const fileList = await getChangedFilesStatuses({ cwd: sourceGitRoot, paths: ["specification"] }); + const sha = opts.sha as string; + const sourceBranch = opts.sourceBranch as string; + const targetBranch = opts.targetBranch as string; + const repo = opts.repo as string; + const owner = opts.owner as string; + const prNumber = opts.number as string; + const isDraft = opts.isDraft as boolean; + + // create github client (use token if available, otherwise unauthenticated. we will throw if unhandled) + const github = new Octokit({ + ...(process.env.GITHUB_TOKEN && { auth: process.env.GITHUB_TOKEN }), + }); + + const labels = ( + await github.paginate(github.rest.issues.listLabelsOnIssue, { + owner, + repo, + issue_number: Number(prNumber), + per_page: 100, + }) + ).map((label: any) => label.name); + + // this is a request to get the list of RPaaS folders from azure-rest-api-specs -> main branch -> dump specification folder names + const mainSpecFolders = await getRPaaSFolderList(github, owner, repo); + + const labelContext: LabelContext = { + present: new Set(labels), + toAdd: new Set(), + toRemove: new Set(), + }; + + const prContext = new PRContext(sourceGitRoot, targetGitRoot, labelContext, { + sha, + sourceBranch, + targetBranch, + repo, + prNumber, + owner, + fileList, + isDraft, + }); + + let impact = await evaluateImpact(prContext, labelContext, mainSpecFolders); + + console.log("Evaluated impact: ", JSON.stringify(impact, null, 2)); + + // Write to a temp file that can get picked up later. + // Intentionally doesn't use GITHUB_STEP_SUMMARY, since it's not a markdown summary for GH UI + const summaryFile = resolve("summary.json"); + await writeFile(summaryFile, JSON.stringify(impact, null, 2)); + setOutput("summary", summaryFile); +} diff --git a/eng/tools/summarize-impact/src/diff-types.ts b/eng/tools/summarize-impact/src/diff-types.ts new file mode 100644 index 000000000000..75e15aac7a05 --- /dev/null +++ b/eng/tools/summarize-impact/src/diff-types.ts @@ -0,0 +1,50 @@ +export enum FileTypes { + ExampleFile = "ExampleFile", + ReadmeFile = "ReadmeFile", + SwaggerFile = "SwaggerFile", + TypeSpecFile = "TypeSpecFile", +} + +export enum ChangeTypes { + Addition = "Addition", + Deletion = "Deletion", + Update = "Update", +} + +export type PRChange = { + fileType: FileTypes; + changeType: ChangeTypes; + filePath: string; + additionalInfo?: any; +}; + +export type ReadmeTag = { + readme: string; + tags: DiffResult; +}; + +export type TagConfigDiff = { + name: string; + oldConfig?: any; + newConfig?: any; + difference?: any; + changedInputFiles?: string[]; +}; + +export type TagDiff = { + readme: string; + changes: string[]; + insertions: string[]; + deletions: string[]; + differences?: TagConfigDiff[]; +}; + +export type ChangeHandler = { + [key in FileTypes]?: (event: PRChange) => void | Promise; +}; + +export type DiffResult = { + additions?: T[]; + deletions?: T[]; + changes?: T[]; +}; diff --git a/eng/tools/summarize-impact/src/impact.ts b/eng/tools/summarize-impact/src/impact.ts new file mode 100644 index 000000000000..cb9401e7d64c --- /dev/null +++ b/eng/tools/summarize-impact/src/impact.ts @@ -0,0 +1,802 @@ +#!/usr/bin/env node + +import { existsSync, readFileSync } from "fs"; +import { glob } from "glob"; +import { dirname, join, resolve } from "path"; + +import * as commonmark from "commonmark"; +import yaml from "js-yaml"; +import pkg from "lodash"; +const { isEqual } = pkg; + +import { + ChangeHandler, + ChangeTypes, + DiffResult, + FileTypes, + PRChange, + ReadmeTag, +} from "./diff-types.js"; + +import { Label, LabelContext, PRType } from "./labelling-types.js"; + +import { ImpactAssessment } from "./ImpactAssessment.js"; +import { PRContext } from "./PRContext.js"; + +import { dataPlane, resourceManager } from "@azure-tools/specs-shared/changed-files"; +import { Readme } from "@azure-tools/specs-shared/readme"; +import { Octokit } from "@octokit/rest"; + +// todo: we need to populate this so that we can tell if it's a new APIVersion down stream +// TODO: move to .github/shared +export async function isNewApiVersion(context: PRContext): Promise { + const handlers: ChangeHandler[] = []; + let isAddingNewApiVersion = false; + const apiVersionSet = new Set(); + + const rpFolders = new Set(); + + const createSwaggerFileHandler = () => { + return (e: PRChange) => { + if (e.changeType === ChangeTypes.Addition) { + const apiVersion = getApiVersionFromSwaggerFile(e.filePath); + if (apiVersion) { + apiVersionSet.add(apiVersion); + } + const rpFolder = getRPFolderFromSwaggerFile(e.filePath); + if (rpFolder !== undefined) { + rpFolders.add(rpFolder); + } + console.log(`apiVersion: ${apiVersion}, rpFolder: ${rpFolder}`); + } else if (e.changeType === ChangeTypes.Update) { + const rpFolder = getRPFolderFromSwaggerFile(e.filePath); + if (rpFolder !== undefined) { + rpFolders.add(rpFolder); + } + } + }; + }; + + handlers.push({ SwaggerFile: createSwaggerFileHandler() }); + await processPrChanges(context, handlers); + + console.log(`rpFolders: ${Array.from(rpFolders).join(",")}`); + + const firstRPFolder = Array.from(rpFolders)[0]; + + console.log(`apiVersion: ${Array.from(apiVersionSet).join(",")}`); + + if (firstRPFolder === undefined) { + console.log("RP folder not found."); + return false; + } + + const targetBranchRPFolder = resolve(context.targetDirectory, firstRPFolder); + + console.log(`targetBranchRPFolder: ${targetBranchRPFolder}`); + + const existingApiVersions = getAllApiVersionFromRPFolder(targetBranchRPFolder); + + console.log(`existingApiVersions: ${existingApiVersions.join(",")}`); + + for (const apiVersion of apiVersionSet) { + if (!existingApiVersions.includes(apiVersion)) { + console.log(`The apiVersion ${apiVersion} is added. and not found in existing ApiVersions`); + isAddingNewApiVersion = true; + } + } + return isAddingNewApiVersion; +} + +export async function evaluateImpact( + context: PRContext, + labelContext: LabelContext, + mainSpecFolders: string[], +): Promise { + const typeSpecLabelShouldBePresent = await processTypeSpec(context, labelContext); + + // examine changed files. if changedpaths includes data-plane, add "data-plane" + // same for "resource-manager". We care about whether resourcemanager will be present for a later check + const { resourceManagerLabelShouldBePresent, dataPlaneShouldBePresent } = await processPRType( + context, + labelContext, + ); + + // Has to be run in a PR context. Uses addition and update to understand + // if the suppressions have been changed. If they have, suppressionReviewRequired must be added + // as a label + const suppressionRequired = await processSuppression(context, labelContext); + + // needs to examine "after" context to understand if a readme that was changed is RPaaS or not + const { rpaasLabelShouldBePresent } = await processRPaaS(context, labelContext); + + // Has to be in PR context. Uses the addition to understand if the newRPNamespace label should be present. + const { newRPNamespaceLabelShouldBePresent } = await processNewRPNamespace( + context, + labelContext, + resourceManagerLabelShouldBePresent, + ); + + // doesn't necessarily need to be in the PR context. + // Uses the previous outputs that DID need to be a PR context, but otherwise only examines targetBranch and those + // output labels. + const { ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent, rpaasExceptionLabelShouldBePresent } = + await processNewRpNamespaceWithoutRpaasLabel( + context, + labelContext, + resourceManagerLabelShouldBePresent, + newRPNamespaceLabelShouldBePresent, + rpaasLabelShouldBePresent, + ); + + // examines the additions. if the changetype is addition, then it will add the ciRpaasRPNotInPrivateRepo label + const { ciRpaasRPNotInPrivateRepoLabelShouldBePresent } = + await processRpaasRpNotInPrivateRepoLabel( + context, + labelContext, + resourceManagerLabelShouldBePresent, + rpaasLabelShouldBePresent, + mainSpecFolders, + ); + + const newApiVersion = await isNewApiVersion(context); + + return { + suppressionReviewRequired: suppressionRequired, + rpaasChange: rpaasLabelShouldBePresent, + newRP: newRPNamespaceLabelShouldBePresent, + rpaasRPMissing: ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent, + rpaasRpNotInPrivateRepo: ciRpaasRPNotInPrivateRepoLabelShouldBePresent, + resourceManagerRequired: resourceManagerLabelShouldBePresent, + dataPlaneRequired: dataPlaneShouldBePresent, + rpaasExceptionRequired: rpaasExceptionLabelShouldBePresent, + typeSpecChanged: typeSpecLabelShouldBePresent, + isNewApiVersion: newApiVersion, + isDraft: context.isDraft, + targetBranch: context.targetBranch, + }; +} + +export function isManagementPR(filePaths: string[]): boolean { + return filePaths.some(resourceManager); +} + +export function isDataPlanePR(filePaths: string[]): boolean { + return filePaths.some(dataPlane); +} + +export function getAllApiVersionFromRPFolder(rpFolder: string): string[] { + const allSwaggerFilesFromRPFolder = glob.sync(`${rpFolder}/**/*.json`); + console.log(`allSwaggerFilesFromRPFolder: ${allSwaggerFilesFromRPFolder}`); + + const apiVersions: Set = new Set(); + for (const it of allSwaggerFilesFromRPFolder) { + if (!it.includes("examples")) { + const apiVersion = getApiVersionFromSwaggerFile(it); + console.log(`Get api version from swagger file: ${it}, apiVersion: ${apiVersion}`); + if (apiVersion) { + apiVersions.add(apiVersion); + } + } + } + return [...apiVersions]; +} + +export function getApiVersionFromSwaggerFile(swaggerFile: string): string | undefined { + const swagger = readFileSync(swaggerFile).toString(); + const swaggerObject = JSON.parse(swagger); + if (swaggerObject["info"] && swaggerObject["info"]["version"]) { + return swaggerObject["info"]["version"]; + } + return undefined; +} + +export function getRPFolderFromSwaggerFile(swaggerFile: string): string | undefined { + const resourceProvider = getResourceProviderFromFilePath(swaggerFile); + + if (resourceProvider === undefined) { + return undefined; + } + + const lastIdx = swaggerFile.lastIndexOf(resourceProvider!); + return swaggerFile.substring(0, lastIdx + resourceProvider!.length); +} + +export const getResourceProviderFromFilePath = (filePath: string): string | undefined => { + // Example filePath: + // specification/purview/data-plane/Azure.Analytics.Purview.Workflow/preview/2023-10-01-preview/purviewWorkflow.json + // Match: + // /Azure.Analytics.Purview.Workflow/ + // Note: + // The regex matches a directory name in the path of form: /Foo.Bar.Baz/, where: + // - The directory name must have at least one period. If it would match 0 periods, + // then in the example path it would match to "/purview/" instead. + // - The directory name can have one or more periods. + // The "Foo.Bar.Baz" example given above has two periods. + const regex = /\/([a-z0-9]+(\.[a-z0-9]+)+)\//i; + const match = filePath.match(regex); + if (match && match.length > 0) { + return match[1]; + } + // Second matching attempt to cover scenarios in which: + // - the resource provider is for data-plane + // - and it has no dots in the name. + // + // Example filePath: + // specification/communication/data-plane/Sms/preview/2024-02-05-preview/communicationServicesSms.json + // Match: + // /Sms/ + // + // For details see: https://github.com/Azure/azure-sdk-tools/issues/7552 + const regexForDirImmediatelyAfterDataPlane = /\/data-plane\/([a-z0-9]+)\//i; + const match2 = filePath.match(regexForDirImmediatelyAfterDataPlane); + if (match2 && match2.length > 0) { + return match2[1]; + } + + return undefined; +}; + +async function processTypeSpec(ctx: PRContext, labelContext: LabelContext): Promise { + console.log("ENTER definition processTypeSpec"); + const typeSpecLabel = new Label("TypeSpec", labelContext.present); + // By default this label should not be present. We may determine later in this function that it should be present after all. + typeSpecLabel.shouldBePresent = false; + const handlers: ChangeHandler[] = []; + const typeSpecFileHandler = () => { + return (_: PRChange) => { + // Note: this code will be executed if the PR has a diff on a TypeSpec file, + // as defined in public/swagger-validation-common/src/context.ts/defaultFilePatterns/typespec + typeSpecLabel.shouldBePresent = true; + }; + }; + const swaggerFileHandler = () => { + return (prChange: PRChange) => { + if ( + prChange.changeType !== ChangeTypes.Deletion && + isSwaggerGeneratedByTypeSpec(prChange.filePath) + ) { + typeSpecLabel.shouldBePresent = true; + } + }; + }; + handlers.push({ + TypeSpecFile: typeSpecFileHandler(), + SwaggerFile: swaggerFileHandler(), + }); + await processPrChanges(ctx, handlers); + typeSpecLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + console.log("RETURN definition processTypeSpec"); + + return typeSpecLabel.shouldBePresent; +} + +function isSwaggerGeneratedByTypeSpec(swaggerFilePath: string): boolean { + try { + return !!JSON.parse(readFileSync(swaggerFilePath).toString())?.info["x-typespec-generated"]; + } catch { + return false; + } +} + +export async function processPrChanges(ctx: PRContext, Handlers: ChangeHandler[]) { + console.log("ENTER definition processPrChanges"); + const prChanges = await getPRChanges(ctx); + prChanges.forEach((prChange) => { + Handlers.forEach((handler) => { + if (prChange.fileType in handler) { + handler?.[prChange.fileType]?.(prChange); + } + }); + }); + console.log("RETURN definition processPrChanges"); +} + +export async function processPRChangesAsync(ctx: PRContext, Handlers: ChangeHandler[]) { + console.log("ENTER definition processPRChangesAsync"); + const prChanges = await getPRChanges(ctx); + + for (const prChange of prChanges) { + for (const handler of Handlers) { + if (prChange.fileType in handler) { + await handler?.[prChange.fileType]?.(prChange); + } + } + } + + console.log("RETURN definition processPRChangesAsync"); +} + +export async function getPRChanges(ctx: PRContext): Promise { + console.log("ENTER definition getPRChanges"); + const results: PRChange[] = []; + + function newChange( + fileType: FileTypes, + changeType: ChangeTypes, + filePath?: string, + additionalInfo?: any, + ) { + if (filePath) { + results.push({ + filePath, + fileType, + changeType, + additionalInfo, + }); + } + } + + function newChanges(fileType: FileTypes, changeType: ChangeTypes, files?: string[]) { + if (files) { + files.forEach((filePath) => newChange(fileType, changeType, filePath)); + } + } + + function genChanges(type: FileTypes, diffs: DiffResult) { + newChanges(type, ChangeTypes.Addition, diffs.additions); + newChanges(type, ChangeTypes.Deletion, diffs.deletions); + newChanges(type, ChangeTypes.Update, diffs.changes); + } + + function genReadmeChanges(readmeDiffs?: DiffResult) { + if (readmeDiffs) { + readmeDiffs.additions?.forEach((d) => + newChange(FileTypes.ReadmeFile, ChangeTypes.Addition, d.readme, d.tags), + ); + readmeDiffs.changes?.forEach((d) => + newChange(FileTypes.ReadmeFile, ChangeTypes.Update, d.readme, d.tags), + ); + readmeDiffs.deletions?.forEach((d) => + newChange(FileTypes.ReadmeFile, ChangeTypes.Deletion, d.readme, d.tags), + ); + } + } + + genChanges(FileTypes.SwaggerFile, ctx.getSwaggerDiffs()); + genChanges(FileTypes.TypeSpecFile, ctx.getTypeSpecDiffs()); + genChanges(FileTypes.ExampleFile, ctx.getExampleDiffs()); + genReadmeChanges(await ctx.getReadmeDiffs()); + + console.log("RETURN definition getPRChanges"); + return results; +} + +// related pending work: +// If a PR has both data-plane and resource-manager labels, it should fail with appropriate messaging #8144 +// https://github.com/Azure/azure-sdk-tools/issues/8144 +async function processPRType( + context: PRContext, + labelContext: LabelContext, +): Promise<{ resourceManagerLabelShouldBePresent: boolean; dataPlaneShouldBePresent: boolean }> { + console.log("ENTER definition processPRType"); + const types: PRType[] = await getPRType(context); + + const resourceManagerLabelShouldBePresent = processPRTypeLabel( + PRType.ResourceManager, + types, + labelContext, + ); + const dataPlaneShouldBePresent = processPRTypeLabel(PRType.DataPlane, types, labelContext); + + console.log("RETURN definition processPRType"); + return { resourceManagerLabelShouldBePresent, dataPlaneShouldBePresent }; +} + +async function getPRType(context: PRContext): Promise { + console.log("ENTER definition getPRType"); + const prChanges: PRChange[] = await getPRChanges(context); + + logPRChanges(prChanges); + + const changedFilePaths: string[] = prChanges.map((it) => it.filePath); + + const prTypes: PRType[] = []; + if (changedFilePaths.length > 0) { + if (isDataPlanePR(changedFilePaths)) { + prTypes.push(PRType.DataPlane); + } + if (isManagementPR(changedFilePaths)) { + prTypes.push(PRType.ResourceManager); + } + } + console.log("RETURN definition getPRType"); + return prTypes; + + function logPRChanges(prChanges: PRChange[]) { + console.log(`PR changes table (count: ${prChanges.length}):`); + console.log("LEGEND: changeType | fileType | filePath"); + prChanges + .map((it) => `${it.changeType} | ${it.fileType} | ${it.filePath}`) + .forEach((it) => console.log(it)); + console.log("END of PR changes table"); + + console.log("PR changes with additionalInfo:"); + prChanges.filter((it) => it.additionalInfo != null).forEach((it) => console.log(it)); + console.log("END of PR changes with additionalInfo"); + } +} + +function processPRTypeLabel( + labelName: PRType, + types: PRType[], + labelContext: LabelContext, +): boolean { + const label = new Label(labelName, labelContext.present); + label.shouldBePresent = types.includes(labelName); + + label.applyStateChange(labelContext.toAdd, labelContext.toRemove); + return label.shouldBePresent; +} + +async function processSuppression(context: PRContext, labelContext: LabelContext) { + console.log("ENTER definition processSuppression"); + + const suppressionReviewRequiredLabel = new Label( + "SuppressionReviewRequired", + labelContext.present, + ); + // By default this label should not be present. We may determine later in this function that it should be present after all. + suppressionReviewRequiredLabel.shouldBePresent = false; + const handlers: ChangeHandler[] = []; + + const createReadmeFileHandler = () => { + return (e: PRChange) => { + if ( + (e.changeType === ChangeTypes.Addition && getSuppressions(e.filePath).length) || + (e.changeType === ChangeTypes.Update && + diffSuppression( + resolve(context.targetDirectory, e.filePath), + resolve(context.sourceDirectory, e.filePath), + ).length) + ) { + suppressionReviewRequiredLabel.shouldBePresent = true; + } + }; + }; + handlers.push({ + ReadmeFile: createReadmeFileHandler(), + }); + await processPrChanges(context, handlers); + + suppressionReviewRequiredLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + console.log("RETURN definition processSuppression"); + + return suppressionReviewRequiredLabel.shouldBePresent; +} + +function getSuppressions(readmePath: string) { + const walkToNode = ( + walker: commonmark.NodeWalker, + cb: (node: commonmark.Node) => boolean, + ): commonmark.Node | undefined => { + let event = walker.next(); + + while (event) { + const curNode = event.node; + if (cb(curNode)) { + return curNode; + } + event = walker.next(); + } + return undefined; + }; + const getAllCodeBlockNodes = (startNode: commonmark.Node) => { + const walker = startNode.walker(); + const result = []; + while (true) { + const a = walkToNode(walker, (n) => n.type === "code_block"); + if (!a) { + break; + } + result.push(a); + } + return result; + }; + let suppressionResult: any[] = []; + try { + const readme = readFileSync(readmePath).toString(); + const codeBlocks = getAllCodeBlockNodes(new commonmark.Parser().parse(readme)); + for (const block of codeBlocks) { + if (block.literal) { + try { + const blockObject = yaml.load(block.literal) as any; + const directives = blockObject?.["directive"]; + if (directives && Array.isArray(directives)) { + suppressionResult = suppressionResult.concat(directives.filter((s) => s.suppress)); + } + const suppressions = blockObject?.["suppressions"]; + if (suppressions && Array.isArray(suppressions)) { + suppressionResult = suppressionResult.concat(suppressions); + } + } catch (e) {} + } + } + } catch (e) {} + return suppressionResult; +} + +export function diffSuppression(readmeBefore: string, readmeAfter: string) { + const beforeSuppressions = getSuppressions(readmeBefore); + const afterSuppressions = getSuppressions(readmeAfter); + const newSuppressions = []; + for (const suppression of afterSuppressions) { + const properties = ["suppress", "from", "where", "code", "reason"]; + if ( + -1 === + beforeSuppressions.findIndex((s) => properties.every((p) => isEqual(s[p], suppression[p]))) + ) { + newSuppressions.push(suppression); + } + } + return newSuppressions; +} + +async function processRPaaS( + context: PRContext, + labelContext: LabelContext, +): Promise<{ rpaasLabelShouldBePresent: boolean }> { + console.log("ENTER definition processRPaaS"); + const rpaasLabel = new Label("RPaaS", labelContext.present); + // By default this label should not be present. We may determine later in this function that it should be present after all. + rpaasLabel.shouldBePresent = false; + + const handlers: ChangeHandler[] = []; + const createReadmeFileHandler = () => { + return async (e: PRChange) => { + if ( + e.changeType !== ChangeTypes.Deletion && + (await isRPSaaS(join(context.sourceDirectory, e.filePath))) + ) { + rpaasLabel.shouldBePresent = true; + } + }; + }; + handlers.push({ + ReadmeFile: createReadmeFileHandler(), + }); + await processPRChangesAsync(context, handlers); + + rpaasLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + console.log("RETURN definition processRPaaS"); + return { rpaasLabelShouldBePresent: rpaasLabel.shouldBePresent }; +} + +async function isRPSaaS(readmeFilePath: string) { + const config: any = await new Readme(readmeFilePath).getGlobalConfig(); + return config["openapi-subtype"] === "rpaas" || config["openapi-subtype"] === "providerHub"; +} + +async function processNewRPNamespace( + context: PRContext, + labelContext: LabelContext, + resourceManagerLabelShouldBePresent: boolean, +): Promise<{ newRPNamespaceLabelShouldBePresent: boolean }> { + console.log("ENTER definition processNewRPNamespace"); + const newRPNamespaceLabel = new Label("new-rp-namespace", labelContext.present); + // By default this label should not be present. We may determine later in this function that it should be present after all. + newRPNamespaceLabel.shouldBePresent = false; + const handlers: ChangeHandler[] = []; + + let skip = false; + + const targetBranch = context.targetBranch; + if (targetBranch !== "main" && targetBranch !== "ARMCoreRPDev") { + console.log(`Not main or ARMCoreRPDev branch, skip new RP namespace check`); + skip = true; + } + + if (!skip && !resourceManagerLabelShouldBePresent) { + console.log(`Not resource-manager, skip new RP namespace check`); + skip = true; + } + + if (!skip) { + const createSwaggerFileHandler = () => { + return (e: PRChange) => { + if (e.changeType === ChangeTypes.Addition) { + const rpFolder = getRPFolderFromSwaggerFile(dirname(e.filePath)); + console.log(`Processing newRPNameSpace rpFolder: ${rpFolder}`); + if (rpFolder !== undefined) { + const rpFolderFullPath = resolve(context.targetDirectory, rpFolder); + if (!existsSync(rpFolderFullPath)) { + console.log(`Adding newRPNameSpace rpFolder: ${rpFolder}`); + newRPNamespaceLabel.shouldBePresent = true; + } + } + } + }; + }; + + handlers.push({ SwaggerFile: createSwaggerFileHandler() }); + await processPrChanges(context, handlers); + } + + newRPNamespaceLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + console.log("RETURN definition processNewRPNamespace"); + return { newRPNamespaceLabelShouldBePresent: newRPNamespaceLabel.shouldBePresent }; +} + +// CODESYNC: +// - see entries for related labels in https://github.com/Azure/azure-rest-api-specs/blob/main/.github.amrom.workers.devment.yml +// - requiredLabelsRules.ts / requiredLabelsRules +async function processNewRpNamespaceWithoutRpaasLabel( + context: PRContext, + labelContext: LabelContext, + resourceManagerLabelShouldBePresent: boolean, + newRPNamespaceLabelShouldBePresent: boolean, + rpaasLabelShouldBePresent: boolean, +): Promise<{ + ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent: boolean; + rpaasExceptionLabelShouldBePresent: boolean; +}> { + console.log("ENTER definition processNewRpNamespaceWithoutRpaasLabel"); + const ciNewRPNamespaceWithoutRpaaSLabel = new Label( + "CI-NewRPNamespaceWithoutRPaaS", + labelContext.present, + ); + // By default this label should not be present. We may determine later in this function that it should be present after all. + ciNewRPNamespaceWithoutRpaaSLabel.shouldBePresent = false; + + const rpaasExceptionLabel = new Label("RPaaSException", labelContext.present); + + let skip = false; + + if (!resourceManagerLabelShouldBePresent) { + console.log(`Not resource-manager, skip checking for 'CI-NewRPNamespaceWithoutRPaaS'`); + skip = true; + } + + if (!skip) { + const branch = context.targetBranch; + if (branch === "RPSaaSDev" || branch === "RPSaaSMaster") { + console.log( + `PR is targeting '${branch}' branch. Skip checking for 'CI-NewRPNamespaceWithoutRPaaS'`, + ); + skip = true; + } + } + + if (!skip && newRPNamespaceLabelShouldBePresent && !rpaasLabelShouldBePresent) { + console.log( + "Adding new RP namespace, but not RPaaS. Label 'CI-NewRPNamespaceWithoutRPaaS' should be present.", + ); + ciNewRPNamespaceWithoutRpaaSLabel.shouldBePresent = true; + } + + rpaasExceptionLabel.shouldBePresent = + rpaasExceptionLabel.present && ciNewRPNamespaceWithoutRpaaSLabel.shouldBePresent; + + ciNewRPNamespaceWithoutRpaaSLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + rpaasExceptionLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + console.log("RETURN definition processNewRpNamespaceWithoutRpaasLabel"); + + return { + ciNewRPNamespaceWithoutRpaaSLabelShouldBePresent: + ciNewRPNamespaceWithoutRpaaSLabel.shouldBePresent, + rpaasExceptionLabelShouldBePresent: rpaasExceptionLabel.shouldBePresent as boolean, + }; +} + +export const getRPaaSFolderList = async ( + client: Octokit, + owner: string, + repoName: string, +): Promise => { + const branch = "main"; + const folder = "specification"; + + const res = await client.rest.repos.getContent({ + owner, + repo: repoName, + path: folder, + ref: branch, + }); + + console.log( + `Get RPSaaS folder list from ${owner}/${repoName}/${folder} successfully. status: ${res.status}`, + ); + + // Extract folder names from the response + if (Array.isArray(res.data)) { + const folderNames = res.data + .filter((item: any) => item.type === "dir") // Only get directories + .map((item: any) => item.name); // Extract the name property + + console.log(`Found ${folderNames.length} folders: ${folderNames.join(", ")}`); + return folderNames; + } + + console.log("No folders found or unexpected response format"); + return []; +}; + +export function getRPRootFolderName(swaggerFile: string): string | undefined { + const RPFolder = getRPFolderFromSwaggerFile(swaggerFile); + const dirList = RPFolder?.split("/"); + let idx = 0; + //find the index of "specification" in the path + for (let i = 0; i < dirList!.length; i++) { + if (dirList![i] === "specification") { + idx = i; + break; + } + } + // The RP root folder name is the folder name after "specification" + if (idx + 1 < dirList!.length) { + return dirList![idx + 1]; + } + + return undefined; +} + +async function processRpaasRpNotInPrivateRepoLabel( + context: PRContext, + labelContext: LabelContext, + resourceManagerLabelShouldBePresent: boolean, + rpaasLabelShouldBePresent: boolean, + rpFolderNames: string[], +): Promise<{ ciRpaasRPNotInPrivateRepoLabelShouldBePresent: boolean }> { + console.log("ENTER definition processRpaasRpNotInPrivateRepoLabel"); + const ciRpaasRPNotInPrivateRepoLabel = new Label( + "CI-RpaaSRPNotInPrivateRepo", + labelContext.present, + ); + // By default this label should not be present. We may determine later in this function that it should be present after all. + ciRpaasRPNotInPrivateRepoLabel.shouldBePresent = false; + + let skip = false; + + if (!resourceManagerLabelShouldBePresent) { + console.log(`Not resource-manager, skip RPaaSRPNotInPrivateRepo check`); + skip = true; + } + + if (!skip) { + const targetBranch = context.targetBranch; + if (targetBranch !== "main" || !rpaasLabelShouldBePresent) { + console.log("Not main branch or not RPaaS PR, skip block PR when RPaaS RP not onboard"); + skip = true; + } + } + + if (!skip) { + console.log(`RPaaS RP folder list: ${rpFolderNames}`); + + const handlers: ChangeHandler[] = []; + + const processPrChange = () => { + return (e: PRChange) => { + if (e.changeType === ChangeTypes.Addition) { + const rpFolderName = getRPRootFolderName(e.filePath); + console.log( + `Processing processRpaasRpNotInPrivateRepoLabel rpFolderName: ${rpFolderName}`, + ); + + if (rpFolderName === undefined) { + console.log(`RP folder is undefined for changed file path '${e.filePath}'.`); + return; + } + + if (!rpFolderNames.includes(rpFolderName)) { + console.log( + `This RP is RPSaaS RP but could not find rpFolderName: ${rpFolderName} in RPFolderNames: ${rpFolderNames}. ` + + `Label 'CI-RpaaSRPNotInPrivateRepo' should be present.`, + ); + ciRpaasRPNotInPrivateRepoLabel.shouldBePresent = true; + } + } + }; + }; + + handlers.push({ SwaggerFile: processPrChange() }); + await processPrChanges(context, handlers); + } + + ciRpaasRPNotInPrivateRepoLabel.applyStateChange(labelContext.toAdd, labelContext.toRemove); + console.log("RETURN definition processRpaasRpNotInPrivateRepoLabel"); + + return { + ciRpaasRPNotInPrivateRepoLabelShouldBePresent: ciRpaasRPNotInPrivateRepoLabel.shouldBePresent, + }; +} diff --git a/eng/tools/summarize-impact/src/labelling-types.ts b/eng/tools/summarize-impact/src/labelling-types.ts new file mode 100644 index 000000000000..78fda41fa36d --- /dev/null +++ b/eng/tools/summarize-impact/src/labelling-types.ts @@ -0,0 +1,126 @@ +export enum PRType { + DataPlane = "data-plane", + ResourceManager = "resource-manager", +} + +/** + * The LabelContext is used by prSummary.ts / summary() and downstream invocations. + * + * The "present" set represents the set of labels that are currently present on the PR + * processed by given invocation of summary(). It is obtained via GitHub Octokit API at the beginning + * of summary(). + * + * The "toAdd" set is the set of labels to be added to the PR at the end of invocation of summary(). + * This is to be done by calling GitHub Octokit API to add the labels. + * + * The "toRemove" set is analogous to "toAdd" set, but instead it is the set of labels to be removed. + * + * The general pattern used in the code to populate "toAdd" or "toRemove" sets to be ready for + * Octokit invocation is as follows: + * + * - the summary() function passes the context through its invocation chain. + * - given function responsible for given label, like e.g. for label "ARMReview", + * creates a new instance of Label: const armReviewLabel = new Label("ARMReview", labelContext.present) + * - the function then processes the label to determine if armReviewLabel.shouldBePresent is to be set to true or false. + * - the function at the end of its invocation calls armReviewLabel.applyStateChanges(labelContext.toAdd, labelContext.toRemove) + * to update the sets. + * - the function may optionally return { armReviewLabel.shouldBePresent } to allow the caller to pass this value + * further to downstream business logic that depends on it. + * - at the end of invocation summary() calls Octokit passing it as input labelContext.toAdd and labelContext.toRemove. + * + * todo: this type is duplicated in JSDoc over in summarize-checks.js + */ +export type LabelContext = { + present: Set; + toAdd: Set; + toRemove: Set; +}; + +export class Label { + name: string; + + /** Is the label currently present on the pull request? + * + * This is determined at the time of construction of this object. + */ + present?: boolean; + + /** Should this label be present on the pull request? + * + * Must be defined before applyStateChange is called. + * + * Not set at the construction time to facilitate determining desired presence + * of multiple labels in single code block, without intermixing it with + * label construction logic. + */ + shouldBePresent: boolean | undefined = undefined; + + constructor(name: string, presentLabels?: Set) { + this.name = name; + this.present = presentLabels?.has(this.name) ?? undefined; + } + + /** + * If the label should be added, add its name to labelsToAdd. + * If the label should be removed, add its name to labelsToRemove. + * Otherwise, do nothing. + * + * Precondition: this.shouldBePresent has been defined. + */ + applyStateChange(labelsToAdd: Set, labelsToRemove: Set): void { + if (this.shouldBePresent === undefined) { + console.warn( + "ASSERTION VIOLATION! " + + `Cannot applyStateChange for label '${this.name}' ` + + "as its desired presence hasn't been defined. Returning early.", + ); + return; + } + + if (!this.present && this.shouldBePresent) { + if (!labelsToAdd.has(this.name)) { + console.log( + `Label.applyStateChange: '${this.name}' was not present and should be present. Scheduling addition.`, + ); + labelsToAdd.add(this.name); + } else { + console.log( + `Label.applyStateChange: '${this.name}' was not present and should be present. It is already scheduled for addition.`, + ); + } + } else if (this.present && !this.shouldBePresent) { + if (!labelsToRemove.has(this.name)) { + console.log( + `Label.applyStateChange: '${this.name}' was present and should not be present. Scheduling removal.`, + ); + labelsToRemove.add(this.name); + } else { + console.log( + `Label.applyStateChange: '${this.name}' was present and should not be present. It is already scheduled for removal.`, + ); + } + } else if (this.present === this.shouldBePresent) { + console.log( + `Label.applyStateChange: '${this.name}' is ${this.present ? "present" : "not present"}. This is the desired state.`, + ); + } else { + console.warn( + "ASSERTION VIOLATION! " + + `Label.applyStateChange: '${this.name}' is ${this.present ? "present" : "not present"} while it should be ${this.shouldBePresent ? "present" : "not present"}. ` + + `At this point of execution this should not happen.`, + ); + } + } + + isEqualToOrPrefixOf(label: string): boolean { + return this.name.endsWith("*") ? label.startsWith(this.name.slice(0, -1)) : this.name === label; + } + + logString(): string { + return ( + `Label: name: ${this.name}, ` + + `present: ${this.present}, ` + + `shouldBePresent: ${this.shouldBePresent}. ` + ); + } +} diff --git a/eng/tools/summarize-impact/test/cli.test.ts b/eng/tools/summarize-impact/test/cli.test.ts new file mode 100644 index 000000000000..1e44c8e87f8f --- /dev/null +++ b/eng/tools/summarize-impact/test/cli.test.ts @@ -0,0 +1,112 @@ +import { describe, expect, it } from "vitest"; //vi + +import path from "path"; + +import { getChangedFilesStatuses } from "@azure-tools/specs-shared/changed-files"; +import { Octokit } from "@octokit/rest"; +import { PRContext } from "../src/PRContext.js"; +import { evaluateImpact, getRPaaSFolderList } from "../src/impact.js"; +import { LabelContext } from "../src/labelling-types.js"; + +describe("Check Changes", () => { + it.skipIf(!process.env.GITHUB_TOKEN || !process.env.INTEGRATION_TEST)( + "Integration test 35346", + async () => { + const targetDirectory = path.join("/home/semick/repo/rest-s/35346", "before"); + const sourceDirectory = path.join("/home/semick/repo/rest-s/35346", "after"); + + // Change to source directory and save original + const originalCwd = process.cwd(); + process.chdir(sourceDirectory); + + try { + const changedFileDetails = await getChangedFilesStatuses({ + cwd: sourceDirectory, + baseCommitish: "origin/main", + }); + const labelContext: LabelContext = { + present: new Set(), + toAdd: new Set(), + toRemove: new Set(), + }; + + const github = new Octokit({ + ...(process.env.GITHUB_TOKEN && { auth: process.env.GITHUB_TOKEN }), + }); + const rpaaSFolderList = await getRPaaSFolderList(github, "Azure", "azure-rest-api-specs"); + + const prContext = new PRContext(sourceDirectory, targetDirectory, labelContext, { + sha: "ad7c74cb27d2cf3ba83996aaea36b07caa4d16c8", + sourceBranch: "dev/nandiniy/DTLTypeSpec", + targetBranch: "main", + repo: "azure-rest-api-specs", + prNumber: "35346", + owner: "Azure", + fileList: changedFileDetails, + isDraft: false, + }); + + const result = await evaluateImpact(prContext, labelContext, rpaaSFolderList); + + expect(result).toBeDefined(); + expect(result.typeSpecChanged).toBeTruthy(); + expect(result.dataPlaneRequired).toBeFalsy(); + expect(result.resourceManagerRequired).toBeTruthy(); + expect(result.suppressionReviewRequired).toBeTruthy(); + expect(changedFileDetails).toBeDefined(); + expect(changedFileDetails.total).toEqual(293); + } finally { + // Restore original directory + process.chdir(originalCwd); + } + }, + 60000000, + ); + + it.skipIf(!process.env.GITHUB_TOKEN || !process.env.INTEGRATION_TEST)( + "Integration test 35982", + async () => { + const targetDirectory = path.join("/home/semick/repo/rest-s/35982", "before"); + const sourceDirectory = path.join("/home/semick/repo/rest-s/35982", "after"); + + // Change to source directory and save original + const originalCwd = process.cwd(); + process.chdir(sourceDirectory); + + try { + const changedFileDetails = await getChangedFilesStatuses({ + cwd: sourceDirectory, + baseCommitish: "origin/main", + }); + const labelContext: LabelContext = { + present: new Set(), + toAdd: new Set(), + toRemove: new Set(), + }; + + const prContext = new PRContext(sourceDirectory, targetDirectory, labelContext, { + sha: "2bd8350d465081401a0f4f03e633eca41f0991de", + sourceBranch: "features/users/deepika/cosmos-connectors-confluent", + targetBranch: "main", + repo: "azure-rest-api-specs", + prNumber: "35982", + owner: "Azure", + fileList: changedFileDetails, + isDraft: false, + }); + + const result = await evaluateImpact(prContext, labelContext, []); + expect(result.isNewApiVersion).toBeTruthy(); + expect(result.typeSpecChanged).toBeTruthy(); + expect(result.resourceManagerRequired).toBeTruthy(); + expect(result.isNewApiVersion).toBeTruthy(); + expect(result.rpaasChange).toBeTruthy(); + expect(result).toBeDefined(); + } finally { + // Restore original directory + process.chdir(originalCwd); + } + }, + 60000000, + ); +}); diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json new file mode 100644 index 000000000000..243b6576d7e8 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json @@ -0,0 +1,557 @@ +{ + "swagger": "2.0", + "info": { + "title": "Microsoft.Contoso management service", + "version": "2021-11-01", + "description": "Microsoft.Contoso Resource Provider management API.", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + }, + { + "name": "Employees" + } + ], + "paths": { + "/providers/Microsoft.Contoso/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Operations_List": { + "$ref": "./examples/Operations_List.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListBySubscription", + "tags": [ + "Employees" + ], + "description": "List Employee resources by subscription ID", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListBySubscription": { + "$ref": "./examples/Employees_ListBySubscription.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListByResourceGroup", + "tags": [ + "Employees" + ], + "description": "List Employee resources by resource group", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListByResourceGroup": { + "$ref": "./examples/Employees_ListByResourceGroup.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees/{employeeName}": { + "get": { + "operationId": "Employees_Get", + "tags": [ + "Employees" + ], + "description": "Get a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Get": { + "$ref": "./examples/Employees_Get.json" + } + } + }, + "put": { + "operationId": "Employees_CreateOrUpdate", + "tags": [ + "Employees" + ], + "description": "Create a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/Employee" + } + } + ], + "responses": { + "200": { + "description": "Resource 'Employee' update operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "201": { + "description": "Resource 'Employee' create operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + }, + "headers": { + "Azure-AsyncOperation": { + "type": "string", + "description": "A link to the status monitor" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_CreateOrUpdate": { + "$ref": "./examples/Employees_CreateOrUpdate.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + }, + "patch": { + "operationId": "Employees_Update", + "tags": [ + "Employees" + ], + "description": "Update a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "properties", + "in": "body", + "description": "The resource properties to be updated.", + "required": true, + "schema": { + "$ref": "#/definitions/EmployeeUpdate" + } + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Update": { + "$ref": "./examples/Employees_Update.json" + } + } + }, + "delete": { + "operationId": "Employees_Delete", + "tags": [ + "Employees" + ], + "description": "Delete a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "202": { + "description": "Resource deletion accepted.", + "headers": { + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "204": { + "description": "Resource does not exist." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Delete": { + "$ref": "./examples/Employees_Delete.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "Azure.ResourceManager.CommonTypes.TrackedResourceUpdate": { + "type": "object", + "title": "Tracked Resource", + "description": "The resource model definition for an Azure Resource Manager tracked top level resource which has 'tags' and a 'location'", + "properties": { + "tags": { + "type": "object", + "description": "Resource tags.", + "additionalProperties": { + "type": "string" + } + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/Resource" + } + ] + }, + "Employee": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/TrackedResource" + } + ] + }, + "EmployeeListResult": { + "type": "object", + "description": "The response of a Employee list operation.", + "properties": { + "value": { + "type": "array", + "description": "The Employee items on this page", + "items": { + "$ref": "#/definitions/Employee" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "EmployeeProperties": { + "type": "object", + "description": "Employee properties", + "properties": { + "age": { + "type": "integer", + "format": "int32", + "description": "Age of employee" + }, + "city": { + "type": "string", + "description": "City of employee" + }, + "profile": { + "type": "string", + "format": "base64url", + "description": "Profile of employee" + }, + "provisioningState": { + "$ref": "#/definitions/ProvisioningState", + "description": "The status of the last operation.", + "readOnly": true + } + } + }, + "EmployeeUpdate": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "#/definitions/Azure.ResourceManager.CommonTypes.TrackedResourceUpdate" + } + ] + }, + "ProvisioningState": { + "type": "string", + "description": "The resource provisioning state.", + "enum": [ + "Succeeded", + "Failed", + "Canceled", + "Provisioning", + "Updating", + "Deleting", + "Accepted" + ], + "x-ms-enum": { + "name": "ProvisioningState", + "modelAsString": true, + "values": [ + { + "name": "Succeeded", + "value": "Succeeded", + "description": "Resource has been created." + }, + { + "name": "Failed", + "value": "Failed", + "description": "Resource creation failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "Resource creation was canceled." + }, + { + "name": "Provisioning", + "value": "Provisioning", + "description": "The resource is being provisioned" + }, + { + "name": "Updating", + "value": "Updating", + "description": "The resource is updating" + }, + { + "name": "Deleting", + "value": "Deleting", + "description": "The resource is being deleted" + }, + { + "name": "Accepted", + "value": "Accepted", + "description": "The resource create request has been accepted" + } + ] + }, + "readOnly": true + } + }, + "parameters": {} +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/readme.md b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/readme.md new file mode 100644 index 000000000000..080f47e82d94 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager2/resource-manager/readme.md @@ -0,0 +1,44 @@ +# contosowidgermanager 2 + +> see https://aka.ms/autorest +This is the AutoRest configuration file for Contoso. + +## Getting Started + +To build the SDKs for My API, simply install AutoRest via `npm` (`npm install -g autorest`) and then run: + +> `autorest readme.md` +To see additional help and options, run: + +> `autorest --help` +For other options on installation see [Installing AutoRest](https://aka.ms/autorest/install) on the AutoRest github page. + +--- + +## Configuration + +```yaml +suppressions: + - code: AvoidAnonymousTypes +``` + +### Basic Information + +These are the global settings for the containerstorage. + +```yaml +openapi-type: arm +openapi-subtype: rpaas +tag: package-2021-11-01 +``` + +### Tag: package-2021-11-01 + +These settings apply only when `--tag=package-2021-11-01` is specified on the command line. + +```yaml $(tag) == 'package-2021-11-01' +input-file: + - Microsoft.Contoso/stable/2021-11-01/contoso.json +``` + +--- diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json new file mode 100644 index 000000000000..243b6576d7e8 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json @@ -0,0 +1,557 @@ +{ + "swagger": "2.0", + "info": { + "title": "Microsoft.Contoso management service", + "version": "2021-11-01", + "description": "Microsoft.Contoso Resource Provider management API.", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + }, + { + "name": "Employees" + } + ], + "paths": { + "/providers/Microsoft.Contoso/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Operations_List": { + "$ref": "./examples/Operations_List.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListBySubscription", + "tags": [ + "Employees" + ], + "description": "List Employee resources by subscription ID", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListBySubscription": { + "$ref": "./examples/Employees_ListBySubscription.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListByResourceGroup", + "tags": [ + "Employees" + ], + "description": "List Employee resources by resource group", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListByResourceGroup": { + "$ref": "./examples/Employees_ListByResourceGroup.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees/{employeeName}": { + "get": { + "operationId": "Employees_Get", + "tags": [ + "Employees" + ], + "description": "Get a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Get": { + "$ref": "./examples/Employees_Get.json" + } + } + }, + "put": { + "operationId": "Employees_CreateOrUpdate", + "tags": [ + "Employees" + ], + "description": "Create a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/Employee" + } + } + ], + "responses": { + "200": { + "description": "Resource 'Employee' update operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "201": { + "description": "Resource 'Employee' create operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + }, + "headers": { + "Azure-AsyncOperation": { + "type": "string", + "description": "A link to the status monitor" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_CreateOrUpdate": { + "$ref": "./examples/Employees_CreateOrUpdate.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + }, + "patch": { + "operationId": "Employees_Update", + "tags": [ + "Employees" + ], + "description": "Update a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "properties", + "in": "body", + "description": "The resource properties to be updated.", + "required": true, + "schema": { + "$ref": "#/definitions/EmployeeUpdate" + } + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Update": { + "$ref": "./examples/Employees_Update.json" + } + } + }, + "delete": { + "operationId": "Employees_Delete", + "tags": [ + "Employees" + ], + "description": "Delete a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "202": { + "description": "Resource deletion accepted.", + "headers": { + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "204": { + "description": "Resource does not exist." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Delete": { + "$ref": "./examples/Employees_Delete.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "Azure.ResourceManager.CommonTypes.TrackedResourceUpdate": { + "type": "object", + "title": "Tracked Resource", + "description": "The resource model definition for an Azure Resource Manager tracked top level resource which has 'tags' and a 'location'", + "properties": { + "tags": { + "type": "object", + "description": "Resource tags.", + "additionalProperties": { + "type": "string" + } + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/Resource" + } + ] + }, + "Employee": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/TrackedResource" + } + ] + }, + "EmployeeListResult": { + "type": "object", + "description": "The response of a Employee list operation.", + "properties": { + "value": { + "type": "array", + "description": "The Employee items on this page", + "items": { + "$ref": "#/definitions/Employee" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "EmployeeProperties": { + "type": "object", + "description": "Employee properties", + "properties": { + "age": { + "type": "integer", + "format": "int32", + "description": "Age of employee" + }, + "city": { + "type": "string", + "description": "City of employee" + }, + "profile": { + "type": "string", + "format": "base64url", + "description": "Profile of employee" + }, + "provisioningState": { + "$ref": "#/definitions/ProvisioningState", + "description": "The status of the last operation.", + "readOnly": true + } + } + }, + "EmployeeUpdate": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "#/definitions/Azure.ResourceManager.CommonTypes.TrackedResourceUpdate" + } + ] + }, + "ProvisioningState": { + "type": "string", + "description": "The resource provisioning state.", + "enum": [ + "Succeeded", + "Failed", + "Canceled", + "Provisioning", + "Updating", + "Deleting", + "Accepted" + ], + "x-ms-enum": { + "name": "ProvisioningState", + "modelAsString": true, + "values": [ + { + "name": "Succeeded", + "value": "Succeeded", + "description": "Resource has been created." + }, + { + "name": "Failed", + "value": "Failed", + "description": "Resource creation failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "Resource creation was canceled." + }, + { + "name": "Provisioning", + "value": "Provisioning", + "description": "The resource is being provisioned" + }, + { + "name": "Updating", + "value": "Updating", + "description": "The resource is updating" + }, + { + "name": "Deleting", + "value": "Deleting", + "description": "The resource is being deleted" + }, + { + "name": "Accepted", + "value": "Accepted", + "description": "The resource create request has been accepted" + } + ] + }, + "readOnly": true + } + }, + "parameters": {} +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/readme.md b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/readme.md new file mode 100644 index 000000000000..1efd3bcce7ff --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgermanager3/resource-manager/readme.md @@ -0,0 +1,43 @@ +# contosowidgermanager 2 + +> see https://aka.ms/autorest +This is the AutoRest configuration file for Contoso. + +## Getting Started + +To build the SDKs for My API, simply install AutoRest via `npm` (`npm install -g autorest`) and then run: + +> `autorest readme.md` +To see additional help and options, run: + +> `autorest --help` +For other options on installation see [Installing AutoRest](https://aka.ms/autorest/install) on the AutoRest github page. + +--- + +## Configuration + +```yaml +suppressions: + - code: AvoidAnonymousTypes +``` + +### Basic Information + +These are the global settings for the containerstorage. + +```yaml +openapi-type: arm +tag: package-2021-11-01 +``` + +### Tag: package-2021-11-01 + +These settings apply only when `--tag=package-2021-11-01` is specified on the command line. + +```yaml $(tag) == 'package-2021-11-01' +input-file: + - Microsoft.Contoso/stable/2021-11-01/contoso.json +``` + +--- diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp new file mode 100644 index 000000000000..c731eeb01215 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp @@ -0,0 +1,7 @@ +import "./main.tsp"; +import "@azure-tools/typespec-client-generator-core"; + +using Azure.Contoso.WidgetManager; +using Azure.ClientGenerator.Core; + +@@clientName(Widgets, "ContosoWidgets", "csharp"); diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp new file mode 100644 index 000000000000..3caf321e31e4 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp @@ -0,0 +1,65 @@ +import "@typespec/http"; +import "@typespec/rest"; +import "@typespec/versioning"; +import "@azure-tools/typespec-azure-core"; +import "../Contoso.WidgetManager.Shared"; + +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.Core; + +@useAuth(AadOauth2Auth<["https://contoso.azure.com/.default"]>) +@service(#{ title: "Contoso Widget Manager" }) +@versioned(Contoso.WidgetManager.Versions) +namespace Azure.Contoso.WidgetManager; + +@doc("Versions info.") +enum Versions { + @doc("The 2022-11-01-preview version.") + @useDependency(Azure.Core.Versions.v1_0_Preview_1) + v2022_11_01_Preview: "2022-11-01-preview", + + @doc("The 2022-12-01 version.") + @useDependency(Azure.Core.Versions.v1_0_Preview_1) + v2022_12_01: "2022-12-01", +} + +@doc("A widget.") +@resource("widgets") +model WidgetSuite { + @key("widgetName") + @doc("The widget name.") + @visibility(Lifecycle.Read) + name: string; + + @doc("The ID of the widget's manufacturer.") + manufacturerId: string; + + @doc("The faked shared model.") + sharedModel?: FakedSharedModel; +} + +interface Widgets { + @doc("Fetch a Widget by name.") + getWidget is ResourceRead; + + @doc("Gets status of a Widget operation.") + getWidgetOperationStatus is GetResourceOperationStatus; + + @doc("Creates or updates a Widget asynchronously.") + @pollingOperation(Widgets.getWidgetOperationStatus) + createOrUpdateWidget is StandardResourceOperations.LongRunningResourceCreateOrUpdate; + + @doc("Delete a Widget asynchronously.") + @pollingOperation(Widgets.getWidgetOperationStatus) + deleteWidget is LongRunningResourceDelete; + + @doc("List Widget resources") + listWidgets is ResourceList< + WidgetSuite, + { + parameters: StandardListQueryParameters; + } + >; +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml new file mode 100644 index 000000000000..b4b63595ee09 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml @@ -0,0 +1,53 @@ +parameters: + "service-dir": + default: "sdk/contosowidgetmanager" + "dependencies": + default: "" +emit: + - "@azure-tools/typespec-autorest" +linter: + extends: + - "@azure-tools/typespec-azure-rulesets/data-plane" +options: + "@azure-tools/typespec-autorest": + azure-resource-provider-folder: "data-plane" + emit-lro-options: "none" + emitter-output-dir: "{project-root}/.." + output-file: "{azure-resource-provider-folder}/{service-name}/{version-status}/{version}/widgets.json" + "@azure-tools/typespec-python": + package-dir: "azure-contoso-widgetmanager" + namespace: "azure.contoso.widgetmanager" + generate-test: true + generate-sample: true + flavor: azure + "@azure-tools/typespec-csharp": + package-dir: "Azure.Template.Contoso" + clear-output-folder: true + model-namespace: false + namespace: "{package-dir}" + flavor: azure + "@azure-typespec/http-client-csharp": + namespace: Azure.Template.Contoso + model-namespace: false + "@azure-tools/typespec-ts": + package-dir: "contoso-widgetmanager-rest" + package-details: + name: "@azure-rest/contoso-widgetmanager" + flavor: azure + "@azure-tools/typespec-java": + package-dir: "azure-contoso-widgetmanager" + namespace: com.azure.contoso.widgetmanager + flavor: azure + "@azure-tools/typespec-go": + module: "github.com/Azure/azure-sdk-for-go/{service-dir}/{package-dir}" + service-dir: "sdk/contosowidget" + package-dir: "azmanager" + module-version: "0.0.1" + generate-fakes: true + inject-spans: true + single-client: true + slice-elements-byval: true + flavor: azure + "@azure-tools/typespec-client-generator-cli": + additionalDirectories: + - "specification/contosowidgetmanager/Contoso.WidgetManager.Shared/" diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json new file mode 100644 index 000000000000..6ff90650c918 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json @@ -0,0 +1,548 @@ +{ + "swagger": "2.0", + "info": { + "title": "Contoso Widget Manager", + "version": "2022-12-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "AadOauth2Auth": [ + "https://contoso.azure.com/.default" + ] + } + ], + "securityDefinitions": { + "AadOauth2Auth": { + "type": "oauth2", + "description": "The Azure Active Directory OAuth2 Flow", + "flow": "accessCode", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "https://contoso.azure.com/.default": "" + }, + "tokenUrl": "https://login.microsoftonline.com/common/oauth2/token" + } + }, + "tags": [], + "paths": { + "/widgets": { + "get": { + "operationId": "Widgets_ListWidgets", + "description": "List Widget resources", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedWidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_ListWidgets": { + "$ref": "./examples/Widgets_ListWidgetsSample.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/widgets/{widgetName}": { + "get": { + "operationId": "Widgets_GetWidget", + "description": "Fetch a Widget by name.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Get widget by widget name.": { + "$ref": "./examples/Widgets_GetWidgetSample.json" + } + } + }, + "patch": { + "operationId": "Widgets_CreateOrUpdateWidget", + "description": "Creates or updates a Widget asynchronously.", + "consumes": [ + "application/merge-patch+json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "resource", + "in": "body", + "description": "The resource instance.", + "required": true, + "schema": { + "$ref": "#/definitions/WidgetSuiteCreateOrUpdate" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "201": { + "description": "The request has succeeded and a new resource has been created as a result.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_CreateOrUpdateWidget": { + "$ref": "./examples/Widgets_CreateOrUpdateWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + }, + "delete": { + "operationId": "Widgets_DeleteWidget", + "description": "Delete a Widget asynchronously.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + } + }, + "required": [ + "id", + "status" + ] + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Delete widget by widget name using long-running operation.": { + "$ref": "./examples/Widgets_DeleteWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/widgets/{widgetName}/operations/{operationId}": { + "get": { + "operationId": "Widgets_GetWidgetOperationStatus", + "description": "Gets status of a Widget operation.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "operationId", + "in": "path", + "description": "The unique ID of the operation.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + }, + "result": { + "$ref": "#/definitions/WidgetSuite", + "description": "The result of the operation." + } + }, + "required": [ + "id", + "status" + ] + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_GetWidgetOperationStatus": { + "$ref": "./examples/Widgets_GetWidgetOperationStatusSample.json" + } + } + } + } + }, + "definitions": { + "Azure.Core.Foundations.Error": { + "type": "object", + "description": "The error object.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "target": { + "type": "string", + "description": "The target of the error." + }, + "details": { + "type": "array", + "description": "An array of details about specific errors that led to this reported error.", + "items": { + "$ref": "#/definitions/Azure.Core.Foundations.Error" + } + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "An object containing more specific information than the current object about the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "Azure.Core.Foundations.ErrorResponse": { + "type": "object", + "description": "A response containing error details.", + "properties": { + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "The error object." + } + }, + "required": [ + "error" + ] + }, + "Azure.Core.Foundations.InnerError": { + "type": "object", + "description": "An object containing more specific information about the error. As per Azure REST API guidelines - https://aka.ms/AzureRestApiGuidelines#handling-errors.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "Inner error." + } + } + }, + "Azure.Core.Foundations.OperationState": { + "type": "string", + "description": "Enum describing allowed operation states.", + "enum": [ + "NotStarted", + "Running", + "Succeeded", + "Failed", + "Canceled" + ], + "x-ms-enum": { + "name": "OperationState", + "modelAsString": true, + "values": [ + { + "name": "NotStarted", + "value": "NotStarted", + "description": "The operation has not started." + }, + { + "name": "Running", + "value": "Running", + "description": "The operation is in progress." + }, + { + "name": "Succeeded", + "value": "Succeeded", + "description": "The operation has completed successfully." + }, + { + "name": "Failed", + "value": "Failed", + "description": "The operation has failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "The operation has been canceled by the user." + } + ] + } + }, + "FakedSharedModel": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + }, + "required": [ + "tag", + "createdAt" + ] + }, + "FakedSharedModelCreateOrUpdate": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + } + }, + "PagedWidgetSuite": { + "type": "object", + "description": "Paged collection of WidgetSuite items", + "properties": { + "value": { + "type": "array", + "description": "The WidgetSuite items on this page", + "items": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "WidgetSuite": { + "type": "object", + "description": "A widget.", + "properties": { + "name": { + "type": "string", + "description": "The widget name.", + "readOnly": true + }, + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModel", + "description": "The faked shared model." + } + }, + "required": [ + "name", + "manufacturerId" + ] + }, + "WidgetSuiteCreateOrUpdate": { + "type": "object", + "description": "A widget.", + "properties": { + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModelCreateOrUpdate", + "description": "The faked shared model." + } + } + } + }, + "parameters": { + "Azure.Core.Foundations.ApiVersionParameter": { + "name": "api-version", + "in": "query", + "description": "The API version to use for this operation.", + "required": true, + "type": "string", + "minLength": 1, + "x-ms-parameter-location": "method", + "x-ms-client-name": "apiVersion" + } + } +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2024-12-01/widgets.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2024-12-01/widgets.json new file mode 100644 index 000000000000..ab6ef35dc336 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2024-12-01/widgets.json @@ -0,0 +1,548 @@ +{ + "swagger": "2.0", + "info": { + "title": "Contoso Widget Manager", + "version": "2024-12-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "AadOauth2Auth": [ + "https://contoso.azure.com/.default" + ] + } + ], + "securityDefinitions": { + "AadOauth2Auth": { + "type": "oauth2", + "description": "The Azure Active Directory OAuth2 Flow", + "flow": "accessCode", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "https://contoso.azure.com/.default": "" + }, + "tokenUrl": "https://login.microsoftonline.com/common/oauth2/token" + } + }, + "tags": [], + "paths": { + "/widgets": { + "get": { + "operationId": "Widgets_ListWidgets", + "description": "List Widget resources", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedWidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_ListWidgets": { + "$ref": "./examples/Widgets_ListWidgetsSample.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/widgets/{widgetName}": { + "get": { + "operationId": "Widgets_GetWidget", + "description": "Fetch a Widget by name.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Get widget by widget name.": { + "$ref": "./examples/Widgets_GetWidgetSample.json" + } + } + }, + "patch": { + "operationId": "Widgets_CreateOrUpdateWidget", + "description": "Creates or updates a Widget asynchronously.", + "consumes": [ + "application/merge-patch+json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "resource", + "in": "body", + "description": "The resource instance.", + "required": true, + "schema": { + "$ref": "#/definitions/WidgetSuiteCreateOrUpdate" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "201": { + "description": "The request has succeeded and a new resource has been created as a result.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_CreateOrUpdateWidget": { + "$ref": "./examples/Widgets_CreateOrUpdateWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + }, + "delete": { + "operationId": "Widgets_DeleteWidget", + "description": "Delete a Widget asynchronously.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + } + }, + "required": [ + "id", + "status" + ] + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Delete widget by widget name using long-running operation.": { + "$ref": "./examples/Widgets_DeleteWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/widgets/{widgetName}/operations/{operationId}": { + "get": { + "operationId": "Widgets_GetWidgetOperationStatus", + "description": "Gets status of a Widget operation.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "operationId", + "in": "path", + "description": "The unique ID of the operation.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + }, + "result": { + "$ref": "#/definitions/WidgetSuite", + "description": "The result of the operation." + } + }, + "required": [ + "id", + "status" + ] + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_GetWidgetOperationStatus": { + "$ref": "./examples/Widgets_GetWidgetOperationStatusSample.json" + } + } + } + } + }, + "definitions": { + "Azure.Core.Foundations.Error": { + "type": "object", + "description": "The error object.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "target": { + "type": "string", + "description": "The target of the error." + }, + "details": { + "type": "array", + "description": "An array of details about specific errors that led to this reported error.", + "items": { + "$ref": "#/definitions/Azure.Core.Foundations.Error" + } + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "An object containing more specific information than the current object about the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "Azure.Core.Foundations.ErrorResponse": { + "type": "object", + "description": "A response containing error details.", + "properties": { + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "The error object." + } + }, + "required": [ + "error" + ] + }, + "Azure.Core.Foundations.InnerError": { + "type": "object", + "description": "An object containing more specific information about the error. As per Azure REST API guidelines - https://aka.ms/AzureRestApiGuidelines#handling-errors.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "Inner error." + } + } + }, + "Azure.Core.Foundations.OperationState": { + "type": "string", + "description": "Enum describing allowed operation states.", + "enum": [ + "NotStarted", + "Running", + "Succeeded", + "Failed", + "Canceled" + ], + "x-ms-enum": { + "name": "OperationState", + "modelAsString": true, + "values": [ + { + "name": "NotStarted", + "value": "NotStarted", + "description": "The operation has not started." + }, + { + "name": "Running", + "value": "Running", + "description": "The operation is in progress." + }, + { + "name": "Succeeded", + "value": "Succeeded", + "description": "The operation has completed successfully." + }, + { + "name": "Failed", + "value": "Failed", + "description": "The operation has failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "The operation has been canceled by the user." + } + ] + } + }, + "FakedSharedModel": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + }, + "required": [ + "tag", + "createdAt" + ] + }, + "FakedSharedModelCreateOrUpdate": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + } + }, + "PagedWidgetSuite": { + "type": "object", + "description": "Paged collection of WidgetSuite items", + "properties": { + "value": { + "type": "array", + "description": "The WidgetSuite items on this page", + "items": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "WidgetSuite": { + "type": "object", + "description": "A widget.", + "properties": { + "name": { + "type": "string", + "description": "The widget name.", + "readOnly": true + }, + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModel", + "description": "The faked shared model." + } + }, + "required": [ + "name", + "manufacturerId" + ] + }, + "WidgetSuiteCreateOrUpdate": { + "type": "object", + "description": "A widget.", + "properties": { + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModelCreateOrUpdate", + "description": "The faked shared model." + } + } + } + }, + "parameters": { + "Azure.Core.Foundations.ApiVersionParameter": { + "name": "api-version", + "in": "query", + "description": "The API version to use for this operation.", + "required": true, + "type": "string", + "minLength": 1, + "x-ms-parameter-location": "method", + "x-ms-client-name": "apiVersion" + } + } +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/readme.md b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/readme.md new file mode 100644 index 000000000000..c68224d67f46 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/data-plane/readme.md @@ -0,0 +1,69 @@ +# Contoso.WidgetManager + +> see https://aka.ms/autorest + +This is the AutoRest configuration file for Contoso.WidgetManager. + +## Configuration + +### Basic Information + +This is a TypeSpec project so we only want to readme to default the default tag and point to the outputted swagger file. +This is used for some tools such as doc generation and swagger apiview generation it isn't used for SDK code gen as we +use the native TypeSpec code generation configured in the tspconfig.yaml file. + +```yaml +openapi-type: data-plane +tag: package-2022-12-01 +``` + +### Tag: package-2022-12-01 + +These settings apply only when `--tag=package-2022-12-01` is specified on the command line. + +```yaml $(tag) == 'package-2022-12-01' +input-file: + - Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json +``` + +### Suppress non-TypeSpec SDK related linting rules + +These set of linting rules aren't applicable to the new TypeSpec SDK code generators so suppressing them here. Eventually we will +opt-out these rules from running in the linting tools for TypeSpec generated swagger files. + +```yaml +suppressions: + - code: AvoidAnonymousTypes + - code: PatchInOperationName + - code: OperationIdNounVerb + - code: RequiredReadOnlyProperties + - code: SchemaNamesConvention + - code: SchemaDescriptionOrTitle +``` + +### Suppress non-TypeSpec SDK related linting rules + +These set of linting rules aren't applicable to the new TypeSpec SDK code generators so suppressing them here. Eventually we will +opt-out these rules from running in the linting tools for TypeSpec generated swagger files. + +```yaml +suppressions: + - code: AvoidAnonymousTypes + - code: PatchInOperationName + - code: OperationIdNounVerb + - code: RequiredReadOnlyProperties + - code: SchemaNamesConvention + - code: SchemaDescriptionOrTitle +``` + +### Suppress rules that might be fixed + +These set of linting rules we expect to fixed in typespec-autorest emitter but for now suppressing. +Github issue filed at https://github.com/Azure/typespec-azure/issues/2762 + +```yaml +suppressions: + - code: LroExtension + - code: SchemaTypeAndFormat + - code: PathParameterSchema +``` diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json new file mode 100644 index 000000000000..243b6576d7e8 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json @@ -0,0 +1,557 @@ +{ + "swagger": "2.0", + "info": { + "title": "Microsoft.Contoso management service", + "version": "2021-11-01", + "description": "Microsoft.Contoso Resource Provider management API.", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + }, + { + "name": "Employees" + } + ], + "paths": { + "/providers/Microsoft.Contoso/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Operations_List": { + "$ref": "./examples/Operations_List.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListBySubscription", + "tags": [ + "Employees" + ], + "description": "List Employee resources by subscription ID", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListBySubscription": { + "$ref": "./examples/Employees_ListBySubscription.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListByResourceGroup", + "tags": [ + "Employees" + ], + "description": "List Employee resources by resource group", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListByResourceGroup": { + "$ref": "./examples/Employees_ListByResourceGroup.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees/{employeeName}": { + "get": { + "operationId": "Employees_Get", + "tags": [ + "Employees" + ], + "description": "Get a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Get": { + "$ref": "./examples/Employees_Get.json" + } + } + }, + "put": { + "operationId": "Employees_CreateOrUpdate", + "tags": [ + "Employees" + ], + "description": "Create a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/Employee" + } + } + ], + "responses": { + "200": { + "description": "Resource 'Employee' update operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "201": { + "description": "Resource 'Employee' create operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + }, + "headers": { + "Azure-AsyncOperation": { + "type": "string", + "description": "A link to the status monitor" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_CreateOrUpdate": { + "$ref": "./examples/Employees_CreateOrUpdate.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + }, + "patch": { + "operationId": "Employees_Update", + "tags": [ + "Employees" + ], + "description": "Update a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "properties", + "in": "body", + "description": "The resource properties to be updated.", + "required": true, + "schema": { + "$ref": "#/definitions/EmployeeUpdate" + } + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Update": { + "$ref": "./examples/Employees_Update.json" + } + } + }, + "delete": { + "operationId": "Employees_Delete", + "tags": [ + "Employees" + ], + "description": "Delete a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "202": { + "description": "Resource deletion accepted.", + "headers": { + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "204": { + "description": "Resource does not exist." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Delete": { + "$ref": "./examples/Employees_Delete.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "Azure.ResourceManager.CommonTypes.TrackedResourceUpdate": { + "type": "object", + "title": "Tracked Resource", + "description": "The resource model definition for an Azure Resource Manager tracked top level resource which has 'tags' and a 'location'", + "properties": { + "tags": { + "type": "object", + "description": "Resource tags.", + "additionalProperties": { + "type": "string" + } + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/Resource" + } + ] + }, + "Employee": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/TrackedResource" + } + ] + }, + "EmployeeListResult": { + "type": "object", + "description": "The response of a Employee list operation.", + "properties": { + "value": { + "type": "array", + "description": "The Employee items on this page", + "items": { + "$ref": "#/definitions/Employee" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "EmployeeProperties": { + "type": "object", + "description": "Employee properties", + "properties": { + "age": { + "type": "integer", + "format": "int32", + "description": "Age of employee" + }, + "city": { + "type": "string", + "description": "City of employee" + }, + "profile": { + "type": "string", + "format": "base64url", + "description": "Profile of employee" + }, + "provisioningState": { + "$ref": "#/definitions/ProvisioningState", + "description": "The status of the last operation.", + "readOnly": true + } + } + }, + "EmployeeUpdate": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "#/definitions/Azure.ResourceManager.CommonTypes.TrackedResourceUpdate" + } + ] + }, + "ProvisioningState": { + "type": "string", + "description": "The resource provisioning state.", + "enum": [ + "Succeeded", + "Failed", + "Canceled", + "Provisioning", + "Updating", + "Deleting", + "Accepted" + ], + "x-ms-enum": { + "name": "ProvisioningState", + "modelAsString": true, + "values": [ + { + "name": "Succeeded", + "value": "Succeeded", + "description": "Resource has been created." + }, + { + "name": "Failed", + "value": "Failed", + "description": "Resource creation failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "Resource creation was canceled." + }, + { + "name": "Provisioning", + "value": "Provisioning", + "description": "The resource is being provisioned" + }, + { + "name": "Updating", + "value": "Updating", + "description": "The resource is updating" + }, + { + "name": "Deleting", + "value": "Deleting", + "description": "The resource is being deleted" + }, + { + "name": "Accepted", + "value": "Accepted", + "description": "The resource create request has been accepted" + } + ] + }, + "readOnly": true + } + }, + "parameters": {} +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/readme.md b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/readme.md new file mode 100644 index 000000000000..90df859dec77 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/after/specification/contosowidgetmanager/resource-manager/readme.md @@ -0,0 +1,44 @@ +# Contoso.WidgetManager + +> see https://aka.ms/autorest +This is the AutoRest configuration file for Contoso. + +## Getting Started + +To build the SDKs for My API, simply install AutoRest via `npm` (`npm install -g autorest`) and then run: + +> `autorest readme.md` +To see additional help and options, run: + +> `autorest --help` +For other options on installation see [Installing AutoRest](https://aka.ms/autorest/install) on the AutoRest github page. + +--- + +## Configuration + +```yaml +suppressions: + - code: AvoidAnonymousTypes +``` + +### Basic Information + +These are the global settings for the containerstorage. + +```yaml +openapi-type: arm +openapi-subtype: rpaas +tag: package-2021-11-01 +``` + +### Tag: package-2021-11-01 + +These settings apply only when `--tag=package-2021-11-01` is specified on the command line. + +```yaml $(tag) == 'package-2021-11-01' +input-file: + - Microsoft.Contoso/stable/2021-11-01/contoso.json +``` + +--- diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp new file mode 100644 index 000000000000..c731eeb01215 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/client.tsp @@ -0,0 +1,7 @@ +import "./main.tsp"; +import "@azure-tools/typespec-client-generator-core"; + +using Azure.Contoso.WidgetManager; +using Azure.ClientGenerator.Core; + +@@clientName(Widgets, "ContosoWidgets", "csharp"); diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp new file mode 100644 index 000000000000..3caf321e31e4 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp @@ -0,0 +1,65 @@ +import "@typespec/http"; +import "@typespec/rest"; +import "@typespec/versioning"; +import "@azure-tools/typespec-azure-core"; +import "../Contoso.WidgetManager.Shared"; + +using TypeSpec.Http; +using TypeSpec.Rest; +using TypeSpec.Versioning; +using Azure.Core; + +@useAuth(AadOauth2Auth<["https://contoso.azure.com/.default"]>) +@service(#{ title: "Contoso Widget Manager" }) +@versioned(Contoso.WidgetManager.Versions) +namespace Azure.Contoso.WidgetManager; + +@doc("Versions info.") +enum Versions { + @doc("The 2022-11-01-preview version.") + @useDependency(Azure.Core.Versions.v1_0_Preview_1) + v2022_11_01_Preview: "2022-11-01-preview", + + @doc("The 2022-12-01 version.") + @useDependency(Azure.Core.Versions.v1_0_Preview_1) + v2022_12_01: "2022-12-01", +} + +@doc("A widget.") +@resource("widgets") +model WidgetSuite { + @key("widgetName") + @doc("The widget name.") + @visibility(Lifecycle.Read) + name: string; + + @doc("The ID of the widget's manufacturer.") + manufacturerId: string; + + @doc("The faked shared model.") + sharedModel?: FakedSharedModel; +} + +interface Widgets { + @doc("Fetch a Widget by name.") + getWidget is ResourceRead; + + @doc("Gets status of a Widget operation.") + getWidgetOperationStatus is GetResourceOperationStatus; + + @doc("Creates or updates a Widget asynchronously.") + @pollingOperation(Widgets.getWidgetOperationStatus) + createOrUpdateWidget is StandardResourceOperations.LongRunningResourceCreateOrUpdate; + + @doc("Delete a Widget asynchronously.") + @pollingOperation(Widgets.getWidgetOperationStatus) + deleteWidget is LongRunningResourceDelete; + + @doc("List Widget resources") + listWidgets is ResourceList< + WidgetSuite, + { + parameters: StandardListQueryParameters; + } + >; +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml new file mode 100644 index 000000000000..b4b63595ee09 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml @@ -0,0 +1,53 @@ +parameters: + "service-dir": + default: "sdk/contosowidgetmanager" + "dependencies": + default: "" +emit: + - "@azure-tools/typespec-autorest" +linter: + extends: + - "@azure-tools/typespec-azure-rulesets/data-plane" +options: + "@azure-tools/typespec-autorest": + azure-resource-provider-folder: "data-plane" + emit-lro-options: "none" + emitter-output-dir: "{project-root}/.." + output-file: "{azure-resource-provider-folder}/{service-name}/{version-status}/{version}/widgets.json" + "@azure-tools/typespec-python": + package-dir: "azure-contoso-widgetmanager" + namespace: "azure.contoso.widgetmanager" + generate-test: true + generate-sample: true + flavor: azure + "@azure-tools/typespec-csharp": + package-dir: "Azure.Template.Contoso" + clear-output-folder: true + model-namespace: false + namespace: "{package-dir}" + flavor: azure + "@azure-typespec/http-client-csharp": + namespace: Azure.Template.Contoso + model-namespace: false + "@azure-tools/typespec-ts": + package-dir: "contoso-widgetmanager-rest" + package-details: + name: "@azure-rest/contoso-widgetmanager" + flavor: azure + "@azure-tools/typespec-java": + package-dir: "azure-contoso-widgetmanager" + namespace: com.azure.contoso.widgetmanager + flavor: azure + "@azure-tools/typespec-go": + module: "github.com/Azure/azure-sdk-for-go/{service-dir}/{package-dir}" + service-dir: "sdk/contosowidget" + package-dir: "azmanager" + module-version: "0.0.1" + generate-fakes: true + inject-spans: true + single-client: true + slice-elements-byval: true + flavor: azure + "@azure-tools/typespec-client-generator-cli": + additionalDirectories: + - "specification/contosowidgetmanager/Contoso.WidgetManager.Shared/" diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json new file mode 100644 index 000000000000..6ff90650c918 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json @@ -0,0 +1,548 @@ +{ + "swagger": "2.0", + "info": { + "title": "Contoso Widget Manager", + "version": "2022-12-01", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "AadOauth2Auth": [ + "https://contoso.azure.com/.default" + ] + } + ], + "securityDefinitions": { + "AadOauth2Auth": { + "type": "oauth2", + "description": "The Azure Active Directory OAuth2 Flow", + "flow": "accessCode", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "https://contoso.azure.com/.default": "" + }, + "tokenUrl": "https://login.microsoftonline.com/common/oauth2/token" + } + }, + "tags": [], + "paths": { + "/widgets": { + "get": { + "operationId": "Widgets_ListWidgets", + "description": "List Widget resources", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedWidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_ListWidgets": { + "$ref": "./examples/Widgets_ListWidgetsSample.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/widgets/{widgetName}": { + "get": { + "operationId": "Widgets_GetWidget", + "description": "Fetch a Widget by name.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Get widget by widget name.": { + "$ref": "./examples/Widgets_GetWidgetSample.json" + } + } + }, + "patch": { + "operationId": "Widgets_CreateOrUpdateWidget", + "description": "Creates or updates a Widget asynchronously.", + "consumes": [ + "application/merge-patch+json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "resource", + "in": "body", + "description": "The resource instance.", + "required": true, + "schema": { + "$ref": "#/definitions/WidgetSuiteCreateOrUpdate" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "201": { + "description": "The request has succeeded and a new resource has been created as a result.", + "schema": { + "$ref": "#/definitions/WidgetSuite" + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_CreateOrUpdateWidget": { + "$ref": "./examples/Widgets_CreateOrUpdateWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + }, + "delete": { + "operationId": "Widgets_DeleteWidget", + "description": "Delete a Widget asynchronously.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + } + }, + "required": [ + "id", + "status" + ] + }, + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Delete widget by widget name using long-running operation.": { + "$ref": "./examples/Widgets_DeleteWidgetSample.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/widgets/{widgetName}/operations/{operationId}": { + "get": { + "operationId": "Widgets_GetWidgetOperationStatus", + "description": "Gets status of a Widget operation.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "widgetName", + "in": "path", + "description": "The widget name.", + "required": true, + "type": "string" + }, + { + "name": "operationId", + "in": "path", + "description": "The unique ID of the operation.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "type": "object", + "description": "Provides status details for long running operations.", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the operation." + }, + "status": { + "$ref": "#/definitions/Azure.Core.Foundations.OperationState", + "description": "The status of the operation" + }, + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "Error object that describes the error when status is \"Failed\"." + }, + "result": { + "$ref": "#/definitions/WidgetSuite", + "description": "The result of the operation." + } + }, + "required": [ + "id", + "status" + ] + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/Azure.Core.Foundations.ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "Widgets_GetWidgetOperationStatus": { + "$ref": "./examples/Widgets_GetWidgetOperationStatusSample.json" + } + } + } + } + }, + "definitions": { + "Azure.Core.Foundations.Error": { + "type": "object", + "description": "The error object.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "target": { + "type": "string", + "description": "The target of the error." + }, + "details": { + "type": "array", + "description": "An array of details about specific errors that led to this reported error.", + "items": { + "$ref": "#/definitions/Azure.Core.Foundations.Error" + } + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "An object containing more specific information than the current object about the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "Azure.Core.Foundations.ErrorResponse": { + "type": "object", + "description": "A response containing error details.", + "properties": { + "error": { + "$ref": "#/definitions/Azure.Core.Foundations.Error", + "description": "The error object." + } + }, + "required": [ + "error" + ] + }, + "Azure.Core.Foundations.InnerError": { + "type": "object", + "description": "An object containing more specific information about the error. As per Azure REST API guidelines - https://aka.ms/AzureRestApiGuidelines#handling-errors.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "innererror": { + "$ref": "#/definitions/Azure.Core.Foundations.InnerError", + "description": "Inner error." + } + } + }, + "Azure.Core.Foundations.OperationState": { + "type": "string", + "description": "Enum describing allowed operation states.", + "enum": [ + "NotStarted", + "Running", + "Succeeded", + "Failed", + "Canceled" + ], + "x-ms-enum": { + "name": "OperationState", + "modelAsString": true, + "values": [ + { + "name": "NotStarted", + "value": "NotStarted", + "description": "The operation has not started." + }, + { + "name": "Running", + "value": "Running", + "description": "The operation is in progress." + }, + { + "name": "Succeeded", + "value": "Succeeded", + "description": "The operation has completed successfully." + }, + { + "name": "Failed", + "value": "Failed", + "description": "The operation has failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "The operation has been canceled by the user." + } + ] + } + }, + "FakedSharedModel": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + }, + "required": [ + "tag", + "createdAt" + ] + }, + "FakedSharedModelCreateOrUpdate": { + "type": "object", + "description": "Faked shared model", + "properties": { + "tag": { + "type": "string", + "description": "The tag." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The created date." + } + } + }, + "PagedWidgetSuite": { + "type": "object", + "description": "Paged collection of WidgetSuite items", + "properties": { + "value": { + "type": "array", + "description": "The WidgetSuite items on this page", + "items": { + "$ref": "#/definitions/WidgetSuite" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "WidgetSuite": { + "type": "object", + "description": "A widget.", + "properties": { + "name": { + "type": "string", + "description": "The widget name.", + "readOnly": true + }, + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModel", + "description": "The faked shared model." + } + }, + "required": [ + "name", + "manufacturerId" + ] + }, + "WidgetSuiteCreateOrUpdate": { + "type": "object", + "description": "A widget.", + "properties": { + "manufacturerId": { + "type": "string", + "description": "The ID of the widget's manufacturer." + }, + "sharedModel": { + "$ref": "#/definitions/FakedSharedModelCreateOrUpdate", + "description": "The faked shared model." + } + } + } + }, + "parameters": { + "Azure.Core.Foundations.ApiVersionParameter": { + "name": "api-version", + "in": "query", + "description": "The API version to use for this operation.", + "required": true, + "type": "string", + "minLength": 1, + "x-ms-parameter-location": "method", + "x-ms-client-name": "apiVersion" + } + } +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/readme.md b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/readme.md new file mode 100644 index 000000000000..c68224d67f46 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/data-plane/readme.md @@ -0,0 +1,69 @@ +# Contoso.WidgetManager + +> see https://aka.ms/autorest + +This is the AutoRest configuration file for Contoso.WidgetManager. + +## Configuration + +### Basic Information + +This is a TypeSpec project so we only want to readme to default the default tag and point to the outputted swagger file. +This is used for some tools such as doc generation and swagger apiview generation it isn't used for SDK code gen as we +use the native TypeSpec code generation configured in the tspconfig.yaml file. + +```yaml +openapi-type: data-plane +tag: package-2022-12-01 +``` + +### Tag: package-2022-12-01 + +These settings apply only when `--tag=package-2022-12-01` is specified on the command line. + +```yaml $(tag) == 'package-2022-12-01' +input-file: + - Azure.Contoso.WidgetManager/stable/2022-12-01/widgets.json +``` + +### Suppress non-TypeSpec SDK related linting rules + +These set of linting rules aren't applicable to the new TypeSpec SDK code generators so suppressing them here. Eventually we will +opt-out these rules from running in the linting tools for TypeSpec generated swagger files. + +```yaml +suppressions: + - code: AvoidAnonymousTypes + - code: PatchInOperationName + - code: OperationIdNounVerb + - code: RequiredReadOnlyProperties + - code: SchemaNamesConvention + - code: SchemaDescriptionOrTitle +``` + +### Suppress non-TypeSpec SDK related linting rules + +These set of linting rules aren't applicable to the new TypeSpec SDK code generators so suppressing them here. Eventually we will +opt-out these rules from running in the linting tools for TypeSpec generated swagger files. + +```yaml +suppressions: + - code: AvoidAnonymousTypes + - code: PatchInOperationName + - code: OperationIdNounVerb + - code: RequiredReadOnlyProperties + - code: SchemaNamesConvention + - code: SchemaDescriptionOrTitle +``` + +### Suppress rules that might be fixed + +These set of linting rules we expect to fixed in typespec-autorest emitter but for now suppressing. +Github issue filed at https://github.com/Azure/typespec-azure/issues/2762 + +```yaml +suppressions: + - code: LroExtension + - code: SchemaTypeAndFormat + - code: PathParameterSchema +``` diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json new file mode 100644 index 000000000000..243b6576d7e8 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json @@ -0,0 +1,557 @@ +{ + "swagger": "2.0", + "info": { + "title": "Microsoft.Contoso management service", + "version": "2021-11-01", + "description": "Microsoft.Contoso Resource Provider management API.", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "host": "management.azure.com", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "azure_auth": [ + "user_impersonation" + ] + } + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "description": "Azure Active Directory OAuth2 Flow.", + "flow": "implicit", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "tags": [ + { + "name": "Operations" + }, + { + "name": "Employees" + } + ], + "paths": { + "/providers/Microsoft.Contoso/operations": { + "get": { + "operationId": "Operations_List", + "tags": [ + "Operations" + ], + "description": "List the operations for the provider", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/OperationListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Operations_List": { + "$ref": "./examples/Operations_List.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListBySubscription", + "tags": [ + "Employees" + ], + "description": "List Employee resources by subscription ID", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListBySubscription": { + "$ref": "./examples/Employees_ListBySubscription.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees": { + "get": { + "operationId": "Employees_ListByResourceGroup", + "tags": [ + "Employees" + ], + "description": "List Employee resources by resource group", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/EmployeeListResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_ListByResourceGroup": { + "$ref": "./examples/Employees_ListByResourceGroup.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Contoso/employees/{employeeName}": { + "get": { + "operationId": "Employees_Get", + "tags": [ + "Employees" + ], + "description": "Get a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Get": { + "$ref": "./examples/Employees_Get.json" + } + } + }, + "put": { + "operationId": "Employees_CreateOrUpdate", + "tags": [ + "Employees" + ], + "description": "Create a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "resource", + "in": "body", + "description": "Resource create parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/Employee" + } + } + ], + "responses": { + "200": { + "description": "Resource 'Employee' update operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "201": { + "description": "Resource 'Employee' create operation succeeded", + "schema": { + "$ref": "#/definitions/Employee" + }, + "headers": { + "Azure-AsyncOperation": { + "type": "string", + "description": "A link to the status monitor" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_CreateOrUpdate": { + "$ref": "./examples/Employees_CreateOrUpdate.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "azure-async-operation" + }, + "x-ms-long-running-operation": true + }, + "patch": { + "operationId": "Employees_Update", + "tags": [ + "Employees" + ], + "description": "Update a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + }, + { + "name": "properties", + "in": "body", + "description": "The resource properties to be updated.", + "required": true, + "schema": { + "$ref": "#/definitions/EmployeeUpdate" + } + } + ], + "responses": { + "200": { + "description": "Azure operation completed successfully.", + "schema": { + "$ref": "#/definitions/Employee" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Update": { + "$ref": "./examples/Employees_Update.json" + } + } + }, + "delete": { + "operationId": "Employees_Delete", + "tags": [ + "Employees" + ], + "description": "Delete a Employee", + "parameters": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ApiVersionParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/SubscriptionIdParameter" + }, + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/parameters/ResourceGroupNameParameter" + }, + { + "name": "employeeName", + "in": "path", + "description": "The name of the Employee", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9-]{3,24}$" + } + ], + "responses": { + "202": { + "description": "Resource deletion accepted.", + "headers": { + "Location": { + "type": "string", + "description": "The Location header contains the URL where the status of the long running operation can be checked." + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "204": { + "description": "Resource does not exist." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-examples": { + "Employees_Delete": { + "$ref": "./examples/Employees_Delete.json" + } + }, + "x-ms-long-running-operation-options": { + "final-state-via": "location" + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "Azure.ResourceManager.CommonTypes.TrackedResourceUpdate": { + "type": "object", + "title": "Tracked Resource", + "description": "The resource model definition for an Azure Resource Manager tracked top level resource which has 'tags' and a 'location'", + "properties": { + "tags": { + "type": "object", + "description": "Resource tags.", + "additionalProperties": { + "type": "string" + } + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/Resource" + } + ] + }, + "Employee": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "../../../../../common-types/resource-management/v5/types.json#/definitions/TrackedResource" + } + ] + }, + "EmployeeListResult": { + "type": "object", + "description": "The response of a Employee list operation.", + "properties": { + "value": { + "type": "array", + "description": "The Employee items on this page", + "items": { + "$ref": "#/definitions/Employee" + } + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "EmployeeProperties": { + "type": "object", + "description": "Employee properties", + "properties": { + "age": { + "type": "integer", + "format": "int32", + "description": "Age of employee" + }, + "city": { + "type": "string", + "description": "City of employee" + }, + "profile": { + "type": "string", + "format": "base64url", + "description": "Profile of employee" + }, + "provisioningState": { + "$ref": "#/definitions/ProvisioningState", + "description": "The status of the last operation.", + "readOnly": true + } + } + }, + "EmployeeUpdate": { + "type": "object", + "description": "Employee resource", + "properties": { + "properties": { + "$ref": "#/definitions/EmployeeProperties", + "description": "The resource-specific properties for this resource." + } + }, + "allOf": [ + { + "$ref": "#/definitions/Azure.ResourceManager.CommonTypes.TrackedResourceUpdate" + } + ] + }, + "ProvisioningState": { + "type": "string", + "description": "The resource provisioning state.", + "enum": [ + "Succeeded", + "Failed", + "Canceled", + "Provisioning", + "Updating", + "Deleting", + "Accepted" + ], + "x-ms-enum": { + "name": "ProvisioningState", + "modelAsString": true, + "values": [ + { + "name": "Succeeded", + "value": "Succeeded", + "description": "Resource has been created." + }, + { + "name": "Failed", + "value": "Failed", + "description": "Resource creation failed." + }, + { + "name": "Canceled", + "value": "Canceled", + "description": "Resource creation was canceled." + }, + { + "name": "Provisioning", + "value": "Provisioning", + "description": "The resource is being provisioned" + }, + { + "name": "Updating", + "value": "Updating", + "description": "The resource is updating" + }, + { + "name": "Deleting", + "value": "Deleting", + "description": "The resource is being deleted" + }, + { + "name": "Accepted", + "value": "Accepted", + "description": "The resource create request has been accepted" + } + ] + }, + "readOnly": true + } + }, + "parameters": {} +} diff --git a/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/readme.md b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/readme.md new file mode 100644 index 000000000000..ae912e6ab9b0 --- /dev/null +++ b/eng/tools/summarize-impact/test/fixtures/default/before/specification/contosowidgetmanager/resource-manager/readme.md @@ -0,0 +1,48 @@ +# containerstorage + +> see https://aka.ms/autorest +This is the AutoRest configuration file for Contoso. + +## Getting Started + +To build the SDKs for My API, simply install AutoRest via `npm` (`npm install -g autorest`) and then run: + +> `autorest readme.md` +To see additional help and options, run: + +> `autorest --help` +For other options on installation see [Installing AutoRest](https://aka.ms/autorest/install) on the AutoRest github page. + +--- + +## Configuration + +### Basic Information + +These are the global settings for the containerstorage. + +```yaml +openapi-type: arm +openapi-subtype: rpaas +tag: package-2021-11-01 +``` + +### Tag: package-2021-11-01 + +These settings apply only when `--tag=package-2021-11-01` is specified on the command line. + +```yaml $(tag) == 'package-2021-11-01' +input-file: + - Microsoft.Contoso/stable/2021-11-01/contoso.json +``` + +### Tag: package-2021-10-01-preview + +These settings apply only when `--tag=package-2021-10-01-preview` is specified on the command line. + +```yaml $(tag) == 'package-2021-10-01-preview' +input-file: + - Microsoft.Contoso/preview/2021-10-01-preview/contoso.json +``` + +--- diff --git a/eng/tools/summarize-impact/test/impact.test.ts b/eng/tools/summarize-impact/test/impact.test.ts new file mode 100644 index 000000000000..36acfd4a0835 --- /dev/null +++ b/eng/tools/summarize-impact/test/impact.test.ts @@ -0,0 +1,207 @@ +import fs from "fs"; +import path from "path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; //vi +import { PRContext } from "../src/PRContext.js"; +import { evaluateImpact } from "../src/impact.js"; +import { LabelContext } from "../src/labelling-types.js"; + +const FIXTURE_DIRECTORY = path.join(__dirname, "fixtures"); +const BEFORE_REPO_FIXTURE = path.join(FIXTURE_DIRECTORY, "default", "before"); +const AFTER_REPO_FIXTURE = path.join(FIXTURE_DIRECTORY, "default", "after"); + +const DEFAULT_MAIN_SPEC_FOLDERS: string[] = fs + .readdirSync(path.join(BEFORE_REPO_FIXTURE, "specification"), { withFileTypes: true }) + .filter((d) => d.isDirectory()) + .map((d) => path.join(BEFORE_REPO_FIXTURE, "specification", d.name)); + +describe("Impact Detection - default fixture", () => { + let originalCwd: string; + + beforeEach(() => { + // Save the original working directory + originalCwd = process.cwd(); + // Change to the 'after' fixture directory + process.chdir(AFTER_REPO_FIXTURE); + }); + + afterEach(() => { + // Restore the original working directory + process.chdir(originalCwd); + }); + + const testCases = [ + { + description: "Should properly handle no changes", + changedFileDetails: { + additions: [], + modifications: [], + deletions: [], + renames: [], + total: 0, + }, + expectedImpact: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: false, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: false, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should properly identify typespec changes", + changedFileDetails: { + additions: [], + modifications: ["specification/contosowidgetmanager/Contoso.WidgetManager/main.tsp"], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: false, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should properly identify when suppression review is required", + changedFileDetails: { + additions: [], + modifications: ["specification/contosowidgetmanager/resource-manager/readme.md"], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: true, + rpaasChange: true, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: false, + isNewApiVersion: false, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should properly identify a new API Version", + changedFileDetails: { + additions: [ + "specification/contosowidgetmanager/data-plane/Azure.Contoso.WidgetManager/stable/2024-12-01/widgets.json", + ], + modifications: [], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: false, + rpaasChange: false, + newRP: false, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: false, + dataPlaneRequired: true, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "Should properly identify an RP that wasn't present before", + changedFileDetails: { + additions: [ + "specification/contosowidgermanager2/resource-manager/readme.md", + "specification/contosowidgermanager2/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", + ], + modifications: [], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: true, + rpaasChange: true, + newRP: true, + rpaasRPMissing: false, + rpaasRpNotInPrivateRepo: true, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + { + description: "should properly identify a new RP that isn't of rpaas subtype", + changedFileDetails: { + additions: [ + "specification/contosowidgermanager3/resource-manager/readme.md", + "specification/contosowidgermanager3/resource-manager/Microsoft.Contoso/stable/2021-11-01/contoso.json", + ], + modifications: [], + deletions: [], + renames: [], + total: 1, + }, + expectedImpact: { + suppressionReviewRequired: true, + rpaasChange: false, + newRP: true, + rpaasRPMissing: true, + rpaasRpNotInPrivateRepo: false, + resourceManagerRequired: true, + dataPlaneRequired: false, + rpaasExceptionRequired: false, + typeSpecChanged: true, + isNewApiVersion: true, + isDraft: false, + targetBranch: "main", + }, + }, + ]; + it.each(testCases)("$description", async ({ changedFileDetails, expectedImpact }) => { + const labelContext: LabelContext = { + present: new Set(), + toAdd: new Set(), + toRemove: new Set(), + }; + + const prContext = new PRContext(AFTER_REPO_FIXTURE, BEFORE_REPO_FIXTURE, labelContext, { + sha: "2bd8350d465081401a0f4f03e633eca41f0991de", + sourceBranch: "contosotest", + targetBranch: expectedImpact.targetBranch, + repo: "azure-rest-api-specs", + prNumber: "1", + owner: "Azure", + fileList: changedFileDetails, + isDraft: false, + }); + + const actualImpact = await evaluateImpact(prContext, labelContext, DEFAULT_MAIN_SPEC_FOLDERS); + expect(actualImpact).toEqual(expectedImpact); + }); +}); diff --git a/eng/tools/summarize-impact/tsconfig.json b/eng/tools/summarize-impact/tsconfig.json new file mode 100644 index 000000000000..5f48d4c6a5b5 --- /dev/null +++ b/eng/tools/summarize-impact/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": ".", + "allowJs": true, + }, + "include": ["*.ts", "src/**/*.ts", "test/**/*.ts"], +} diff --git a/eng/tools/suppressions/package.json b/eng/tools/suppressions/package.json index 959505dad9fb..e94d0d80cfae 100644 --- a/eng/tools/suppressions/package.json +++ b/eng/tools/suppressions/package.json @@ -26,6 +26,7 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" } diff --git a/eng/tools/tsconfig.json b/eng/tools/tsconfig.json index ac5fe54c268f..ba7486de2512 100644 --- a/eng/tools/tsconfig.json +++ b/eng/tools/tsconfig.json @@ -41,5 +41,8 @@ { "path": "./typespec-validation", }, + { + "path": "./summarize-impact", + }, ], } diff --git a/eng/tools/tsp-client-tests/package.json b/eng/tools/tsp-client-tests/package.json index 168b49e23b67..5951effe30d2 100644 --- a/eng/tools/tsp-client-tests/package.json +++ b/eng/tools/tsp-client-tests/package.json @@ -6,6 +6,7 @@ "@azure-tools/specs-shared": "file:../../../.github/shared", "@types/node": "^20.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, diff --git a/eng/tools/typespec-migration-validation/scripts/download-main.ps1 b/eng/tools/typespec-migration-validation/scripts/download-main.ps1 index be60fe389cb4..0a7b1edcb220 100644 --- a/eng/tools/typespec-migration-validation/scripts/download-main.ps1 +++ b/eng/tools/typespec-migration-validation/scripts/download-main.ps1 @@ -1,6 +1,6 @@ param( [string]$swaggerPath, - [string]$callValidation = $false + [string]$reportFile = $null ) . $PSScriptRoot/../../../scripts/ChangedFiles-Functions.ps1 @@ -95,11 +95,11 @@ if ($swaggerPath.StartsWith("specification")) { $swaggerPath = Join-Path $repoRoot $swaggerPath } -if ($callValidation -eq $true) { - Write-Host "Executing TypeSpec migration validation..." - npx tsmv $swaggerInMain $swaggerPath +if ([string]::IsNullOrEmpty($reportFile)) { + Write-Host "Your next command: npx tsmv $swaggerInMain $swaggerPath --outputFolder {outputFolder}" } -else { - Write-Host "Your next command: npx tsmv $swaggerInMain $swaggerPath {outputFolder}" +else { + Write-Host "Executing TypeSpec migration validation..." + npx tsmv $swaggerInMain $swaggerPath --reportFile $reportFile } diff --git a/eng/tools/typespec-migration-validation/src/commonType.ts b/eng/tools/typespec-migration-validation/src/commonType.ts new file mode 100644 index 000000000000..e67e93be1e5e --- /dev/null +++ b/eng/tools/typespec-migration-validation/src/commonType.ts @@ -0,0 +1,61 @@ +import path from 'path'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +import { readFileContent } from './helper.js'; +import { OpenAPI2Document, OpenAPI2Schema } from '@azure-tools/typespec-autorest'; +import { isRef } from './compare.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +export function getCommonTypeDefinition(refPath: string): [OpenAPI2Schema, OpenAPI2Document] | undefined { + const commonTypeInfo = getCommonTypeInfo(refPath); + if (!commonTypeInfo) { + return undefined; + } + const [filePath, definitionName] = commonTypeInfo; + const fileContent = readCommonTypeFile(filePath); + const commonTypeDefinition = fileContent.definitions?.[definitionName]; + if (!commonTypeDefinition) { + return undefined; + } + return [resolveCommonTypeDefinition(commonTypeDefinition, fileContent), fileContent]; +} + +// For all the properties, we need to inline its type if is a reference to another common type, because we cannot resolve it outside the common type file +function resolveCommonTypeDefinition(commonTypeDefinition: OpenAPI2Schema, fileContent: OpenAPI2Document): OpenAPI2Schema { + for (const property in commonTypeDefinition.properties) { + const propertySchema = commonTypeDefinition.properties[property]; + if (isRef(propertySchema)) { + const refPath = propertySchema.$ref; + if (!refPath.startsWith('#/definitions/')) { + console.warn(`Reference ${propertySchema.$ref} is not a local definition, skipping.`); + continue; + } + const refDefinitionName = refPath.replace('#/definitions/', ''); + const refDefinitionSchema = fileContent.definitions?.[refDefinitionName]; + commonTypeDefinition.properties[property] = refDefinitionSchema ? resolveCommonTypeDefinition(refDefinitionSchema, fileContent) : propertySchema; + } + } + return commonTypeDefinition; +} + +function getCommonTypeInfo(refPath: string): [string, string] | undefined { + const matchResult = refPath.match(/\/common-types\/resource-management\/(v\d)\/(.*)#\/definitions\/(.*)/); + if (!matchResult) { + return undefined; + } + const [, version, fileName, definitionName] = matchResult; + return [path.join(__dirname, '..', '..', '..', '..', '..', 'specification', 'common-types', 'resource-management', version, fileName), definitionName]; +} + +const commonTypeFileCache: Record = {}; +function readCommonTypeFile(filePath: string): OpenAPI2Document { + if (commonTypeFileCache[filePath]) { + return commonTypeFileCache[filePath]; + } + const fileContent = readFileContent(filePath); + const jsonContent: OpenAPI2Document = JSON.parse(fileContent); + commonTypeFileCache[filePath] = jsonContent; + return jsonContent; +} \ No newline at end of file diff --git a/eng/tools/typespec-migration-validation/src/compare.ts b/eng/tools/typespec-migration-validation/src/compare.ts new file mode 100644 index 000000000000..1981967e34fd --- /dev/null +++ b/eng/tools/typespec-migration-validation/src/compare.ts @@ -0,0 +1,473 @@ +import { HttpMethod, OpenAPI2Document, OpenAPI2Operation, OpenAPI2Response, OpenAPI2Schema, OpenAPI2SchemaProperty, Ref, Refable } from "@azure-tools/typespec-autorest"; +import { getCommonTypeDefinition } from "./commonType.js"; + +interface Diff { + before: any; + after: any; + level: "warning" | "error"; +} + +/** + * path: path for the same operationId should be the same + * parameters: parameters for the same operationId should have the same count + * pageable: whether the operation is pageable or not for the same operationId should be the same + * longrunning: whether the operation is long-running or not for the same operationId should be the same + * finalstate: whether the final state of the long-running operation is the same for the same operationId + * finalresult: the final result schema of the long-running operation should be the same as the schema of 200 response + * responses: the response codes for the same operationId should have the same count and same response code + * response: the response schema for the same response code should be the same + */ +interface PathDiff extends Diff { + operationId: string; + type: "path" | "parameters" | "pageable" | "longrunning" | "finalstate" | "finalresult" | "responses" | "response"; +} + +/** + * properties: the property names of the definition should be the same + * property: the property should have same schema + * required: the required properties of the definition should be the same + */ +interface DefinitionDiff extends Diff { + name: string; + propertyName?: string; + changeType?: string; + type: "properties" | "property" | "required"; +} + +export function printDiff(diff: PathDiff | DefinitionDiff): string { + if ("operationId" in diff) { + return `| ${diff.type} | ${diff.level} | ${getPathDiffMessage(diff)} ${JSON.stringify(diff.before)} -> ${JSON.stringify(diff.after)} |\n`; + } else { + return `| ${diff.type} | ${diff.level} | ${getDefinitionDiffMessage(diff)} ${JSON.stringify(diff.before)} -> ${JSON.stringify(diff.after)} |\n`; + } +} + +function getPathDiffMessage(diff: PathDiff): string { + switch (diff.type) { + case "path": + return `The path for operation "${diff.operationId}" changed:`; + case "parameters": + return `The number of parameters for operation "${diff.operationId}" changed:`; + case "pageable": + return `The pageable for operation "${diff.operationId}" changed:`; + case "longrunning": + return `The long-running status for operation "${diff.operationId}" changed:`; + case "finalstate": + return `The final state for operation "${diff.operationId}" changed:`; + case "finalresult": + return `The final result schema for operation "${diff.operationId}" changed:`; + case "responses": + return `The response codes for operation "${diff.operationId}" changed:`; + case "response": + return `The response schema for operation "${diff.operationId}" changed:`; + default: + return `The ${diff.type} for operation "${diff.operationId}" changed:`; + } +} + +function getDefinitionDiffMessage(diff: DefinitionDiff): string { + switch (diff.type) { + case "properties": + return `The property names of definition "${diff.name}" changed:`; + case "property": + return `The ${diff.changeType} of property "${diff.propertyName}" in definition "${diff.name}" changed:`; + case "required": + return `The required properties of definition "${diff.name}" changed:`; + } +} + +export function compareDocuments(oldDocument: OpenAPI2Document, newDocument: OpenAPI2Document): (PathDiff | DefinitionDiff)[] { + const diffs: (PathDiff | DefinitionDiff)[] = []; + diffs.push(...comparePaths(oldDocument, newDocument)); + + // If not exists in the new document, it might be an orphaned definition previously + for (const definition in newDocument.definitions ?? {}) { + // If not exists in old document, it might be an anonymous definition previously + if (oldDocument.definitions?.[definition]) { + diffs.push(...compareNamedDefinition(oldDocument.definitions[definition], oldDocument, newDocument.definitions![definition], newDocument, definition)); + } + } + return diffs; +} + +const definitionNameCache: Set = new Set(); +function compareNamedDefinition(oldDefinition: OpenAPI2Schema, oldDocument: OpenAPI2Document, newDefinition: OpenAPI2Schema, newDocument: OpenAPI2Document, name: string): DefinitionDiff[] { + if (definitionNameCache.has(name)) { + console.warn(`Definition "${name}" has been compared before, skipping.`); + return []; + } + definitionNameCache.add(name); + + const diffs: DefinitionDiff[] = []; + + const oldRequired = oldDefinition.required || []; + const newRequired = newDefinition.required || []; + + if (oldRequired.length !== newRequired.length || + !oldRequired.every(item => newRequired.includes(item)) || + !newRequired.every(item => oldRequired.includes(item))) { + diffs.push({ + before: oldRequired, + after: newRequired, + name, + type: "required", + level: "warning", + }); + } + + const oldProperties = getAllProperties(oldDefinition, oldDocument); + const sortedOldProperties = Object.keys(oldProperties).sort().reduce((obj: Record, key) => { + obj[key] = oldProperties[key]; + return obj; + }, {}); + + const newProperties = getAllProperties(newDefinition, newDocument); + const sortedNewProperties = Object.keys(newProperties).sort().reduce((obj: Record, key) => { + obj[key] = newProperties[key]; + return obj; + }, {}); + + // First check if the properties are the same + const oldKeys = Object.keys(sortedOldProperties); + const newKeys = Object.keys(sortedNewProperties); + if (oldKeys.length !== newKeys.length) { + // Check if newKeys has exactly one more key and it's systemData + const isSystemDataOnlyDifference = newKeys.length === oldKeys.length + 1 && + newKeys.filter(key => !oldKeys.includes(key)).length === 1 && + newKeys.filter(key => !oldKeys.includes(key))[0] === 'systemData'; + + diffs.push({ + before: oldKeys, + after: newKeys, + name, + type: "properties", + level: isSystemDataOnlyDifference ? "warning" : "error" + }); + } + for (const key of oldKeys) { + if (!newKeys.includes(key)) { + diffs.push({ + before: oldKeys, + after: newKeys, + name, + type: "properties", + level: "error" + }); + } + } + for (const key of newKeys) { + if (!oldKeys.includes(key)) { + // Check if the only additional key is systemData + const additionalKeys = newKeys.filter(k => !oldKeys.includes(k)); + const isOnlySystemData = additionalKeys.length === 1 && additionalKeys[0] === 'systemData'; + + diffs.push({ + before: oldKeys, + after: newKeys, + name, + type: "properties", + level: isOnlySystemData ? "warning" : "error" + }); + } + } + + // Then check if the property types are the same + for (const key of oldKeys) { + const oldProperty = sortedOldProperties[key]; + const newProperty = sortedNewProperties[key]; + if (oldProperty && newProperty) { + diffs.push(...compareDefinitionProperty(oldProperty, newProperty, oldDocument, newDocument, key, name)); + } + } + + return diffs; +} + +function compareDefinitionProperty(oldProperty: OpenAPI2SchemaProperty, newProperty: OpenAPI2SchemaProperty, oldDocument: OpenAPI2Document, newDocument: OpenAPI2Document, propertyName: string, modelName: string): DefinitionDiff[] { + const oldPropertySchema = resolveCommonType(oldProperty); + const newPropertySchema = resolveCommonType(newProperty); + if (isRef(oldPropertySchema) && isRef(newPropertySchema)) { + if (oldPropertySchema.$ref !== newPropertySchema.$ref) { + return [{ + before: oldPropertySchema.$ref, + after: newPropertySchema.$ref, + name: modelName, + propertyName, + type: "property", + changeType: "reference", + level: "warning" + }]; + } + } + else if (!isRef(oldPropertySchema) && !isRef(newPropertySchema)) { + return compareNamedDefinition(oldPropertySchema, oldDocument, newPropertySchema, newDocument, `${modelName}.${propertyName}`); + } + + return []; +} + +function comparePaths(oldDocument: OpenAPI2Document, newDocument: OpenAPI2Document): PathDiff[] { + const oldOperations = organizeOperationById(oldDocument); + const newOperations = organizeOperationById(newDocument); + + const pathDiffs: PathDiff[] = []; + for (const operationId in oldOperations) { + if (!newOperations[operationId]) { + pathDiffs.push({ + before: oldOperations[operationId][0], + after: null, + operationId, + type: "path", + level: "error" + }); + } + else { + pathDiffs.push(...compareOperation(oldOperations[operationId][1], newOperations[operationId][1], operationId)); + } + } + for (const operationId in newOperations) { + if (!oldOperations[operationId]) { + pathDiffs.push({ + before: null, + after: newOperations[operationId][0], + operationId, + type: "path", + level: "error" + }); + } + } + return pathDiffs; +} + +function compareOperation(oldOperation: OpenAPI2Operation, newOperation: OpenAPI2Operation, operationId: string): PathDiff[] { + const pathDiffs: PathDiff[] = []; + + if (oldOperation.parameters.length !== newOperation.parameters.length) { + pathDiffs.push({ + before: oldOperation.parameters.length, + after: newOperation.parameters.length, + operationId: operationId, + type: "parameters", + level: "error" + }); + } + + pathDiffs.push(...comparePagination(oldOperation, newOperation, operationId)); + pathDiffs.push(...compareLongRunning(oldOperation, newOperation, operationId)); + pathDiffs.push(...compareResponses(oldOperation, newOperation, operationId)); + + // TO-DO: Compare parameters in detail + return pathDiffs; +} + +function compareResponses(oldOperation: OpenAPI2Operation, newOperation: OpenAPI2Operation, operationId: string): PathDiff[] { + const pathDiffs: PathDiff[] = []; + + const oldResponseCodes = Object.keys(oldOperation.responses ?? {}).filter(code => !code.startsWith("x-")); + const newResponseCodes = Object.keys(newOperation.responses ?? {}).filter(code => !code.startsWith("x-")); + + let sameResponseCodes = true; + for (const code of oldResponseCodes) { + if (!newResponseCodes.includes(code)) { + sameResponseCodes = false; + break; + } + } + for (const code of newResponseCodes) { + if (!oldResponseCodes.includes(code)) { + sameResponseCodes = false; + break; + } + } + + if (!sameResponseCodes) { + pathDiffs.push({ + before: oldResponseCodes, + after: newResponseCodes, + operationId: operationId, + type: "responses", + level: "error" + }); + } + + for (const code of oldResponseCodes) { + pathDiffs.push(...compareResponse(oldOperation.responses?.[code], newOperation.responses?.[code], operationId)); + } + + return pathDiffs; +} + +function compareResponse(oldResponse: Refable | undefined, newResponse: Refable | undefined, operationId: string): PathDiff[] { + let oldSchema = getResponseSchema(oldResponse); + let newSchema = getResponseSchema(newResponse); + if (oldSchema !== newSchema) { + return [{ + before: oldResponse, + after: newResponse, + operationId: operationId, + type: "response", + level: "error" + }]; + } + + return []; +} + +function comparePagination(oldOperation: OpenAPI2Operation, newOperation: OpenAPI2Operation, operationId: string): PathDiff[] { + if ((oldOperation["x-ms-pageable"] === undefined && newOperation["x-ms-pageable"] !== undefined) || (oldOperation["x-ms-pageable"] !== undefined && newOperation["x-ms-pageable"] === undefined)) { + return [{ + before: oldOperation["x-ms-pageable"], + after: newOperation["x-ms-pageable"], + operationId: operationId, + type: "pageable", + level: "error" + }]; + } + + return []; +} + +function compareLongRunning(oldOperation: OpenAPI2Operation, newOperation: OpenAPI2Operation, operationId: string): PathDiff[] { + const pathDiffs: PathDiff[] = []; + + const oldLongRunning = oldOperation["x-ms-long-running-operation"] === true; + const newLongRunning = newOperation["x-ms-long-running-operation"] === true; + if (oldLongRunning !== newLongRunning) { + pathDiffs.push({ + before: oldLongRunning, + after: newLongRunning, + operationId: operationId, + type: "longrunning", + level: "error" + }); + } + + const oldFinalState = oldOperation["x-ms-long-running-operation-options"]?.["final-state-via"] ?? "location"; + const newFinalState = newOperation["x-ms-long-running-operation-options"]?.["final-state-via"] ?? "location"; + if (oldFinalState !== newFinalState) { + pathDiffs.push({ + before: oldFinalState, + after: newFinalState, + operationId: operationId, + type: "finalstate", + level: "error" + }); + } + + if (oldLongRunning) { + const newFinalResult = newOperation["x-ms-long-running-operation-options"]?.["final-state-schema"]; + const newFinalResultSchema = newFinalResult?.split("/").pop(); + if (newFinalResultSchema !== getResponseSchema(oldOperation.responses?.["200"])) { + pathDiffs.push({ + before: getResponseSchema(oldOperation.responses?.["200"]), + after: newFinalResultSchema, + operationId: operationId, + type: "finalresult", + level: "error" + }); + } + } + + return pathDiffs; +} + +function getResponseSchema(response: Refable | undefined): string | undefined { + if (response && (response as OpenAPI2Response).schema && ((response as OpenAPI2Response).schema as any).$ref) { + const refPath = ((response as OpenAPI2Response).schema as any).$ref; + return refPath.split("/").pop(); + } + + return undefined; +} + + +function organizeOperationById(document: OpenAPI2Document): Record { + function isHttpMethod(key: string): key is HttpMethod { + const httpMethods: HttpMethod[] = [ + "get", + "put", + "post", + "delete", + "options", + "head", + "patch", + "trace", + ]; + return httpMethods.includes(key as HttpMethod); + } + + const operationMap: Record = {}; + if (!document.paths) { + return operationMap; + } + + for (const route in document.paths) { + const pathItem = document.paths[route]; + for (const verb in pathItem) { + if (isHttpMethod(verb)) { + const operation = pathItem[verb]; + if (operation && operation.operationId) { + operationMap[operation.operationId] = [route, operation]; + } + } + } + } + + return operationMap; +} + +function getAllProperties(schema: OpenAPI2Schema, document: OpenAPI2Document, properties: Record = {}): Record { + for (const baseSchema of schema.allOf ?? []) { + if (isRef(baseSchema)) { + const refPath = baseSchema.$ref; + const commonTypeDefinition = getCommonTypeDefinition(refPath); + if (commonTypeDefinition) { + getAllProperties(commonTypeDefinition[0], commonTypeDefinition[1], properties); + } + else { + const baseDefinition = getLocalDefinition(refPath, document); + if (baseDefinition) { + getAllProperties(baseDefinition, document, properties); + } + } + } + else { + if (baseSchema.properties) { + for (const key in baseSchema.properties) { + properties[key] = baseSchema.properties[key]; + } + } + } + } + + if (schema.properties) { + for (const key in schema.properties) { + properties[key] = schema.properties[key]; + } + } + return properties; +} + +function resolveCommonType(property: OpenAPI2SchemaProperty): OpenAPI2SchemaProperty { + if (isRef(property)) { + const { $ref, ...propertyWithoutRef } = property; + const commonTypeDefinition = getCommonTypeDefinition($ref); + if (commonTypeDefinition) { + return { ...propertyWithoutRef, ...commonTypeDefinition[0] }; + } + } + + return property; +} + +function getLocalDefinition(refPath: string, document: OpenAPI2Document): OpenAPI2Schema | undefined { + const definitionName = refPath.split("/").pop(); + if (!document.definitions || !document.definitions[definitionName!]) { + console.warn(`Reference to ${definitionName} cannot be found, skipping.`); + } + return document.definitions?.[definitionName!]; +} + +export function isRef(value: Refable): value is Ref { + return (value as Ref).$ref !== undefined; +} \ No newline at end of file diff --git a/eng/tools/typespec-migration-validation/src/configuration.ts b/eng/tools/typespec-migration-validation/src/configuration.ts index 50e897923a51..43ffbb407599 100644 --- a/eng/tools/typespec-migration-validation/src/configuration.ts +++ b/eng/tools/typespec-migration-validation/src/configuration.ts @@ -7,5 +7,5 @@ interface configuration { export const configuration: configuration = { ignoreDescription: true, enumNameToCamelCase: true, - ignorePathCase: false, // Normalize the segments before provider + ignorePathCase: true, // Normalize the path }; diff --git a/eng/tools/typespec-migration-validation/src/document.ts b/eng/tools/typespec-migration-validation/src/document.ts index 64fae6ba7d91..b355219e236d 100644 --- a/eng/tools/typespec-migration-validation/src/document.ts +++ b/eng/tools/typespec-migration-validation/src/document.ts @@ -57,7 +57,13 @@ export function processDocument(document: OpenAPI2Document): OpenAPI2Document { if (configuration.ignorePathCase) { const normalizedRoute = route .replace(/\/resourcegroups\//i, "/resourceGroups/") - .replace(/\/subscriptions\//i, "/subscriptions/"); + .replace(/\/subscriptions\//i, "/subscriptions/") + .split('/') + .map(segment => { + if (segment.length === 0) return segment; + return segment.charAt(0).toLowerCase() + segment.slice(1); + }) + .join('/'); delete newDocument.paths[route]; newDocument.paths[normalizedRoute] = processedPath; } else { diff --git a/eng/tools/typespec-migration-validation/src/index.ts b/eng/tools/typespec-migration-validation/src/index.ts index db2fccaaeb68..f9f92e155f90 100644 --- a/eng/tools/typespec-migration-validation/src/index.ts +++ b/eng/tools/typespec-migration-validation/src/index.ts @@ -9,7 +9,7 @@ import { generatePrompts } from "./fix/troubleshooting.js"; import { mergeFiles, readFileContent } from "./helper.js"; import { addIgnorePath, processIgnoreList } from "./ignore.js"; import { jsonOutput } from "./jsonOutput.js"; -import { logHeader, logWarning } from "./log.js"; +import { logError, logHeader, logWarning } from "./log.js"; import { findChangedPaths, findDifferences, @@ -18,6 +18,8 @@ import { formatDifferenceReport, formatModifiedValuesReport, } from "./summary.js"; +import { compareDocuments, printDiff } from "./compare.js"; +import { writeFile } from "fs/promises"; function parseArguments() { return yargs(hideBin(process.argv)) @@ -49,6 +51,14 @@ function parseArguments() { "Compare two swagger specs", ) .example("$0 oldSpecPath newSpecPath", "Compare using positional arguments") + .example( + "$0 --oldPath ./old-spec --newPath ./new-spec --reportFile ./custom-report.md", + "Compare specs with custom report file", + ) + .example( + "$0 --oldPath ./old-spec --newPath ./new-spec --outputFolder ./validation-results", + "Compare specs with custom output folder", + ) .example( "$0 add-ignore --path \"paths['/api/resource'].put.parameters[0].required__added\" --outputFolder ./results", "Add a path to ignore file", @@ -67,6 +77,11 @@ function parseArguments() { alias: "out", describe: "Output folder for analysis results", type: "string", + }) + .option("reportFile", { + alias: "r", + describe: "Path to the report file", + type: "string", }) .option("ignoreDescription", { description: "Ignore description differences", @@ -94,9 +109,6 @@ function parseArguments() { if (!argv.newPath && positional.length > 1) { argv.newPath = positional[1]!.toString(); } - if (!argv.outputFolder && positional.length > 2) { - argv.outputFolder = positional[2]!.toString(); - } if (!argv.oldPath || !argv.newPath) { throw new Error("Both oldPath and newPath are required"); @@ -154,7 +166,7 @@ export async function main() { // If using add-ignore command, the command handler will exit the process - const { oldPath, newPath, outputFolder, ignoreDescription, ignorePathCase } = args; + const { oldPath, newPath, reportFile, outputFolder, ignoreDescription, ignorePathCase } = args; configuration.ignoreDescription = ignoreDescription; if (ignorePathCase !== undefined) { configuration.ignorePathCase = ignorePathCase; @@ -166,8 +178,8 @@ export async function main() { const sortedOldFile = sortOpenAPIDocument(processedOldFile); logHeader(`Processing new swagger from: ${newPath}...`); - const newFile = readFileContent(newPath!); - const processedNewFile = processDocument(JSON.parse(newFile.toString())); + const newFile = JSON.parse(readFileContent(newPath!).toString()); + const processedNewFile = processDocument(newFile); const sortedNewFile = sortOpenAPIDocument(processedNewFile); logHeader("Comparing old and new Swagger files..."); @@ -192,45 +204,66 @@ export async function main() { JSON.stringify(sortedNewFile, null, 2), ); } + logHeader("Comparing finished."); - let report: string = ""; - const diffForFile = diff(sortedOldFile, sortedNewFile); + let outputMarkdown = ""; + const compareResult = compareDocuments( + mergedOldfile, + newFile, + ); + if (compareResult.length === 0) { + logHeader("No differences found."); + } + else { + outputMarkdown += "| Type | Level | Message |\n"; + outputMarkdown += "| ---- | ----- | ------- |\n"; + for (const diff of compareResult) { + outputMarkdown += printDiff(diff); + } + console.log(outputMarkdown); + } - // // TO-DELETE: Read the diff file from disk - // const diffForFile = JSON.parse(fs.readFileSync(`C:/Users/pashao/GIT/azure-rest-api-specs/specification/agrifood/validation-results/diff.json`, 'utf-8')); + if (outputFolder) { + let report: string = ""; + const diffForFile = diff(sortedOldFile, sortedNewFile); + if (diffForFile === undefined || Object.keys(diffForFile).length === 0) { + return; + } - const changedPaths = findChangedPaths(diffForFile); - if (changedPaths.length > 0) { - logWarning( - `Found ${changedPaths.length} changed paths in the diff. If it is just case change and you confirm it is expected, run tsmv with --ignorePathCase option to ignore case changes.`, - ); - const changedPathsReport = formatChangedPathsReport(changedPaths); - console.log(changedPathsReport); - report += changedPathsReport; - } + // // TO-DELETE: Read the diff file from disk + // const diffForFile = JSON.parse(fs.readFileSync(`C:/Users/pashao/GIT/azure-rest-api-specs/specification/agrifood/validation-results/diff.json`, 'utf-8')); - const differences = findDifferences(diffForFile); - const differencesReport = formatDifferenceReport(differences); - console.log(differencesReport); - report += differencesReport; + const changedPaths = findChangedPaths(diffForFile); + if (changedPaths.length > 0) { + logWarning( + `Found ${changedPaths.length} changed paths in the diff.`, + ); + const changedPathsReport = formatChangedPathsReport(changedPaths); + report += changedPathsReport; + } - const modifiedValues = findModifiedValues(diffForFile); - const modifiedValuesReport = formatModifiedValuesReport(modifiedValues); - console.log(modifiedValuesReport); - report += modifiedValuesReport; + const differences = findDifferences(diffForFile); + const differencesReport = formatDifferenceReport(differences); + report += differencesReport; - if (outputFolder) { - fs.writeFileSync(`${outputFolder}/diff.json`, JSON.stringify(diffForFile, null, 2)); - fs.writeFileSync(`${outputFolder}/API_CHANGES.md`, report); - logHeader(`Difference report written to ${outputFolder}/API_CHANGES.md`); - - const suggestedPrompt = generatePrompts(diffForFile); - if (suggestedPrompt.length > 0) { - logWarning(`Considering these suggested prompts for the diff:`); - suggestedPrompt.forEach((prompt) => { - console.log(prompt); - }); + const modifiedValues = findModifiedValues(diffForFile); + const modifiedValuesReport = formatModifiedValuesReport(modifiedValues); + report += modifiedValuesReport; + + if (diffForFile) { + fs.writeFileSync(`${outputFolder}/diff.json`, JSON.stringify(diffForFile, null, 2)); + fs.writeFileSync(`${outputFolder}/API_CHANGES.md`, report); + logHeader(`Difference report written to ${outputFolder}/API_CHANGES.md`); + + const suggestedPrompt = generatePrompts(diffForFile); + if (suggestedPrompt.length > 0) { + logWarning(`Considering these suggested prompts for the diff:`); + suggestedPrompt.forEach((prompt) => { + console.log(prompt); + }); + } } + if (args.jsonOutput) { fs.writeFileSync(`${outputFolder}/tsmv_output.json`, JSON.stringify(jsonOutput, null, 2)); logHeader(`JSON output written to ${outputFolder}/tsmv_output.json`); @@ -238,7 +271,14 @@ export async function main() { `---- Start of Json Output ----\n${JSON.stringify(jsonOutput, null, 2)}\n---- End of Json Output ----`, ); } - } else { - console.log(report); + } + else if (reportFile) { + if (compareResult.length > 0) { + await writeFile(reportFile, outputMarkdown); + } + if (compareResult.filter((x) => x.level === "error").length > 0) { + logError("Differences found. Please fix the issues before proceeding."); + process.exit(1); + } } } diff --git a/eng/tools/typespec-requirement/package.json b/eng/tools/typespec-requirement/package.json index 1e49e720bf91..31905ba7ada5 100644 --- a/eng/tools/typespec-requirement/package.json +++ b/eng/tools/typespec-requirement/package.json @@ -6,6 +6,7 @@ "@types/node": "^20.0.0", "execa": "^9.3.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, diff --git a/eng/tools/typespec-validation/package.json b/eng/tools/typespec-validation/package.json index 4da6c6a28298..0f8d06aa4862 100644 --- a/eng/tools/typespec-validation/package.json +++ b/eng/tools/typespec-validation/package.json @@ -21,6 +21,7 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, diff --git a/eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts b/eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts index 2a7ac6a0990e..e7711bbd5274 100644 --- a/eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts +++ b/eng/tools/typespec-validation/src/rules/sdk-tspconfig-validation.ts @@ -1,8 +1,8 @@ import { join } from "path"; +import { Suppression } from "suppressions"; import { parse as yamlParse } from "yaml"; -import { Rule } from "../rule.js"; import { RuleResult } from "../rule-result.js"; -import { Suppression } from "suppressions"; +import { Rule } from "../rule.js"; import { fileExists, getSuppressions, readTspConfig } from "../utils.js"; type ExpectedValueType = string | boolean | RegExp; @@ -387,15 +387,6 @@ export class TspConfigGoMgmtModuleEqualStringSubRule extends TspconfigEmitterOpt } } -export class TspConfigGoMgmtFixConstStutteringTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { - constructor() { - super("@azure-tools/typespec-go", "fix-const-stuttering", true); - } - protected skip(_: any, folder: string) { - return skipForDataPlane(folder); - } -} - export class TspConfigGoMgmtGenerateSamplesTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { super("@azure-tools/typespec-go", "generate-samples", true); @@ -414,13 +405,17 @@ export class TspConfigGoMgmtHeadAsBooleanTrueSubRule extends TspconfigEmitterOpt } } -// ----- Go az sub rules ----- -export class TspConfigGoAzGenerateFakesTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { +export class TspConfigGoMgmtGenerateFakesTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { super("@azure-tools/typespec-go", "generate-fakes", true); } + protected skip(_: any, folder: string) { + return skipForDataPlane(folder); + } } +// ----- Go az sub rules ----- + export class TspConfigGoAzInjectSpansTrueSubRule extends TspconfigEmitterOptionsSubRuleBase { constructor() { super("@azure-tools/typespec-go", "inject-spans", true); @@ -539,9 +534,8 @@ export const defaultRules = [ new TspConfigGoMgmtServiceDirMatchPatternSubRule(), new TspConfigGoMgmtPackageDirectorySubRule(), new TspConfigGoMgmtModuleEqualStringSubRule(), - new TspConfigGoMgmtFixConstStutteringTrueSubRule(), new TspConfigGoMgmtGenerateSamplesTrueSubRule(), - new TspConfigGoAzGenerateFakesTrueSubRule(), + new TspConfigGoMgmtGenerateFakesTrueSubRule(), new TspConfigGoMgmtHeadAsBooleanTrueSubRule(), new TspConfigGoAzInjectSpansTrueSubRule(), new TspConfigGoDpServiceDirMatchPatternSubRule(), @@ -596,7 +590,7 @@ export class SdkTspConfigValidationRule implements Rule { const emitterName = emitterOptionSubRule.getEmitterName(); if (emitterName === "@azure-tools/typespec-csharp" && isSubRuleSuccess === false) { console.warn( - `Validation on option "${emitterOptionSubRule.getPathOfKeyToValidate()}" in "${emitterName}" are failed. However, per ${emitterName}’s decision, we will treat it as passed.`, + `Validation on option "${emitterOptionSubRule.getPathOfKeyToValidate()}" in "${emitterName}" are failed. However, per ${emitterName}’s decision, we will treat it as passed, please refer to https://eng.ms/docs/products/azure-developer-experience/onboard/request-exception`, ); isSubRuleSuccess = true; } @@ -607,7 +601,7 @@ export class SdkTspConfigValidationRule implements Rule { const stdOutputFailedResults = failedResults.length > 0 - ? `${failedResults.map((r) => r.errorOutput).join("\n")}\nPlease see https://aka.ms/azsdk/spec-gen-sdk-config for more info.\nFor additional information on TypeSpec validation, please refer to https://aka.ms/azsdk/specs/typespec-validation` + ? `${failedResults.map((r) => r.errorOutput).join("\n")}\nPlease see https://aka.ms/azsdk/spec-gen-sdk-config for more info.\nFor additional information on TypeSpec validation, please refer to https://aka.ms/azsdk/specs/typespec-validation\nFor exception requests, please refer to https://eng.ms/docs/products/azure-developer-experience/onboard/request-exception` : ""; return { diff --git a/eng/tools/typespec-validation/test/sdk-tspconfig-validation.test.ts b/eng/tools/typespec-validation/test/sdk-tspconfig-validation.test.ts index 37e3829c2f9e..20dd12780a11 100644 --- a/eng/tools/typespec-validation/test/sdk-tspconfig-validation.test.ts +++ b/eng/tools/typespec-validation/test/sdk-tspconfig-validation.test.ts @@ -1,42 +1,41 @@ import { afterEach, beforeEach, describe, it, MockInstance, vi } from "vitest"; +import { contosoTspConfig } from "@azure-tools/specs-shared/test/examples"; +import { strictEqual } from "node:assert"; +import { join } from "path"; +import { stringify } from "yaml"; import { SdkTspConfigValidationRule, TspConfigCommonAzServiceDirMatchPatternSubRule, - TspConfigTsMgmtModularExperimentalExtensibleEnumsTrueSubRule, - TspConfigTsMgmtModularPackageDirectorySubRule, - TspConfigTsMgmtModularPackageNameMatchPatternSubRule, - TspConfigTsDpPackageDirectorySubRule, - TspConfigTsRlcDpPackageNameMatchPatternSubRule, - TspConfigGoMgmtServiceDirMatchPatternSubRule, - TspConfigGoMgmtPackageDirectorySubRule, - TspConfigGoMgmtModuleEqualStringSubRule, - TspConfigGoMgmtFixConstStutteringTrueSubRule, - TspConfigGoMgmtGenerateSamplesTrueSubRule, - TspConfigGoMgmtHeadAsBooleanTrueSubRule, - TspConfigGoAzGenerateFakesTrueSubRule, + TspConfigCsharpAzClearOutputFolderTrueSubRule, + TspConfigCsharpAzNamespaceEqualStringSubRule, + TspConfigCsharpAzPackageDirectorySubRule, + TspConfigCsharpMgmtPackageDirectorySubRule, TspConfigGoAzInjectSpansTrueSubRule, TspConfigGoDpModuleMatchPatternSubRule, TspConfigGoDpPackageDirectoryMatchPatternSubRule, TspConfigGoDpServiceDirMatchPatternSubRule, + TspConfigGoMgmtGenerateFakesTrueSubRule, + TspConfigGoMgmtGenerateSamplesTrueSubRule, + TspConfigGoMgmtHeadAsBooleanTrueSubRule, + TspConfigGoMgmtModuleEqualStringSubRule, + TspConfigGoMgmtPackageDirectorySubRule, + TspConfigGoMgmtServiceDirMatchPatternSubRule, TspConfigJavaAzPackageDirectorySubRule, TspConfigJavaMgmtNamespaceFormatSubRule, - TspConfigPythonMgmtPackageDirectorySubRule, + TspConfigPythonDpPackageDirectorySubRule, TspConfigPythonMgmtNamespaceSubRule, + TspConfigPythonMgmtPackageDirectorySubRule, TspConfigPythonMgmtPackageGenerateSampleTrueSubRule, TspConfigPythonMgmtPackageGenerateTestTrueSubRule, - TspConfigCsharpAzPackageDirectorySubRule, - TspConfigCsharpAzNamespaceEqualStringSubRule, - TspConfigCsharpAzClearOutputFolderTrueSubRule, - TspConfigCsharpMgmtPackageDirectorySubRule, TspconfigSubRuleBase, - TspConfigPythonDpPackageDirectorySubRule, + TspConfigTsDpPackageDirectorySubRule, + TspConfigTsMgmtModularExperimentalExtensibleEnumsTrueSubRule, + TspConfigTsMgmtModularPackageDirectorySubRule, + TspConfigTsMgmtModularPackageNameMatchPatternSubRule, TspConfigTsMlcDpPackageNameMatchPatternSubRule, + TspConfigTsRlcDpPackageNameMatchPatternSubRule, } from "../src/rules/sdk-tspconfig-validation.js"; -import { contosoTspConfig } from "@azure-tools/specs-shared/test/examples"; -import { join } from "path"; -import { strictEqual } from "node:assert"; -import { stringify } from "yaml"; import * as utils from "../src/utils.js"; @@ -287,15 +286,6 @@ const goManagementModuleTestCases = createEmitterOptionTestCases( [new TspConfigGoMgmtModuleEqualStringSubRule()], ); -const goManagementFixConstStutteringTestCases = createEmitterOptionTestCases( - "@azure-tools/typespec-go", - managementTspconfigFolder, - "fix-const-stuttering", - true, - false, - [new TspConfigGoMgmtFixConstStutteringTrueSubRule()], -); - const goManagementGenerateExamplesTestCases = createEmitterOptionTestCases( "@azure-tools/typespec-go", managementTspconfigFolder, @@ -311,16 +301,7 @@ const goManagementGenerateFakesTestCases = createEmitterOptionTestCases( "generate-fakes", true, false, - [new TspConfigGoAzGenerateFakesTrueSubRule()], -); - -const goDpGenerateFakesTestCases = createEmitterOptionTestCases( - "@azure-tools/typespec-go", - "", - "generate-fakes", - true, - false, - [new TspConfigGoAzGenerateFakesTrueSubRule()], + [new TspConfigGoMgmtGenerateFakesTrueSubRule()], ); const goManagementHeadAsBooleanTestCases = createEmitterOptionTestCases( @@ -619,7 +600,6 @@ options: subRules: [ new TspConfigGoMgmtPackageDirectorySubRule(), new TspConfigGoMgmtModuleEqualStringSubRule(), - new TspConfigGoMgmtFixConstStutteringTrueSubRule(), ], tspconfigContent: ` options: @@ -661,12 +641,10 @@ describe("tspconfig", function () { ...goManagementServiceDirTestCases, ...goManagementPackageDirTestCases, ...goManagementModuleTestCases, - ...goManagementFixConstStutteringTestCases, ...goManagementGenerateExamplesTestCases, ...goManagementGenerateFakesTestCases, ...goManagementHeadAsBooleanTestCases, ...goManagementInjectSpansTestCases, - ...goDpGenerateFakesTestCases, ...goDpInjectSpansTestCases, ...goDpModuleTestCases, ...goDpPackageDirTestCases, diff --git a/package-lock.json b/package-lock.json index fd28f54b508f..f2bcc269c952 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,32 +6,35 @@ "": { "name": "azure-rest-api-specs", "devDependencies": { - "@autorest/openapi-to-typespec": "0.11.3", - "@azure-tools/spec-gen-sdk": "~0.8.0", + "@autorest/openapi-to-typespec": "0.11.7", + "@azure-tools/spec-gen-sdk": "~0.9.1", "@azure-tools/specs-shared": "file:.github/shared", "@azure-tools/typespec-apiview": "0.7.2", - "@azure-tools/typespec-autorest": "0.58.0", - "@azure-tools/typespec-azure-core": "0.58.0", - "@azure-tools/typespec-azure-portal-core": "0.58.0", - "@azure-tools/typespec-azure-resource-manager": "0.58.0", - "@azure-tools/typespec-azure-rulesets": "0.58.0", - "@azure-tools/typespec-client-generator-cli": "0.25.0", - "@azure-tools/typespec-client-generator-core": "0.58.0", + "@azure-tools/typespec-autorest": "0.59.0", + "@azure-tools/typespec-azure-core": "0.59.0", + "@azure-tools/typespec-azure-portal-core": "0.59.0", + "@azure-tools/typespec-azure-resource-manager": "0.59.1", + "@azure-tools/typespec-azure-rulesets": "0.59.0", + "@azure-tools/typespec-client-generator-cli": "0.27.0", + "@azure-tools/typespec-client-generator-core": "0.59.0", "@azure-tools/typespec-liftr-base": "0.8.0", "@azure/avocado": "^0.9.1", - "@typespec/compiler": "1.2.1", - "@typespec/events": "0.72.1", - "@typespec/http": "1.2.1", - "@typespec/openapi": "1.2.1", - "@typespec/openapi3": "1.2.1", - "@typespec/prettier-plugin-typespec": "1.2.1", - "@typespec/rest": "0.72.1", - "@typespec/sse": "0.72.1", - "@typespec/streams": "0.72.1", - "@typespec/versioning": "0.72.1", - "@typespec/xml": "0.72.1", + "@microsoft.azure/openapi-validator": "2.2.4", + "@microsoft.azure/openapi-validator-core": "1.0.6", + "@microsoft.azure/openapi-validator-rulesets": "2.1.7", + "@typespec/compiler": "1.3.0", + "@typespec/events": "0.73.0", + "@typespec/http": "1.3.0", + "@typespec/openapi": "1.3.0", + "@typespec/openapi3": "1.3.0", + "@typespec/prettier-plugin-typespec": "1.3.0", + "@typespec/rest": "0.73.0", + "@typespec/sse": "0.73.0", + "@typespec/streams": "0.73.0", + "@typespec/versioning": "0.73.0", + "@typespec/xml": "0.73.0", "azure-rest-api-specs-eng-tools": "file:eng/tools", - "oav": "^3.6.1", + "oav": "^3.6.4", "prettier": "~3.5.3", "typescript": "~5.8.2" }, @@ -44,13 +47,14 @@ "name": "@azure-tools/specs-shared", "dev": true, "dependencies": { - "@apidevtools/json-schema-ref-parser": "^14.1.0", + "@apidevtools/json-schema-ref-parser": "^14.1.1", "debug": "^4.4.0", "js-yaml": "^4.1.0", - "marked": "^16.0.0", + "marked": "^16.1.1", "simple-git": "^3.27.0" }, "bin": { + "api-doc-preview": "cmd/api-doc-preview.js", "spec-model": "cmd/spec-model.js" }, "devDependencies": { @@ -64,15 +68,16 @@ "eslint": "^9.22.0", "globals": "^16.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "semver": "^7.7.1", "typescript": "~5.8.2", "vitest": "^3.0.7" } }, ".github/shared/node_modules/marked": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.0.0.tgz", - "integrity": "sha512-MUKMXDjsD/eptB7GPzxo4xcnLS6oo7/RHimUMHEDRhUooPwmN9BEpMl7AEOJv3bmso169wHI2wUF9VQgL7zfmA==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.1.1.tgz", + "integrity": "sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==", "dev": true, "license": "MIT", "bin": { @@ -92,6 +97,7 @@ "@azure-tools/openapi-diff-runner": "file:openapi-diff-runner", "@azure-tools/sdk-suppressions": "file:sdk-suppressions", "@azure-tools/spec-gen-sdk-runner": "file:spec-gen-sdk-runner", + "@azure-tools/summarize-impact": "file:summarize-impact", "@azure-tools/suppressions": "file:suppressions", "@azure-tools/tsp-client-tests": "file:tsp-client-tests", "@azure-tools/typespec-migration-validation": "file:typespec-migration-validation", @@ -103,14 +109,16 @@ "name": "@azure-tools/lint-diff", "dev": true, "dependencies": { - "@apidevtools/json-schema-ref-parser": "^14.1.0", + "@apidevtools/json-schema-ref-parser": "^14.1.1", "@azure-tools/specs-shared": "file:../../../.github/shared", - "@microsoft.azure/openapi-validator": "^2.2.4", + "@microsoft.azure/openapi-validator": "2.2.4", + "@microsoft.azure/openapi-validator-core": "1.0.6", + "@microsoft.azure/openapi-validator-rulesets": "2.1.7", "autorest": "^3.7.2", "axios": "^1.8.3", "change-case": "^5.4.4", "deep-eql": "^5.0.2", - "marked": "^16.0.0" + "marked": "^16.1.1" }, "bin": { "lint-diff": "cmd/lint-diff.js" @@ -122,6 +130,7 @@ "execa": "^9.5.2", "memfs": "^4.17.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.2" }, @@ -130,9 +139,9 @@ } }, "eng/tools/lint-diff/node_modules/@types/node": { - "version": "18.19.119", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.119.tgz", - "integrity": "sha512-d0F6m9itIPaKnrvEMlzE48UjwZaAnFW7Jwibacw9MNdqadjKNpUm9tfJYDwmShJmgqcoqYUX3EMKO1+RWiuuNg==", + "version": "18.19.121", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.121.tgz", + "integrity": "sha512-bHOrbyztmyYIi4f1R0s17QsPs1uyyYnGcXeZoGEd227oZjry0q6XQBQxd82X1I57zEfwO8h9Xo+Kl5gX1d9MwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -197,6 +206,24 @@ "node": ">=20" } }, + "eng/tools/node_modules/commonmark": { + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.31.2.tgz", + "integrity": "sha512-2fRLTyb9r/2835k5cwcAwOj0DEc44FARnMp5veGsJ+mEAZdi52sNopLu07ZyElQUz058H43whzlERDIaaSw4rg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "entities": "~3.0.1", + "mdurl": "~1.0.1", + "minimist": "~1.2.8" + }, + "bin": { + "commonmark": "bin/commonmark" + }, + "engines": { + "node": "*" + } + }, "eng/tools/node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", @@ -204,6 +231,59 @@ "dev": true, "license": "MIT" }, + "eng/tools/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "eng/tools/node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "eng/tools/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "eng/tools/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -211,10 +291,20 @@ "dev": true, "license": "MIT" }, + "eng/tools/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "eng/tools/node_modules/marked": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.0.0.tgz", - "integrity": "sha512-MUKMXDjsD/eptB7GPzxo4xcnLS6oo7/RHimUMHEDRhUooPwmN9BEpMl7AEOJv3bmso169wHI2wUF9VQgL7zfmA==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.1.1.tgz", + "integrity": "sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==", "dev": true, "license": "MIT", "bin": { @@ -240,6 +330,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "eng/tools/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "eng/tools/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -333,7 +440,7 @@ "dependencies": { "@azure-tools/specs-shared": "file:../../../.github/shared", "js-yaml": "^4.1.0", - "oav": "^3.5.1", + "oav": "^3.6.4", "simple-git": "^3.27.0" }, "bin": { @@ -342,6 +449,7 @@ "devDependencies": { "@types/node": "^20.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, @@ -363,6 +471,7 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, @@ -391,6 +500,7 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, @@ -410,8 +520,9 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "eslint": "^9.21.0", - "eslint-plugin-unicorn": "^59.0.0", + "eslint-plugin-unicorn": "^60.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "typescript-eslint": "^8.26.0", "vitest": "^3.0.7" @@ -420,6 +531,37 @@ "node": ">=20.0.0" } }, + "eng/tools/summarize-impact": { + "name": "@azure-tools/summarize-impact", + "dev": true, + "dependencies": { + "@azure-tools/openapi-tools-common": "^1.2.2", + "@azure-tools/specs-shared": "file:../../../.github/shared", + "@azure/openapi-markdown": "0.9.4", + "@octokit/rest": "^22.0.0", + "commonmark": "0.31.2", + "glob": "^11.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.20", + "simple-git": "^3.27.0" + }, + "bin": { + "summarize-impact": "cmd/summarize-impact.js" + }, + "devDependencies": { + "@types/commonmark": "0.27.10", + "@types/glob": "^9.0.0", + "@types/lodash": "^4.14.161", + "@types/node": "^20.0.0", + "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", + "typescript": "~5.8.2", + "vitest": "^3.0.7" + }, + "engines": { + "node": ">=20.0.0" + } + }, "eng/tools/suppressions": { "name": "@azure-tools/suppressions", "dev": true, @@ -435,6 +577,7 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, @@ -449,6 +592,7 @@ "@azure-tools/specs-shared": "file:../../../.github/shared", "@types/node": "^20.0.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, @@ -482,9 +626,9 @@ } }, "eng/tools/typespec-migration-validation/node_modules/@types/node": { - "version": "18.19.119", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.119.tgz", - "integrity": "sha512-d0F6m9itIPaKnrvEMlzE48UjwZaAnFW7Jwibacw9MNdqadjKNpUm9tfJYDwmShJmgqcoqYUX3EMKO1+RWiuuNg==", + "version": "18.19.121", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.121.tgz", + "integrity": "sha512-bHOrbyztmyYIi4f1R0s17QsPs1uyyYnGcXeZoGEd227oZjry0q6XQBQxd82X1I57zEfwO8h9Xo+Kl5gX1d9MwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -498,6 +642,7 @@ "@types/node": "^20.0.0", "execa": "^9.3.0", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, @@ -526,6 +671,7 @@ "@types/node": "^20.0.0", "@vitest/coverage-v8": "^3.0.7", "prettier": "~3.5.3", + "prettier-plugin-organize-imports": "^4.2.0", "typescript": "~5.8.2", "vitest": "^3.0.7" }, @@ -699,9 +845,9 @@ } }, "node_modules/@autorest/openapi-to-typespec": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@autorest/openapi-to-typespec/-/openapi-to-typespec-0.11.3.tgz", - "integrity": "sha512-YcPwG8OkGaMAznF46fmElT0zbq/JoSBAd1OSooW6s5irGvOmFci1rsvhstnbdzrxu+eUHrzzbXX1ZSE14WvGOw==", + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@autorest/openapi-to-typespec/-/openapi-to-typespec-0.11.7.tgz", + "integrity": "sha512-5FLqi/J2YNC5EF3oVX6HuAanoSJlNM6UFAL//dzX2CBpx+y+++6l9XyPD3rEom9GVV8hGdylsACn50BwoQ1RvA==", "dev": true, "license": "MIT", "dependencies": { @@ -709,7 +855,7 @@ "@autorest/extension-base": "~3.6.1", "@azure-tools/codegen": "~2.10.1", "@azure-tools/openapi": "~3.6.1", - "@typespec/prettier-plugin-typespec": "^1.1.0", + "@typespec/prettier-plugin-typespec": "^1.3.0", "change-case-all": "~2.1.0", "lodash": "~4.17.20", "pluralize": "^8.0.0", @@ -867,9 +1013,9 @@ "link": true }, "node_modules/@azure-tools/spec-gen-sdk": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@azure-tools/spec-gen-sdk/-/spec-gen-sdk-0.8.1.tgz", - "integrity": "sha512-Im994/0TxjYouIsu41YFafIgfqM9DsJRrScLK71yn7uXp8LN2rbmWZGcj4nW0DFu39COQoDr8RoYvEa+fThfZw==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@azure-tools/spec-gen-sdk/-/spec-gen-sdk-0.9.1.tgz", + "integrity": "sha512-H6hDGHtbHWl/dldB+y5wrYtx965kYNleDEWFjWvMIS29yYwDCljlo9AAadLCmlXvi46ksXh2nePYxBjeTafv9A==", "dev": true, "license": "MIT", "dependencies": { @@ -913,6 +1059,10 @@ "resolved": ".github/shared", "link": true }, + "node_modules/@azure-tools/summarize-impact": { + "resolved": "eng/tools/summarize-impact", + "link": true + }, "node_modules/@azure-tools/suppressions": { "resolved": "eng/tools/suppressions", "link": true @@ -946,55 +1096,55 @@ } }, "node_modules/@azure-tools/typespec-autorest": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.58.0.tgz", - "integrity": "sha512-zhQbbbp7SekE7uNeyAdAzpTKimsaRbpI5g4mBReCT2X2CS4BWuk+mRDTb617Wrkn8Axy2M9dtlopGT0AXdMNTw==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-autorest/-/typespec-autorest-0.59.0.tgz", + "integrity": "sha512-IdjicuLJRNFZUWHJd1Z8e9RQUlVUyUb2v7pT1rYkhpxAxFb9uluVVpi09GkUFimkrUZ+HkFO/jI2zbIAptZ2FQ==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.58.0", - "@azure-tools/typespec-azure-resource-manager": "^0.58.0", - "@azure-tools/typespec-client-generator-core": "^0.58.0", - "@typespec/compiler": "^1.2.0", - "@typespec/http": "^1.2.0", - "@typespec/openapi": "^1.2.0", - "@typespec/rest": "^0.72.0", - "@typespec/versioning": "^0.72.0" + "@azure-tools/typespec-azure-core": "^0.59.0", + "@azure-tools/typespec-azure-resource-manager": "^0.59.0", + "@azure-tools/typespec-client-generator-core": "^0.59.0", + "@typespec/compiler": "^1.3.0", + "@typespec/http": "^1.3.0", + "@typespec/openapi": "^1.3.0", + "@typespec/rest": "^0.73.0", + "@typespec/versioning": "^0.73.0" } }, "node_modules/@azure-tools/typespec-azure-core": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.58.0.tgz", - "integrity": "sha512-Z4vX+ic85hCPr27t9DKCVTj2MjHDaXDvh10Z4wVokXIR2/GjAQrHQp4OFu/0R+cwqmuXb6nnuBZNikwKk7dNzw==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-core/-/typespec-azure-core-0.59.0.tgz", + "integrity": "sha512-3vTWDTSR+P0qeyFcOKTgXortNOeA3nsyKTPpZqfFZVTtNFiiO17UWAM2Eg3i0IpNQ3qxMMAksIkwt1bqltTDqA==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.0", - "@typespec/http": "^1.2.0", - "@typespec/rest": "^0.72.0" + "@typespec/compiler": "^1.3.0", + "@typespec/http": "^1.3.0", + "@typespec/rest": "^0.73.0" } }, "node_modules/@azure-tools/typespec-azure-portal-core": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-portal-core/-/typespec-azure-portal-core-0.58.0.tgz", - "integrity": "sha512-t5yc4PxoyK6LqzihYeHMNpc74kg42rsNFgmBEdSUdMc9ZVDeNSf3d0nER+t0LkbJt/MIbTcPCT5/Eri11Nsnhg==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-portal-core/-/typespec-azure-portal-core-0.59.0.tgz", + "integrity": "sha512-aQv9/pY4eFBa2ioHHvS83xC6F8KpkTJOn6x7Q8bbkTONxC1HDwzqjrK/VGiL5Sn0RIJcZhB4Q2+EViLXSCRPlg==", "dev": true, "license": "MIT", "peerDependencies": { - "@azure-tools/typespec-azure-resource-manager": "^0.58.0", - "@typespec/compiler": "^1.2.0" + "@azure-tools/typespec-azure-resource-manager": "^0.59.0", + "@typespec/compiler": "^1.3.0" } }, "node_modules/@azure-tools/typespec-azure-resource-manager": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.58.0.tgz", - "integrity": "sha512-OSDADgmvvTiQgAh/7cu2yiSdc4SM5JYk3W84F+1lRiM80JG8PiRXMSZcIHIQnnySUVSiT1oMuVK+Fx5prUWJuQ==", + "version": "0.59.1", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-resource-manager/-/typespec-azure-resource-manager-0.59.1.tgz", + "integrity": "sha512-miZW7g0mIvUuJrnxYEVBly+wMbzC1Ki824WxlLSTjNJirEolprnLVDxY1Ih+sreGu7HJ6VtCmPQX0NuHH2DFDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1005,34 +1155,34 @@ "node": ">=20.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.58.0", - "@typespec/compiler": "^1.2.0", - "@typespec/http": "^1.2.0", - "@typespec/openapi": "^1.2.0", - "@typespec/rest": "^0.72.0", - "@typespec/versioning": "^0.72.0" + "@azure-tools/typespec-azure-core": "^0.59.0", + "@typespec/compiler": "^1.3.0", + "@typespec/http": "^1.3.0", + "@typespec/openapi": "^1.3.0", + "@typespec/rest": "^0.73.0", + "@typespec/versioning": "^0.73.0" } }, "node_modules/@azure-tools/typespec-azure-rulesets": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.58.0.tgz", - "integrity": "sha512-AkbhFxbeD3mYHbdnQEd1iWhSuQ2KxoN5JOU6BFP7mo/RVjIgGjj2ekro1tCnq381LOXdFfEoJievSFI5fvyRxQ==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-azure-rulesets/-/typespec-azure-rulesets-0.59.0.tgz", + "integrity": "sha512-+eKYH25ptj6SZHT+/YfxrX+g6HMAQQTphmHmqOoRCbbfPaE8l855OaDrwEgh5NDLV8WXnHpzTNI0dsYipEp10g==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.58.0", - "@azure-tools/typespec-azure-resource-manager": "^0.58.0", - "@azure-tools/typespec-client-generator-core": "^0.58.0", - "@typespec/compiler": "^1.2.0" + "@azure-tools/typespec-azure-core": "^0.59.0", + "@azure-tools/typespec-azure-resource-manager": "^0.59.0", + "@azure-tools/typespec-client-generator-core": "^0.59.0", + "@typespec/compiler": "^1.3.0" } }, "node_modules/@azure-tools/typespec-client-generator-cli": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-cli/-/typespec-client-generator-cli-0.25.0.tgz", - "integrity": "sha512-8ot4Lm1LuwAy8qeciY0bKtJgBFxJ8qdQ3sI2U9o6Lel37IENuNbklC8pDRM2VvfP+5hjzBTrG7f54iH6IMAJpA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-cli/-/typespec-client-generator-cli-0.27.0.tgz", + "integrity": "sha512-m1XQdbA8WdMKcv+f2ehfgGXPm+Br6Rneqa1Rmvi4kNHO2Bs1pvsCUiD+2hh0DD7t78pkcQ0bJLzeU35kzblo3g==", "dev": true, "license": "MIT", "dependencies": { @@ -1060,9 +1210,9 @@ } }, "node_modules/@azure-tools/typespec-client-generator-core": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.58.0.tgz", - "integrity": "sha512-PK9WjPFXR14hrGyUwhstHuNVC4fkkQeNVHvvYhz3VaP6wh+iD7P3IOeIzzRJv/qSyv82t7BrVXdQAYir434ysw==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.59.0.tgz", + "integrity": "sha512-5+pVcOr7Uyq4IaZ3oLjLi8jnBuR+t/B8hQF4CZo+goe/uK+WkmDfw1TlY14G2ve7W7v8m9+Sk2nil6bN1uHWEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1074,16 +1224,16 @@ "node": ">=20.0.0" }, "peerDependencies": { - "@azure-tools/typespec-azure-core": "^0.58.0", - "@typespec/compiler": "^1.2.0", - "@typespec/events": "^0.72.0", - "@typespec/http": "^1.2.0", - "@typespec/openapi": "^1.2.0", - "@typespec/rest": "^0.72.0", - "@typespec/sse": "^0.72.0", - "@typespec/streams": "^0.72.0", - "@typespec/versioning": "^0.72.0", - "@typespec/xml": "^0.72.0" + "@azure-tools/typespec-azure-core": "^0.59.0", + "@typespec/compiler": "^1.3.0", + "@typespec/events": "^0.73.0", + "@typespec/http": "^1.3.0", + "@typespec/openapi": "^1.3.0", + "@typespec/rest": "^0.73.0", + "@typespec/sse": "^0.73.0", + "@typespec/streams": "^0.73.0", + "@typespec/versioning": "^0.73.0", + "@typespec/xml": "^0.73.0" } }, "node_modules/@azure-tools/typespec-liftr-base": { @@ -1493,15 +1643,16 @@ } }, "node_modules/@azure/ms-rest-js/node_modules/form-data": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", - "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" }, @@ -1728,13 +1879,13 @@ } }, "node_modules/@azure/oad/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">= 8" + "node": ">= 12" } }, "node_modules/@azure/oad/node_modules/string-width": { @@ -1943,9 +2094,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1990,9 +2141,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -2007,9 +2158,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -2024,9 +2175,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -2041,9 +2192,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -2058,9 +2209,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -2075,9 +2226,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -2092,9 +2243,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -2109,9 +2260,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -2126,9 +2277,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -2143,9 +2294,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -2160,9 +2311,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -2177,9 +2328,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -2194,9 +2345,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -2211,9 +2362,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -2228,9 +2379,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -2245,9 +2396,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -2262,9 +2413,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -2279,9 +2430,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -2296,9 +2447,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -2313,9 +2464,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -2330,9 +2481,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -2347,9 +2498,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", "cpu": [ "arm64" ], @@ -2364,9 +2515,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -2381,9 +2532,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -2398,9 +2549,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -2415,9 +2566,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -2549,9 +2700,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, "license": "MIT", "engines": { @@ -2572,9 +2723,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2669,15 +2820,15 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.9.tgz", - "integrity": "sha512-DBJBkzI5Wx4jFaYm221LHvAhpKYkhVS0k9plqHwaHhofGNxvYB7J3Bz8w+bFJ05zaMb0sZNHo4KdmENQFlNTuQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.0.tgz", + "integrity": "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -2694,14 +2845,14 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.13", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.13.tgz", - "integrity": "sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw==", + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz", + "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -2716,14 +2867,14 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.14", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.14.tgz", - "integrity": "sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A==", + "version": "10.1.15", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.15.tgz", + "integrity": "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -2744,14 +2895,14 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.14", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.14.tgz", - "integrity": "sha512-yd2qtLl4QIIax9DTMZ1ZN2pFrrj+yL3kgIWxm34SS6uwCr0sIhsNyudUjAo5q3TqI03xx4SEBkUJqZuAInp9uA==", + "version": "4.2.15", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.15.tgz", + "integrity": "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "external-editor": "^3.1.0" }, "engines": { @@ -2767,14 +2918,14 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.16.tgz", - "integrity": "sha512-oiDqafWzMtofeJyyGkb1CTPaxUkjIcSxePHHQCfif8t3HV9pHcw1Kgdw3/uGpDvaFfeTluwQtWiqzPVjAqS3zA==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.17.tgz", + "integrity": "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2790,9 +2941,9 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", - "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", "dev": true, "license": "MIT", "engines": { @@ -2800,14 +2951,14 @@ } }, "node_modules/@inquirer/input": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.0.tgz", - "integrity": "sha512-opqpHPB1NjAmDISi3uvZOTrjEEU5CWVu/HBkDby8t93+6UxYX0Z7Ps0Ltjm5sZiEbWenjubwUkivAEYQmy9xHw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.1.tgz", + "integrity": "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -2822,14 +2973,14 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.16.tgz", - "integrity": "sha512-kMrXAaKGavBEoBYUCgualbwA9jWUx2TjMA46ek+pEKy38+LFpL9QHlTd8PO2kWPUgI/KB+qi02o4y2rwXbzr3Q==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.17.tgz", + "integrity": "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -2844,14 +2995,14 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.16.tgz", - "integrity": "sha512-g8BVNBj5Zeb5/Y3cSN+hDUL7CsIFDIuVxb9EPty3lkxBaYpjL5BNRKSYOF9yOLe+JOcKFd+TSVeADQ4iSY7rbg==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.17.tgz", + "integrity": "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2" }, "engines": { @@ -2867,22 +3018,22 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.6.0.tgz", - "integrity": "sha512-jAhL7tyMxB3Gfwn4HIJ0yuJ5pvcB5maYUcouGcgd/ub79f9MqZ+aVnBtuFf+VC2GTkCBF+R+eo7Vi63w5VZlzw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.0.tgz", + "integrity": "sha512-JHwGbQ6wjf1dxxnalDYpZwZxUEosT+6CPGD9Zh4sm9WXdtUp9XODCQD3NjSTmu+0OAyxWXNOqf0spjIymJa2Tw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.1.9", - "@inquirer/confirm": "^5.1.13", - "@inquirer/editor": "^4.2.14", - "@inquirer/expand": "^4.0.16", - "@inquirer/input": "^4.2.0", - "@inquirer/number": "^3.0.16", - "@inquirer/password": "^4.0.16", - "@inquirer/rawlist": "^4.1.4", - "@inquirer/search": "^3.0.16", - "@inquirer/select": "^4.2.4" + "@inquirer/checkbox": "^4.2.0", + "@inquirer/confirm": "^5.1.14", + "@inquirer/editor": "^4.2.15", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.1.0", + "@inquirer/select": "^4.3.1" }, "engines": { "node": ">=18" @@ -2897,14 +3048,14 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.4.tgz", - "integrity": "sha512-5GGvxVpXXMmfZNtvWw4IsHpR7RzqAR624xtkPd1NxxlV5M+pShMqzL4oRddRkg8rVEOK9fKdJp1jjVML2Lr7TQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.5.tgz", + "integrity": "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2920,15 +3071,15 @@ } }, "node_modules/@inquirer/search": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.16.tgz", - "integrity": "sha512-POCmXo+j97kTGU6aeRjsPyuCpQQfKcMXdeTMw708ZMtWrj5aykZvlUxH4Qgz3+Y1L/cAVZsSpA+UgZCu2GMOMg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.0.tgz", + "integrity": "sha512-PMk1+O/WBcYJDq2H7foV0aAZSmDdkzZB9Mw2v/DmONRJopwA/128cS9M/TXWLKKdEQKZnKwBzqu2G4x/2Nqx8Q==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2944,15 +3095,15 @@ } }, "node_modules/@inquirer/select": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.4.tgz", - "integrity": "sha512-unTppUcTjmnbl/q+h8XeQDhAqIOmwWYWNyiiP2e3orXrg6tOaa5DHXja9PChCSbChOsktyKgOieRZFnajzxoBg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.1.tgz", + "integrity": "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.14", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -2969,9 +3120,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", - "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", "dev": true, "license": "MIT", "engines": { @@ -3238,9 +3389,9 @@ } }, "node_modules/@jsonjoy.com/json-pack": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz", - "integrity": "sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.4.0.tgz", + "integrity": "sha512-Akn8XZqN3xO9YGcgvIiTauBBXTP92QSvw6EcGha+p5nm7brhbwvev5gw4fi+ouLGrBpfPpb72+S5pxl4mkMIGQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3261,9 +3412,9 @@ } }, "node_modules/@jsonjoy.com/util": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.6.0.tgz", - "integrity": "sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.8.0.tgz", + "integrity": "sha512-HeR0JQNEdBozt+FrfyM5T0X3R+fIN0D+BRDkxPP5o41fTWzHfeZEqtK16aTW8haU+h+SG7XYq9PP5kobvOmkSA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3424,6 +3575,172 @@ "node": ">= 8" } }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.3.tgz", + "integrity": "sha512-oNXsh2ywth5aowwIa7RKtawnkdH6LgU1ztfP9AIUCQCvzysB+WeU8o2kyyosDPwBZutPpjZDKPQGIzzrfTWweQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.1", + "@octokit/request": "^10.0.2", + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/endpoint": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", + "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz", + "integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.2", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz", + "integrity": "sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.1.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-16.0.0.tgz", + "integrity": "sha512-kJVUQk6/dx/gRNLWUnAWKFs1kVPn5O5CYZyssyEoNYaFedqZxsfYs7DwI3d67hGz4qOwaJ1dpm07hOAD1BXx6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.1.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.3.tgz", + "integrity": "sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.0", + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz", + "integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/rest": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.0.tgz", + "integrity": "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.2", + "@octokit/plugin-paginate-rest": "^13.0.1", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^25.1.0" + } + }, "node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -3510,9 +3827,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.1.tgz", + "integrity": "sha512-oENme6QxtLCqjChRUUo3S6X8hjCXnWmJWnedD7VbGML5GUtaOtAyx+fEEXnBXVf0CBZApMQU0Idwi0FmyxzQhw==", "cpu": [ "arm" ], @@ -3524,9 +3841,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.1.tgz", + "integrity": "sha512-OikvNT3qYTl9+4qQ9Bpn6+XHM+ogtFadRLuT2EXiFQMiNkXFLQfNVppi5o28wvYdHL2s3fM0D/MZJ8UkNFZWsw==", "cpu": [ "arm64" ], @@ -3538,9 +3855,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.1.tgz", + "integrity": "sha512-EFYNNGij2WllnzljQDQnlFTXzSJw87cpAs4TVBAWLdkvic5Uh5tISrIL6NRcxoh/b2EFBG/TK8hgRrGx94zD4A==", "cpu": [ "arm64" ], @@ -3552,9 +3869,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.1.tgz", + "integrity": "sha512-ZaNH06O1KeTug9WI2+GRBE5Ujt9kZw4a1+OIwnBHal92I8PxSsl5KpsrPvthRynkhMck4XPdvY0z26Cym/b7oA==", "cpu": [ "x64" ], @@ -3566,9 +3883,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.1.tgz", + "integrity": "sha512-n4SLVebZP8uUlJ2r04+g2U/xFeiQlw09Me5UFqny8HGbARl503LNH5CqFTb5U5jNxTouhRjai6qPT0CR5c/Iig==", "cpu": [ "arm64" ], @@ -3580,9 +3897,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.1.tgz", + "integrity": "sha512-8vu9c02F16heTqpvo3yeiu7Vi1REDEC/yES/dIfq3tSXe6mLndiwvYr3AAvd1tMNUqE9yeGYa5w7PRbI5QUV+w==", "cpu": [ "x64" ], @@ -3594,9 +3911,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.1.tgz", + "integrity": "sha512-K4ncpWl7sQuyp6rWiGUvb6Q18ba8mzM0rjWJ5JgYKlIXAau1db7hZnR0ldJvqKWWJDxqzSLwGUhA4jp+KqgDtQ==", "cpu": [ "arm" ], @@ -3608,9 +3925,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.1.tgz", + "integrity": "sha512-YykPnXsjUjmXE6j6k2QBBGAn1YsJUix7pYaPLK3RVE0bQL2jfdbfykPxfF8AgBlqtYbfEnYHmLXNa6QETjdOjQ==", "cpu": [ "arm" ], @@ -3622,9 +3939,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.1.tgz", + "integrity": "sha512-kKvqBGbZ8i9pCGW3a1FH3HNIVg49dXXTsChGFsHGXQaVJPLA4f/O+XmTxfklhccxdF5FefUn2hvkoGJH0ScWOA==", "cpu": [ "arm64" ], @@ -3636,9 +3953,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.1.tgz", + "integrity": "sha512-zzX5nTw1N1plmqC9RGC9vZHFuiM7ZP7oSWQGqpbmfjK7p947D518cVK1/MQudsBdcD84t6k70WNczJOct6+hdg==", "cpu": [ "arm64" ], @@ -3650,9 +3967,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.1.tgz", + "integrity": "sha512-O8CwgSBo6ewPpktFfSDgB6SJN9XDcPSvuwxfejiddbIC/hn9Tg6Ai0f0eYDf3XvB/+PIWzOQL+7+TZoB8p9Yuw==", "cpu": [ "loong64" ], @@ -3663,10 +3980,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.1.tgz", + "integrity": "sha512-JnCfFVEKeq6G3h3z8e60kAp8Rd7QVnWCtPm7cxx+5OtP80g/3nmPtfdCXbVl063e3KsRnGSKDHUQMydmzc/wBA==", "cpu": [ "ppc64" ], @@ -3678,9 +3995,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.1.tgz", + "integrity": "sha512-dVxuDqS237eQXkbYzQQfdf/njgeNw6LZuVyEdUaWwRpKHhsLI+y4H/NJV8xJGU19vnOJCVwaBFgr936FHOnJsQ==", "cpu": [ "riscv64" ], @@ -3692,9 +4009,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.1.tgz", + "integrity": "sha512-CvvgNl2hrZrTR9jXK1ye0Go0HQRT6ohQdDfWR47/KFKiLd5oN5T14jRdUVGF4tnsN8y9oSfMOqH6RuHh+ck8+w==", "cpu": [ "riscv64" ], @@ -3706,9 +4023,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.1.tgz", + "integrity": "sha512-x7ANt2VOg2565oGHJ6rIuuAon+A8sfe1IeUx25IKqi49OjSr/K3awoNqr9gCwGEJo9OuXlOn+H2p1VJKx1psxA==", "cpu": [ "s390x" ], @@ -3720,9 +4037,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.1.tgz", + "integrity": "sha512-9OADZYryz/7E8/qt0vnaHQgmia2Y0wrjSSn1V/uL+zw/i7NUhxbX4cHXdEQ7dnJgzYDS81d8+tf6nbIdRFZQoQ==", "cpu": [ "x64" ], @@ -3734,9 +4051,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.1.tgz", + "integrity": "sha512-NuvSCbXEKY+NGWHyivzbjSVJi68Xfq1VnIvGmsuXs6TCtveeoDRKutI5vf2ntmNnVq64Q4zInet0UDQ+yMB6tA==", "cpu": [ "x64" ], @@ -3748,9 +4065,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.1.tgz", + "integrity": "sha512-mWz+6FSRb82xuUMMV1X3NGiaPFqbLN9aIueHleTZCc46cJvwTlvIh7reQLk4p97dv0nddyewBhwzryBHH7wtPw==", "cpu": [ "arm64" ], @@ -3762,9 +4079,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.1.tgz", + "integrity": "sha512-7Thzy9TMXDw9AU4f4vsLNBxh7/VOKuXi73VH3d/kHGr0tZ3x/ewgL9uC7ojUKmH1/zvmZe2tLapYcZllk3SO8Q==", "cpu": [ "ia32" ], @@ -3776,9 +4093,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.1.tgz", + "integrity": "sha512-7GVB4luhFmGUNXXJhH2jJwZCFB3pIOixv2E3s17GQHBFUOQaISlt7aGcQgqvCaDSxTZJUzlK/QJ1FN8S94MrzQ==", "cpu": [ "x64" ], @@ -4629,6 +4946,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/glob": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-9.0.0.tgz", + "integrity": "sha512-00UxlRaIUvYm4R4W9WYkN8/J+kV8fmOQ7okeH6YFtGWFMt3odD45tpG5yA5wnL7HE6lLgjaTW5n14ju2hl2NNA==", + "deprecated": "This is a stub types definition. glob provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "*" + } + }, "node_modules/@types/js-yaml": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", @@ -4665,9 +4993,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.8.tgz", - "integrity": "sha512-HzbgCY53T6bfu4tT7Aq3TvViJyHjLjPNaAS3HOuMc9pw97KHsUtXNX4L+wu59g1WnjsZSko35MbEqnO58rihhw==", + "version": "20.19.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", + "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", "dev": true, "license": "MIT", "dependencies": { @@ -4734,17 +5062,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -4758,7 +5086,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -4774,16 +5102,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -4799,14 +5127,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -4821,14 +5149,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4839,9 +5167,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -4856,15 +5184,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -4881,9 +5209,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -4895,16 +5223,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4950,16 +5278,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4974,13 +5302,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -4992,22 +5320,22 @@ } }, "node_modules/@typespec/asset-emitter": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/asset-emitter/-/asset-emitter-0.72.1.tgz", - "integrity": "sha512-lk41TinsVknczgl64OrEVQ+S6K5WiLAzDgIclaOVKu0ld1vNADz9grqwOtnTiYCz0pWRyZE+xhrq/9XkszU3lg==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@typespec/asset-emitter/-/asset-emitter-0.73.0.tgz", + "integrity": "sha512-SigCa9k8gS+AiHE7Ky/kcwyqFM5kuJ0wXT+Dy89Jbd+wwrYu+mKXyXbScrTdc+MBzut+rFltFENgEYXsSvA/mA==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.3.0" } }, "node_modules/@typespec/compiler": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.2.1.tgz", - "integrity": "sha512-lUdHCRBPtianNN6QKt0G9qyyuSu7azbqKcYNimNLYQwrEIDcgSfQAUnoja9s+gtzCQQRzfbUZ8WLBC2b9cC81Q==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-1.3.0.tgz", + "integrity": "sha512-OqpoNP3C2y8riA6C5RofPMvmj9jNiGyyhde0tM2ZE7IBOv7BBaTDqw4CJD22YnC8JEilRfPmvdVCViNrPHEjrA==", "dev": true, "license": "MIT", "dependencies": { @@ -5020,7 +5348,7 @@ "is-unicode-supported": "^2.1.0", "mustache": "~4.2.0", "picocolors": "~1.1.1", - "prettier": "~3.5.3", + "prettier": "~3.6.2", "semver": "^7.7.1", "tar": "^7.4.3", "temporal-polyfill": "^0.3.0", @@ -5109,6 +5437,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@typespec/compiler/node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/@typespec/compiler/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -5190,30 +5534,30 @@ } }, "node_modules/@typespec/events": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.72.1.tgz", - "integrity": "sha512-vUtA/mQD9csOCRLy9/EPS3oaUEmOiBXNhDkxtb7RYaZLA5975cprP+6o4ntSk6yCYQEo0/YtDcCbS4th2VGIqQ==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@typespec/events/-/events-0.73.0.tgz", + "integrity": "sha512-etlhp86amDaElD/UX27u9I4O58zREov73HkkV3xbdTWpv2RqOKyD3mkyGAWsW3hKaGVIxwHOvKcOZ2j+b07Gpw==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.3.0" } }, "node_modules/@typespec/http": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.2.1.tgz", - "integrity": "sha512-HEPHgVFO2oQL6uZCtpqnRYVZizfSu9BO6vAgdRl1FYJWD2G0f/A4/hK6LEgpyZP44k39M1xMSqVrll2KZ5zpnw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-1.3.0.tgz", + "integrity": "sha512-4W3KsmBHZGgECVbvyh7S7KQG06948XyVVzae+UbVDDxoUj/x4Ry0AXw3q4HmzB2BVhxw6JBrwBuVa5mxjVMzdw==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/streams": "^0.72.1" + "@typespec/compiler": "^1.3.0", + "@typespec/streams": "^0.73.0" }, "peerDependenciesMeta": { "@typespec/streams": { @@ -5222,28 +5566,28 @@ } }, "node_modules/@typespec/openapi": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.2.1.tgz", - "integrity": "sha512-PSoM6c5M7epiFdFDPL4zIJKRPUgJepMtOtO1vVOSIFuz26DcFQpc8xzBy7LBsRneSfp8b6XbsiaNXNcBP/9A1w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-1.3.0.tgz", + "integrity": "sha512-BSeshjCZQodVGyVHn7ytcUeIcUGjqbG2Ac0NLOQaaKnISVrhTWNcgo5aFTqxAa24ZL/EuhqlSauLyYce2EV9fw==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/http": "^1.2.1" + "@typespec/compiler": "^1.3.0", + "@typespec/http": "^1.3.0" } }, "node_modules/@typespec/openapi3": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-1.2.1.tgz", - "integrity": "sha512-PG4+yDTm1YI1rrxFAS3B8WZc6S66pl2WPK+9pP/5b0He9NkFmA53BIodgXpV2QuhvChCbEjr/CDa94ufv8+cKw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-1.3.0.tgz", + "integrity": "sha512-ZG+swQYtdBgyTUbwPI03YQJpPUYhORtbcx6mIFNsKhsTRRC2UDq63jUNCIFCTYI6DJPzkVpra56YPNCXmQLZMg==", "dev": true, "license": "MIT", "dependencies": { "@apidevtools/swagger-parser": "~12.0.0", - "@typespec/asset-emitter": "^0.72.1", + "@typespec/asset-emitter": "^0.73.0", "openapi-types": "~12.1.3", "yaml": "~2.8.0" }, @@ -5254,11 +5598,11 @@ "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/http": "^1.2.1", - "@typespec/json-schema": "^1.2.1", - "@typespec/openapi": "^1.2.1", - "@typespec/versioning": "^0.72.1" + "@typespec/compiler": "^1.3.0", + "@typespec/http": "^1.3.0", + "@typespec/json-schema": "^1.3.0", + "@typespec/openapi": "^1.3.0", + "@typespec/versioning": "^0.73.0" }, "peerDependenciesMeta": { "@typespec/json-schema": { @@ -5273,56 +5617,72 @@ } }, "node_modules/@typespec/prettier-plugin-typespec": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@typespec/prettier-plugin-typespec/-/prettier-plugin-typespec-1.2.1.tgz", - "integrity": "sha512-RCMHl8r5BRS8+xi+Pj60d9W5lLCG/iQfVuctnL69bqUVYHnrK2ffcjHTTr0TRmDqXpxKFqFTtYJdurBaW1OjCg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@typespec/prettier-plugin-typespec/-/prettier-plugin-typespec-1.3.0.tgz", + "integrity": "sha512-RKBOYTRZvSma3hnlV9yIYC/BJNXkLLP90vIqPgUIGCTiNJ5XOMxXc+dg1ORFFTXo980oN3AnqVnCU3/rqSjvNw==", "dev": true, "license": "MIT", "dependencies": { - "prettier": "~3.5.3" + "prettier": "~3.6.2" + } + }, + "node_modules/@typespec/prettier-plugin-typespec/node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/@typespec/rest": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.72.1.tgz", - "integrity": "sha512-w0C91JhrVos8mAdd3OVwrcS6aSjuKlw7LtoazHenAmou/zSACKZbH4g6ko1BY8fv5lgl+q7VZ3/52uEWHOTxpw==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.73.0.tgz", + "integrity": "sha512-28hgFGvreBg34Xuguw+E++pQC/kbRxy1Bpx/9nU7x87Ly6ykns3lpx74gjY9ByE8VYKVbXtC7lzdnp19DRSjIQ==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/http": "^1.2.1" + "@typespec/compiler": "^1.3.0", + "@typespec/http": "^1.3.0" } }, "node_modules/@typespec/sse": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.72.1.tgz", - "integrity": "sha512-J5Qitfi7uGhgkWO9aPKqHsEojt3aZHv2QbWrFvO1AkWPXHPML+1l66dmHg3XIQTmGAiUnCAj/JzS4W0E0yp9Dg==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@typespec/sse/-/sse-0.73.0.tgz", + "integrity": "sha512-WTnRJ1b1M3RPzlHxhnK9sh6+AGKPKWpuA0TSAqzyxb/uRHFYLNeoDKPOnlQ749SJ8lJz71Oh0nUsP3vB0EzO6Q==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1", - "@typespec/events": "^0.72.1", - "@typespec/http": "^1.2.1", - "@typespec/streams": "^0.72.1" + "@typespec/compiler": "^1.3.0", + "@typespec/events": "^0.73.0", + "@typespec/http": "^1.3.0", + "@typespec/streams": "^0.73.0" } }, "node_modules/@typespec/streams": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.72.1.tgz", - "integrity": "sha512-TJdFxpW9lgazOluDdT9N8Ojnb7T/hXMZOL094D2idBf33aeqJvSHZtWgY4po8hTsQLk8Y4m4WJJ70nT9DUEOdg==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@typespec/streams/-/streams-0.73.0.tgz", + "integrity": "sha512-pL4xffHXEIhBQKPlB9L4AKuM0bn44WsGKjnz91wa6wBtP/CbsPrGQicof0Z7GPGdddtDi4G8PWGmJtVFw53V9g==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.3.0" } }, "node_modules/@typespec/ts-http-runtime": { @@ -5341,29 +5701,29 @@ } }, "node_modules/@typespec/versioning": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.72.1.tgz", - "integrity": "sha512-v1tBN2TcJilYpmb67v96YIVCGy8Su/c9hmuU6WABudWnYr26O4O+6gK2zx69RFxXORw+iw/LiDCU3XtFHbb7IQ==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.73.0.tgz", + "integrity": "sha512-cfFvzTsvsu4VpdwZcRULr3p/fawKZnjiJClQxlLcYW0dLs/5k5jh7l0YyPkYvgkOcncUrIB6hIu82tQhKrMDKQ==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.3.0" } }, "node_modules/@typespec/xml": { - "version": "0.72.1", - "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.72.1.tgz", - "integrity": "sha512-CmHXpwOojFT7cRI6ooB682OBIUVP9jMMx5bSoF9nrXg047Quaj7J0qPIwrG7d3O5lkogwPxqtPJPYaVq0+9gfg==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@typespec/xml/-/xml-0.73.0.tgz", + "integrity": "sha512-vlMM8/L22O/PbI3ovj3qoww/3Z8wNwn7og4jzlGRM93jZBJvrOeDSwZo1Dc4rMJyU6KfjPkP3/l5TLbgW8x0zA==", "dev": true, "license": "MIT", "engines": { "node": ">=20.0.0" }, "peerDependencies": { - "@typespec/compiler": "^1.2.1" + "@typespec/compiler": "^1.3.0" } }, "node_modules/@vitest/coverage-v8": { @@ -5876,14 +6236,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", - "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, @@ -5929,6 +6289,13 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -6104,9 +6471,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "dev": true, "funding": [ { @@ -6832,9 +7199,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.185", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.185.tgz", - "integrity": "sha512-dYOZfUk57hSMPePoIQ1fZWl1Fkj+OshhEVuPacNKWzC1efe56OsHY3l/jCfiAgIICOU3VgOIdoq7ahg7r7n6MQ==", + "version": "1.5.192", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.192.tgz", + "integrity": "sha512-rP8Ez0w7UNw/9j5eSXCe10o1g/8B1P5SM90PCCMVkIRQn2R0LEHWz4Eh9RnxkniuDe1W0cTSOB3MLlkTGDcuCg==", "dev": true, "license": "ISC" }, @@ -7046,9 +7413,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7059,32 +7426,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/escalade": { @@ -7111,9 +7478,9 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", "dependencies": { @@ -7123,8 +7490,8 @@ "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -7172,65 +7539,39 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", + "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", + "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", - "globals": "^16.0.0", + "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" + "node": "^20.10.0 || >=21.0.0" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "eslint": ">=9.29.0" } }, "node_modules/eslint-scope": { @@ -7502,6 +7843,23 @@ ], "license": "MIT" }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7910,9 +8268,9 @@ } }, "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { @@ -9906,9 +10264,9 @@ } }, "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", "dev": true, "license": "MIT" }, @@ -10014,9 +10372,9 @@ "license": "MIT" }, "node_modules/memfs": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.2.tgz", - "integrity": "sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.23.0.tgz", + "integrity": "sha512-SucHN2lcWf0jrnw+jP6FoVW6l/zGJiXfNMdApZzG0x/0mAIMdwAeR5mjfsCH5U3BoqpUEtqzz+dSQSO0H/eqxg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -10540,9 +10898,9 @@ } }, "node_modules/oav": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/oav/-/oav-3.6.2.tgz", - "integrity": "sha512-bOH69U424+lrZMAXLPe7Fh65wN3BSZgk4d8/veacmUJ2lmeY3Xe6LqnX1Z8X9LnsbdrZycvKTz5LJ6KNrEcQLA==", + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/oav/-/oav-3.6.4.tgz", + "integrity": "sha512-vmUmWMoD4iiRbaaOm4PfNYDO2Osb6/HYlLmZQp/Vgu7lCpfZdox1xKKYgLozJ997qfHw28VNTXrzFFjqE7/h7g==", "dev": true, "license": "MIT", "dependencies": { @@ -11509,6 +11867,23 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-organize-imports": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prettier": ">=2.0", + "typescript": ">=2.9", + "vue-tsc": "^2.1.0 || 3" + }, + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } + } + }, "node_modules/pretty-ms": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", @@ -11896,9 +12271,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.46.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.1.tgz", + "integrity": "sha512-33xGNBsDJAkzt0PvninskHlWnTIPgDtTwhg0U38CUoNP/7H6wI2Cz6dUeoNPbjdTdsYTGuiFFASuUOWovH0SyQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11912,26 +12287,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.46.1", + "@rollup/rollup-android-arm64": "4.46.1", + "@rollup/rollup-darwin-arm64": "4.46.1", + "@rollup/rollup-darwin-x64": "4.46.1", + "@rollup/rollup-freebsd-arm64": "4.46.1", + "@rollup/rollup-freebsd-x64": "4.46.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.1", + "@rollup/rollup-linux-arm-musleabihf": "4.46.1", + "@rollup/rollup-linux-arm64-gnu": "4.46.1", + "@rollup/rollup-linux-arm64-musl": "4.46.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.1", + "@rollup/rollup-linux-ppc64-gnu": "4.46.1", + "@rollup/rollup-linux-riscv64-gnu": "4.46.1", + "@rollup/rollup-linux-riscv64-musl": "4.46.1", + "@rollup/rollup-linux-s390x-gnu": "4.46.1", + "@rollup/rollup-linux-x64-gnu": "4.46.1", + "@rollup/rollup-linux-x64-musl": "4.46.1", + "@rollup/rollup-win32-arm64-msvc": "4.46.1", + "@rollup/rollup-win32-ia32-msvc": "4.46.1", + "@rollup/rollup-win32-x64-msvc": "4.46.1", "fsevents": "~2.3.2" } }, @@ -13307,16 +13682,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -13400,6 +13775,13 @@ "moment": "^2.14.1" } }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "dev": true, + "license": "ISC" + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -13542,15 +13924,15 @@ } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" @@ -14298,9 +14680,9 @@ } }, "node_modules/zod": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.5.tgz", - "integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==", + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.13.tgz", + "integrity": "sha512-jv+zRxuZQxTrFHzxZ46ezL2FtnE+M4HIJHJEwLsZ7UjrXHltdG6HrxvqM0twoVCWxJiYf8WqKjAcjztegpkB+Q==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index e8b300990f79..5da3fe8dcb5f 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,40 @@ { "name": "azure-rest-api-specs", "devDependencies": { - "@azure-tools/spec-gen-sdk": "~0.8.0", + "@azure-tools/spec-gen-sdk": "~0.9.1", "@azure-tools/specs-shared": "file:.github/shared", "@azure-tools/typespec-apiview": "0.7.2", - "@azure-tools/typespec-autorest": "0.58.0", - "@azure-tools/typespec-azure-core": "0.58.0", - "@azure-tools/typespec-azure-portal-core": "0.58.0", - "@azure-tools/typespec-azure-resource-manager": "0.58.0", - "@azure-tools/typespec-azure-rulesets": "0.58.0", - "@azure-tools/typespec-client-generator-cli": "0.25.0", - "@azure-tools/typespec-client-generator-core": "0.58.0", + "@azure-tools/typespec-autorest": "0.59.0", + "@azure-tools/typespec-azure-core": "0.59.0", + "@azure-tools/typespec-azure-portal-core": "0.59.0", + "@azure-tools/typespec-azure-resource-manager": "0.59.1", + "@azure-tools/typespec-azure-rulesets": "0.59.0", + "@azure-tools/typespec-client-generator-cli": "0.27.0", + "@azure-tools/typespec-client-generator-core": "0.59.0", "@azure-tools/typespec-liftr-base": "0.8.0", - "@autorest/openapi-to-typespec": "0.11.3", + "@autorest/openapi-to-typespec": "0.11.7", "@azure/avocado": "^0.9.1", - "@typespec/compiler": "1.2.1", - "@typespec/http": "1.2.1", - "@typespec/sse": "0.72.1", - "@typespec/events": "0.72.1", - "@typespec/openapi": "1.2.1", - "@typespec/openapi3": "1.2.1", - "@typespec/prettier-plugin-typespec": "1.2.1", - "@typespec/rest": "0.72.1", - "@typespec/streams": "0.72.1", - "@typespec/versioning": "0.72.1", - "@typespec/xml": "0.72.1", + "@microsoft.azure/openapi-validator": "2.2.4", + "@microsoft.azure/openapi-validator-core": "1.0.6", + "@microsoft.azure/openapi-validator-rulesets": "2.1.7", + "@typespec/compiler": "1.3.0", + "@typespec/http": "1.3.0", + "@typespec/sse": "0.73.0", + "@typespec/events": "0.73.0", + "@typespec/openapi": "1.3.0", + "@typespec/openapi3": "1.3.0", + "@typespec/prettier-plugin-typespec": "1.3.0", + "@typespec/rest": "0.73.0", + "@typespec/streams": "0.73.0", + "@typespec/versioning": "0.73.0", + "@typespec/xml": "0.73.0", "azure-rest-api-specs-eng-tools": "file:eng/tools", - "oav": "^3.6.1", + "oav": "^3.6.4", "prettier": "~3.5.3", "typescript": "~5.8.2" }, "overrides": { - "@typespec/asset-emitter": "0.72.1", + "@typespec/asset-emitter": "0.73.0", "jsonpath-plus": "^10.3.0" }, "engines": {