diff --git a/tools/tsp-client/README.md b/tools/tsp-client/README.md index de613ebf5cb..41ff416a7a6 100644 --- a/tools/tsp-client/README.md +++ b/tools/tsp-client/README.md @@ -45,6 +45,7 @@ Convert an existing swagger specification to a TypeSpec project. This command sh ## 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] @@ -55,7 +56,7 @@ Convert an existing swagger specification to a TypeSpec project. This command sh --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 local directory. [string] + 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] diff --git a/tools/tsp-client/src/index.ts b/tools/tsp-client/src/index.ts index d49ac5e55bb..0960d7dfc26 100644 --- a/tools/tsp-client/src/index.ts +++ b/tools/tsp-client/src/index.ts @@ -1,16 +1,15 @@ -import { npmCommand } from "./npm.js"; +import { npmCommand, npxCommand } from "./npm.js"; import { createTempDirectory, removeDirectory, readTspLocation, getEmitterFromRepoConfig } from "./fs.js"; import { Logger, printBanner, enableDebug, printVersion } from "./log.js"; import { TspLocation, compileTsp, discoverMainFile, resolveTspConfigUrl } from "./typespec.js"; import { getOptions } from "./options.js"; -import { mkdir, writeFile, cp, readFile, access, stat } from "node:fs/promises"; +import { mkdir, writeFile, cp, readFile, access, stat, rename } from "node:fs/promises"; import { addSpecFiles, checkoutCommit, cloneRepo, getRepoRoot, sparseCheckout } from "./git.js"; import { doesFileExist } from "./network.js"; import { parse as parseYaml } from "yaml"; import { joinPaths, normalizePath, resolvePath } from "@typespec/compiler"; import { formatAdditionalDirectories, getAdditionalDirectoryName } from "./utils.js"; import { resolve } from "node:path"; -import { spawn } from "node:child_process"; import { config as dotenvConfig } from "dotenv"; @@ -240,24 +239,25 @@ async function generate({ } -async function convert(readme: string, outputDir: string): Promise { - return new Promise((resolve, reject) => { - const autorest = spawn("npx", ["autorest", readme, "--openapi-to-typespec", "--use=@autorest/openapi-to-typespec", `--output-folder=${outputDir}`], { - cwd: outputDir, - stdio: "inherit", - shell: true, - }); - autorest.once("exit", (code) => { - if (code === 0) { - resolve(); - } else { - reject(new Error(`openapi to typespec conversion failed exited with code ${code}`)); - } - }); - autorest.once("error", (err) => { - reject(new Error(`openapi to typespec conversion failed with error: ${err}`)); - }); - }); +async function convert(readme: string, outputDir: string, arm?: boolean): Promise { + const args = ["autorest", "--openapi-to-typespec", "--csharp=false", `--output-folder="${outputDir}"`, "--use=@autorest/openapi-to-typespec", `"${readme}"`]; + if (arm) { + const generateMetadataCmd = ["autorest", "--csharp", "--max-memory-size=8192", '--use="https://aka.ms/azsdk/openapi-to-typespec-csharp"', `--output-folder="${outputDir}"`, "--mgmt-debug.only-generate-metadata", "--azure-arm", "--skip-csproj", `"${readme}"`]; + try { + await npxCommand(outputDir, generateMetadataCmd); + } catch (err) { + Logger.error(`Error occurred while attempting to generate ARM metadata: ${err}`); + process.exit(1); + } + try { + await rename(joinPaths(outputDir, "metadata.json"), joinPaths(outputDir, "resources.json")); + } catch (err) { + Logger.error(`Error occurred while attempting to rename metadata.json to resources.json: ${err}`); + process.exit(1); + } + args.push("--isArm"); + } + return await npxCommand(outputDir, args); } @@ -330,7 +330,7 @@ async function main() { if (await doesFileExist(readme)) { readme = normalizePath(resolve(readme)); } - await convert(readme, rootUrl); + await convert(readme, rootUrl, options.arm); break; default: throw new Error(`Unknown command: ${options.command}`); diff --git a/tools/tsp-client/src/log.ts b/tools/tsp-client/src/log.ts index 17b8e20f997..7a718e95839 100644 --- a/tools/tsp-client/src/log.ts +++ b/tools/tsp-client/src/log.ts @@ -70,6 +70,7 @@ Commands: convert Convert a swagger specification to TypeSpec [string] 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] diff --git a/tools/tsp-client/src/npm.ts b/tools/tsp-client/src/npm.ts index 4c3a1babd65..471443125c8 100644 --- a/tools/tsp-client/src/npm.ts +++ b/tools/tsp-client/src/npm.ts @@ -39,6 +39,26 @@ export async function npmCommand(workingDir: string, args: string[]): Promise { + return new Promise((resolve, reject) => { + const npm = spawn("npx", args, { + cwd: workingDir, + stdio: "inherit", + shell: true, + }); + npm.once("exit", (code) => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`npx ${args[0]} failed exited with code ${code}`)); + } + }); + npm.once("error", (err) => { + reject(new Error(`npx ${args[0]} failed with error: ${err}`)); + }); + }); +} + let packageVersion: string; export async function getPackageVersion(): Promise { if (!packageVersion) { diff --git a/tools/tsp-client/src/options.ts b/tools/tsp-client/src/options.ts index bee47f5eb56..ba896f749c8 100644 --- a/tools/tsp-client/src/options.ts +++ b/tools/tsp-client/src/options.ts @@ -18,6 +18,7 @@ export interface Options { localSpecRepo?: string; emitterOptions?: string; swaggerReadme?: string; + arm: boolean; } export async function getOptions(): Promise { @@ -64,6 +65,9 @@ export async function getOptions(): Promise { }, ["swagger-readme"]: { type: "string", + }, + arm: { + type: "boolean", } }, }); @@ -165,5 +169,6 @@ export async function getOptions(): Promise { localSpecRepo: values["local-spec-repo"], emitterOptions: values["emitter-options"], swaggerReadme: values["swagger-readme"], + arm: values.arm ?? false, }; }