From 161add8c6d5c3d6e74b5dcde4a3b6a4e41cc57a2 Mon Sep 17 00:00:00 2001 From: catalinaperalta Date: Tue, 4 Jun 2024 16:49:55 -0700 Subject: [PATCH 1/7] Add tsp-client docs section (#806) Add a section with information about `tsp-client`. --------- Co-authored-by: Catalina Peralta --- cspell.yaml | 2 + .../DataPlane Generation - DPG/00howtogen.mdx | 2 + .../Generating with tsp-client/tsp_client.md | 194 ++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 docs/howtos/Generating with tsp-client/tsp_client.md diff --git a/cspell.yaml b/cspell.yaml index 90b1d8e537..1ad02e3cfd 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -73,3 +73,5 @@ words: - tcgc - userrp - WINDOWSVMIMAGE + - contosowidgetmanager + - locationyaml diff --git a/docs/howtos/DataPlane Generation - DPG/00howtogen.mdx b/docs/howtos/DataPlane Generation - DPG/00howtogen.mdx index b5763c64b3..9871d15309 100644 --- a/docs/howtos/DataPlane Generation - DPG/00howtogen.mdx +++ b/docs/howtos/DataPlane Generation - DPG/00howtogen.mdx @@ -53,3 +53,5 @@ options: "@azure-tools/typespec-python": package-name: azure-service-template ``` + +Several language repositories support the `tsp-client` tool to facilitate generating client libraries. For more information on the tool, see [Getting started with `tsp-client`](<./../Generating with tsp-client/tsp_client.md>). diff --git a/docs/howtos/Generating with tsp-client/tsp_client.md b/docs/howtos/Generating with tsp-client/tsp_client.md new file mode 100644 index 0000000000..574a6a20bf --- /dev/null +++ b/docs/howtos/Generating with tsp-client/tsp_client.md @@ -0,0 +1,194 @@ +# Getting started with `tsp-client` + +:::info +**Short link:** [aka.ms/azsdk/tsp-client](https://aka.ms/azsdk/tsp-client) +::: + +`tsp-client` is a simple command line tool to facilitate generating client libraries from TypeSpec. + +## Installation + +``` +npm install -g @azure-tools/typespec-client-generator-cli +``` + +## Usage + +``` +tsp-client [options] +``` + +## Commands + +Use one of the supported commands to get started generating clients from a TypeSpec project. +This tool will default to using your current working directory to generate clients in and will +use it to look for relevant configuration files. To specify a different output directory, use +the `-o` or `--output-dir` option. + +### init + +Initialize the client library directory using a tspconfig.yaml. When running this command pass in a path to a local or remote tspconfig.yaml with the `-c` or `--tsp-config` flag. + +The `init` command generates a directory structure following the standard pattern used across Azure SDK language repositories, creates a [tsp-location.yaml](#tsp-locationyaml) file to control generation, and performs an initial generation of the client library. If you want to skip client library generation, then pass the `--skip-sync-and-generate` flag. + +:::warning +This command should be run from the root of the repository. Example repository root: `azure-sdk-for-python/` +::: + +Example: + +``` +azure-sdk-for-python> tsp-client init -c https://github.com/Azure/azure-rest-api-specs/blob/431eb865a581da2cd7b9e953ae52cb146f31c2a6/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml +``` + +### update + +The `update` command will look for a [tsp-location.yaml](#tsp-locationyaml) file in your current directory to sync a TypeSpec project and generate a client library. The update flow calls the `sync` and `generate` commands internally, so if you need to separate these steps, use the `sync` and `generate` commands separately instead. + +Example: + +``` +azure-sdk-for-python/sdk/contosowidgetmanager/azure-contoso-widgetmanager> tsp-client update +``` + +### sync + +Sync a TypeSpec project with the parameters specified in tsp-location.yaml. + +By default the `sync` command will look for a tsp-location.yaml to get the project details and sync them to a temporary directory called `TempTypeSpecFiles`. Alternately, you can pass in the `--local-spec-repo` flag with the path to your local TypeSpec project to pull those files into your temporary directory. + +Example: + +``` +azure-sdk-for-python/sdk/contosowidgetmanager/azure-contoso-widgetmanager> tsp-client sync +``` + +### generate + +Generate a client library from a TypeSpec project. The `generate` command should be run after the `sync` command. `generate` relies on the existence of the `TempTypeSpecFiles` directory created by the `sync` command and on an `emitter-package.json` file checked into your repository at the following path: `/eng/emitter-package.json`. The `emitter-package.json` file is used to install project dependencies and get the appropriate emitter package. + +Example: + +``` +azure-sdk-for-python/sdk/contosowidgetmanager/azure-contoso-widgetmanager> tsp-client generate +``` + +### convert + +Convert an existing swagger specification to a TypeSpec project. This command should only be run once to get started working on a TypeSpec project. TypeSpec projects will need to be optimized manually and fully reviewed after conversion. When using this command a path or url to a swagger README file is required through the `--swagger-readme` flag. + +Example: + +``` +azure-rest-api-specs/specification/contosowidgetmanager> tsp-client convert --swagger-readme ./data-plane/readme.md -o ./Contoso.WidgetManager +``` + +## Options + +``` + --arm Convert ARM swagger specification to TypeSpec [boolean] + -c, --tsp-config The tspconfig.yaml file to use [string] + --commit Commit to be used for project init or update [string] + -d, --debug Enable debug logging [boolean] + --emitter-options The options to pass to the emitter [string] + --generate-lock-file Generate a lock file under the eng/ directory from + an existing emitter-package.json [boolean] + -h, --help Show help [boolean] + --local-spec-repo Path to local repository with the TypeSpec project [string] + --save-inputs Don't clean up the temp directory after generation [boolean] + --skip-sync-and-generate Skip sync and generate during project init [boolean] + --swagger-readme Path or url to swagger readme file [string] + -o, --output-dir Specify an alternate output directory for the + generated files. Default is your current directory [string] + --repo Repository where the project is defined for init + or update [string] + -v, --version Show version number [boolean] +``` + +## Important concepts + +### Per project setup + +Each project will need to have a configuration file called tsp-location.yaml that will tell the tool where to find the TypeSpec project. + +#### tsp-location.yaml + +This file is created through the `tsp-client init` command or you can manually create it under the project directory to run other commands supported by this tool. + +:::info +This file should live under the project directory for each service. +::: + +The file has the following properties: + +| Property | Description | IsRequired | +| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | +| directory | The top level directory where the main.tsp for the service lives. This should be relative to the spec repo root such as `specification/cognitiveservices/OpenAI.Inference` | true | +| additionalDirectories | Sometimes a typespec file will use a relative import that might not be under the main directory. In this case a single `directory` will not be enough to pull down all necessary files. To support this you can specify additional directories as a list to sync so that all needed files are synced. | false: default = null | +| commit | The commit sha for the version of the typespec files you want to generate off of. This allows us to have idempotence on generation until we opt into pointing at a later version. | true | +| repo | The repo this spec lives in. This should be either `Azure/azure-rest-api-specs` or `Azure/azure-rest-api-specs-pr`. | true | + +Example: + +```yml +directory: specification/contosowidgetmanager/Contoso.WidgetManager +commit: 431eb865a581da2cd7b9e953ae52cb146f31c2a6 +repo: Azure/azure-rest-api-specs +additionalDirectories: + - specification/contosowidgetmanager/Contoso.WidgetManager.Shared/ +``` + +## Per repository set up + +Each repository that intends to support `tsp-client` for generating and updating client libraries will need to set up an `emitter-package.json` file under the `eng/` directory at the root of the repository. Client libraries generated with this tool will be outputted based on the information in the tspconfig.yaml file of the TypeSpec specification. The service directory is specified through the `parameters.service-dir.default` parameter in tspconfig.yaml, additionally the `package-dir` option for the specific emitter is appended to the end of the path. + +See the following example of a valid tspconfig.yaml file: https://github.com/Azure/azure-rest-api-specs/blob/main/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml + +Using the tspconfig.yaml linked above, by default, the client libraries will be generated in the following directory for C#: `/sdk/contosowidgetmanager/Azure.Template.Contoso/`. + +### Required set up + +Please note that these requirements apply on the repository where the client library is going to be generated. Repo owners should make sure to follow these requirements. Users working within a repository that already accepts this tool can refer to the [Usage](#usage) section. + +- Add an emitter-package.json to the repo following this [configuration](#emitter-packagejson). +- Add the [TempTypeSpecFiles](#TempTypeSpecFiles) directory to the .gitignore file for your repository. + +### TempTypeSpecFiles + +This tool creates a `TempTypeSpecFiles` directory when syncing a TypeSpec project to your local repository. This temporary folder will contain a copy of the TypeSpec project specified by the parameters set in the tsp-location.yaml file. If you pass the `--save-inputs` flag to the commandline tool, this directory will not be deleted. Repositories should add an entry in the .gitignore so that none of these files are accidentally checked in if `--save-inputs` flag is passed in. + +```text +# .gitignore file +TempTypeSpecFiles/ +``` + +### emitter-package.json (Required) + +`emitter-package.json` will be used the same as a `package.json` file. If the is no `emitter-package-lock.json` file, the tool will run `npm install` on the contents of `emitter-package.json`. This file allows each repository to pin the version of their emitter and other dependencies to be used when generating client libraries. +The file should be checked into this location `/eng/emitter-package.json` + +Example: + +```json +{ + "main": "dist/src/index.js", + "dependencies": { + "@azure-tools/typespec-python": "0.21.0" + } +} +``` + +:::note +tsp compile currently requires the "main" line to be there. +::: + +This file replaces the package.json checked into the `azure-rest-api-spec` repository. + +### emitter-package-lock.json (Optional) + +`emitter-package-lock.json` will be used the same as a `package-lock.json`. The tool will run a clean npm installation before generating client libraries. This file allows consistent dependency trees and allows each repository to control their dependency installation. +The file should be checked into this location: `/eng/emitter-package-lock.json` + +:::warning +The tool will run `npm ci` to install dependencies, so ensure that the `emitter-package-lock.json` and `emitter-package.json` files both exist and are in sync with each other. +::: From ab7a066d4ac0ae23a40f9ff8f4b6037559bda34c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 16:34:32 +0000 Subject: [PATCH 2/7] Bump core from `40df1ec` to `24f81bf` (#958) Bumps [core](https://github.com/microsoft/typespec) from `40df1ec` to `24f81bf`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Timothee Guerin --- core | 2 +- docs/emitters/typespec-azure-rulesets/reference/linter.md | 3 ++- packages/typespec-azure-rulesets/README.md | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core b/core index 40df1ec9a3..24f81bf395 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 40df1ec9a307a6bde664b2ad08c4f823d6956cc0 +Subproject commit 24f81bf39523e296a4ae8406cc919581740c3a2a diff --git a/docs/emitters/typespec-azure-rulesets/reference/linter.md b/docs/emitters/typespec-azure-rulesets/reference/linter.md index 840cb50456..8b4774ab0c 100644 --- a/docs/emitters/typespec-azure-rulesets/reference/linter.md +++ b/docs/emitters/typespec-azure-rulesets/reference/linter.md @@ -13,13 +13,14 @@ Add the following in `tspconfig.yaml`: ```yaml linter: extends: - - "@azure-tools/typespec-azure-rulesets/data-plane" + - "@azure-tools/typespec-azure-rulesets/all" ``` ## RuleSets Available ruleSets: +- `@azure-tools/typespec-azure-rulesets/all` - `@azure-tools/typespec-azure-rulesets/data-plane` - `@azure-tools/typespec-azure-rulesets/resource-manager` diff --git a/packages/typespec-azure-rulesets/README.md b/packages/typespec-azure-rulesets/README.md index 79d6df4ec8..c80e0d0ab3 100644 --- a/packages/typespec-azure-rulesets/README.md +++ b/packages/typespec-azure-rulesets/README.md @@ -17,13 +17,14 @@ Add the following in `tspconfig.yaml`: ```yaml linter: extends: - - "@azure-tools/typespec-azure-rulesets/data-plane" + - "@azure-tools/typespec-azure-rulesets/all" ``` ### RuleSets Available ruleSets: +- `@azure-tools/typespec-azure-rulesets/all` - `@azure-tools/typespec-azure-rulesets/data-plane` - `@azure-tools/typespec-azure-rulesets/resource-manager` From ecff57768997792a387d86517ad01e73355612bf Mon Sep 17 00:00:00 2001 From: Chenjie Shi Date: Fri, 7 Jun 2024 00:57:15 +0800 Subject: [PATCH 3/7] [tcgc] refine logic of core model filtering (#962) previous logic has two problems: 1. it could not handle any models found in model properties, union variants and so on 2. it will include some of the model used in templated model but not used in real operation new logic: 1. do not do any filtering when traversal 2. do the filtering at final stage of `getAllModels` 3. set usage to `None` to filter out model not used --- .../fix_core_model_filter-2024-5-6-15-47-3.md | 7 + .../src/internal-utils.ts | 4 +- .../src/types.ts | 132 +++++++----------- .../test/public-utils.test.ts | 7 +- 4 files changed, 60 insertions(+), 90 deletions(-) create mode 100644 .chronus/changes/fix_core_model_filter-2024-5-6-15-47-3.md diff --git a/.chronus/changes/fix_core_model_filter-2024-5-6-15-47-3.md b/.chronus/changes/fix_core_model_filter-2024-5-6-15-47-3.md new file mode 100644 index 0000000000..ed5b034d63 --- /dev/null +++ b/.chronus/changes/fix_core_model_filter-2024-5-6-15-47-3.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +refine logic of core model filtering \ No newline at end of file diff --git a/packages/typespec-client-generator-core/src/internal-utils.ts b/packages/typespec-client-generator-core/src/internal-utils.ts index 70c1d96ee2..9bf11d8c70 100644 --- a/packages/typespec-client-generator-core/src/internal-utils.ts +++ b/packages/typespec-client-generator-core/src/internal-utils.ts @@ -265,13 +265,13 @@ export function intOrFloat(value: number): "int32" | "float32" { } /** - * Whether a model is an Azure.Core model or not + * Whether a model or enum or union as enum is in Azure.Core[.Foundations] namespace * @param t * @returns */ export function isAzureCoreModel(t: Type): boolean { return ( - t.kind === "Model" && + (t.kind === "Model" || t.kind === "Enum" || t.kind === "Union") && t.namespace !== undefined && ["Azure.Core", "Azure.Core.Foundations"].includes(getNamespaceFullName(t.namespace)) ); diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index 83d0615710..64dbfe9e6d 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -1136,34 +1136,6 @@ function updateModelsMap( } } -function checkAndGetClientType( - context: TCGCContext, - type: Type, - operation?: Operation -): [SdkType[], readonly Diagnostic[]] { - const diagnostics = createDiagnosticCollector(); - const retval: SdkType[] = []; - if (type.kind === "Model") { - if (isExclude(context, type)) return diagnostics.wrap([]); // eslint-disable-line deprecation/deprecation - const effectivePayloadType = getEffectivePayloadType(context, type); - if (context.filterOutCoreModels && isAzureCoreModel(effectivePayloadType)) { - if (effectivePayloadType.templateMapper && effectivePayloadType.name) { - effectivePayloadType.templateMapper.args - .filter((arg): arg is Type => "kind" in arg) - .filter((arg) => arg.kind === "Model" && arg.name) - .forEach((arg) => { - retval.push(...diagnostics.pipe(checkAndGetClientType(context, arg, operation))); - }); - return diagnostics.wrap(retval); - } else { - return diagnostics.wrap([]); - } - } - } - const clientType = diagnostics.pipe(getClientTypeWithDiagnostics(context, type, operation)); - return diagnostics.wrap([clientType]); -} - interface ModelUsageOptions { seenModelNames?: Set; propagation?: boolean; @@ -1256,69 +1228,58 @@ function updateTypesFromOperation( const generateConvenient = shouldGenerateConvenient(context, operation); for (const param of operation.parameters.properties.values()) { if (isNeverOrVoidType(param.type)) continue; - const paramTypes = diagnostics.pipe(checkAndGetClientType(context, param.type, operation)); + const sdkType = diagnostics.pipe(getClientTypeWithDiagnostics(context, param.type, operation)); if (generateConvenient) { - paramTypes.forEach((paramType) => { - updateUsageOfModel(context, UsageFlags.Input, paramType); - }); + updateUsageOfModel(context, UsageFlags.Input, sdkType); } } for (const param of httpOperation.parameters.parameters) { if (isNeverOrVoidType(param.param.type)) continue; - const paramTypes = diagnostics.pipe( - checkAndGetClientType(context, param.param.type, operation) + const sdkType = diagnostics.pipe( + getClientTypeWithDiagnostics(context, param.param.type, operation) ); if (generateConvenient) { - paramTypes.forEach((paramType) => { - updateUsageOfModel(context, UsageFlags.Input, paramType); - }); + updateUsageOfModel(context, UsageFlags.Input, sdkType); } } const httpBody = httpOperation.parameters.body; if (httpBody && !isNeverOrVoidType(httpBody.type)) { - const bodies = diagnostics.pipe(checkAndGetClientType(context, httpBody.type, operation)); + const sdkType = diagnostics.pipe( + getClientTypeWithDiagnostics(context, httpBody.type, operation) + ); if (generateConvenient) { - bodies.forEach((body) => { - // spread body model should be none usage - if (body.kind === "model" && body.isGeneratedName) return; - updateUsageOfModel(context, UsageFlags.Input, body); - }); + // spread body model should be none usage + if (sdkType.kind !== "model" || !sdkType.isGeneratedName) { + updateUsageOfModel(context, UsageFlags.Input, sdkType); + } if (httpBody.contentTypes.includes("application/merge-patch+json")) { - bodies.forEach((body) => { - updateUsageOfModel(context, UsageFlags.JsonMergePatch, body); - }); + updateUsageOfModel(context, UsageFlags.JsonMergePatch, sdkType); } } if (isMultipartFormData(context, httpBody.type, operation)) { - bodies.forEach((body) => { - updateUsageOfModel(context, UsageFlags.MultipartFormData, body, { - propagation: false, - }); + updateUsageOfModel(context, UsageFlags.MultipartFormData, sdkType, { + propagation: false, }); } } for (const response of httpOperation.responses) { for (const innerResponse of response.responses) { if (innerResponse.body?.type && !isNeverOrVoidType(innerResponse.body.type)) { - const responseBodies = diagnostics.pipe( - checkAndGetClientType(context, innerResponse.body.type, operation) + const sdkType = diagnostics.pipe( + getClientTypeWithDiagnostics(context, innerResponse.body.type, operation) ); if (generateConvenient) { - responseBodies.forEach((responseBody) => { - updateUsageOfModel(context, UsageFlags.Output, responseBody); - }); + updateUsageOfModel(context, UsageFlags.Output, sdkType); } } if (innerResponse.headers) { for (const header of Object.values(innerResponse.headers)) { if (isNeverOrVoidType(header.type)) continue; - const headerTypes = diagnostics.pipe( - checkAndGetClientType(context, header.type, operation) + const sdkType = diagnostics.pipe( + getClientTypeWithDiagnostics(context, header.type, operation) ); if (generateConvenient) { - headerTypes.forEach((headerType) => { - updateUsageOfModel(context, UsageFlags.Output, headerType); - }); + updateUsageOfModel(context, UsageFlags.Output, sdkType); } } } @@ -1327,22 +1288,18 @@ function updateTypesFromOperation( const lroMetaData = getLroMetadata(program, operation); if (lroMetaData && generateConvenient) { if (lroMetaData.finalResult !== undefined && lroMetaData.finalResult !== "void") { - const finalResults = diagnostics.pipe( - checkAndGetClientType(context, lroMetaData.finalResult, operation) + const sdkType = diagnostics.pipe( + getClientTypeWithDiagnostics(context, lroMetaData.finalResult, operation) ); - finalResults.forEach((finalResult) => { - updateUsageOfModel(context, UsageFlags.Output, finalResult); - }); + updateUsageOfModel(context, UsageFlags.Output, sdkType); if (!context.arm) { // TODO: currently skipping adding of envelopeResult due to arm error // https://github.com/Azure/typespec-azure/issues/311 - const envelopeResults = diagnostics.pipe( - checkAndGetClientType(context, lroMetaData.envelopeResult, operation) + const sdkType = diagnostics.pipe( + getClientTypeWithDiagnostics(context, lroMetaData.envelopeResult, operation) ); - envelopeResults.forEach((envelopeResult) => { - updateUsageOfModel(context, UsageFlags.Output, envelopeResult); - }); + updateUsageOfModel(context, UsageFlags.Output, sdkType); } } } @@ -1394,18 +1351,12 @@ function handleServiceOrphanType(context: TCGCContext, type: Model | Enum | Unio const diagnostics = createDiagnosticCollector(); // eslint-disable-next-line deprecation/deprecation if (type.kind === "Model" && isInclude(context, type)) { - const sdkModels = diagnostics.pipe(checkAndGetClientType(context, type)); - sdkModels.forEach((sdkModel) => { - updateUsageOfModel(context, UsageFlags.Input | UsageFlags.Output, sdkModel); - }); + const sdkType = diagnostics.pipe(getClientTypeWithDiagnostics(context, type)); + updateUsageOfModel(context, UsageFlags.Input | UsageFlags.Output, sdkType); } if (getAccessOverride(context, type) !== undefined) { - const sdkModels = diagnostics.pipe(checkAndGetClientType(context, type)); - sdkModels - .filter((sdkModel) => ["model", "enum", "array", "dict", "union"].includes(sdkModel.kind)) - .forEach((sdkModel) => { - updateUsageOfModel(context, UsageFlags.None, sdkModel); - }); + const sdkType = diagnostics.pipe(getClientTypeWithDiagnostics(context, type)); + updateUsageOfModel(context, UsageFlags.None, sdkType); } } @@ -1440,6 +1391,19 @@ function modelChecks(context: TCGCContext): [void, readonly Diagnostic[]] { return verifyNoConflictingMultipartModelUsage(context); } +function filterOutModels(context: TCGCContext) { + for (const [type, sdkType] of context.modelsMap?.entries() ?? []) { + if (type.kind === "Model") { + if (isExclude(context, type)) sdkType.usage = UsageFlags.None; // eslint-disable-line deprecation/deprecation + } + if (type.kind === "Enum" || type.kind === "Model" || type.kind === "Union") { + if (context.filterOutCoreModels && isAzureCoreModel(type)) { + sdkType.usage = UsageFlags.None; + } + } + } +} + export function getAllModelsWithDiagnostics( context: TCGCContext, options: GetAllModelsOptions = {} @@ -1491,10 +1455,8 @@ export function getAllModelsWithDiagnostics( const servers = getServers(context.program, client.service); if (servers !== undefined && servers[0].parameters !== undefined) { for (const param of servers[0].parameters.values()) { - const sdkModels = diagnostics.pipe(checkAndGetClientType(context, param)); - sdkModels.forEach((sdkModel) => { - updateUsageOfModel(context, UsageFlags.Input, sdkModel); - }); + const sdkType = diagnostics.pipe(getClientTypeWithDiagnostics(context, param)); + updateUsageOfModel(context, UsageFlags.Input, sdkType); } } // versioned enums @@ -1517,6 +1479,8 @@ export function getAllModelsWithDiagnostics( } // update access updateAccessOfModel(context); + // filter out models + filterOutModels(context); let filter = 0; if (options.input && options.output) { filter = Number.MAX_SAFE_INTEGER; diff --git a/packages/typespec-client-generator-core/test/public-utils.test.ts b/packages/typespec-client-generator-core/test/public-utils.test.ts index ef7e423a87..455ab2ab9f 100644 --- a/packages/typespec-client-generator-core/test/public-utils.test.ts +++ b/packages/typespec-client-generator-core/test/public-utils.test.ts @@ -1734,8 +1734,8 @@ describe("typespec-client-generator-core: public-utils", () => { }); await runnerWithCore.compile(lroCode); const models = runnerWithCore.context.experimental_sdkPackage.models; - strictEqual(models.length, 2); - deepStrictEqual(models.map((x) => x.name).sort(), ["ExportedUser", "User"].sort()); + strictEqual(models.length, 1); + deepStrictEqual(models[0].name, "ExportedUser"); }); it("filter-out-core-models false", async () => { const runnerWithCore = await createSdkTestRunner({ @@ -1746,7 +1746,7 @@ describe("typespec-client-generator-core: public-utils", () => { await runnerWithCore.compile(lroCode); runnerWithCore.context.filterOutCoreModels = false; const models = getAllModels(runnerWithCore.context); - strictEqual(models.length, 9); + strictEqual(models.length, 8); // there should only be one non-core model deepStrictEqual( models.map((x) => x.name).sort(), @@ -1758,7 +1758,6 @@ describe("typespec-client-generator-core: public-utils", () => { "ExportedUser", "ErrorResponse", "OperationStatusExportedUserError", - "User", "Versions", ].sort() ); From f1975d95dcd6c54d1ec5741a64023197774684b9 Mon Sep 17 00:00:00 2001 From: iscai-msft <43154838+iscai-msft@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:28:44 -0400 Subject: [PATCH 4/7] [tcgc] add versioning doc (#719) fixes #716 --------- Co-authored-by: iscai-msft Co-authored-by: Laurent Mazuel Co-authored-by: xiaofeicao Co-authored-by: Chenjie Shi Co-authored-by: FAREAST\chunyu Co-authored-by: Qiaoqiao Zhang --- cspell.yaml | 1 + .../09versioning.mdx | 544 ++++++++++++++++++ 2 files changed, 545 insertions(+) create mode 100644 docs/howtos/DataPlane Generation - DPG/09versioning.mdx diff --git a/cspell.yaml b/cspell.yaml index 1ad02e3cfd..a4232e4861 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -63,6 +63,7 @@ words: - oncophenotype - PAYG - prismjs + - pytest - psscriptanalyzer - qnas - regionality diff --git a/docs/howtos/DataPlane Generation - DPG/09versioning.mdx b/docs/howtos/DataPlane Generation - DPG/09versioning.mdx new file mode 100644 index 0000000000..4587cf876d --- /dev/null +++ b/docs/howtos/DataPlane Generation - DPG/09versioning.mdx @@ -0,0 +1,544 @@ +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +# Versioning + +This doc details what emitters will generate for versioned specs + +## Single api Version + +If there is just one api version in the spec, we will generate the api surface area for that one version. + + + + +```typespec +import "@typespec/versioning"; +import "@typespec/http"; + +using TypeSpec.Versioning; +using TypeSpec.Http; + +@versioned(My.Service.Versions) +@service +namespace My.Service; + +enum Versions { + v2023_11_01: "2023-11-01", +} + +model StableModel { + stableFeature: string; +} + +op stableFunctionality(@body stableModel: StableModel): void; +``` + + + + + +```python +import pytest +from my.service import MyServiceClient, models + +client = MyServiceClient(endpoint=..., credential=...) +# client's api_version will be "2023-11-01" + +stable_model = models.StableModel(stable_feature="present") +print(stable_model) +client.stable_functionality(stable_model) # call goes through + +preview_client = MyServiceClient(endpoint=..., credential=..., api_version="2023-11-01") +# python allows you to override the api version, even if only one version is defined in the spec +``` + + + + +```csharp +//ServiceVersion enum +public enum ServiceVersion +{ + /// Service version "2023-11-01". + V2023_11_01 = 1, +} + +Uri endpoint = new Uri(""); +ServiceClient client = new ServiceClient(endpoint); +//client's api_version will be "2023-11-01" + +StableModel stableModel = new StableModel(""); +Response response = client.StableFunctionality(stableModel); + +Uri endpoint = new Uri(""); +ServiceClientOptions options = new ServiceClientOptions(ServiceVersion.V2023_11_01); +ServiceClient client = new ServiceClient(endpoint, options); +//You can specify the service api-version when create client instance. Now client's api_version will be "2023-11-01" +``` + + + + +```typescript +// there's no apiVersion defined in the all the operations, TypeScript emitter will ignore it. +``` + + + + +```java +// ServiceVersion enum +public enum ServiceServiceVersion implements ServiceVersion { + V2023_11_01("2023-11-01"); + + public static ServiceServiceVersion getLatest() {} // V2023_11_01 +} + +// Client API +ServiceClientClient serviceClientClient = new ServiceClientClientBuilder() + // override the api version, even if only one version is defined in the spec + .serviceVersion(ServiceServiceVersion.V2023_11_01) + // other configurations + .buildClient(); +// client's api-version will be 2023-11-01 + +StableModel stableModel = new StableModel("present"); + +// call goes through +serviceClientClient.stableFunctionality(stableModel); +``` + + + + +```go + +``` + + + + +## Multiple api versions + +The configuration flag `api-version` allows you to toggle the behavior that our emitters will generate. + +We will get the versioning information from the `Versions` enum that you pass to the `@versioned` decorator from the `@typespec/versioning` library. + +> NOTE: The ordering of the values in the `Versions` enum is very important. We use this information to determine the order of versions. Our default value will be the **last entry** in the `Versions` list + +### Default + +By default our emitters will only generate the surface used by the latest api version if there are multiple defined. This includes generating only the models used in the surface area of the latest api version. + +Documentation and enums showing the available api versions will still include all of the known api versions, meaning there will be documentation for both the preview and stable releases. + +For the below example, all languages will generate the api surface of default version `v2023_11_01`. There will be no generation of the operation `previewFunctionality`, and we will also not generate the `PreviewModel` because it's only used in `previewFunctionality`, and therefore is not used in the api surface of `v2023_11_01`. + + + + +```typespec +import "@typespec/versioning"; +import "@typespec/http"; + +using TypeSpec.Versioning; +using TypeSpec.Http; + +@versioned(My.Service.Versions) +@service +namespace My.Service; + +enum Versions { + v2023_11_01_preview: "2023-11-01-preview", + v2023_11_01: "2023-11-01", +} + +model PreviewModel { + betaFeature: string; +} + +model StableModel { + stableFeature: string; +} + +@added(Versions.v2023_11_01_preview) +@removed(Versions.v2023_11_01) +op previewFunctionality(@body previewModel: PreviewModel): void; + +op stableFunctionality(@body stableModel: StableModel): void; +``` + + + + + +```python +import pytest +from my.service import MyServiceClient, models + +client = MyServiceClient(endpoint=..., credential=...) +# client's api_version will be "2023-11-01" + +stable_model = models.StableModel(stable_feature="present") +print(stable_model) +client.stable_functionality(stable_model) # call goes through + +with pytest.expect(ImportError): + preview_model = models.PreviewModel(preview_functionality="not present") + +with pytest.expect(AttributeError): + client.preview_functionality({"previewFunctionality": "not present"}) +``` + + + + +```csharp +//ServiceVersion enum +public enum ServiceVersion +{ + /// Service version "2023-11-01-preview". + V2023_11_01_Preview = 1, + /// Service version "2023-11-01". + V2023_11_01 = 2, +} + +Uri endpoint = new Uri(""); +ServiceClient client = new ServiceClient(endpoint); +//client's api-version will be "2023-11-01" + +StableModel stableModel = new StableModel(""); +Response response = client.StableFunctionality(stableModel); + +//neither PreviewModel nor PreviewFunctionality will be generated +``` + + + + +```typescript +// there is no apiVersion parameters defined in all operations, TypeScript emitter will ignore it. +``` + + + + +```java +// ServiceVersion enum +public enum ServiceServiceVersion implements ServiceVersion { + V2023_11_01("2023-11-01"); + + public static ServiceServiceVersion getLatest() {} // V2023_11_01 +} + +// Client API +ServiceClientClient serviceClientClient = new ServiceClientClientBuilder() + // other configurations + .buildClient(); +// client's api-version will be 2023-11-01 + +StableModel stableModel = new StableModel("present"); + +// call goes through +serviceClientClient.stableFunctionality(stableModel); + +// neither PreviewModel nor previewFunctionality will be generated +``` + + + + +```go + +``` + + + + +### Override to a specific version + +You can override the signature to return the api surface area for a specific api version. + +In this example, we are going to override to return the preview api surface area for our spec. The preview api surface area contains all of the functionality. + + + + +```typespec +import "@typespec/versioning"; +import "@typespec/http"; + +using TypeSpec.Versioning; +using TypeSpec.Http; + +@versioned(My.Service.Versions) +@service +namespace My.Service; + +enum Versions { + v2023_11_01_preview: "2023-11-01-preview", + v2023_11_01: "2023-11-01", +} + +model PreviewModel { + betaFeature: string; +} + +model StableModel { + stableFeature: string; +} + +@added(Versions.v2023_11_01_preview) +@removed(Versions.v2023_11_01) +op previewFunctionality(@body previewModel: PreviewModel): void; + +op stableFunctionality(@body stableModel: StableModel): void; +``` + + + + + +```yaml +--- +options: + "@azure-tools/typespec-python": + api-version: "2023-11-01-preview" + "@azure-tools/typespec-csharp": + api-version: "2023-11-01-preview" + "@azure-tools/typespec-ts": + api-version: "2023-11-01-preview" + "@azure-tools/typespec-java": + api-version: "2023-11-01-preview" + "@azure-tools/typespec-go": + api-version: "2023-11-01-preview" +``` + + + + + +```python +import pytest +from my.service import MyServiceClient, models + +preview_client = MyServiceClient(endpoint=..., credential=...) +# client's api_version will be "2023-11-01-preview" + +stable_model = models.StableModel(stable_feature="present") +print(stable_model) +preview_client.stable_functionality(stable_model) # call goes through + +preview_model = models.PreviewModel(preview_functionality="present") +# the model is generated as part of the api surface + +preview_client.preview_functionality(preview_model) # call goes through +``` + + + + +```csharp +// ServiceVersion enum +public enum ServiceVersion +{ + /// Service version "2023-11-01-preview". + V2023_11_01_Preview = 1 +} + +Uri endpoint = new Uri(""); +ServiceClient client = new ServiceClient(endpoint); +// client's api-version will be "2023-11-01-preview" + +//call PreviewFunctionality +PreviewModel previewModel = new PreviewModel(""); +Response response = client.PreviewFunctionality(previewModel); + +//call StableFunctionality +StableModel stableModel = new StableModel(""); +Response response = client.StableFunctionality(stableModel); +``` + + + + +```typescript +// there is no apiVersion parameters defined in all operations, TypeScript emitter will ignore it. +``` + + + + +```java +// ServiceVersion enum +public enum ServiceServiceVersion implements ServiceVersion { + V2023_11_01_PREVIEW("2023-11-01-preview"); + + public static ServiceServiceVersion getLatest() {} // V2023_11_01_PREVIEW +} + +// Client API +ServiceClientClient serviceClientClient = new ServiceClientClientBuilder() + // other configurations + .buildClient(); +// client's api-version will be 2023-11-01-preview + +StableModel stableModel = new StableModel("present"); + +// call goes through +serviceClientClient.stableFunctionality(stableModel); + +PreviewModel previewModel = new PreviewModel("present"); +// call goes through +serviceClientClient.previewFunctionality(previewModel); +``` + + + + +```go + +``` + + + + +### Override to return all + +You can also override the signature to return the combined api surface area of all of the separate api versions. Different languages have different support for versioning validation + + + + +```typespec +import "@typespec/versioning"; +import "@typespec/http"; + +using TypeSpec.Versioning; +using TypeSpec.Http; + +@versioned(My.Service.Versions) +@service +namespace My.Service; + +enum Versions { + v2023_11_01_preview: "2023-11-01-preview", + v2023_11_01: "2023-11-01", +} + +model PreviewModel { + betaFeature: string; +} + +model StableModel { + stableFeature: string; +} + +@added(Versions.v2023_11_01_preview) +@removed(Versions.v2023_11_01) +op previewFunctionality(@body previewModel: PreviewModel): void; + +op stableFunctionality(@body stableModel: StableModel): void; +``` + + + + + +```yaml +--- +options: + "@azure-tools/typespec-python": + api-version: "all" + "@azure-tools/typespec-csharp": + api-version: "all" + "@azure-tools/typespec-ts": + api-version: "all" + "@azure-tools/typespec-java": + api-version: "all" + "@azure-tools/typespec-go": + api-version: "all" +``` + + + + + +```python +import pytest +from my.service import MyServiceClient, models + +client = MyServiceClient(endpoint=..., credential=...) +# client's api_version will be "2023-11-01" + +stable_model = models.StableModel(stable_feature="present") +print(stable_model) +client.stable_functionality(stable_model) # call goes through + +preview_model = models.PreviewModel(preview_functionality="present") +# the model is generated as part of the api surface + +with pytest.expect(ValueError) as ex: + client.preview_functionality(preview_model) +assert "preview_functionality is not available in api version 2023-11-01" in str(ex) + +preview_client = MyServiceClient(endpoint=..., credential=..., api_version="2023-11-01-preview") + +preview_client.preview_functionality(preview_model) # call goes through +``` + + + + +```csharp +//ServiceVersion enum +public enum ServiceVersion +{ + /// Service version "2023-11-01-preview". + V2023_11_01_Preview = 1, + /// Service version "2023-11-01". + V2023_11_01 = 2, +} + +Uri endpoint = new Uri(""); +ServiceClient client = new ServiceClient(endpoint); +//client's api_version will be "2023-11-01" + +//call PreviewFunctionality +PreviewModel previewModel = new PreviewModel(""); +Response response = client.PreviewFunctionality(previewModel); + +//call StableFunctionality +StableModel stableModel = new StableModel(""); +Response response = client.StableFunctionality(stableModel); + +Uri endpoint = new Uri(""); +ServiceClientOptions options = new ServiceClientOptions(ServiceVersion.V2023_11_01_Preview); +ServiceClient client = new ServiceClient(endpoint, options); +//You can specify the service api-version when create client instance. Now client's api_version will be "2023-11-01-preview" +``` + + + + +```typescript +// there is no apiVersion parameters defined in all operations, TypeScript emitter will ignore it. +``` + + + + +```java + +``` + + + + +```go + +``` + + + From 85171a8b2f284c4c07c6a8237dfe55562fc5cf51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jun 2024 19:32:12 +0000 Subject: [PATCH 5/7] Bump core from `24f81bf` to `1d66867` (#967) Bumps [core](https://github.com/microsoft/typespec) from `24f81bf` to `1d66867`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Timothee Guerin --- .gitignore | 3 + core | 2 +- .../reference/linter.md | 3 +- .../e2e/smoke.e2e.ts | 2 +- packages/typespec-azure-rulesets/README.md | 3 +- pnpm-lock.yaml | 165 +++++++++++------- 6 files changed, 109 insertions(+), 69 deletions(-) diff --git a/.gitignore b/.gitignore index 899d106f81..7bdd354b4f 100644 --- a/.gitignore +++ b/.gitignore @@ -205,3 +205,6 @@ docs/**/js-api/ # vitest temporary files **/vitest.config.ts.timestamp* + +# Playwright temp file +.last_run.json diff --git a/core b/core index 24f81bf395..1d668673c3 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 24f81bf39523e296a4ae8406cc919581740c3a2a +Subproject commit 1d668673c39225ef172b91b885d2f2ffe378d01c diff --git a/docs/emitters/typespec-azure-rulesets/reference/linter.md b/docs/emitters/typespec-azure-rulesets/reference/linter.md index 8b4774ab0c..840cb50456 100644 --- a/docs/emitters/typespec-azure-rulesets/reference/linter.md +++ b/docs/emitters/typespec-azure-rulesets/reference/linter.md @@ -13,14 +13,13 @@ Add the following in `tspconfig.yaml`: ```yaml linter: extends: - - "@azure-tools/typespec-azure-rulesets/all" + - "@azure-tools/typespec-azure-rulesets/data-plane" ``` ## RuleSets Available ruleSets: -- `@azure-tools/typespec-azure-rulesets/all` - `@azure-tools/typespec-azure-rulesets/data-plane` - `@azure-tools/typespec-azure-rulesets/resource-manager` diff --git a/packages/typespec-azure-playground-website/e2e/smoke.e2e.ts b/packages/typespec-azure-playground-website/e2e/smoke.e2e.ts index 3d88939541..a583a67343 100644 --- a/packages/typespec-azure-playground-website/e2e/smoke.e2e.ts +++ b/packages/typespec-azure-playground-website/e2e/smoke.e2e.ts @@ -9,7 +9,7 @@ test.describe("typespec-azure-playground-website UI tests", () => { await page.goto(host); const samplesDropDown = page.locator("_react=SamplesDropdown").locator("select"); await samplesDropDown.selectOption({ label: "Azure Resource Manager framework" }); - const outputContainer = page.locator("_react=OutputContent"); + const outputContainer = page.locator("_react=FileOutput"); await expect(outputContainer).toContainText(`"title": "ContosoProviderHubClient"`); }); }); diff --git a/packages/typespec-azure-rulesets/README.md b/packages/typespec-azure-rulesets/README.md index c80e0d0ab3..79d6df4ec8 100644 --- a/packages/typespec-azure-rulesets/README.md +++ b/packages/typespec-azure-rulesets/README.md @@ -17,14 +17,13 @@ Add the following in `tspconfig.yaml`: ```yaml linter: extends: - - "@azure-tools/typespec-azure-rulesets/all" + - "@azure-tools/typespec-azure-rulesets/data-plane" ``` ### RuleSets Available ruleSets: -- `@azure-tools/typespec-azure-rulesets/all` - `@azure-tools/typespec-azure-rulesets/data-plane` - `@azure-tools/typespec-azure-rulesets/resource-manager` diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5db8b370b4..2038c332a8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -880,12 +880,12 @@ importers: c8: specifier: ^9.1.0 version: 9.1.0 - copyfiles: - specifier: ~2.4.1 - version: 2.4.1 cross-env: specifier: ~7.0.3 version: 7.0.3 + es-module-shims: + specifier: ~1.10.0 + version: 1.10.0 rimraf: specifier: ~5.0.7 version: 5.0.7 @@ -895,6 +895,9 @@ importers: vite: specifier: ^5.2.11 version: 5.2.11(@types/node@18.11.19) + vite-plugin-checker: + specifier: ^0.6.4 + version: 0.6.4(eslint@8.57.0)(typescript@5.4.5)(vite@5.2.11) vite-plugin-dts: specifier: ^3.9.1 version: 3.9.1(@types/node@18.11.19)(typescript@5.4.5)(vite@5.2.11) @@ -11163,6 +11166,13 @@ packages: dependencies: ansi-split: 1.0.1 + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + /ansi-html-community@0.0.8: resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} engines: {'0': node >= 0.8.0} @@ -12045,14 +12055,6 @@ packages: optionalDependencies: '@colors/colors': 1.5.0 - /cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -12315,19 +12317,6 @@ packages: webpack: 5.91.0(@swc/core@1.5.7) dev: false - /copyfiles@2.4.1: - resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} - hasBin: true - dependencies: - glob: 7.2.3 - minimatch: 3.1.2 - mkdirp: 1.0.4 - noms: 0.0.0 - through2: 2.0.5 - untildify: 4.0.0 - yargs: 16.2.0 - dev: true - /core-js-compat@3.37.1: resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==} dependencies: @@ -13690,7 +13679,6 @@ packages: /es-module-shims@1.10.0: resolution: {integrity: sha512-3PmuShQBd9d8pulTFx6L7HKgncnZ1oeSSbrEfnUasb3Tv974BAvyFtW1HLPJSkh5fCaU9JNZbBzPdbxSwg2zqA==} - dev: false /es-object-atoms@1.0.0: resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} @@ -15921,6 +15909,7 @@ packages: /isarray@0.0.1: resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + dev: false /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -17821,13 +17810,6 @@ packages: /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - /noms@0.0.0: - resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} - dependencies: - inherits: 2.0.4 - readable-stream: 1.0.34 - dev: true - /non-layered-tidy-tree-layout@2.0.2: resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==} @@ -19546,15 +19528,6 @@ packages: mute-stream: 0.0.8 dev: true - /readable-stream@1.0.34: - resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 0.0.1 - string_decoder: 0.10.31 - dev: true - /readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} dependencies: @@ -20640,10 +20613,6 @@ packages: es-object-atoms: 1.0.0 dev: true - /string_decoder@0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - dev: true - /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -21239,6 +21208,11 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} @@ -21775,6 +21749,57 @@ packages: - terser dev: true + /vite-plugin-checker@0.6.4(eslint@8.57.0)(typescript@5.4.5)(vite@5.2.11): + resolution: {integrity: sha512-2zKHH5oxr+ye43nReRbC2fny1nyARwhxdm0uNYp/ERy4YvU9iZpNOsueoi/luXw5gnpqRSvjcEPxXbS153O2wA==} + engines: {node: '>=14.16'} + peerDependencies: + eslint: '>=7' + meow: ^9.0.0 + optionator: ^0.9.1 + stylelint: '>=13' + typescript: '*' + vite: '>=2.0.0' + vls: '*' + vti: '*' + vue-tsc: '>=1.3.9' + peerDependenciesMeta: + eslint: + optional: true + meow: + optional: true + optionator: + optional: true + stylelint: + optional: true + typescript: + optional: true + vls: + optional: true + vti: + optional: true + vue-tsc: + optional: true + dependencies: + '@babel/code-frame': 7.24.2 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + chokidar: 3.6.0 + commander: 8.3.0 + eslint: 8.57.0 + fast-glob: 3.3.2 + fs-extra: 11.2.0 + npm-run-path: 4.0.1 + semver: 7.6.2 + strip-ansi: 6.0.1 + tiny-invariant: 1.3.3 + typescript: 5.4.5 + vite: 5.2.11(@types/node@18.11.19) + vscode-languageclient: 7.0.0 + vscode-languageserver: 7.0.0 + vscode-languageserver-textdocument: 1.0.11 + vscode-uri: 3.0.8 + dev: true + /vite-plugin-dts@3.9.1(@types/node@18.11.19)(typescript@5.4.5)(vite@5.2.11): resolution: {integrity: sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -21893,10 +21918,24 @@ packages: - terser dev: true + /vscode-jsonrpc@6.0.0: + resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==} + engines: {node: '>=8.0.0 || >=10.0.0'} + dev: true + /vscode-jsonrpc@8.2.0: resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} engines: {node: '>=14.0.0'} + /vscode-languageclient@7.0.0: + resolution: {integrity: sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==} + engines: {vscode: ^1.52.0} + dependencies: + minimatch: 3.1.2 + semver: 7.6.2 + vscode-languageserver-protocol: 3.16.0 + dev: true + /vscode-languageclient@9.0.1: resolution: {integrity: sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==} engines: {vscode: ^1.82.0} @@ -21906,6 +21945,13 @@ packages: vscode-languageserver-protocol: 3.17.5 dev: true + /vscode-languageserver-protocol@3.16.0: + resolution: {integrity: sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==} + dependencies: + vscode-jsonrpc: 6.0.0 + vscode-languageserver-types: 3.16.0 + dev: true + /vscode-languageserver-protocol@3.17.5: resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} dependencies: @@ -21915,9 +21961,20 @@ packages: /vscode-languageserver-textdocument@1.0.11: resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} + /vscode-languageserver-types@3.16.0: + resolution: {integrity: sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==} + dev: true + /vscode-languageserver-types@3.17.5: resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + /vscode-languageserver@7.0.0: + resolution: {integrity: sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==} + hasBin: true + dependencies: + vscode-languageserver-protocol: 3.16.0 + dev: true + /vscode-languageserver@9.0.1: resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} hasBin: true @@ -22490,28 +22547,10 @@ packages: engines: {node: '>= 14'} hasBin: true - /yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - dev: true - /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - dependencies: - cliui: 7.0.4 - escalade: 3.1.2 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - dev: true - /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} From a8f03375864cc0ca3394e99e5af18047209e4d9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Jun 2024 08:12:01 -0700 Subject: [PATCH 6/7] Bump core from `1d66867` to `0a7869f` (#971) Bumps [core](https://github.com/microsoft/typespec) from `1d66867` to `0a7869f`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index 1d668673c3..0a7869f84f 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 1d668673c39225ef172b91b885d2f2ffe378d01c +Subproject commit 0a7869f84f21af8a7b41ac16474161e03cc27a0b From d179b2a19aebde333dab7190c40ef9239660220e Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 7 Jun 2024 11:14:52 -0700 Subject: [PATCH 7/7] Autorest emit all files programatically (#972) Expose an API like we have in openapi3 emitter to emit all services at all version programmatically --- ...t-all-programmatically-2024-5-7-9-28-38.md | 7 + packages/typespec-autorest/src/emit.ts | 191 +++++++++++++----- packages/typespec-autorest/src/index.ts | 2 +- packages/typespec-autorest/src/openapi.ts | 18 +- packages/typespec-autorest/src/types.ts | 62 ++++++ 5 files changed, 209 insertions(+), 71 deletions(-) create mode 100644 .chronus/changes/autorest-emitt-all-programmatically-2024-5-7-9-28-38.md create mode 100644 packages/typespec-autorest/src/types.ts diff --git a/.chronus/changes/autorest-emitt-all-programmatically-2024-5-7-9-28-38.md b/.chronus/changes/autorest-emitt-all-programmatically-2024-5-7-9-28-38.md new file mode 100644 index 0000000000..9f73b1e8f7 --- /dev/null +++ b/.chronus/changes/autorest-emitt-all-programmatically-2024-5-7-9-28-38.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@azure-tools/typespec-autorest" +--- + +Add API to programmatically get all the OpenAPI2 documents for all services at all versions in a spec \ No newline at end of file diff --git a/packages/typespec-autorest/src/emit.ts b/packages/typespec-autorest/src/emit.ts index 7287fb9183..565d6dcab9 100644 --- a/packages/typespec-autorest/src/emit.ts +++ b/packages/typespec-autorest/src/emit.ts @@ -1,4 +1,4 @@ -import { SdkContext, createSdkContext } from "@azure-tools/typespec-client-generator-core"; +import { createSdkContext } from "@azure-tools/typespec-client-generator-core"; import { EmitContext, Namespace, @@ -21,6 +21,11 @@ import { getOpenAPIForService, sortOpenAPIDocument, } from "./openapi.js"; +import type { + AutorestEmitterResult, + AutorestServiceRecord, + AutorestVersionedServiceRecord, +} from "./types.js"; import { AutorestEmitterContext } from "./utils.js"; /** @@ -48,17 +53,34 @@ const defaultOptions = { } as const; export async function $onEmit(context: EmitContext) { - const resolvedOptions = { ...defaultOptions, ...context.options }; + const tracer = getTracer(context.program); + + const options = resolveAutorestOptions( + context.program, + context.emitterOutputDir, + context.options + ); + tracer.trace("options", JSON.stringify(options, null, 2)); + + await emitAllServiceAtAllVersions(context.program, options); +} + +export function resolveAutorestOptions( + program: Program, + emitterOutputDir: string, + options: AutorestEmitterOptions +): ResolvedAutorestEmitterOptions { + const resolvedOptions = { ...defaultOptions, ...options }; const armTypesDir = interpolatePath( resolvedOptions["arm-types-dir"] ?? "{project-root}/../../common-types/resource-management", { - "project-root": context.program.projectRoot, - "emitter-output-dir": context.emitterOutputDir, + "project-root": program.projectRoot, + "emitter-output-dir": emitterOutputDir, } ); - const options: ResolvedAutorestEmitterOptions = { + return { outputFile: resolvedOptions["output-file"], - outputDir: context.emitterOutputDir, + outputDir: emitterOutputDir, azureResourceProviderFolder: resolvedOptions["azure-resource-provider-folder"], examplesDirectory: resolvedOptions["examples-directory"], version: resolvedOptions["version"], @@ -69,37 +91,37 @@ export async function $onEmit(context: EmitContext) { armTypesDir, useReadOnlyStatusSchema: resolvedOptions["use-read-only-status-schema"], }; - const tracer = getTracer(context.program); - tracer.trace("options", JSON.stringify(options, null, 2)); - - const tcgcSdkContext = createSdkContext(context, "@azure-tools/typespec-autorest", { - versionStrategy: "ignore", - }); - - await emitAllServiceAtAllVersions(context.program, tcgcSdkContext, options); } -export async function emitAllServiceAtAllVersions( +export async function getAllServicesAtAllVersions( program: Program, - tcgcSdkContext: SdkContext, options: ResolvedAutorestEmitterOptions -) { +): Promise { + const tcgcSdkContext = createSdkContext( + { program, options: {} } as any, + "@azure-tools/typespec-autorest", + { + versionStrategy: "ignore", + } + ); + const services = listServices(program); if (services.length === 0) { services.push({ type: program.getGlobalNamespaceType() }); } + const serviceRecords: AutorestServiceRecord[] = []; for (const service of services) { const originalProgram = program; const versions = buildVersionProjections(program, service.type).filter( (v) => !options.version || options.version === v.version ); - for (const record of versions) { + + if (versions.length === 1 && versions[0].version === undefined) { let projectedProgram; - if (record.projections.length > 0) { - projectedProgram = program = projectProgram(originalProgram, record.projections); + if (versions[0].projections.length > 0) { + projectedProgram = program = projectProgram(originalProgram, versions[0].projections); } - const projectedServiceNs: Namespace = projectedProgram ? (projectedProgram.projector.projectedTypes.get(service.type) as Namespace) : service.type; @@ -109,50 +131,111 @@ export async function emitAllServiceAtAllVersions( : getService(program, projectedServiceNs)!; const context: AutorestEmitterContext = { program, - outputFile: resolveOutputFile( - program, - projectedService, - services.length > 1, - options, - record.version - ), + outputFile: resolveOutputFile(program, service, services.length > 1, options), service: projectedService, - version: record.version, tcgcSdkContext, }; + const result = await getOpenAPIForService(context, options); - if (!program.compilerOptions.noEmit && !program.hasError()) { - // Sort the document - const sortedDocument = sortOpenAPIDocument(result.document); - - // Write out the OpenAPI document to the output path - await emitFile(program, { - path: context.outputFile, - content: prettierOutput(JSON.stringify(sortedDocument, null, 2)), - newLine: options.newLine, + serviceRecords.push({ + service, + versioned: false, + ...result, + }); + } else { + const serviceRecord: AutorestVersionedServiceRecord = { + service, + versioned: true, + versions: [], + }; + serviceRecords.push(serviceRecord); + + for (const record of versions) { + const projectedProgram = (program = projectProgram(originalProgram, record.projections)); + + const projectedServiceNs: Namespace = projectedProgram + ? (projectedProgram.projector.projectedTypes.get(service.type) as Namespace) + : service.type; + const projectedService = + projectedServiceNs === program.getGlobalNamespaceType() + ? { type: program.getGlobalNamespaceType() } + : getService(program, projectedServiceNs)!; + const context: AutorestEmitterContext = { + program, + outputFile: resolveOutputFile( + program, + projectedService, + services.length > 1, + options, + record.version + ), + service: projectedService, + version: record.version, + tcgcSdkContext, + }; + const result = await getOpenAPIForService(context, options); + serviceRecord.versions.push({ + ...result, + service: projectedService, + version: record.version!, }); + } + } + } - // Copy examples to the output directory - if (options.examplesDirectory && result.operationExamples.length > 0) { - const examplesPath = resolvePath(getDirectoryPath(context.outputFile), "examples"); - await program.host.mkdirp(examplesPath); - for (const { examples } of result.operationExamples) { - if (examples) { - for (const { relativePath, file } of Object.values(examples)) { - await emitFile(program, { - path: resolvePath(examplesPath, relativePath), - content: file.text, - newLine: options.newLine, - }); - } - } - } - } + return serviceRecords; +} + +async function emitAllServiceAtAllVersions( + program: Program, + options: ResolvedAutorestEmitterOptions +) { + const services = await getAllServicesAtAllVersions(program, options); + if (program.compilerOptions.noEmit || program.hasError()) { + return; + } + for (const serviceRecord of services) { + if (serviceRecord.versioned) { + for (const documentRecord of serviceRecord.versions) { + await emitOutput(program, documentRecord, options); } + } else { + await emitOutput(program, serviceRecord, options); } } } +async function emitOutput( + program: Program, + result: AutorestEmitterResult, + options: ResolvedAutorestEmitterOptions +) { + const sortedDocument = sortOpenAPIDocument(result.document); + + // Write out the OpenAPI document to the output path + await emitFile(program, { + path: result.outputFile, + content: prettierOutput(JSON.stringify(sortedDocument, null, 2)), + newLine: options.newLine, + }); + + // Copy examples to the output directory + if (options.examplesDirectory && result.operationExamples.length > 0) { + const examplesPath = resolvePath(getDirectoryPath(result.outputFile), "examples"); + await program.host.mkdirp(examplesPath); + for (const { examples } of result.operationExamples) { + if (examples) { + for (const { relativePath, file } of Object.values(examples)) { + await emitFile(program, { + path: resolvePath(examplesPath, relativePath), + content: file.text, + newLine: options.newLine, + }); + } + } + } + } +} function prettierOutput(output: string) { return output + "\n"; } diff --git a/packages/typespec-autorest/src/index.ts b/packages/typespec-autorest/src/index.ts index df5549aa52..fe82d89f4d 100644 --- a/packages/typespec-autorest/src/index.ts +++ b/packages/typespec-autorest/src/index.ts @@ -1,5 +1,5 @@ export * from "./decorators.js"; -export { $onEmit } from "./emit.js"; +export { $onEmit, getAllServicesAtAllVersions, resolveAutorestOptions } from "./emit.js"; export { $lib, AutorestEmitterOptions } from "./lib.js"; export { getOpenAPIForService, diff --git a/packages/typespec-autorest/src/openapi.ts b/packages/typespec-autorest/src/openapi.ts index 11b7de2a2f..8c46f7e7f8 100644 --- a/packages/typespec-autorest/src/openapi.ts +++ b/packages/typespec-autorest/src/openapi.ts @@ -31,7 +31,6 @@ import { Operation, Program, Scalar, - SourceFile, StringLiteral, StringTemplate, SyntaxKind, @@ -146,6 +145,7 @@ import { PrimitiveItems, Refable, } from "./openapi2-document.js"; +import type { AutorestEmitterResult, LoadedExample } from "./types.js"; import { AutorestEmitterContext, getClientName, resolveOperationId } from "./utils.js"; interface SchemaContext { @@ -229,16 +229,6 @@ interface ProcessedSchema extends PendingSchema { schema: OpenAPI2Schema | undefined; } -export interface OperationExamples { - readonly operationId: string; - readonly examples: LoadedExample[]; -} - -export interface AutorestEmitterResult { - readonly document: OpenAPI2Document; - readonly operationExamples: OperationExamples[]; -} - export async function getOpenAPIForService( context: AutorestEmitterContext, options: AutorestDocumentEmitterOptions @@ -360,6 +350,7 @@ export async function getOpenAPIForService( } }) .filter((x) => x) as any, + outputFile: context.outputFile, }; function resolveHost( @@ -2302,11 +2293,6 @@ export function sortOpenAPIDocument(doc: OpenAPI2Document): OpenAPI2Document { return sorted; } -interface LoadedExample { - readonly relativePath: string; - readonly file: SourceFile; - readonly data: any; -} async function loadExamples( host: CompilerHost, options: AutorestDocumentEmitterOptions, diff --git a/packages/typespec-autorest/src/types.ts b/packages/typespec-autorest/src/types.ts new file mode 100644 index 0000000000..0b06c87441 --- /dev/null +++ b/packages/typespec-autorest/src/types.ts @@ -0,0 +1,62 @@ +import type { Service, SourceFile } from "@typespec/compiler"; +import type { OpenAPI2Document } from "./openapi2-document.js"; + +/** + * A record containing the the OpenAPI 3 documents corresponding to + * a particular service definition. + */ +export type AutorestServiceRecord = + | AutorestUnversionedServiceRecord + | AutorestVersionedServiceRecord; + +export interface AutorestUnversionedServiceRecord extends AutorestEmitterResult { + /** The service that generated this OpenAPI document */ + readonly service: Service; + + /** Whether the service is versioned */ + readonly versioned: false; +} + +export interface AutorestVersionedServiceRecord { + /** The service that generated this OpenAPI document */ + readonly service: Service; + + /** Whether the service is versioned */ + readonly versioned: true; + + /** The OpenAPI 3 document records for each version of this service */ + readonly versions: AutorestVersionedDocumentRecord[]; +} + +/** + * A record containing an unversioned OpenAPI document and associated metadata. + */ +export interface AutorestVersionedDocumentRecord extends AutorestEmitterResult { + /** The service that generated this OpenAPI document. */ + readonly service: Service; + + /** The version of the service. Absent if the service is unversioned. */ + readonly version: string; +} + +export interface OperationExamples { + readonly operationId: string; + readonly examples: LoadedExample[]; +} + +export interface AutorestEmitterResult { + /** The OpenAPI document*/ + readonly document: OpenAPI2Document; + + /** The examples */ + readonly operationExamples: OperationExamples[]; + + /** Output file used */ + readonly outputFile: string; +} + +export interface LoadedExample { + readonly relativePath: string; + readonly file: SourceFile; + readonly data: any; +}