diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 80db0d33996f..f1698b664620 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -229,6 +229,7 @@ specifiers: '@rush-temp/arm-workloads': file:./projects/arm-workloads.tgz '@rush-temp/arm-workspaces': file:./projects/arm-workspaces.tgz '@rush-temp/attestation': file:./projects/attestation.tgz + '@rush-temp/azure-codesigning-rest': file:./projects/azure-codesigning-rest.tgz '@rush-temp/communication-alpha-ids': file:./projects/communication-alpha-ids.tgz '@rush-temp/communication-call-automation': file:./projects/communication-call-automation.tgz '@rush-temp/communication-chat': file:./projects/communication-chat.tgz @@ -582,6 +583,7 @@ dependencies: '@rush-temp/arm-workloads': file:projects/arm-workloads.tgz '@rush-temp/arm-workspaces': file:projects/arm-workspaces.tgz '@rush-temp/attestation': file:projects/attestation.tgz + '@rush-temp/azure-codesigning-rest': file:projects/azure-codesigning-rest.tgz '@rush-temp/communication-alpha-ids': file:projects/communication-alpha-ids.tgz '@rush-temp/communication-call-automation': file:projects/communication-call-automation.tgz '@rush-temp/communication-chat': file:projects/communication-chat.tgz @@ -2844,7 +2846,7 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/chai-as-promised/7.1.5: @@ -2866,7 +2868,7 @@ packages: /@types/connect/3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/cookie/0.4.1: @@ -2876,7 +2878,7 @@ packages: /@types/cors/2.8.13: resolution: {integrity: sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/debug/4.1.8: @@ -2888,7 +2890,7 @@ packages: /@types/decompress/4.2.4: resolution: {integrity: sha512-/C8kTMRTNiNuWGl5nEyKbPiMv6HA+0RbEXzFhFBEzASM6+oa4tJro9b8nj7eRlOFfuLdzUU+DS/GPDlvvzMOhA==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/eslint/8.4.10: @@ -2909,7 +2911,7 @@ packages: /@types/express-serve-static-core/4.17.35: resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 '@types/send': 0.17.1 @@ -2927,13 +2929,13 @@ packages: /@types/fs-extra/8.1.2: resolution: {integrity: sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/fs-extra/9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/http-errors/2.0.1: @@ -2950,7 +2952,7 @@ packages: /@types/is-buffer/2.0.0: resolution: {integrity: sha512-0f7N/e3BAz32qDYvgB4d2cqv1DqUwvGxHkXsrucICn8la1Vb6Yl6Eg8mPScGwUiqHJeE7diXlzaK+QMA9m4Gxw==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/json-schema/7.0.12: @@ -2964,13 +2966,13 @@ packages: /@types/jsonwebtoken/9.0.2: resolution: {integrity: sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/jws/3.2.5: resolution: {integrity: sha512-xGTxZH34xOryaTN8CMsvhh9lfNqFuHiMoRvsLYWQdBJHqiECyfInXVl2eK8Jz2emxZWMIn5RBlmr3oDVPeWujw==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/linkify-it/3.0.2: @@ -3029,20 +3031,20 @@ packages: /@types/mysql/2.15.19: resolution: {integrity: sha512-wSRg2QZv14CWcZXkgdvHbbV2ACufNy5EgI8mBBxnJIptchv7DBy/h53VMa2jDhyo0C9MO4iowE6z9vF8Ja1DkQ==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/node-fetch/2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 form-data: 3.0.1 dev: false /@types/node-fetch/2.6.4: resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 form-data: 3.0.1 dev: false @@ -3075,7 +3077,7 @@ packages: /@types/pg/8.6.1: resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 pg-protocol: 1.6.0 pg-types: 2.2.0 dev: false @@ -3103,7 +3105,7 @@ packages: /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/resolve/1.20.2: @@ -3126,7 +3128,7 @@ packages: resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==} dependencies: '@types/mime': 1.3.2 - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/serve-static/1.15.2: @@ -3134,7 +3136,7 @@ packages: dependencies: '@types/http-errors': 2.0.1 '@types/mime': 3.0.1 - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/shimmer/1.0.2: @@ -3154,13 +3156,13 @@ packages: /@types/stoppable/1.1.1: resolution: {integrity: sha512-b8N+fCADRIYYrGZOcmOR8ZNBOqhktWTB/bMUl5LvGtT201QKJZOOH5UsFyI3qtteM6ZAJbJqZoBcLqqxKIwjhw==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/through/0.0.30: resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/tough-cookie/4.0.2: @@ -3174,7 +3176,7 @@ packages: /@types/tunnel/0.0.3: resolution: {integrity: sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/underscore/1.11.6: @@ -3192,19 +3194,19 @@ packages: /@types/ws/7.4.7: resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/ws/8.5.5: resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/xml2js/0.4.11: resolution: {integrity: sha512-JdigeAKmCyoJUiQljjr7tQG3if9NkqGUgwEUqBvV0N7LM4HyQk7UXCnusRa1lnvXAEYJ8mw8GtZWioagNztOwA==} dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false /@types/yargs-parser/21.0.0: @@ -3221,7 +3223,7 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 14.18.54 + '@types/node': 16.18.39 dev: false optional: true @@ -4498,7 +4500,7 @@ packages: resolution: {integrity: sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==} deprecated: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797) dependencies: - ms: 2.1.1 + ms: 2.1.3 dev: false /debug/3.2.7: @@ -4677,7 +4679,7 @@ packages: cosmiconfig: 7.1.0 debug: 4.3.4 deps-regex: 0.1.4 - ignore: 5.1.9 + ignore: 5.2.4 is-core-module: 2.12.1 js-yaml: 3.14.1 json5: 2.2.3 @@ -4818,7 +4820,7 @@ packages: dependencies: semver: 7.5.4 shelljs: 0.8.5 - typescript: 5.2.0-dev.20230731 + typescript: 5.2.0-dev.20230801 dev: false /duplexer3/0.1.5: @@ -4880,7 +4882,7 @@ packages: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.13 - '@types/node': 14.18.54 + '@types/node': 16.18.39 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 @@ -5050,7 +5052,7 @@ packages: dependencies: debug: 3.2.7 is-core-module: 2.12.1 - resolve: 1.22.3 + resolve: 1.22.2 dev: false /eslint-module-utils/2.8.0_eslint@8.46.0: @@ -5869,7 +5871,7 @@ packages: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.0.4 + minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 dev: false @@ -6194,7 +6196,7 @@ packages: /ignore-walk/3.0.4: resolution: {integrity: sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==} dependencies: - minimatch: 3.0.8 + minimatch: 3.1.2 dev: false /ignore/5.1.9: @@ -6287,7 +6289,7 @@ packages: cli-cursor: 3.1.0 cli-width: 3.0.0 external-editor: 3.1.0 - figures: 3.0.0 + figures: 3.2.0 lodash: 4.17.21 mute-stream: 0.0.8 run-async: 2.4.1 @@ -8762,7 +8764,7 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 14.18.54 + '@types/node': 16.18.39 long: 5.2.3 dev: false @@ -10335,8 +10337,8 @@ packages: hasBin: true dev: false - /typescript/5.2.0-dev.20230731: - resolution: {integrity: sha512-RJVLgnDgu67ZrohYy0aBea+5TICfRod36+24zw0bR/KJDQJO9mlIjTC0k+/PKw87fXP5JuUHqepEk15PvFya7A==} + /typescript/5.2.0-dev.20230801: + resolution: {integrity: sha512-f8FmzL+1+agawPPUzsd38vTiZD/z+RtGax/3+tzxsBP15O/4lMsliTEXRXF7ESC7a1s/f9HsNNRMne438PPZsw==} engines: {node: '>=14.17'} hasBin: true dev: false @@ -17565,6 +17567,47 @@ packages: - utf-8-validate dev: false + file:projects/azure-codesigning-rest.tgz: + resolution: {integrity: sha512-yNnPC3Ql+DfDdcIWAZpXtamkaG3JK+eYzKG+Osg+mzFG6W6sGFjyosCg3iABtfAbqFNugFRB0Zz3pIG6R6rHsg==, tarball: file:projects/azure-codesigning-rest.tgz} + name: '@rush-temp/azure-codesigning-rest' + version: 0.0.0 + dependencies: + '@azure/identity': 2.1.0 + '@microsoft/api-extractor': 7.36.3_@types+node@14.18.54 + '@types/chai': 4.3.5 + '@types/mocha': 7.0.2 + '@types/node': 14.18.54 + autorest: 3.6.3 + chai: 4.3.7 + cross-env: 7.0.3 + dotenv: 16.3.1 + eslint: 8.46.0 + karma: 6.4.2 + karma-chrome-launcher: 3.2.0 + karma-coverage: 2.2.1 + karma-env-preprocessor: 0.1.1 + karma-firefox-launcher: 1.3.0 + karma-junit-reporter: 2.0.1_karma@6.4.2 + karma-mocha: 2.0.1 + karma-mocha-reporter: 2.2.5_karma@6.4.2 + karma-source-map-support: 1.4.0 + karma-sourcemap-loader: 0.3.8 + mkdirp: 2.1.6 + mocha: 7.2.0 + mocha-junit-reporter: 1.23.3_mocha@7.2.0 + nyc: 15.1.0 + prettier: 2.8.8 + rimraf: 3.0.2 + source-map-support: 0.5.21 + tslib: 2.6.1 + typescript: 5.0.4 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + dev: false + file:projects/communication-alpha-ids.tgz: resolution: {integrity: sha512-Z5UKcnMA79I3zXDvLr02SA8y89FbXoR+Dtoqcqv0NBj3FJd4vvZMyrIc6tgdkAxBNKj0RqU85YIo0QUct8REsQ==, tarball: file:projects/communication-alpha-ids.tgz} name: '@rush-temp/communication-alpha-ids' diff --git a/rush.json b/rush.json index 8efa6dffdc9d..7d4e54f7493a 100644 --- a/rush.json +++ b/rush.json @@ -1,7 +1,7 @@ /** * This is the main configuration file for Rush. * For full documentation, please see https://rushjs.io - */ { + */{ "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", /** * (Required) This specifies the version of the Rush engine to be used in this repo. @@ -2044,6 +2044,11 @@ "packageName": "@azure/arm-sphere", "projectFolder": "sdk/sphere/arm-sphere", "versionPolicyName": "management" + }, + { + "packageName": "@azure-rest/azure-codesigning-rest", + "projectFolder": "sdk/codesigning/azurecodesigning-rest", + "versionPolicyName": "client" } ] -} +} \ No newline at end of file diff --git a/sdk/codesigning/azurecodesigning-rest/.eslintrc.json b/sdk/codesigning/azurecodesigning-rest/.eslintrc.json new file mode 100644 index 000000000000..619797ac39b6 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/.eslintrc.json @@ -0,0 +1,11 @@ +{ + "plugins": ["@azure/azure-sdk"], + "extends": ["plugin:@azure/azure-sdk/azure-sdk-base"], + "rules": { + "@azure/azure-sdk/ts-modules-only-named": "warn", + "@azure/azure-sdk/ts-apiextractor-json-types": "warn", + "@azure/azure-sdk/ts-package-json-types": "warn", + "@azure/azure-sdk/ts-package-json-engine-is-present": "warn", + "tsdoc/syntax": "warn" + } +} diff --git a/sdk/codesigning/azurecodesigning-rest/CHANGELOG.md b/sdk/codesigning/azurecodesigning-rest/CHANGELOG.md new file mode 100644 index 000000000000..95121a616909 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0-beta.1 (2023-08-01) + + - Initial Release diff --git a/sdk/codesigning/azurecodesigning-rest/README.md b/sdk/codesigning/azurecodesigning-rest/README.md new file mode 100644 index 000000000000..0025b62590f4 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/README.md @@ -0,0 +1,59 @@ +# Azure CodeSigning REST client library for JavaScript + +Azure CodeSigning is a service that provides managed code signing for all. + +**Please rely heavily on our [REST client docs](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/rest-clients.md) to use this library** + +Key links: + +- [Source code](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/codesigning/azurecodesigning-rest) +- [Package (NPM)](https://www.npmjs.com/package/@azure-rest/azure-codesigning-rest) +- [API reference documentation](https://docs.microsoft.com/javascript/api/@azure-rest/azure-codesigning-rest?view=azure-node-preview) +- [Samples](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/codesigning/azurecodesigning-rest/samples) + +## Getting started + +### Currently supported environments + +- LTS versions of Node.js + +### Prerequisites + +- You must have an [Azure subscription](https://azure.microsoft.com/free/) to use this package. + +### Install the `@azure-rest/azure-codesigning-rest` package + +Install the Azure CodeSigning REST client REST client library for JavaScript with `npm`: + +```bash +npm install @azure-rest/azure-codesigning-rest +``` + +### Create and authenticate a `CodeSigningClient` + +To use an [Azure Active Directory (AAD) token credential](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/identity/identity/samples/AzureIdentityExamples.md#authenticating-with-a-pre-fetched-access-token), +provide an instance of the desired credential type obtained from the +[@azure/identity](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity#credentials) library. + +To authenticate with AAD, you must first `npm` install [`@azure/identity`](https://www.npmjs.com/package/@azure/identity) + +After setup, you can choose which type of [credential](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity#credentials) from `@azure/identity` to use. +As an example, [DefaultAzureCredential](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity#defaultazurecredential) +can be used to authenticate the client. + +Set the values of the client ID, tenant ID, and client secret of the AAD application as environment variables: +AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET + +## Troubleshooting + +### Logging + +Enabling logging may help uncover useful information about failures. In order to see a log of HTTP requests and responses, set the `AZURE_LOG_LEVEL` environment variable to `info`. Alternatively, logging can be enabled at runtime by calling `setLogLevel` in the `@azure/logger`: + +```javascript +const { setLogLevel } = require("@azure/logger"); + +setLogLevel("info"); +``` + +For more detailed instructions on how to enable logs, you can look at the [@azure/logger package docs](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/core/logger). diff --git a/sdk/codesigning/azurecodesigning-rest/api-extractor.json b/sdk/codesigning/azurecodesigning-rest/api-extractor.json new file mode 100644 index 000000000000..ff8dc21ce7eb --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/api-extractor.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "mainEntryPointFilePath": "./types/src/index.d.ts", + "docModel": { + "enabled": true + }, + "apiReport": { + "enabled": true, + "reportFolder": "./review" + }, + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "", + "publicTrimmedFilePath": "./types/azure-codesigning-rest.d.ts" + }, + "messages": { + "tsdocMessageReporting": { + "default": { + "logLevel": "none" + } + }, + "extractorMessageReporting": { + "ae-missing-release-tag": { + "logLevel": "none" + }, + "ae-unresolved-link": { + "logLevel": "none" + } + } + } +} \ No newline at end of file diff --git a/sdk/codesigning/azurecodesigning-rest/karma.conf.js b/sdk/codesigning/azurecodesigning-rest/karma.conf.js new file mode 100644 index 000000000000..a9d5f1b5fc59 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/karma.conf.js @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// https://github.com/karma-runner/karma-chrome-launcher +process.env.CHROME_BIN = require("puppeteer").executablePath(); +require("dotenv").config(); +const { relativeRecordingsPath } = require("@azure-tools/test-recorder"); +process.env.RECORDINGS_RELATIVE_PATH = relativeRecordingsPath(); + +module.exports = function (config) { + config.set({ + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: "./", + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ["source-map-support", "mocha"], + + plugins: [ + "karma-mocha", + "karma-mocha-reporter", + "karma-chrome-launcher", + "karma-firefox-launcher", + "karma-env-preprocessor", + "karma-coverage", + "karma-sourcemap-loader", + "karma-junit-reporter", + "karma-source-map-support", + ], + + // list of files / patterns to load in the browser + files: [ + "dist-test/index.browser.js", + { + pattern: "dist-test/index.browser.js.map", + type: "html", + included: false, + served: true, + }, + ], + + // list of files / patterns to exclude + exclude: [], + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + "**/*.js": ["sourcemap", "env"], + // IMPORTANT: COMMENT following line if you want to debug in your browsers!! + // Preprocess source file to calculate code coverage, however this will make source file unreadable + // "dist-test/index.js": ["coverage"] + }, + + envPreprocessor: [ + "TEST_MODE", + "ENDPOINT", + "AZURE_CLIENT_SECRET", + "AZURE_CLIENT_ID", + "AZURE_TENANT_ID", + "SUBSCRIPTION_ID", + "RECORDINGS_RELATIVE_PATH", + ], + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ["mocha", "coverage", "junit"], + + coverageReporter: { + // specify a common output directory + dir: "coverage-browser/", + reporters: [ + { type: "json", subdir: ".", file: "coverage.json" }, + { type: "lcovonly", subdir: ".", file: "lcov.info" }, + { type: "html", subdir: "html" }, + { type: "cobertura", subdir: ".", file: "cobertura-coverage.xml" }, + ], + }, + + junitReporter: { + outputDir: "", // results will be saved as $outputDir/$browserName.xml + outputFile: "test-results.browser.xml", // if included, results will be saved as $outputDir/$browserName/$outputFile + suite: "", // suite will become the package name attribute in xml testsuite element + useBrowserName: false, // add browser name to report and classes names + nameFormatter: undefined, // function (browser, result) to customize the name attribute in xml testcase element + classNameFormatter: undefined, // function (browser, result) to customize the classname attribute in xml testcase element + properties: {}, // key value pair of properties to add to the section of the report + }, + + // web server port + port: 9876, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + // --no-sandbox allows our tests to run in Linux without having to change the system. + // --disable-web-security allows us to authenticate from the browser without having to write tests using interactive auth, which would be far more complex. + browsers: ["ChromeHeadlessNoSandbox"], + customLaunchers: { + ChromeHeadlessNoSandbox: { + base: "ChromeHeadless", + flags: ["--no-sandbox", "--disable-web-security"], + }, + }, + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false, + + // Concurrency level + // how many browser should be started simultaneous + concurrency: 1, + + browserNoActivityTimeout: 60000000, + browserDisconnectTimeout: 10000, + browserDisconnectTolerance: 3, + + client: { + mocha: { + // change Karma's debug.html to the mocha web reporter + reporter: "html", + timeout: "600000", + }, + }, + }); +}; diff --git a/sdk/codesigning/azurecodesigning-rest/package.json b/sdk/codesigning/azurecodesigning-rest/package.json new file mode 100644 index 000000000000..8cd8ef80cf76 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/package.json @@ -0,0 +1,119 @@ +{ + "name": "@azure-rest/azure-codesigning-rest", + "sdk-type": "client", + "author": "Microsoft Corporation", + "version": "1.0.0-beta.1", + "description": "Azure Codesigning Service", + "keywords": [ + "node", + "azure", + "cloud", + "typescript", + "browser", + "isomorphic" + ], + "license": "MIT", + "main": "dist/index.js", + "module": "./dist-esm/src/index.js", + "types": "./types/azure-codesigning-rest.d.ts", + "repository": "github:Azure/azure-sdk-for-js", + "bugs": { + "url": "https://github.com/Azure/azure-sdk-for-js/issues" + }, + "files": [ + "dist/", + "dist-esm/src/", + "types/azure-codesigning-rest.d.ts", + "README.md", + "LICENSE", + "review/*" + ], + "engines": { + "node": ">=14.0.0" + }, + "scripts": { + "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", + "build:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c 2>&1", + "build:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c 2>&1", + "build:samples": "echo skipped.", + "build:test": "tsc -p . && dev-tool run bundle", + "build:debug": "tsc -p . && dev-tool run bundle && api-extractor run --local", + "check-format": "prettier --list-different --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"*.{js,json}\" \"test/**/*.ts\"", + "clean": "rimraf dist dist-browser dist-esm test-dist temp types *.tgz *.log", + "execute:samples": "echo skipped", + "extract-api": "rimraf review && mkdirp ./review && api-extractor run --local", + "format": "prettier --write --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"*.{js,json}\" \"test/**/*.ts\"", + "generate:client": "echo skipped", + "integration-test:browser": "dev-tool run test:browser", + "integration-test:node": "dev-tool run test:node-js-input -- --timeout 5000000 'dist-esm/test/**/*.spec.js'", + "integration-test": "npm run integration-test:node && npm run integration-test:browser", + "lint:fix": "eslint package.json api-extractor.json src test --ext .ts --fix --fix-type [problem,suggestion]", + "lint": "eslint package.json api-extractor.json src test --ext .ts", + "pack": "npm pack 2>&1", + "test:browser": "npm run clean && npm run build:test && npm run unit-test:browser", + "test:node": "npm run clean && npm run build:test && npm run unit-test:node", + "test": "npm run clean && npm run build:test && npm run unit-test", + "unit-test": "npm run unit-test:node && npm run unit-test:browser", + "unit-test:node": "dev-tool run test:node-ts-input -- --timeout 1200000 --exclude 'test/**/browser/*.spec.ts' 'test/**/*.spec.ts'", + "unit-test:browser": "dev-tool run test:browser", + "build": "npm run clean && tsc -p . && dev-tool run bundle && mkdirp ./review && api-extractor run --local" + }, + "sideEffects": false, + "autoPublish": false, + "dependencies": { + "@azure/core-auth": "^1.3.0", + "@azure-rest/core-client": "^1.1.4", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0", + "@azure/core-paging": "^1.5.0", + "@azure/core-lro": "^2.5.4", + "@azure/abort-controller": "^1.0.0" + }, + "devDependencies": { + "@microsoft/api-extractor": "^7.31.1", + "autorest": "latest", + "@types/node": "^14.0.0", + "dotenv": "^16.0.0", + "eslint": "^8.0.0", + "mkdirp": "^2.1.2", + "prettier": "^2.5.1", + "rimraf": "^3.0.0", + "source-map-support": "^0.5.9", + "typescript": "~5.0.0", + "@azure/dev-tool": "^1.0.0", + "@azure/eslint-plugin-azure-sdk": "^3.0.0", + "@azure-tools/test-credential": "^1.0.0", + "@azure/identity": "^2.0.1", + "@azure-tools/test-recorder": "^3.0.0", + "mocha": "^7.1.1", + "@types/mocha": "^7.0.2", + "mocha-junit-reporter": "^1.18.0", + "cross-env": "^7.0.2", + "@types/chai": "^4.2.8", + "chai": "^4.2.0", + "karma-chrome-launcher": "^3.0.0", + "karma-coverage": "^2.0.0", + "karma-env-preprocessor": "^0.1.1", + "karma-firefox-launcher": "^1.1.0", + "karma-junit-reporter": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-mocha": "^2.0.1", + "karma-source-map-support": "~1.4.0", + "karma-sourcemap-loader": "^0.3.8", + "karma": "^6.2.0", + "nyc": "^15.0.0" + }, + "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/codesigning/azurecodesigning-rest/README.md", + "//metadata": { + "constantPaths": [ + { + "path": "src/codeSigningClient.ts", + "prefix": "userAgentInfo" + } + ] + }, + "browser": { + "./dist-esm/test/public/utils/env.js": "./dist-esm/test/public/utils/env.browser.js" + } +} \ No newline at end of file diff --git a/sdk/codesigning/azurecodesigning-rest/review/azure-codesigning-rest.api.md b/sdk/codesigning/azurecodesigning-rest/review/azure-codesigning-rest.api.md new file mode 100644 index 000000000000..f0068a8a5954 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/review/azure-codesigning-rest.api.md @@ -0,0 +1,281 @@ +## API Report File for "@azure-rest/azure-codesigning-rest" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { Client } from '@azure-rest/core-client'; +import { ClientOptions } from '@azure-rest/core-client'; +import { CreateHttpPollerOptions } from '@azure/core-lro'; +import { ErrorModel } from '@azure-rest/core-client'; +import { ErrorResponse } from '@azure-rest/core-client'; +import { HttpResponse } from '@azure-rest/core-client'; +import { OperationState } from '@azure/core-lro'; +import { Paged } from '@azure/core-paging'; +import { PagedAsyncIterableIterator } from '@azure/core-paging'; +import { PathUncheckedResponse } from '@azure-rest/core-client'; +import { RawHttpHeaders } from '@azure/core-rest-pipeline'; +import { RequestParameters } from '@azure-rest/core-client'; +import { SimplePollerLike } from '@azure/core-lro'; +import { StreamableMethod } from '@azure-rest/core-client'; +import { TokenCredential } from '@azure/core-auth'; + +// @public (undocumented) +export type CodeSigningClient = Client & { + path: Routes; +}; + +// @public +export interface CodeSigningSubmissionOptions { + authenticodeHashList?: string[]; + digest: string; + fileHashList?: string[]; + signatureAlgorithm: string; +} + +// @public +export interface CodeSignOperationStatusOutput { + id: string; + signResult?: CodeSignResultOutput; +} + +// @public +export interface CodeSignResultOutput { + operationId: string; + signature?: string; + signingCertificate?: string; +} + +// @public +function createClient(region: string, credentials: TokenCredential, options?: ClientOptions): CodeSigningClient; +export default createClient; + +// @public +export type ExtendedKeyUsageListOutput = Paged; + +// @public +export interface ExtendedKeyUsageOutput { + ekus: string[]; +} + +// @public +export type GetArrayType = T extends Array ? TData : never; + +// @public (undocumented) +export interface GetCodeSigningStatus { + get(options?: GetCodeSigningStatusParameters): StreamableMethod; +} + +// @public +export interface GetCodeSigningStatus200Response extends HttpResponse { + // (undocumented) + body: OperationStatusOutput; + // (undocumented) + status: "200"; +} + +// @public (undocumented) +export interface GetCodeSigningStatusDefaultHeaders { + "x-ms-error-code"?: string; +} + +// @public (undocumented) +export interface GetCodeSigningStatusDefaultResponse extends HttpResponse { + // (undocumented) + body: ErrorResponse; + // (undocumented) + headers: RawHttpHeaders & GetCodeSigningStatusDefaultHeaders; + // (undocumented) + status: string; +} + +// @public +export interface GetCodeSigningStatusLogicalResponse extends HttpResponse { + // (undocumented) + body: OperationStatusOutput; + // (undocumented) + status: "200"; +} + +// @public (undocumented) +export type GetCodeSigningStatusParameters = RequestParameters; + +// @public +export function getLongRunningPoller(client: Client, initialResponse: Sign202Response | SignDefaultResponse, options?: CreateHttpPollerOptions>): Promise, TResult>>; + +// @public (undocumented) +export function getLongRunningPoller(client: Client, initialResponse: GetCodeSigningStatus200Response | GetCodeSigningStatusDefaultResponse, options?: CreateHttpPollerOptions>): Promise, TResult>>; + +// @public +export type GetPage = (pageLink: string, maxPageSize?: number) => Promise<{ + page: TPage; + nextPageLink?: string; +}>; + +// @public (undocumented) +export interface GetSignRootCertificate { + get(options?: GetSignRootCertificateParameters): StreamableMethod; +} + +// @public +export interface GetSignRootCertificate200Response extends HttpResponse { + body: Uint8Array; + // (undocumented) + status: "200"; +} + +// @public (undocumented) +export interface GetSignRootCertificateDefaultHeaders { + "x-ms-error-code"?: string; +} + +// @public (undocumented) +export interface GetSignRootCertificateDefaultResponse extends HttpResponse { + // (undocumented) + body: ErrorResponse; + // (undocumented) + headers: RawHttpHeaders & GetSignRootCertificateDefaultHeaders; + // (undocumented) + status: string; +} + +// @public (undocumented) +export type GetSignRootCertificateParameters = RequestParameters; + +// @public (undocumented) +export function isUnexpected(response: GetCodeSigningStatus200Response | GetCodeSigningStatusLogicalResponse | GetCodeSigningStatusDefaultResponse): response is GetCodeSigningStatusDefaultResponse; + +// @public (undocumented) +export function isUnexpected(response: GetSignRootCertificate200Response | GetSignRootCertificateDefaultResponse): response is GetSignRootCertificateDefaultResponse; + +// @public (undocumented) +export function isUnexpected(response: ListSignEkus200Response | ListSignEkusDefaultResponse): response is ListSignEkusDefaultResponse; + +// @public (undocumented) +export function isUnexpected(response: Sign202Response | SignLogicalResponse | SignDefaultResponse): response is SignDefaultResponse; + +// @public (undocumented) +export interface ListSignEkus { + get(options?: ListSignEkusParameters): StreamableMethod; +} + +// @public +export interface ListSignEkus200Response extends HttpResponse { + // (undocumented) + body: ExtendedKeyUsageListOutput; + // (undocumented) + status: "200"; +} + +// @public (undocumented) +export interface ListSignEkusDefaultHeaders { + "x-ms-error-code"?: string; +} + +// @public (undocumented) +export interface ListSignEkusDefaultResponse extends HttpResponse { + // (undocumented) + body: ErrorResponse; + // (undocumented) + headers: RawHttpHeaders & ListSignEkusDefaultHeaders; + // (undocumented) + status: string; +} + +// @public (undocumented) +export type ListSignEkusParameters = RequestParameters; + +// @public +export interface OperationStatusOutput { + error?: ErrorModel; + id: string; + result?: CodeSignResultOutput; + status: string; +} + +// @public +export function paginate(client: Client, initialResponse: TResponse, options?: PagingOptions): PagedAsyncIterableIterator>; + +// @public +export type PaginateReturn = TResult extends { + body: { + value?: infer TPage; + }; +} ? GetArrayType : Array; + +// @public +export interface PagingOptions { + customGetPage?: GetPage[]>; +} + +// @public +export interface ResourceOperationStatusOutput { + error?: ErrorModel; + id: string; + result?: CodeSignOperationStatusOutput; + status: string; +} + +// @public (undocumented) +export interface Routes { + (path: "/codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}/sign/{operationId}", operationId: string, codeSigningAccountName: string, certificateProfileName: string): GetCodeSigningStatus; + (path: "/codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}/sign/rootcert", codeSigningAccountName: string, certificateProfileName: string): GetSignRootCertificate; + (path: "/codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}/sign/eku", codeSigningAccountName: string, certificateProfileName: string): ListSignEkus; + (path: "/codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}:sign", codeSigningAccountName: string, certificateProfileName: string): Sign; +} + +// @public (undocumented) +export interface Sign { + post(options?: SignParameters): StreamableMethod; +} + +// @public (undocumented) +export interface Sign202Headers { + "operation-location": string; +} + +// @public +export interface Sign202Response extends HttpResponse { + // (undocumented) + body: ResourceOperationStatusOutput; + // (undocumented) + headers: RawHttpHeaders & Sign202Headers; + // (undocumented) + status: "202"; +} + +// @public (undocumented) +export interface SignBodyParam { + // (undocumented) + body?: CodeSigningSubmissionOptions; +} + +// @public (undocumented) +export interface SignDefaultHeaders { + "x-ms-error-code"?: string; +} + +// @public (undocumented) +export interface SignDefaultResponse extends HttpResponse { + // (undocumented) + body: ErrorResponse; + // (undocumented) + headers: RawHttpHeaders & SignDefaultHeaders; + // (undocumented) + status: string; +} + +// @public +export interface SignLogicalResponse extends HttpResponse { + // (undocumented) + body: ResourceOperationStatusOutput; + // (undocumented) + status: "200"; +} + +// @public (undocumented) +export type SignParameters = SignBodyParam & RequestParameters; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/sdk/codesigning/azurecodesigning-rest/src/clientDefinitions.ts b/sdk/codesigning/azurecodesigning-rest/src/clientDefinitions.ts new file mode 100644 index 000000000000..355e650dfa1e --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/clientDefinitions.ts @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + GetCodeSigningStatusParameters, + GetSignRootCertificateParameters, + ListSignEkusParameters, + SignParameters, +} from "./parameters"; +import { + GetCodeSigningStatus200Response, + GetCodeSigningStatusDefaultResponse, + GetSignRootCertificate200Response, + GetSignRootCertificateDefaultResponse, + ListSignEkus200Response, + ListSignEkusDefaultResponse, + Sign202Response, + SignDefaultResponse, +} from "./responses"; +import { Client, StreamableMethod } from "@azure-rest/core-client"; + +export interface GetCodeSigningStatus { + /** This status operation requires that a Sign request has been submitted and the operationId is known. */ + get( + options?: GetCodeSigningStatusParameters + ): StreamableMethod< + GetCodeSigningStatus200Response | GetCodeSigningStatusDefaultResponse + >; +} + +export interface GetSignRootCertificate { + /** The root certificate is generated as part of the initial account creation and it is used to sign the bits for the profile provided. */ + get( + options?: GetSignRootCertificateParameters + ): StreamableMethod< + GetSignRootCertificate200Response | GetSignRootCertificateDefaultResponse + >; +} + +export interface ListSignEkus { + /** The list of extended key usages are used to determine the purpose of the certificate usage as part of the codesigning operation. */ + get( + options?: ListSignEkusParameters + ): StreamableMethod; +} + +export interface Sign { + /** Submit a codesign operation under the created codesign account and profile name provided. */ + post( + options?: SignParameters + ): StreamableMethod; +} + +export interface Routes { + /** Resource for '/codesigningaccounts/\{codeSigningAccountName\}/certificateprofiles/\{certificateProfileName\}/sign/\{operationId\}' has methods for the following verbs: get */ + ( + path: "/codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}/sign/{operationId}", + operationId: string, + codeSigningAccountName: string, + certificateProfileName: string + ): GetCodeSigningStatus; + /** Resource for '/codesigningaccounts/\{codeSigningAccountName\}/certificateprofiles/\{certificateProfileName\}/sign/rootcert' has methods for the following verbs: get */ + ( + path: "/codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}/sign/rootcert", + codeSigningAccountName: string, + certificateProfileName: string + ): GetSignRootCertificate; + /** Resource for '/codesigningaccounts/\{codeSigningAccountName\}/certificateprofiles/\{certificateProfileName\}/sign/eku' has methods for the following verbs: get */ + ( + path: "/codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}/sign/eku", + codeSigningAccountName: string, + certificateProfileName: string + ): ListSignEkus; + /** Resource for '/codesigningaccounts/\{codeSigningAccountName\}/certificateprofiles/\{certificateProfileName\}:sign' has methods for the following verbs: post */ + ( + path: "/codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}:sign", + codeSigningAccountName: string, + certificateProfileName: string + ): Sign; +} + +export type CodeSigningClient = Client & { + path: Routes; +}; diff --git a/sdk/codesigning/azurecodesigning-rest/src/codeSigningClient.ts b/sdk/codesigning/azurecodesigning-rest/src/codeSigningClient.ts new file mode 100644 index 000000000000..03a0e0780284 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/codeSigningClient.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { getClient, ClientOptions } from "@azure-rest/core-client"; +import { logger } from "./logger"; +import { TokenCredential } from "@azure/core-auth"; +import { CodeSigningClient } from "./clientDefinitions"; + +/** + * Initialize a new instance of `CodeSigningClient` + * @param region - The Azure region wherein requests for signing will be sent. + * @param credentials - uniquely identify client credential + * @param options - the parameter for all optional parameters + */ +export default function createClient( + region: string, + credentials: TokenCredential, + options: ClientOptions = {} +): CodeSigningClient { + const baseUrl = options.baseUrl ?? `https://${region}.codesigning.azure.net/`; + options.apiVersion = options.apiVersion ?? "2023-06-15-preview"; + options = { + ...options, + credentials: { + scopes: options.credentials?.scopes ?? [ + "https://codesigning.azure.net/.default", + ], + }, + }; + + const userAgentInfo = `azsdk-js-azure-codesigning-rest/1.0.0-beta.1`; + const userAgentPrefix = + options.userAgentOptions && options.userAgentOptions.userAgentPrefix + ? `${options.userAgentOptions.userAgentPrefix} ${userAgentInfo}` + : `${userAgentInfo}`; + options = { + ...options, + userAgentOptions: { + userAgentPrefix, + }, + loggingOptions: { + logger: options.loggingOptions?.logger ?? logger.info, + }, + }; + + const client = getClient(baseUrl, credentials, options) as CodeSigningClient; + + return client; +} diff --git a/sdk/codesigning/azurecodesigning-rest/src/index.ts b/sdk/codesigning/azurecodesigning-rest/src/index.ts new file mode 100644 index 000000000000..2b8048ad81ed --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/index.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import CodeSigningClient from "./codeSigningClient"; + +export * from "./codeSigningClient"; +export * from "./parameters"; +export * from "./responses"; +export * from "./clientDefinitions"; +export * from "./isUnexpected"; +export * from "./models"; +export * from "./outputModels"; +export * from "./paginateHelper"; +export * from "./pollingHelper"; + +export default CodeSigningClient; diff --git a/sdk/codesigning/azurecodesigning-rest/src/isUnexpected.ts b/sdk/codesigning/azurecodesigning-rest/src/isUnexpected.ts new file mode 100644 index 000000000000..69e95239b29e --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/isUnexpected.ts @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + GetCodeSigningStatus200Response, + GetCodeSigningStatusLogicalResponse, + GetCodeSigningStatusDefaultResponse, + GetSignRootCertificate200Response, + GetSignRootCertificateDefaultResponse, + ListSignEkus200Response, + ListSignEkusDefaultResponse, + Sign202Response, + SignLogicalResponse, + SignDefaultResponse, +} from "./responses"; + +const responseMap: Record = { + "GET /codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}/sign/{operationId}": + ["200"], + "GET /codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}/sign/rootcert": + ["200"], + "GET /codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}/sign/eku": + ["200"], + "POST /codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}:sign": + ["202"], + "GET /codesigningaccounts/{codeSigningAccountName}/certificateprofiles/{certificateProfileName}:sign": + ["200", "202"], +}; + +export function isUnexpected( + response: + | GetCodeSigningStatus200Response + | GetCodeSigningStatusLogicalResponse + | GetCodeSigningStatusDefaultResponse +): response is GetCodeSigningStatusDefaultResponse; +export function isUnexpected( + response: + | GetSignRootCertificate200Response + | GetSignRootCertificateDefaultResponse +): response is GetSignRootCertificateDefaultResponse; +export function isUnexpected( + response: ListSignEkus200Response | ListSignEkusDefaultResponse +): response is ListSignEkusDefaultResponse; +export function isUnexpected( + response: Sign202Response | SignLogicalResponse | SignDefaultResponse +): response is SignDefaultResponse; +export function isUnexpected( + response: + | GetCodeSigningStatus200Response + | GetCodeSigningStatusLogicalResponse + | GetCodeSigningStatusDefaultResponse + | GetSignRootCertificate200Response + | GetSignRootCertificateDefaultResponse + | ListSignEkus200Response + | ListSignEkusDefaultResponse + | Sign202Response + | SignLogicalResponse + | SignDefaultResponse +): response is + | GetCodeSigningStatusDefaultResponse + | GetSignRootCertificateDefaultResponse + | ListSignEkusDefaultResponse + | SignDefaultResponse { + const lroOriginal = response.headers["x-ms-original-url"]; + const url = new URL(lroOriginal ?? response.request.url); + const method = response.request.method; + let pathDetails = responseMap[`${method} ${url.pathname}`]; + if (!pathDetails) { + pathDetails = getParametrizedPathSuccess(method, url.pathname); + } + return !pathDetails.includes(response.status); +} + +function getParametrizedPathSuccess(method: string, path: string): string[] { + const pathParts = path.split("/"); + + // Traverse list to match the longest candidate + // matchedLen: the length of candidate path + // matchedValue: the matched status code array + let matchedLen = -1, + matchedValue: string[] = []; + + // Iterate the responseMap to find a match + for (const [key, value] of Object.entries(responseMap)) { + // Extracting the path from the map key which is in format + // GET /path/foo + if (!key.startsWith(method)) { + continue; + } + const candidatePath = getPathFromMapKey(key); + // Get each part of the url path + const candidateParts = candidatePath.split("/"); + + // track if we have found a match to return the values found. + let found = true; + for ( + let i = candidateParts.length - 1, j = pathParts.length - 1; + i >= 1 && j >= 1; + i--, j-- + ) { + if ( + candidateParts[i]?.startsWith("{") && + candidateParts[i]?.indexOf("}") !== -1 + ) { + const start = candidateParts[i]!.indexOf("}") + 1, + end = candidateParts[i]?.length; + // If the current part of the candidate is a "template" part + // Try to use the suffix of pattern to match the path + // {guid} ==> $ + // {guid}:export ==> :export$ + const isMatched = new RegExp( + `${candidateParts[i]?.slice(start, end)}` + ).test(pathParts[j] || ""); + + if (!isMatched) { + found = false; + break; + } + continue; + } + + // If the candidate part is not a template and + // the parts don't match mark the candidate as not found + // to move on with the next candidate path. + if (candidateParts[i] !== pathParts[j]) { + found = false; + break; + } + } + + // We finished evaluating the current candidate parts + // Update the matched value if and only if we found the longer pattern + if (found && candidatePath.length > matchedLen) { + matchedLen = candidatePath.length; + matchedValue = value; + } + } + + return matchedValue; +} + +function getPathFromMapKey(mapKey: string): string { + const pathStart = mapKey.indexOf("/"); + return mapKey.slice(pathStart); +} diff --git a/sdk/codesigning/azurecodesigning-rest/src/logger.ts b/sdk/codesigning/azurecodesigning-rest/src/logger.ts new file mode 100644 index 000000000000..f90ae0e72a91 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/logger.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { createClientLogger } from "@azure/logger"; +export const logger = createClientLogger("azure-codesigning-rest"); diff --git a/sdk/codesigning/azurecodesigning-rest/src/models.ts b/sdk/codesigning/azurecodesigning-rest/src/models.ts new file mode 100644 index 000000000000..2e81f6c03d31 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/models.ts @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** The codesign request information to be signed by the service. */ +export interface CodeSigningSubmissionOptions { + /** + * The supported signature algorithm identifiers. + * + * Possible values: RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, ES256K + */ + signatureAlgorithm: string; + /** Content digest to codesign. */ + digest: string; + /** List of full file digital signatures. */ + fileHashList?: string[]; + /** List of authenticode digital signatures. */ + authenticodeHashList?: string[]; +} diff --git a/sdk/codesigning/azurecodesigning-rest/src/outputModels.ts b/sdk/codesigning/azurecodesigning-rest/src/outputModels.ts new file mode 100644 index 000000000000..b8e8c38d4fae --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/outputModels.ts @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Paged } from "@azure/core-paging"; +import { ErrorModel } from "@azure-rest/core-client"; + +/** The sign status model. */ +export interface CodeSignResultOutput { + /** Response Id of the codesign operation. */ + operationId: string; + /** Digital signature of the requested content digest. */ + signature?: string; + /** + * Signing certificate corresponding to the private key used to codesign the requested + * digest. + */ + signingCertificate?: string; +} + +/** Provides status details for long running operations. */ +export interface OperationStatusOutput { + /** The unique ID of the operation. */ + id: string; + /** + * The status of the operation + * + * Possible values: InProgress, Succeeded, Failed, Canceled + */ + status: string; + /** Error object that describes the error when status is "Failed". */ + error?: ErrorModel; + /** The result of the operation. */ + result?: CodeSignResultOutput; +} + +/** Extended key usage object identifier that are allowed. */ +export interface ExtendedKeyUsageOutput { + /** An element of ekus. */ + ekus: string[]; +} + +/** Provides status details for long running operations. */ +export interface ResourceOperationStatusOutput { + /** The unique ID of the operation. */ + id: string; + /** + * The status of the operation + * + * Possible values: InProgress, Succeeded, Failed, Canceled + */ + status: string; + /** Error object that describes the error when status is "Failed". */ + error?: ErrorModel; + /** The result of the operation. */ + result?: CodeSignOperationStatusOutput; +} + +/** The codesign operation status response. */ +export interface CodeSignOperationStatusOutput { + /** Unique Id of the operation. */ + id: string; + /** The result of the codesign operation including the signature and signing certificate. */ + signResult?: CodeSignResultOutput; +} + +/** Paged collection of ExtendedKeyUsage items */ +export type ExtendedKeyUsageListOutput = Paged; diff --git a/sdk/codesigning/azurecodesigning-rest/src/paginateHelper.ts b/sdk/codesigning/azurecodesigning-rest/src/paginateHelper.ts new file mode 100644 index 000000000000..1c9af35b1efd --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/paginateHelper.ts @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + getPagedAsyncIterator, + PagedAsyncIterableIterator, + PagedResult, +} from "@azure/core-paging"; +import { + Client, + createRestError, + PathUncheckedResponse, +} from "@azure-rest/core-client"; + +/** + * Helper type to extract the type of an array + */ +export type GetArrayType = T extends Array ? TData : never; + +/** + * The type of a custom function that defines how to get a page and a link to the next one if any. + */ +export type GetPage = ( + pageLink: string, + maxPageSize?: number +) => Promise<{ + page: TPage; + nextPageLink?: string; +}>; + +/** + * Options for the paging helper + */ +export interface PagingOptions { + /** + * Custom function to extract pagination details for crating the PagedAsyncIterableIterator + */ + customGetPage?: GetPage[]>; +} + +/** + * Helper type to infer the Type of the paged elements from the response type + * This type is generated based on the swagger information for x-ms-pageable + * specifically on the itemName property which indicates the property of the response + * where the page items are found. The default value is `value`. + * This type will allow us to provide strongly typed Iterator based on the response we get as second parameter + */ +export type PaginateReturn = TResult extends { + body: { value?: infer TPage }; +} + ? GetArrayType + : Array; + +/** + * Helper to paginate results from an initial response that follows the specification of Autorest `x-ms-pageable` extension + * @param client - Client to use for sending the next page requests + * @param initialResponse - Initial response containing the nextLink and current page of elements + * @param customGetPage - Optional - Function to define how to extract the page and next link to be used to paginate the results + * @returns - PagedAsyncIterableIterator to iterate the elements + */ +export function paginate( + client: Client, + initialResponse: TResponse, + options: PagingOptions = {} +): PagedAsyncIterableIterator> { + // Extract element type from initial response + type TElement = PaginateReturn; + let firstRun = true; + const itemName = "value"; + const nextLinkName = "nextLink"; + const { customGetPage } = options; + const pagedResult: PagedResult = { + firstPageLink: "", + getPage: + typeof customGetPage === "function" + ? customGetPage + : async (pageLink: string) => { + const result = firstRun + ? initialResponse + : await client.pathUnchecked(pageLink).get(); + firstRun = false; + checkPagingRequest(result); + const nextLink = getNextLink(result.body, nextLinkName); + const values = getElements(result.body, itemName); + return { + page: values, + nextPageLink: nextLink, + }; + }, + }; + + return getPagedAsyncIterator(pagedResult); +} + +/** + * Gets for the value of nextLink in the body + */ +function getNextLink(body: unknown, nextLinkName?: string): string | undefined { + if (!nextLinkName) { + return undefined; + } + + const nextLink = (body as Record)[nextLinkName]; + + if (typeof nextLink !== "string" && typeof nextLink !== "undefined") { + throw new Error( + `Body Property ${nextLinkName} should be a string or undefined` + ); + } + + return nextLink; +} + +/** + * Gets the elements of the current request in the body. + */ +function getElements(body: unknown, itemName: string): T[] { + const value = (body as Record)[itemName] as T[]; + + // value has to be an array according to the x-ms-pageable extension. + // The fact that this must be an array is used above to calculate the + // type of elements in the page in PaginateReturn + if (!Array.isArray(value)) { + throw new Error( + `Couldn't paginate response\n Body doesn't contain an array property with name: ${itemName}` + ); + } + + return value ?? []; +} + +/** + * Checks if a request failed + */ +function checkPagingRequest(response: PathUncheckedResponse): void { + const Http2xxStatusCodes = [ + "200", + "201", + "202", + "203", + "204", + "205", + "206", + "207", + "208", + "226", + ]; + if (!Http2xxStatusCodes.includes(response.status)) { + throw createRestError( + `Pagination failed with unexpected statusCode ${response.status}`, + response + ); + } +} diff --git a/sdk/codesigning/azurecodesigning-rest/src/parameters.ts b/sdk/codesigning/azurecodesigning-rest/src/parameters.ts new file mode 100644 index 000000000000..a0c45e683f24 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/parameters.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { RequestParameters } from "@azure-rest/core-client"; +import { CodeSigningSubmissionOptions } from "./models"; + +export type GetCodeSigningStatusParameters = RequestParameters; +export type GetSignRootCertificateParameters = RequestParameters; +export type ListSignEkusParameters = RequestParameters; + +export interface SignBodyParam { + body?: CodeSigningSubmissionOptions; +} + +export type SignParameters = SignBodyParam & RequestParameters; diff --git a/sdk/codesigning/azurecodesigning-rest/src/pollingHelper.ts b/sdk/codesigning/azurecodesigning-rest/src/pollingHelper.ts new file mode 100644 index 000000000000..dbfe4ea5b5c0 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/pollingHelper.ts @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Client, HttpResponse } from "@azure-rest/core-client"; +import { + CreateHttpPollerOptions, + LongRunningOperation, + LroResponse, + OperationState, + SimplePollerLike, + createHttpPoller, +} from "@azure/core-lro"; +import { + GetCodeSigningStatus200Response, + GetCodeSigningStatusDefaultResponse, + GetCodeSigningStatusLogicalResponse, + Sign202Response, + SignDefaultResponse, + SignLogicalResponse, +} from "./responses"; +/** + * Helper function that builds a Poller object to help polling a long running operation. + * @param client - Client to use for sending the request to get additional pages. + * @param initialResponse - The initial response. + * @param options - Options to set a resume state or custom polling interval. + * @returns - A poller object to poll for operation state updates and eventually get the final response. + */ +export async function getLongRunningPoller< + TResult extends SignLogicalResponse | SignDefaultResponse +>( + client: Client, + initialResponse: Sign202Response | SignDefaultResponse, + options?: CreateHttpPollerOptions> +): Promise, TResult>>; +export async function getLongRunningPoller< + TResult extends + | GetCodeSigningStatusLogicalResponse + | GetCodeSigningStatusDefaultResponse +>( + client: Client, + initialResponse: + | GetCodeSigningStatus200Response + | GetCodeSigningStatusDefaultResponse, + options?: CreateHttpPollerOptions> +): Promise, TResult>>; +export async function getLongRunningPoller( + client: Client, + initialResponse: TResult, + options: CreateHttpPollerOptions> = {} +): Promise, TResult>> { + const poller: LongRunningOperation = { + requestMethod: initialResponse.request.method, + requestPath: initialResponse.request.url, + sendInitialRequest: async () => { + // In the case of Rest Clients we are building the LRO poller object from a response that's the reason + // we are not triggering the initial request here, just extracting the information from the + // response we were provided. + return getLroResponse(initialResponse); + }, + sendPollRequest: async (path) => { + // This is the callback that is going to be called to poll the service + // to get the latest status. We use the client provided and the polling path + // which is an opaque URL provided by caller, the service sends this in one of the following headers: operation-location, azure-asyncoperation or location + // depending on the lro pattern that the service implements. If non is provided we default to the initial path. + const response = await client + .pathUnchecked(path ?? initialResponse.request.url) + .get(); + const lroResponse = getLroResponse(response as TResult); + lroResponse.rawResponse.headers["x-ms-original-url"] = + initialResponse.request.url; + return lroResponse; + }, + }; + + options.resolveOnUnsuccessful = options.resolveOnUnsuccessful ?? true; + return await createHttpPoller(poller, options); +} + +/** + * Converts a Rest Client response to a response that the LRO implementation understands + * @param response - a rest client http response + * @returns - An LRO response that the LRO implementation understands + */ +function getLroResponse( + response: TResult +): LroResponse { + if (Number.isNaN(response.status)) { + throw new TypeError( + `Status code of the response is not a number. Value: ${response.status}` + ); + } + + return { + flatResponse: response, + rawResponse: { + ...response, + statusCode: Number.parseInt(response.status), + body: response.body, + }, + }; +} diff --git a/sdk/codesigning/azurecodesigning-rest/src/responses.ts b/sdk/codesigning/azurecodesigning-rest/src/responses.ts new file mode 100644 index 000000000000..5b886b911df5 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/src/responses.ts @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { RawHttpHeaders } from "@azure/core-rest-pipeline"; +import { HttpResponse, ErrorResponse } from "@azure-rest/core-client"; +import { + OperationStatusOutput, + ExtendedKeyUsageListOutput, + ResourceOperationStatusOutput, +} from "./outputModels"; + +/** The request has succeeded. */ +export interface GetCodeSigningStatus200Response extends HttpResponse { + status: "200"; + body: OperationStatusOutput; +} + +export interface GetCodeSigningStatusDefaultHeaders { + /** String error code indicating what went wrong. */ + "x-ms-error-code"?: string; +} + +export interface GetCodeSigningStatusDefaultResponse extends HttpResponse { + status: string; + body: ErrorResponse; + headers: RawHttpHeaders & GetCodeSigningStatusDefaultHeaders; +} + +/** The final response for long-running getCodeSigningStatus operation */ +export interface GetCodeSigningStatusLogicalResponse extends HttpResponse { + status: "200"; + body: OperationStatusOutput; +} + +/** The request has succeeded. */ +export interface GetSignRootCertificate200Response extends HttpResponse { + status: "200"; + /** Value may contain any sequence of octets */ + body: Uint8Array; +} + +export interface GetSignRootCertificateDefaultHeaders { + /** String error code indicating what went wrong. */ + "x-ms-error-code"?: string; +} + +export interface GetSignRootCertificateDefaultResponse extends HttpResponse { + status: string; + body: ErrorResponse; + headers: RawHttpHeaders & GetSignRootCertificateDefaultHeaders; +} + +/** The request has succeeded. */ +export interface ListSignEkus200Response extends HttpResponse { + status: "200"; + body: ExtendedKeyUsageListOutput; +} + +export interface ListSignEkusDefaultHeaders { + /** String error code indicating what went wrong. */ + "x-ms-error-code"?: string; +} + +export interface ListSignEkusDefaultResponse extends HttpResponse { + status: string; + body: ErrorResponse; + headers: RawHttpHeaders & ListSignEkusDefaultHeaders; +} + +export interface Sign202Headers { + /** The location for monitoring the operation state. */ + "operation-location": string; +} + +/** The request has been accepted for processing, but processing has not yet completed. */ +export interface Sign202Response extends HttpResponse { + status: "202"; + body: ResourceOperationStatusOutput; + headers: RawHttpHeaders & Sign202Headers; +} + +export interface SignDefaultHeaders { + /** String error code indicating what went wrong. */ + "x-ms-error-code"?: string; +} + +export interface SignDefaultResponse extends HttpResponse { + status: string; + body: ErrorResponse; + headers: RawHttpHeaders & SignDefaultHeaders; +} + +/** The final response for long-running sign operation */ +export interface SignLogicalResponse extends HttpResponse { + status: "200"; + body: ResourceOperationStatusOutput; +} diff --git a/sdk/codesigning/azurecodesigning-rest/test/public/sampleTest.spec.ts b/sdk/codesigning/azurecodesigning-rest/test/public/sampleTest.spec.ts new file mode 100644 index 000000000000..bce68e428645 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/test/public/sampleTest.spec.ts @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Recorder } from "@azure-tools/test-recorder"; +import { assert } from "chai"; +import { createRecorder } from "./utils/recordedClient"; +import { Context } from "mocha"; + +describe("My test", () => { + let recorder: Recorder; + + beforeEach(async function (this: Context) { + recorder = await createRecorder(this); + }); + + afterEach(async function () { + await recorder.stop(); + }); + + it("sample test", async function () { + assert.equal(1, 1); + }); +}); diff --git a/sdk/codesigning/azurecodesigning-rest/test/public/utils/env.browser.ts b/sdk/codesigning/azurecodesigning-rest/test/public/utils/env.browser.ts new file mode 100644 index 000000000000..fd2aca680c7b --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/test/public/utils/env.browser.ts @@ -0,0 +1,2 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. diff --git a/sdk/codesigning/azurecodesigning-rest/test/public/utils/env.ts b/sdk/codesigning/azurecodesigning-rest/test/public/utils/env.ts new file mode 100644 index 000000000000..0e06855b73ae --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/test/public/utils/env.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as dotenv from "dotenv"; + +dotenv.config(); diff --git a/sdk/codesigning/azurecodesigning-rest/test/public/utils/recordedClient.ts b/sdk/codesigning/azurecodesigning-rest/test/public/utils/recordedClient.ts new file mode 100644 index 000000000000..6cc58bc15e11 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/test/public/utils/recordedClient.ts @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Context } from "mocha"; +import { Recorder, RecorderStartOptions } from "@azure-tools/test-recorder"; +import "./env"; + +const envSetupForPlayback: Record = { + ENDPOINT: "https://endpoint", + AZURE_CLIENT_ID: "azure_client_id", + AZURE_CLIENT_SECRET: "azure_client_secret", + AZURE_TENANT_ID: "88888888-8888-8888-8888-888888888888", + SUBSCRIPTION_ID: "azure_subscription_id", +}; + +const recorderEnvSetup: RecorderStartOptions = { + envSetupForPlayback, +}; + +/** + * creates the recorder and reads the environment variables from the `.env` file. + * Should be called first in the test suite to make sure environment variables are + * read before they are being used. + */ +export async function createRecorder(context: Context): Promise { + const recorder = new Recorder(context.currentTest); + await recorder.start(recorderEnvSetup); + return recorder; +} diff --git a/sdk/codesigning/azurecodesigning-rest/tsconfig.json b/sdk/codesigning/azurecodesigning-rest/tsconfig.json new file mode 100644 index 000000000000..d5bf593423c9 --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.package", + "compilerOptions": { + "outDir": "./dist-esm", + "declarationDir": "./types" + }, + "include": [ + "src/**/*.ts", + "./test/**/*.ts" + ] +} \ No newline at end of file diff --git a/sdk/codesigning/azurecodesigning-rest/tsp-location.yaml b/sdk/codesigning/azurecodesigning-rest/tsp-location.yaml new file mode 100644 index 000000000000..f70c0bccc3ff --- /dev/null +++ b/sdk/codesigning/azurecodesigning-rest/tsp-location.yaml @@ -0,0 +1,5 @@ +directory: specification/codesigning/CodeSigning +repo: Azure/azure-rest-api-specs +additionalDirectories: [] +commit: 80c5b33a4334eed1df2547e775e187b9e58d2a19 + diff --git a/sdk/codesigning/ci.yml b/sdk/codesigning/ci.yml new file mode 100644 index 000000000000..11504489c66d --- /dev/null +++ b/sdk/codesigning/ci.yml @@ -0,0 +1,33 @@ +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + - feature/* + - release/* + - hotfix/* + exclude: + - feature/v4 + paths: + include: + - sdk/codesigning/ +pr: + branches: + include: + - main + - feature/* + - release/* + - hotfix/* + exclude: + - feature/v4 + paths: + include: + - sdk/codesigning/ +extends: + template: /eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: codesigning + Artifacts: + - name: azure-rest-azure-codesigning-rest + safeName: azurerestazurecodesigningrest