{"id":2426,"date":"2023-02-13T12:55:31","date_gmt":"2023-02-13T20:55:31","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/azure-sdk\/?p=2426"},"modified":"2023-02-17T13:20:13","modified_gmt":"2023-02-17T21:20:13","slug":"level-up-your-cloud-testing-game-with-the-azure-sdk-test-proxy","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/azure-sdk\/level-up-your-cloud-testing-game-with-the-azure-sdk-test-proxy\/","title":{"rendered":"Level up your cloud testing game with the Azure SDK test proxy"},"content":{"rendered":"<p>Testing is an essential step in the software development\nprocess. Finding bugs through testing before releasing software to\nproduction saves time and money over the lifetime of the product. However, testing software\nagainst a live cloud service like Azure can be costly, since services must\nbe provisioned and maintained in order to run the tests.<\/p>\n<p>The Azure SDK team does a large amount of testing prior to releasing SDK libraries.\nEven though we&#8217;re part of the Azure business, using Azure resources still\nincurs significant costs we must account for. Nothing is free after all,\nand running our tests requires spinning up and maintaining cloud\nresources that incur costs to the company.<\/p>\n<p>To reduce our cloud costs, the Azure SDK team created a lightweight test proxy\nthat records app interactions with Azure and plays them back on\ndemand, eliminating the need to access live Azure services.\nThe test proxy uses key features from our Azure Core library to capture, record,\nand play back interactions between the client and Azure services.<\/p>\n<p>Two significant benefits of using recordings are that you don&#8217;t need to create mock\nimplementations for each service interaction and unit testing is greatly accelerated.<\/p>\n<p>Our internal use of the test proxy has been a huge benefit to us, and we&#8217;re excited\nto share this tool with the broader Azure developer community.<\/p>\n<p>Although the test proxy isn&#8217;t an officially supported Microsoft product, we believe\nit&#8217;s useful enough to be freely available as part of our open source\n<a href=\"https:\/\/github.com\/Azure\/azure-sdk-tools\/tree\/main\/tools\/test-proxy\">Azure SDK Tools<\/a>\nGitHub repository. We welcome contributions and are grateful for any support in enhancing\nthe usefulness of the test proxy.<\/p>\n<p>If you&#8217;d like a brief overview of how the test proxy works and you can\nspare four minutes, watch this video first:<\/p>\n<p><iframe title=\"YouTube video player\" src=\"https:\/\/www.youtube.com\/embed\/T_o3PipAYbg\" width=\"560\" height=\"315\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<p>The test proxy provides record and playback capabilities compatible with Azure SDKs for .NET, Python,\nJava, JavaScript, Go, and C++.<\/p>\n<p>To use the test proxy, you must be able to reroute your app requests to\nthe test proxy via modifications to the request headers.<\/p>\n<p>Although this article was written using C# constructs and\nsyntax, the same principles apply to our SDKs for Python, Java,\nJavaScript, Go, and C++.<\/p>\n<p>To integrate the record and playback text proxy into your testing\nenvironment, it&#8217;s important to have a basic understanding of some of\nthe key concepts regarding how Azure SDK libraries work.<\/p>\n<h2>Azure SDKs<\/h2>\n<p><a href=\"http:\/\/aka.ms\/azsdk\">Azure SDKs<\/a> are a set of language-specific\nlibraries we offer that makes it easier for developers to interact with\nAzure services, such as storage, compute, and databases, by providing a\nconsistent and easy-to-use interface.<\/p>\n<p>These libraries abstract away the complexity of interacting with Azure\nservices, allowing you to focus on building your application logic.<\/p>\n<h2>Azure service clients<\/h2>\n<p>A service client refers to the component\nof an Azure SDK that allows your app to interact with a specific Azure\nservice via an API.<\/p>\n<p>For example, if you want to interact with Azure Table Storage in C#, you\nuse a Table Storage client provided by the <a href=\"https:\/\/www.nuget.org\/packages\/Azure.Data.Tables\">Azure.Data.Tables<\/a> library. The Table\nStorage client allows you to perform operations like creating, reading,\nupdating, and deleting table resources.<\/p>\n<h2>HTTP pipeline<\/h2>\n<p>The Azure SDK HTTP pipeline is the part of a service client responsible for\nmanaging communication between your app and Azure. It establishes the\nconnection, sends and receives HTTP messages, and acts as the gateway\nfor the Azure SDK clients in your app to interact with Azure services.\nOur HTTP pipeline consists of two main components: an HTTP transport\nand HTTP pipeline policies.<\/p>\n<p>HTTP pipeline policies are a series of steps executed for each HTTP\nrequest-response roundtrip. Each policy has a specific purpose, and it\nmay act on a request, a response, or both. Some policy examples are a\nretry policy, an authentication policy, and a logging policy.<\/p>\n<p>To learn more about HTTP pipeline policies, <a href=\"https:\/\/learn.microsoft.com\/_themes\/docs.theme\/master\/en-us\/_themes\/global\/video-embed.html?show=on-net&amp;ep=understanding-the-azurecore-library\">this\nvideo<\/a>\nis a great resource.<\/p>\n<p>For the remainder of this article, I&#8217;m focusing on the HTTP\ntransport component.<\/p>\n<h2>HTTP transport<\/h2>\n<p>Every Azure SDK that adheres to our <a href=\"https:\/\/azure.github.io\/azure-sdk\/general_introduction.html\">current design\nguidelines<\/a>\nuses the <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/overview\/azure\/core-readme?view=azure-dotnet\">Azure.Core<\/a>\nshared client library for handling common functionality. The HTTP\nPipeline Transport is a class in the Azure Core library that is used to\nsend HTTP requests and receive responses.\n<a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/azure.core.pipeline.httppipelinetransport?view=azure-dotnet\">HttpPipelineTransport<\/a>\nis an abstract class, so it <strong>must be implemented<\/strong> either by a service\nclient or your own custom implementation before it can be used.<\/p>\n<p>When you create an Azure service client using the Azure SDKs, the Azure\nCore library creates a transport object for you by default in the form\nof an <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/azure.core.pipeline.httpclienttransport?view=azure-dotnet\">HttpClientTransport <\/a>object.<\/p>\n<p>It&#8217;s possible to provide your own implementation of\n<code>HttpPipelineTransport<\/code>, overriding the default Azure Core HTTP pipeline.\nThe ability to override the default HTTP transport is what allows us to\ninsert our record and playback test proxy into the mix.<\/p>\n<p>You only need one custom instance of an <code>HttpPipelineTransport<\/code>,\nregardless of how many Azure services you&#8217;re connecting to. All the SDK service clients\ncan utilize the same transport.<\/p>\n<p>The <code>HttpPipelineTransport<\/code> class contains three abstract methods:<\/p>\n<ul>\n<li><code>CreateRequest()<\/code> &#8211; Creates a new transport-specific instance of an\nhttp request.<\/li>\n<li><code>Process(HttpMessage)<\/code> &#8211; Sends the request contained by <code>HttpMessage<\/code>\nand sets the <code>Response<\/code> property to the response received. <code>Process()<\/code>\nis a synchronous, blocking call.<\/li>\n<li><code>ProcessAsync(HttpMessage)<\/code> &#8211; Sends the request contained by\n<code>HttpMessage<\/code> and sets the <code>Response<\/code> property to the response received.\n<code>ProcessAsync()<\/code> is an asynchronous, non-blocking call.<\/li>\n<\/ul>\n<p>All three methods are defined as <code>abstract<\/code> in the base\n<code>HttpPipelineTransport<\/code> class, which means they all need to be implemented\nby you in your application.<\/p>\n<h2>Client options<\/h2>\n<p>You can override the default HTTP transport of an <code>HttpPipelineTransport<\/code>\nclass using service client options. These service client options are\npassed to the service client constructor when you instantiate a service\nclient object. All SDK clients have their own client options, but all\nclient options are derived from a common <code>ClientOptions<\/code> class defined in\nAzure.Core.<\/p>\n<p>To find the name of the derived <code>ClientOptions<\/code> class for the services\nyou&#8217;re using, refer to the\n<a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/azure.core.clientoptions?view=azure-dotnet\">ClientOptions<\/a>\ndocumentation and expand the list of &#8216;Derived&#8217; classes to search for\nyour service.<\/p>\n<p>The demo associated with this article is using the Cosmos DB API for Table\nStorage, so are defining a\u00a0<a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/azure.data.tables.tableclientoptions?view=azure-dotnet\">TableClientOptions <\/a>class to use for overriding the HTTP transport.<\/p>\n<h2>Record &amp; playback test proxy<\/h2>\n<p>Now that we&#8217;ve established some context, we can talk about the test proxy. To recap, Azure SDK service client traffic is redirected to an intermediary test proxy bound to a <code>localhost<\/code> port. The rerouting is done by changing the HTTP transport utilized by the service clients. The test proxy captures and saves requests and responses during the recording process, then forwards the requests between your app and Azure. Afterwards, the recorded information can be used to simulate Azure during testing by supplying your app with the appropriate request responses.<\/p>\n<p>Full test proxy installation details and documentation are available in\nthe <a href=\"https:\/\/github.com\/Azure\/azure-sdk-tools\/tree\/main\/tools\/test-proxy\/Azure.Sdk.Tools.TestProxy#installation\">test proxy\nreadme<\/a>,\nbut here&#8217;s the short version:<\/p>\n<ol>\n<li><a href=\"https:\/\/dotnet.microsoft.com\/download\">Install .NET 6.0 or later<\/a><\/li>\n<li>Install the test-proxy:\n<pre><code>dotnet tool update azure.sdk.tools.testproxy \\--global \\--add-source https:\/\/pkgs.dev.azure.com\/azure-sdk\/public\/\\_packaging\/azure-sdk-for-net\/nuget\/v3\/index.json \\--version \\\"1.0.0-dev\\*\\\"<\/code><\/pre>\n<\/li>\n<\/ol>\n<p>After installing the tool, run it in a terminal or cmd window by typing\nthe command <code>test-proxy<\/code> in the terminal prompt. You know the test\nproxy is running correctly if you see output like this:<\/p>\n<p><img decoding=\"async\" class=\"\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2023\/02\/2023-2-13-Test-Proxy-Blog-Post-02-Running.jpg\" alt=\"Test proxy output\" width=\"600\" \/><\/p>\n<h2>Test proxy demo<\/h2>\n<p>Going forward I&#8217;m referring to code from a corresponding demo\nproject, which you can <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-csharp\" target=\"_blank\" rel=\"noopener\">get here<\/a>.\nThis project is based on an <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/table\/quickstart-dotnet?tabs=azure-portal%2Cwindows\" target=\"_blank\" rel=\"noopener\">existing Cosmos DB Table Storage demo<\/a>,\nwhich you should refer to for instructions on setting up a table\nresource to use with the test proxy demo.<\/p>\n<p><em>Although this demo was written in C#, the same principles apply to our SDKs for other languages. Demo projects are also available for <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-python\" target=\"_blank\" rel=\"noopener\">Python<\/a>, <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-java\" target=\"_blank\" rel=\"noopener\">Java<\/a>, <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-javascript\" target=\"_blank\" rel=\"noopener\">JavaScript<\/a>, and <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-go\" target=\"_blank\" rel=\"noopener\">Go<\/a>.<\/em><\/p>\n<p>The <code>Test.Proxy.Transport<\/code> namespace defined in <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-csharp\/blob\/97051dfb9990bb9057ee994527bc5af3723134a2\/TestProxyTransport.cs\">TestProxyTransport.cs<\/a>\ncontains necessary code to interact with the test proxy.\nYou can add this source file directly to your own project and add the\nfollowing directive to your main project source code:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2023\/02\/2023-2-13-Test-Proxy-Blog-Post-03-Using-Test.Proxy_.Transport-300x166.jpg\" alt=\"Using Test Proxy Transport Namespace\" width=\"300\" \/><\/p>\n<p>The <code>Test.Proxy.Transport<\/code> namespace consists of three classes:<\/p>\n<ul>\n<li><code>public class TestProxyTransport : HttpPipelineTransport<\/code>\n<ul>\n<li><code>TestProxyTransport<\/code> provides custom implementations of the abstract\nmethods defined in the <code>HttpPipelineTransport<\/code> base class and described\nin the HTTP transport section of this article. These custom\nimplementations allow us to intercept and reroute app traffic sent\nbetween an app and Azure to the test proxy.<\/li>\n<\/ul>\n<\/li>\n<li><code>public class TestProxyVariables<\/code>\n<ul>\n<li><code>TestProxyVariables<\/code> encapsulates variables that store values related\nto the test proxy, such as connection host (localhost), connection\nport (5001), and mode (record\/playback).<\/li>\n<\/ul>\n<\/li>\n<li><code>public class TestProxyMethods<\/code>\n<ul>\n<li><code>TestProxyMethods<\/code> contains functions to start and stop a running test\nproxy processing traffic between your app and Azure.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>The <code>TestProxyTransport<\/code> implementations of <code>Process()<\/code> and <code>ProcessAysnc()<\/code>\ncontain custom code that stashes the original request URI as part of the\nmessage headers and redirects the request to point to the test proxy.<\/p>\n<p><img decoding=\"async\" class=\"\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2023\/02\/2023-2-13-Test-Proxy-Blog-Post-04-Rerouting-Requests.jpg\" alt=\"Rerouting http requests to the test proxy\" width=\"600\" \/><\/p>\n<p>When a request is redirected the test proxy, the test proxy performs\none of two sets of actions depending on whether it&#8217;s running in record\nor playback mode:<\/p>\n<ul>\n<li>Record mode:\n<ol>\n<li>Parse the request headers to obtain the original request URI.<\/li>\n<li>Save the request in the recording file specified by the\nrecording ID and the recording file path.<\/li>\n<li>Forward the original request to Azure using the URI obtained in\nstep 1<\/li>\n<li>When the Azure response arrives, save the response in the\nrecording file as a match to the original request and return the\nresponse to the client app.<\/li>\n<\/ol>\n<\/li>\n<li>Playback mode:\n<ol>\n<li>Parse the headers to obtain the original request URI.<\/li>\n<li>Look up the request URI in the recording file specified by the\nrecording ID and the recording test path.<\/li>\n<li>Return the corresponding response saved in the recording to the\nclient app.<\/li>\n<\/ol>\n<\/li>\n<\/ul>\n<p>Secrets like connections strings are <em>not<\/em> saved in the\nrecording file. Secrets are sanitized before the request is saved to\navoid leaking them.<\/p>\n<p>You can see the sanitization for yourself by looking at the demo project recording\nfile named <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-csharp\/blob\/97051dfb9990bb9057ee994527bc5af3723134a2\/recordings\/RecordAndPlaybackTestProxyDemo.json\">TestProxyExample.json<\/a>. The connection string that was used to access the Cosmos DB Table service has been sanitized\nfrom the recording:<\/p>\n<p><img decoding=\"async\" class=\"\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2023\/02\/2023-2-13-Test-Proxy-Blog-Post-05-Sanitized-Secrets.jpg\" alt=\"Sanitized secrets\" width=\"800\" \/><\/p>\n<p>Finally, I&#8217;ll cover how to actually integrate the test proxy with your\nexisting project.<\/p>\n<p>It&#8217;s easy to do, requiring a minimal amount of prologue and\nepilogue code.<\/p>\n<p>Here&#8217;s the code you need to add to your project as a prologue:<\/p>\n<p><img decoding=\"async\" class=\"\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2023\/02\/2023-2-13-Test-Proxy-Blog-Post-06-Prologue.jpg\" alt=\"Test proxy prologue code\" width=\"800\" \/><\/p>\n<p>You can see this example prologue (and the epilogue) in the demo project file named\n<a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-csharp\/blob\/97051dfb9990bb9057ee994527bc5af3723134a2\/CosmosDBTablesExample.cs\">CosmosDBTablesExample.cs<\/a>.<\/p>\n<p>Notice that the first thing the prologue does is load environment\nvariables related to the test proxy from a local <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-csharp\/blob\/97051dfb9990bb9057ee994527bc5af3723134a2\/.env\">.env file<\/a>. These\nvariables are then used to construct the custom <code>TestProxyTransport<\/code>\nobject.<\/p>\n<p>I&#8217;ve included a simple file parser class named <code>ParseDotEnvFile<\/code> in the\ndemo project file named <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-csharp\/blob\/97051dfb9990bb9057ee994527bc5af3723134a2\/ParseDotEnvFile.cs\">ParseDotEnvFile.cs<\/a>. You could use a third-party library for\nthis same purpose if you prefer.<\/p>\n<p>You don&#8217;t <em>have<\/em> to use a <code>.env<\/code> file for your environment variables, you\ncan set them however you prefer for your environment. You also don&#8217;t\nneed to use environment variables at all. The test proxy parameters\ncould be hard-coded in your test case if you choose.<\/p>\n<p>However, it&#8217;s possible to change the default port the test proxy binds\nto, so I recommend encoding these parameters as variables\nthat can be easily modified. For more information, see the <a href=\"https:\/\/github.com\/Azure\/azure-sdk-tools\/tree\/ec7fdf88739040b22ecaa92be67ae8472cb4ad48\/tools\/test-proxy\/Azure.Sdk.Tools.TestProxy#azure-sdk-tools-test-proxy\">test proxy\ndocumentation<\/a><\/p>\n<p>You also need to add a small epilogue to your code:<\/p>\n<p><img decoding=\"async\" class=\"\" src=\"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-content\/uploads\/sites\/58\/2023\/02\/2023-2-13-Test-Proxy-Blog-Post-07-Epilogue.jpg\" alt=\"Test proxy epilogue code\" width=\"600\" \/><\/p>\n<p>The only function of the epilogue is to stop the test proxy from recording or playback.<\/p>\n<p>Once stopped, the proxy is still active and running but will no longer service requests.\nThe proxy also saves recordings in JSON format once the <code>StopTestProxy()<\/code> method is called.<\/p>\n<p><strong>Your recording WILL NOT be saved if you don&#8217;t stop the test\nproxy at the end of your test.<\/strong><\/p>\n<p>The recording file included in the demo project is provided for illustration purposes only.\nIt can&#8217;t be used to run the demo in playback mode because the Cosmos DB Table resource it refers\nto no longer exists.<\/p>\n<p>When you first run the demo, you need to set <code>PROXY_MODE<\/code> to <code>record<\/code> and provide a valid Cosmos\nDB Table Storage connection string in the <a href=\"https:\/\/github.com\/Azure-Samples\/record-playback-test-proxy-demo-csharp\/blob\/97051dfb9990bb9057ee994527bc5af3723134a2\/.env\">.env file<\/a> to generate a new recording.<\/p>\n<p>Refer to this <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/table\/quickstart-dotnet?tabs=azure-portal%2Cwindows\">Cosmos DB Table Storage demo <\/a>for instructions on setting up a table\nresource to use with the test proxy demo.<\/p>\n<p>Once you have a valid recording, set <code>PROXY_MODE<\/code> to <code>playback<\/code> and rerun the demo.<\/p>\n<p>When you&#8217;re done with the demo, stop the test proxy by pressing &#8216;Ctrl-C`\nin the terminal window where it&#8217;s running.<\/p>\n<h2>Conclusion<\/h2>\n<p>Testing is a crucial step in the software development process,\nincluding software deployed to the cloud. Testing software\nagainst a live cloud service like Azure can be costly, since services must\nbe provisioned and maintained in order to run the tests.<\/p>\n<p>The Azure SDK team has developed a lightweight test proxy that\nallows us to record app interactions with Azure and play them back on\ndemand, significantly reducing our testing costs. We&#8217;re now excited to\nshare this tool with the broader Azure development community and invite\nyou to try it out for yourself.<\/p>\n<p>The test proxy is available for use as-is as part of our open source\n<a href=\"https:\/\/github.com\/Azure\/azure-sdk-tools\">Azure SDK Tools<\/a>\nproject and we welcome your contributions.<\/p>\n<p>The test proxy provides record\/playback capabilities\ncompatible with Azure SDKs for .NET, Python, Java, JavaScript, Go, and C++.\nTo use it in your testing, you need to be able to reroute your app requests\nto the test proxy via modifications to the request headers.<\/p>\n<p>If you have questions about the test proxy, leave them in a comment\nor create an issue in the <a href=\"https:\/\/github.com\/Azure\/azure-sdk-tools\">Azure SDK\nTools<\/a> GitHub repository with\nthe &#8216;Test-Proxy&#8217; label.<\/p>\n<p>Happy coding!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introducing a test proxy that allows you to record app interactions with Azure and play them back on demand, which can significantly reduce your testing costs.<\/p>\n","protected":false},"author":63526,"featured_media":2478,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[886,887,885,884,880,881,882,883,782],"class_list":["post-2426","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-sdk","tag-cut-cloud-costs","tag-reduce-cloud-expenses","tag-reduce-cloud-testing-costs","tag-reducing-cloud-costs","tag-reducing-costs","tag-saving-money","tag-software-development","tag-software-testing","tag-testing"],"acf":[],"blog_post_summary":"<p>Introducing a test proxy that allows you to record app interactions with Azure and play them back on demand, which can significantly reduce your testing costs.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/2426","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/users\/63526"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/comments?post=2426"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/2426\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media\/2478"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media?parent=2426"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/categories?post=2426"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/tags?post=2426"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}