From 83f2b9b204e8487fec3265d431667591dd825234 Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Thu, 17 Nov 2022 16:48:31 +0800 Subject: [PATCH] Update dpg+rlc getStarted doc (#23848) * Update dpg+rlc getStarted doc * Update the doc to enable generte-sample by default * Update according to relevant comments * Update small format issue * Small fix * Update swagger one * Update sample generation and test cases * Update non-block process with sample failure * Add format * Update title * Update RLC-quickstart.md * Update RLC-quickstart.md * Update lint command * Update documentation/RLC-quickstart.md Co-authored-by: Qiaoqiao Zhang <55688292+qiaozha@users.noreply.github.com> * Update documentation/RLC-quickstart.md Co-authored-by: Qiaoqiao Zhang <55688292+qiaozha@users.noreply.github.com> * Update documentation/RLC-quickstart.md Co-authored-by: Qiaoqiao Zhang <55688292+qiaozha@users.noreply.github.com> * Update according to comments * Resolve comments * Remove useless codes * Update spaces * Fix small issues * Update filename * update broken links * Update wordings * Wording change Co-authored-by: Qiaoqiao Zhang <55688292+qiaozha@users.noreply.github.com> --- ...md => Quickstart-on-how-to-write-tests.md} | 15 +- documentation/RLC-Swagger-quickstart.md | 299 ++++++++++++++++++ documentation/RLC-quickstart.md | 215 +++++++------ documentation/Troubleshoot-ci-failure.md | 11 + documentation/images/broken-links.png | Bin 0 -> 29869 bytes 5 files changed, 439 insertions(+), 101 deletions(-) rename documentation/{test-quickstart.md => Quickstart-on-how-to-write-tests.md} (95%) create mode 100644 documentation/RLC-Swagger-quickstart.md create mode 100644 documentation/Troubleshoot-ci-failure.md create mode 100644 documentation/images/broken-links.png diff --git a/documentation/test-quickstart.md b/documentation/Quickstart-on-how-to-write-tests.md similarity index 95% rename from documentation/test-quickstart.md rename to documentation/Quickstart-on-how-to-write-tests.md index fe27608b914..7d296a706fe 100644 --- a/documentation/test-quickstart.md +++ b/documentation/Quickstart-on-how-to-write-tests.md @@ -15,8 +15,8 @@ This page is to help you write and run tests quickly for Javascript Codegen SDK - [Client authentication](#client-authentication) - [AzureAD OAuth2 Authentication](#azuread-oauth2-authentication) - [API Key Authentication](#api-key-authentication) - - [Example 1: Basic Azure data-plane service interaction and recording](#example-1-basic-azure-data-plane-service-interaction-and-recording) - - [Example 2: Basic Azure management service interaction and recording](#example-2-basic-azure-management-service-interaction-and-recording) + - [Example 1: Basic RLC test interaction and recording for Azure data-plane service](#example-1-basic-rlc-test-interaction-and-recording-for-azure-data-plane-service) + - [Example 2: Basic HLC test interaction and recording for Azure management service](#example-2-basic-hlc-test-interaction-and-recording-for-azure-management-service) # Background @@ -199,7 +199,7 @@ await recorder.start(recorderEnvSetup); API key authentication would hit the service's endpoint directly so these traffic will be recorded. It doesn't require any customization in tests. However we must secure the sensitive data and not leak into our recordings, so add a sanitizer to replace your API keys. You could read more on how to add sanitizer at [here](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/test-utils/recorder/README.md). -## Example 1: Basic Azure data-plane service interaction and recording +## Example 1: Basic RLC test interaction and recording for Azure data-plane service At the code structure [section](#code-structure) we described we'll generate sample file for you, if you are the first time to write test cases you could grow up your own based on them. @@ -242,7 +242,7 @@ describe("My test", () => { // Step 1: Create your test case it("Should create a glossary", async () => { // Step 3: Add your test cases - const glossary = await client.path("/atlas/v2/glossary").post({ + const glossaryResponse = await client.path("/atlas/v2/glossary").post({ body: { name: glossaryName, shortDescription: "Example Short Description", @@ -251,8 +251,11 @@ describe("My test", () => { usage: "Example Glossary", }, }); + if (isUnexpected(glossaryResponse)) { + throw new Error(glossaryResponse.body?.error.message); + } // Step 5: Add your assertions - assert.strictEqual(glossary.status, "200"); + assert.strictEqual(glossaryResponse.status, "200"); }); }); ``` @@ -300,7 +303,7 @@ export function createClient(recorder: Recorder, options?: ClientOptions): Purvi } ``` -## Example 2: Basic Azure management service interaction and recording +## Example 2: Basic HLC test interaction and recording for Azure management service At the code structure [section](#code-structure) we described if your SDK is generated base on HLC we'll generate a sample test named `sampleTest.ts` for you. diff --git a/documentation/RLC-Swagger-quickstart.md b/documentation/RLC-Swagger-quickstart.md new file mode 100644 index 00000000000..9f516c7c4e7 --- /dev/null +++ b/documentation/RLC-Swagger-quickstart.md @@ -0,0 +1,299 @@ +Getting Started - Generate the RLC rest-level client libraries with Swagger +=========================================================================== + +# Before you start + +Please refer to this [link](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md#prerequisites) for the environment set up prerequisites in azure-sdk-for-js repository. We highly recommand to read [this blog](https://devblogs.microsoft.com/azure-sdk/azure-rest-libraries-for-javascript/) to get familiar with REST libraries for JavaScript. + + +# Project folder and name convention + +If you are the first time to prepare the SDK, please follow the Azure SDK guidance and discuss with architects to decide the project folder and name convention for RLC libraries. + +1. Project Folder structure. + normally, the folder structure would be something like `sdk/{servicename}/{servicename}-{modulename}-rest`. For example, we have `sdk/agrifood/agrifood-farming-rest` folder for Farmbeats account modules. That folder will be your **${PROJECT_ROOT} folder**. +1. Package Name Convention. + The package name for RLC is something like `@azure-rest/{servicename}-{modulename}`. For example, the package name for Farmbeats module is `@azure-rest/agrifood-farming`. + +# How to generate RLC + +We are working on to automatically generate everything right now, but currently we still need some manual work to get a releasable package. Here're the steps of how to get the package. + +1. **Create a swagger/README.md file.under ${PROJECT_ROOT} folder** + We are using autorest to generate the code, but there's a lot of command options and in order to make the regenerate process easier in the cases of refresh the rest api input or change the code generator version, you need to document the generate command parameters. + Here's an example of the swagger/README.md + + ~~~ + + # Azure Farmbeats TypeScript Protocol Layer + + > see https://aka.ms/autorest + ## Configuration + + ```yaml + package-name: "@azure-rest/agrifood-farming" + title: Farmbeats + description: Farmbeats Client + generate-metadata: true + generate-test: true + generate-sample: true + license-header: MICROSOFT_MIT_NO_VERSION + output-folder: ../ + source-code-folder-path: ./src + input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/683e3f4849ee1d84629d0d0fa17789e80a9cee08/specification/agfood/data-plane/Microsoft.AgFoodPlatform/preview/2021-03-31-preview/agfood.json + package-version: 1.0.0-beta.2 + rest-level-client: true + security: AADToken + security-scopes: https://farmbeats.azure.net/.default + use-extension: + "@autorest/typescript": "6.0.0-rc.1" + ``` + ~~~ + + Here, we need to replace the value in `package-name`, `title`, `description`, `input-file`, `package-version`, `security`,`security-scopes` into **your own service's** `package-name`, `title`, `description` etc. + + **How to configure authentication** + + Autorest only support two types of authentication: Azure Key Credential(AzureKey) and Token credential(AADToken), any other will need to be handled manually. + + This could be either configured in OpenAPI spec or configuration file e.g `README.md`. You could learn more in [Authentication in AutoRest](https://github.com/Azure/autorest/blob/main/docs/generate/authentication.md). + + Here are the details if we configure in README.md file. + - Support AAD token authentication + ```yaml + security: AzureKey + security-header-name: Your-Subscription-Key + ``` + - Support key authentication + ```yaml + security: AADToken + security-scopes: https://yourendpoint.azure.com/.default + ``` + - Support both credentials + ```yaml + security: + - AADToken + - AzureKey + security-header-name: Your-Subscription-Key + security-scopes: https://yourendpoint.azure.com/.default + ``` + - Disable neither authentications + ```yaml + add-credentials: false + ``` + + --- + **NOTE** + + It's always recommended to replace the version of code generator @autorest/typescript with the latest version you can find in [npmjs.com](https://www.npmjs.com/package/@autorest/typescript) in latest tag. + + If the `input-file` is followed by an `. md` file, you need to replace the `input-file` with `require`. If it is a `JSON` file, do not change it. + + We enable the samples generation by default, this may fail the generation due to the example quality or codegen issue. You could turn this option off by `generate-sample: false` to non-block your process. + + **After the first generation, you need to switch `generate-metadata: false` as we have some manual changes in this file and don't want them get overwrite by generated ones.** + + --- + +1. **Edit rush.json** + As the libraries in this azure-sdk-for-js repository are managed by rush, you need to add an entry in rush.json under projects section for the first time to make sure it works. For example: + + ``` + { + "packageName": "@azure-rest/agrifood-farming", + "projectFolder": "sdk/agrifood/agrifood-farming-rest", + "versionPolicyName": "client" + }, + ``` + + Here you also need to replace the `packageName`, `projectFolder` into your own services'. + + --- + **NOTE** + + About the `versionPolicyName`, if the library you are working on is for data-plane, then it should be `client`, if the library you are working on is for control plane, then it should be `mgmt`. + + --- + +1. **Run autorest to generate the SDK** + + Now you can run this command in swagger folder you just created. + + ```shell + autorest --typescript ./README.md + ``` + + After this finishes, you will see the generated code in `${PROJECT_ROOT}/src` folder. + After that, you can get a workable package, and run the following commands to get a artifact if you like. + + ```shell + rush update + rush build -t + cd + rushx pack + ``` + + But we still need to add some tests for it. + +# Improve README.md document + +A minimal README.md is generated by our codegen and you could improve it with more meaningful information. + + +# How to write test for RLC + +In order to release it, we need to add some tests for it to make sure we are delivering high quality packages. But before we add the test, we need to add a `generate-test: true` make the codegen generate the necessary change in `package.json` and `tsconfig.json` so that test framework can work. Once the generation finished, you will see a `sampleTest.spec.ts` file in your `{PROJECT_ROOT}/test/public` folder, which only has an empty test and you may change them into test against your own services. + +See the [Javascript Codegen Quick Start for Test](./Quickstart-on-how-to-write-tests.md) for information on how to write and run tests for the Javascript SDK. + +1. **Prerequisites** + + To record and playback the tests, [Docker](https://www.docker.com/) is required when we run the test, as the [test proxy server](https://github.com/Azure/azure-sdk-tools/tree/main/tools/test-proxy) is run in a container during testing. When running the tests, ensure the Docker daemon is running and you have permission to use it. + +1. **Write the test** + + You could follow the [basic RLC test interaction and recording example](./Quickstart-on-how-to-write-tests.md#example-1-basic-rlc-test-interaction-and-recording-for-azure-data-plane-service) to write your own test step by step. + +1. **Run the test** + Now, you can run the test like this. If you are the first time to run test, you need to set the environment variable `TEST_MODE` to `record`. This will generate recordings for your test they could be used in `playback` mode. + On Linux, you could use `export` to set env variable: + ```shell + rush build -t ${PACKAGE_NAME} + export TEST_MODE=record && rushx test # this will run live test and generate a recordings folder, you will need to submit it in the PR. + ``` + On Windows, you could use `SET`: + ```shell + rush build -t ${PACKAGE_NAME} + SET TEST_MODE=record&& rushx test # this will run live test and generate a recordings folder, you will need to submit it in the PR. + ``` + You can also run the `playback` mode test if your apis don't have breaking changes and you've already done the recording before. + On Linux, you could use below commands: + ```shell + rush build -t ${PACKAGE_NAME} + export TEST_MODE=playback && rushx test # this will run live test and generate a recordings folder, you will need to submit it in the PR. + ``` + On Windows, you can use: + ```shell + rush build -t ${PACKAGE_NAME} + SET TEST_MODE=playback&& rushx test # this will run live test and generate a recordings folder, you will need to submit it in the PR. + ``` + + +# How to write samples + +If you enable `generate-sample: true` option the codegen would do two things for you: +- Add samples metadata in `tsconfig.json` and `package.json` +- Generate a collection of Typescript sample files (based on x-ms-examples in OpenAPI specs) under `samples-dev` folder. + +Please notice that the generated samples might not be directly usable as runnable codes, however we could get the basic idea on how code works, and update them to be more valuable samples. + +And the errors may come from two kinds, the codegen issue or swagger example issue. For the former one we need to report them with codegen owner while as for the latter one we need to fix our swagger examples. + +Now, you can generate both JavaScript and TypeScript workable samples with the following commands. +```shell +npm install -g common/tools/dev-tool # make sure you are in the azure-sdk-for-js repo root directory +cd ${PROJECT_ROOT} +npx dev-tool samples publish -f +``` +You will see the workable samples in the `${PROJECT_ROOT}/samples` folder. + +# Format both the generated code and manual code +After you have finished the generation and added your own tests or samples, You can use the following command to format the code. +```shell +cd ${PROJECT_ROOT} && rushx format +``` + +Also we'll recommand you to run `lint` command to analyze your code and quickly find any problems. + +```shell +cd ${PROJECT_ROOT} && rushx lint +``` + +And we could use `lint:fix` if there are any errors. + +```shell +cd ${PROJECT_ROOT} && rushx lint:fix +``` + +# How to create package + +Now we can use the exact same steps to build an releasable artifact. + +```shell +rush update +rush build -t +cd +export TEST_MODE=record && rushx test +rushx pack +``` +You may send this artifact to your customer if your services are still in private preview and some customers want to try it out. +# Create/Update the ci.yaml + +Now, if everything looks good to you, you can submit a PR in azure-sdk-for-js repo with all the changes you made above. Before you do that, you need to add/update the ci.yml file. Depends on whether there's already one in your package folder. + +If there's no such file then you can add the following template. + +``` yaml +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. +trigger: + branches: + include: + - main + - release/* + - hotfix/* + paths: + include: + - sdk/purview/ +pr: + branches: + include: + - main + - feature/* + - release/* + - hotfix/* + exclude: + - feature/v4 + paths: + include: + - sdk/purview/ +extends: + template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: purview + Artifacts: + - name: azure-rest-agrifood-farming + safeName: azurerestagrifoodfarming +``` + +Please change the paths.include value as your own project path, and change the Artifacts name and safeName into yours. + +If there's already a ci.yml file in your project path. then the only thing you need to do is to add the Artifacts name and safeName of yours into that ci.yml. + +Please notice the Artifacts name should align with your package name. Here the package name is `@azure-rest/agrifood-farming` so the relevant Artifacts name is `azure-rest-agrifood-farming`. + + +# Prepare PR + +The codegen can only help you generate SDK code, there is something you need to update manually: + +## CHANGELOG.md + +CHANGELOG can help customers know the change of new version quicky, so you need to update the it according to the change of this new version. It is also necessary to update release date like `1.0.0-beta.1 (2022-11-11)`(rough time is fine and no need to be very accurate). + +## Version Number + +You shall update the version number according to [semantic versioning rule](https://semver.org/). + +## Test recordings + +Please ensure that your test recordings are committed together with your code. + +## Fix CI for PR +You may meet the CI failures after submitting the PR, so please refer to [Troubleshoot CI Failure](./Troubleshoot-ci-failure.md) to fix it. + +# Create API View +When submitting a PR our pipeline would automatically prepare the API view in [API View Website](https://apiview.dev/). You could see an [example link](https://github.com/Azure/azure-sdk-for-js/pull/23866#issuecomment-1316259448) here. You could click the API view link in that comment to know more details. + +# Release + +After the PR is merged, it is time to release package. Here is the [Release Checklist](https://dev.azure.com/azure-sdk/internal/_wiki/wikis/internal.wiki/8/Release-Checklist?anchor=prepare-release-script) you should know before release. diff --git a/documentation/RLC-quickstart.md b/documentation/RLC-quickstart.md index 549c93ebfd1..8d3442af330 100644 --- a/documentation/RLC-quickstart.md +++ b/documentation/RLC-quickstart.md @@ -1,68 +1,75 @@ -Getting Started - Generate the RLC rest-level client libraries -================================================================ +Getting Started - Generate the RLC rest-level client libraries with Cadl +======================================================================== -# Prerequisites +# Before you start -You may refer to this [link](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md#prerequisites) for the environment set up prerequisites in azure-sdk-for-js repository. +Please refer to this [link](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md#prerequisites) for the environment set up prerequisites in azure-sdk-for-js repository. We highly recommand to read [this blog](https://devblogs.microsoft.com/azure-sdk/azure-rest-libraries-for-javascript/) to get familiar with REST libraries for JavaScript. -# Project folder and name convention +:warning: Note: if you’re still generating from Swagger with RLC, please read [this doc](./RLC-Swagger-quickstart.md) for Swagger specific details. -Before we start, we probably should get to know the project folder and name convention for RLC libraries. -1. Project Folder structure. - normally, the folder structure would be something like `sdk/{servicename}/{servicename}-{modulename}-rest`. For example, we have `sdk/agrifood/agrifood-farming-rest` folder for Farmbeats account modules. That folder will be your **${PROJECT_ROOT} folder**. +# Project folder structure and name convention + +If you are the first time to prepare the SDK, please follow the Azure SDK guidance and discuss with architects to decide the project folder and name convention for RLC libraries. + +1. SDK Repo Root. + The generated libraries should be in the [azure-sdk-for-js](https://github.com/Azure/azure-sdk-for-js) repo, so fork and clone it in your local then the absolute path is called **${SDK_REPO_ROOT} folder**. + +1. Project Folder Structure. + Normally, the folder structure would be something like `sdk/{servicename}/{servicename}-{modulename}-rest`. For example, we have `sdk/agrifood/agrifood-farming-rest` folder for Farmbeats account modules under {SDK_REPO_ROOT}. That folder will be your **${PROJECT_ROOT} folder**. 1. Package Name Convention. The package name for RLC is something like `@azure-rest/{servicename}-{modulename}`. For example, the package name for Farmbeats module is `@azure-rest/agrifood-farming`. + # How to generate RLC We are working on to automatically generate everything right now, but currently we still need some manual work to get a releasable package. Here're the steps of how to get the package. -1. **Create a swagger/README.md file.under ${PROJECT_ROOT} folder** - We are using autorest to generate the code, but there's a lot of command options and in order to make the regenerate process easier in the cases of refresh the rest api input or change the code generator version, you need to document the generate command parameters. - Here's an example of the swagger/README.md +1. **Add Typescript emitter dependency in package.json** + + In Cadl project, modify `package.json` to add dependency for Typescript emitter, then run `npm install` again to install `@azure-tools/cadl-typescript`. - ~~~ - - # Azure Farmbeats TypeScript Protocol Layer - - > see https://aka.ms/autorest - ## Configuration + ```json + "dependencies": { + ... + "@azure-tools/cadl-typescript": "1.0.0-beta.4" + }, + ``` + + --- + **NOTE** + + It's always recommended to replace the version of emitter cadl-typescript with the latest version you can find in [npmjs.com](https://www.npmjs.com/package/@azure-tools/cadl-typescript) in latest tag. + + --- + +1. **Configure Typescript emitter in cadl-project.yaml** + + In Cadl project, modify (or create) `cadl-project.yaml` and configure the SDK generated, using the emitter options on `@azure-tools/cadl-typescript` ```yaml - package-name: "@azure-rest/agrifood-farming" - title: Farmbeats - description: Farmbeats Client - generate-metadata: true - generate-test: true - license-header: MICROSOFT_MIT_NO_VERSION - output-folder: ../ - source-code-folder-path: ./src - input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/683e3f4849ee1d84629d0d0fa17789e80a9cee08/specification/agfood/data-plane/Microsoft.AgFoodPlatform/preview/2021-03-31-preview/agfood.json - package-version: 1.0.0-beta.2 - rest-level-client: true - security: AADToken - security-scopes: https://farmbeats.azure.net/.default - use-extension: - "@autorest/typescript": "6.0.0-rc.1" + emitters: + "@azure-tools/cadl-typescript": + title: Farmbeats + generateMetadata: true + generateTest: true + packageDetails: + name: "@azure-rest/agrifood-farming" + description: "Farmbeats Client" + version: "1.0.0-beta.1" ``` - ~~~ - Here, we need to replace the value in `package-name`, `title`, `description`, `input-file`, `package-version`, `security`,`security-scopes` into **your own service's** `package-name`, `title`, `description` etc. See [security configurations](#how-to-configure-authentication) for more details. - - --- - **NOTE** - - It's always recommended to replace the version of code generator @autorest/typescript with the latest version you can find in [npmjs.com](https://www.npmjs.com/package/@autorest/typescript) in latest tag. - - If the `input-file` is followed by an `. md` file, you need to replace the `input-file` with `require`. If it is a `JSON` file, do not change it - - **After the first generation, you need to switch `generate-metadata: false` as we have some manual changes in this file and don't want them get overwrite by generated ones.** + Here, we need to replace the value in `name`,`description`, `version` in `packageDetails` to **your own service's** package details. Also we have some other options, you could refer to [the link](https://github.com/Azure/autorest.typescript/tree/main/packages/cadl-typescript#emitter-options) for more details. --- - -1. **edit rush.json** - As the libraries in this azure-sdk-for-js repository are managed by rush, you need to add an entry in rush.json under projects section for the first time to make sure it works. For example: + **NOTE** + + After the first generation, you need to switch `generateMetadata: false` as we have some manual changes in this file and don't want them get overwrite by generated ones. + + --- + +1. **Edit rush.json** + As the libraries in azure-sdk-for-js repository are managed by rush, you need to add an entry in rush.json under projects section for the first time to make sure it works. For example: ``` { @@ -79,17 +86,19 @@ We are working on to automatically generate everything right now, but currently About the `versionPolicyName`, if the library you are working on is for data-plane, then it should be `client`, if the library you are working on is for control plane, then it should be `mgmt`. - --- + --- -1. **run autorest to generate the SDK** +1. **Run command to generate the SDK** - Now you can run this command in swagger folder you just created. + We need to configure `--output-dir` to put generated code. The output dir contains two parts: the {SDK_REPO_ROOT} and {PROJECT_ROOT}. + + Assume **{SDK_REPO_ROOT}** is `D:/azure-sdk-for-js` and **{PROJECT_ROOT}** is `sdk/agrifood/agrifood-farming-rest` then we could run this command in **your local Cadl project** to generate the SDK: ```shell - autorest --typescript ./README.md + cadl compile . --emit=@azure-tools/cadl-typescript --output-dir=D:/azure-sdk-for-js/sdk/agrifood/agrifood-farming-rest ``` - After this finishes, you will see the generated code in `${PROJECT_ROOT}/src` folder. + After this finishes, you will see the generated code in `src` folder in your **{PROJECT_ROOT}**. After that, you can get a workable package, and run the following commands to get a artifact if you like. ```shell @@ -99,19 +108,27 @@ We are working on to automatically generate everything right now, but currently rushx pack ``` - But we still need to add some tests for it. + The generated code is not good enough to release yet and you need to update it for better usage experience. + +# Improve README.md document + +A minimal README.md is generated by TypeScript emitter and you could improve it with meaningful information to let customers know more about your service. # How to write test for RLC -In order to release it, we need to add some tests for it to make sure we are delivering high quality packages. but before we add the test, we need to add a `generate-test: true` make the code generator generate the necessary change in `package.json` and `tsconfig.json` so that test framework can work. Once the generation finished, you will see a `sampleTest.spec.ts` file in your `{PROJECT_ROOT}/test/public` folder, which only has a empty test and you may change them into test against your own services. +In order to release it, we need to add some tests for it to make sure we are delivering high quality packages. But before we add the test, we need to add a `generateTest: true` make the codegen generate the necessary change in `package.json` and `tsconfig.json` so that test framework can work. Once the generation finished, you will see a `sampleTest.spec.ts` file in your `{PROJECT_ROOT}/test/public` folder, which only has an empty test and you may change them into test against your own services. -See the [Javascript Codegen Quick Start for Test](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/test-quickstart.md) for information on how to write and run tests for the Javascript SDK. +See the [Javascript Codegen Quick Start for Test](./Quickstart-on-how-to-write-tests.md) for information on how to write and run tests for the Javascript SDK. -## Prerequisites +1. **Prerequisites** -- To record and playback the tests, [Docker](https://www.docker.com/) is required when we run the test, as the [test proxy server](https://github.com/Azure/azure-sdk-tools/tree/main/tools/test-proxy) is run in a container during testing. When running the tests, ensure the Docker daemon is running and you have permission to use it. + To record and playback the tests, [Docker](https://www.docker.com/) is required when we run the test, as the [test proxy server](https://github.com/Azure/azure-sdk-tools/tree/main/tools/test-proxy) is run in a container during testing. When running the tests, ensure the Docker daemon is running and you have permission to use it. -1. **run the test** +1. **Write the test** + + You could follow the [basic RLC test interaction and recording example](./Quickstart-on-how-to-write-tests.md#example-1-basic-rlc-test-interaction-and-recording-for-azure-data-plane-service) to write your test step by step. + +1. **Run the test** Now, you can run the test like this. If you are the first time to run test, you need to set the environment variable `TEST_MODE` to `record`. This will generate recordings for your test they could be used in `playback` mode. On Linux, you could use `export` to set env variable: ```shell @@ -137,12 +154,12 @@ See the [Javascript Codegen Quick Start for Test](https://github.com/Azure/azure # How to write samples -We author TypeScript samples under the `samples-dev` folder. You can use sample-dev template for reference [samples-dev folder](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/template/template/samples-dev) folder and update the relevant information for your service such as package-name, sample code, description, etc. +We author TypeScript samples under the `samples-dev` folder. You can use sample-dev template for reference [samples-dev folder](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/template/template/samples-dev) folder and update the relevant information for your service such as package-name, sample code, description, etc. After the samples-dev folder change is finished, you will need to change the tsconfig.json to make sure the dev samples can be compiled and build correctly. You will need to add this part to the "compilerOptions" of your tsconfig.json file so that the Samples engine could resolve the sample-dev package against the source code of the SDK. ``` json - "paths": { "@msinternal/sql-resource-manager": ["./src/index"] } + "paths": { "@azure-rest/agrifood-farming": ["./src/index"] } ``` And change the *"include"* part into ```json @@ -165,7 +182,7 @@ Now, you can generate both JavaScript and TypeScript workable samples with the f ```shell npm install -g common/tools/dev-tool # make sure you are in the azure-sdk-for-js repo root directory cd ${PROJECT_ROOT} -dev-tool samples publish -f +npx dev-tool samples publish -f ``` You will see the workable samples in the `${PROJECT_ROOT}/samples` folder. @@ -174,10 +191,27 @@ After you have finished the generation and added your own tests or samples, You ```shell cd ${PROJECT_ROOT} && rushx format ``` + +Also we'll recommand you to run `lint` command to analyze your code and quickly find any problems. + +```shell +cd ${PROJECT_ROOT} && rushx lint +``` + +And we could use `lint:fix` if there are any errors. + +```shell +cd ${PROJECT_ROOT} && rushx lint:fix +``` + +# How to do customizations +There is many information about the SDK that AutoRest will never know, so you may want to do your customizations based on generated code. + +We collect some common customization cases and you can read [Customization on the RLC rest-level client libraries](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/RLC-customization.md) for more details. # How to create package -Now we can use the exact same steps to build an releasable artifact. +Now we can use the exact same steps to build a releasable artifact. ```shell rush update @@ -229,39 +263,30 @@ Please change the paths.include value as your own project path, and change the A If there's already a ci.yml file in your project path. then the only thing you need to do is to add the Artifacts name and safeName of yours into that ci.yml. +Please notice the Artifacts name should align with your package name. Here the package name is `@azure-rest/agrifood-farming` so the relevant Artifacts name is `azure-rest-agrifood-farming`. + + +# Prepare PR + +Cadl emitter can only help you generate SDK code, there is something you need to update manually: + +## CHANGELOG.md + +CHANGELOG can help customers know the change of new version quickly, so you need to update the it according to the change of this new version. It is also necessary to update release date like `1.0.0-beta.1 (2022-11-11)`(rough time is fine and no need to be very accurate). + +## Version Number + +You shall update the version number according to [semantic versioning rule](https://semver.org/). + +## Test recordings + +Please ensure that your test recordings are committed together with your code. + +## Fix CI for PR +You may meet the CI failures after submitting the PR, so please refer to [Troubleshoot CI Failure](./Troubleshoot-ci-failure.md) to fix it. + # Create API View -You may also want to create API View when submitting a PR. You can do it easily by uploading a json file to [API View Website](https://apiview.dev/). The json file is under `/temp`, and its name ends with `api.json`. For example: `sdk/compute/arm-compute/temp/arm-compute.api.json`. +When submitting a PR our pipeline would automatically prepare the API view in [API View Website](https://apiview.dev/). You could see an [example link](https://github.com/Azure/azure-sdk-for-js/pull/23866#issuecomment-1316259448) here. Then you could click the API view link in that comment to know more details. +# Release -# How to do customizations -There is many information about the SDK that AutoRest will never know, so you may want to do your customizations based on generated code. - -We collect some common customization cases and you can read [Customization on the RLC rest-level client libraries](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/RLC-customization.md) for more details. - -# How to configure authentication -Autorest only support two types of authentication: Azure Key Credential(AzureKey) and Token credential(AADToken), any other will need to be handled manually. - -This could be either configured in OpenAPI spec or configuration file e.g `README.md`. You could learn more in [Authentication in AutoRest](https://github.com/Azure/autorest/blob/main/docs/generate/authentication.md). - -Here are the details if we configure in README.md file. -- Support AAD token authentication -```yaml -security: AzureKey -security-header-name: Your-Subscription-Key -``` -- Support key authentication -```yaml -security: AADToken -security-scopes: https://yourendpoint.azure.com/.default -``` -- Support both credentials -```yaml -security: - - AADToken - - AzureKey -security-header-name: Your-Subscription-Key -security-scopes: https://yourendpoint.azure.com/.default -``` -- Disable neither authentications -```yaml -add-credentials: false -``` +After the PR is merged, it is time to release package. Here is the [Release Checklist](https://dev.azure.com/azure-sdk/internal/_wiki/wikis/internal.wiki/8/Release-Checklist?anchor=prepare-release-script) you should know before release. diff --git a/documentation/Troubleshoot-ci-failure.md b/documentation/Troubleshoot-ci-failure.md new file mode 100644 index 00000000000..80de374501d --- /dev/null +++ b/documentation/Troubleshoot-ci-failure.md @@ -0,0 +1,11 @@ +# Overview +This doc shows some common problems and resolution in CI. + +# Broken links +![image](./images/broken-links.png) + +Add the broken links into [eng/ignore-links.txt](https://github.com/Azure/azure-sdk-for-js/blob/main/eng/ignore-links.txt) file to bypass this verification or you could update the broken links to valid ones, see [example pr here](https://github.com/Azure/azure-sdk-for-js/pull/23429/commits/1a7b74c4bdad27e423a355a4c7f3dde4ac3c83bc). + + +## Check spelling (cspell) +For new service the error usually happens, Fix spelling in code or in markdown at file [.vscode/cspell.json](https://github.com/Azure/azure-sdk-for-js/blob/main/.vscode/cspell.json). See an example in [devcenter pr](https://github.com/chrissmiller/azure-sdk-for-js/commit/ef18dccae59e98185e3854f8b087230b65735744). diff --git a/documentation/images/broken-links.png b/documentation/images/broken-links.png new file mode 100644 index 0000000000000000000000000000000000000000..d33a03dd5fa59ed7ab17b8cdd1ebaff934242772 GIT binary patch literal 29869 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 F-vCFp^z#4! literal 0 HcmV?d00001