{"id":13446,"date":"2018-04-24T09:14:32","date_gmt":"2018-04-24T12:14:32","guid":{"rendered":"https:\/\/king.host\/blog\/?p=13446"},"modified":"2024-06-03T16:55:33","modified_gmt":"2024-06-03T19:55:33","slug":"testes-de-contrato-pact-php","status":"publish","type":"post","link":"https:\/\/king.host\/blog\/tecnologia\/testes-de-contrato-pact-php\/","title":{"rendered":"Testes de Contrato para Microsservi\u00e7os com Pact PHP"},"content":{"rendered":"<p><strong>Este artigo ir\u00e1 abordar testes de contrato para microsservi\u00e7os com Pact em PHP.<br \/>\n<\/strong><strong>O desenvolvimento de sistemas sempre modificou-se rapidamente ao decorrer do tempo<\/strong>. Novos paradigmas de programa\u00e7\u00e3o foram criados, novos tipos de linguagem, metodologias e processos. A arquitetura dos softwares tamb\u00e9m n\u00e3o poderia ficar fora, come\u00e7ando com uma arquitetura monol\u00edtica, passando para uma arquitetura orientada a servi\u00e7os (<a href=\"https:\/\/www.service-architecture.com\/articles\/web-services\/service-oriented_architecture_soa_definition.html\" target=\"_blank\" rel=\"noopener\">SOA<\/a>), at\u00e9 a arquitetura com maior destaque no momento, os microsservi\u00e7os.<\/p>\n<p>O aumento da utiliza\u00e7\u00e3o das arquiteturas baseadas em servi\u00e7os e microsservi\u00e7os fez com que um problema entrasse em foco, a quebra de contrato entre o fornecedor do servi\u00e7o e o cliente.<\/p>\n<p><strong>Um contrato \u00e9 uma cole\u00e7\u00e3o de acordos entre um cliente (Consumer) e uma API (Provider)<\/strong> que descreve as intera\u00e7\u00f5es que podem ocorrer entre eles.<\/p>\n<p><strong>Consumer Driven Contracts \u00e9 um padr\u00e3o que impulsiona o desenvolvimento do provider do ponto de vista do consumer.<\/strong> \u00c9 o TDD &#8211; Test Driven Development (desenvolvimento dirigido por testes) para servi\u00e7os.<\/p>\n<p>Basicamente o teste funciona da seguinte maneira: o sistema consumidor realiza uma chamada para o provedor e recebe os dados de retorno. A estrutura de dados retornada \u00e9 comparada com a estrutura definida no contrato. Se os dados n\u00e3o forem iguais o teste falha indicando que a API est\u00e1 retornando dados diferentes do que o cliente est\u00e1 esperando, ou seja, houve a quebra de contrato de algum dado recebido.<\/p>\n<p><strong>A ideia geral \u00e9 garantir a integridade da API antes que o sistema consumidor processe os dados da requisi\u00e7\u00e3o enviada pelo provedor<\/strong>, que pode enviar alguma informa\u00e7\u00e3o inconsistente que o consumidor ainda n\u00e3o consegue identificar como v\u00e1lida.<\/p>\n<h2>Testes de contrato com Pact PHP<\/h2>\n<p><strong>O <a href=\"https:\/\/docs.pact.io\/\" target=\"_blank\" rel=\"noopener\">Pact<\/a>, \u00e9 um framework de teste que ajuda voc\u00ea a escrever contratos, e garante que esses contratos estejam atendidos.<\/strong> O Pact tem implementa\u00e7\u00f5es para v\u00e1rias linguagens, entre elas, Java, .NET, Javascript, Go, Python, Swift e PHP.<\/p>\n<p>Para melhor entender como o Pact funciona vamos construir um exemplo do zero com PHP. <strong>Os exemplos abaixo s\u00e3o baseados na <a href=\"https:\/\/www.meetup.com\/pt-BR\/meetup_api\/\" target=\"_blank\" rel=\"noopener\">API do Meetup.com<\/a><\/strong>. Temos um cen\u00e1rio com um consumer\/client e um provider\/api.<\/p>\n<p>Primeiro vamos adicionar as depend\u00eancias que precisamos para realizar os testes, para isso vamos utilizar o composer.<\/p>\n<pre class=\"lang:php decode:true \">$ composer require phpunit\/phpunit --dev\n\n$ composer require mattersight\/phppact --dev<\/pre>\n<p>Ou criando um composer.json com o seguinte conte\u00fado:<\/p>\n<pre class=\"lang:php decode:true\">{\n\t\"require-dev\": {\n    \t\"phpunit\/phpunit\": \"^6.2\",\n    \t\"mattersight\/phppact\": \"^3.0\u201d\n\t}\n}\n<\/pre>\n<p>Vamos criar um diret\u00f3rio src, onde vamos adicionar nossa classe de client com o nome de \u2018ExampleOneMeetupApiClient.php\u2019:<\/p>\n<pre class=\"lang:php decode:true\">&lt;?php\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\Psr7\\Uri;\n\nclass ExampleOneMeetupApiClient\n{\n\tconst version = \"2\";\n\n\t\/**\n \t* @var GuzzleHttp\\Client\n \t*\/\n\tprivate $httpClient;\n\n\t\/**\n \t* @var \\GuzzleHttp\\Psr7\\Uri\n \t*\/\n\tprivate $baseUri;\n\n\tpublic function __construct($baseUri)\n\t{\n    \t$this-&gt;httpClient = new Client();\n    \t$this-&gt;baseUri = $baseUri;\n\t}\n\n\t\/**\n \t* @return \\Psr\\Http\\Message\\ResponseInterface\n \t*\/\n\tpublic function categories()\n\t{\n    \t$uri = $this-&gt;baseUri;\n    \t$uri = $uri-&gt;withPath(ExampleOneMeetupApiClient::version . '\/categories');\n\n    \t$response = $this-&gt;httpClient-&gt;get($uri, [\n        \t'headers' =&gt; ['Content-Type' =&gt; 'application\/json']\n    \t]);\n\n    \treturn $response;\n\t}\n}\n<\/pre>\n<p>Nesta classe estamos fazendo um get para a API de consulta de categorias no sistema do meetup.<\/p>\n<p>Agora <strong>criamos um diret\u00f3rio tests<\/strong> e neste diret\u00f3rio um arquivo chamado <strong>\u2018phpunit.example.one.xml<\/strong>\u2019 para configura\u00e7\u00e3o do <a href=\"https:\/\/phpunit.de\/\" target=\"_blank\" rel=\"noopener\">PHPUnit<\/a>, com as informa\u00e7\u00f5es necess\u00e1rias para executar nossos testes:<\/p>\n<pre class=\"lang:php decode:true \">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;phpunit bootstrap=\"..\/vendor\/autoload.php\"&gt;\n\t&lt;testsuites&gt;\n    \t&lt;testsuite name=\"PhpPact Meetup API Example One Tests\"&gt;\n        \t&lt;directory&gt;.\/&lt;\/directory&gt;\n    \t&lt;\/testsuite&gt;\n\t&lt;\/testsuites&gt;\n\t&lt;listeners&gt;\n    \t&lt;listener class=\"PhpPact\\Consumer\\Listener\\PactTestListener\"&gt;\n        \t&lt;arguments&gt;\n            \t&lt;array&gt;\n                \t&lt;element&gt;\n                    \t&lt;string&gt;PhpPact Meetup API Example One Tests&lt;\/string&gt;\n                \t&lt;\/element&gt;\n            \t&lt;\/array&gt;\n        \t&lt;\/arguments&gt;\n    \t&lt;\/listener&gt;\n\t&lt;\/listeners&gt;\n\t&lt;php&gt;\n    \t&lt;env name=\"PACT_MOCK_SERVER_HOST\" value=\"localhost\"\/&gt;\n    \t&lt;env name=\"PACT_MOCK_SERVER_PORT\" value=\"7200\"\/&gt;\n    \t&lt;env name=\"PACT_CONSUMER_NAME\" value=\"ExampleOne\"\/&gt;\n    \t&lt;env name=\"PACT_CONSUMER_VERSION\" value=\"1.0.0\"\/&gt;\n    \t&lt;env name=\"PACT_CONSUMER_TAG\" value=\"master\"\/&gt;\n    \t&lt;env name=\"PACT_PROVIDER_NAME\" value=\"ExampleAPI\"\/&gt;\n    \t&lt;env name=\"PACT_OUTPUT_DIR\" value=\"\/tmp\"\/&gt;\n\t&lt;\/php&gt;\n&lt;\/phpunit&gt;\n<\/pre>\n<p>E criamos nossa classe de teste no diret\u00f3rio tests que j\u00e1 criamos acima, com o nome de <strong>\u2018ExampleOneMeetupAPIClientTest.php\u2019<\/strong> com o seguinte conte\u00fado:<\/p>\n<pre class=\"lang:php decode:true \">&lt;?php\n\nrequire_once ('..\/src\/ExampleOneMeetupApiClient.php');\n\nuse PhpPact\\Consumer\\InteractionBuilder;\nuse PhpPact\\Consumer\\Model\\ConsumerRequest;\nuse PhpPact\\Consumer\\Model\\ProviderResponse;\nuse PhpPact\\Consumer\\Matcher\\Matcher;\nuse PhpPact\\Standalone\\MockService\\MockServerEnvConfig;\nuse PHPUnit\\Framework\\TestCase;\n\n\n\nclass ExampleOneMeetupAPIClientTest extends TestCase\n{\n\tconst version = '2';\n\n\t\/**\n \t* @test\n \t*\/\n\tpublic function testCategories()\n\t{\n    \t\/\/ build the request\n    \t$path = '\/' . self::version . '\/categories';\n\n    \t\/\/ build the request\n    \t$request = new ConsumerRequest();\n    \t$request\n        \t-&gt;setMethod('GET')\n        \t-&gt;setPath($path)\n        \t-&gt;addHeader('Content-Type', 'application\/json');\n\n    \t\/\/ build the response\n    \t$matcher = new Matcher();\n\n    \t$category1 = new \\stdClass();\n    \t$category1-&gt;name = $matcher-&gt;regex('Games','[gbBG]');\n    \t$category1-&gt;sort_name = 'Games';\n    \t$category1-&gt;id = 11;\n    \t$category1-&gt;shortname = 'Games';\n\n    \t$body = new \\stdClass();\n    \t$body-&gt;results= $matcher-&gt;eachLike($category1);\n\n    \t$response = new ProviderResponse();\n    \t$response\n        \t-&gt;setStatus(200)\n        \t-&gt;addHeader('Content-Type', 'application\/json')\n        \t-&gt;setBody($body);\n\n    \t\/\/ build up the expected results and appropriate responses\n    \t$config  \t= new MockServerEnvConfig();\n\n    \t$mockService = new InteractionBuilder($config);\n    \t$mockService-&gt;given(\"General Meetup Categories\")\n        \t-&gt;uponReceiving(\"A GET request to return JSON using Meetups category api under version 2\")\n        \t-&gt;with($request)\n        \t-&gt;willRespondWith($response);\n\n\n    \t$service = new \\ExampleOneMeetupApiClient($config-&gt;getBaseUri()); \/\/ Pass in the URL to the Mock Server.\n    \t$serviceResponse = $service-&gt;categories();\n\n    \t\/\/ do some asserts on the return\n    \t$this-&gt;assertEquals('200', $serviceResponse-&gt;getStatusCode(), \"Let's make sure we have an OK response\");\n\n    \t\/\/ do something with the body returned\n    \t$body = (string) $serviceResponse-&gt;getBody();\n    \t$this-&gt;assertTrue((json_decode($body) ? true : false), \"Expect the JSON to be decoded without error\");\n\n    \t$hasException = false;\n    \ttry {\n        \t$mockService-&gt;verify();\n    \t} catch(\\Exception $e) {\n        \t$hasException = true;\n    \t}\n\n    \t$this-&gt;assertFalse($hasException, \"We expect the pacts to validate\");\n\t}\n}\n<\/pre>\n<p>Nessa classe estamos testando a api de consulta de categorias do meetup. Para isso estamos criando um <strong>mock da resposta da api<\/strong>, instanciamos nossa classe de client <strong>passando a url do Mock Server<\/strong>. Depois testamos o c\u00f3digo de resposta, o formato da resposta e verificamos o conte\u00fado da resposta, para assim garantir que n\u00e3o temos quebra de contrato.<\/p>\n<p>E <strong>por fim executamos nosso teste, entre no diret\u00f3rio tests e execute<\/strong>:<\/p>\n<pre class=\"lang:php decode:true \">$ php ..\/vendor\/phpunit\/phpunit\/phpunit -c phpunit.example.one.xml<\/pre>\n<p>Temos o seguinte retorno, mostrando os testes que passaram ou n\u00e3o:<\/p>\n<pre class=\"lang:php decode:true \">PHPUnit 6.5.8 by Sebastian Bergmann and contributors.<\/pre>\n<pre class=\"lang:php decode:true \">Starting the mock service with command '\/home\/fsilva\/Documents\/dev\/php-pact\/vendor\/mattersight\/phppact\/src\/PhpPact\/Standalone\/Installer\/..\/..\/..\/..\/pact\/bin\/pact-mock-service' 'service' '--consumer=ExampleOne' '--provider=ExampleAPI' '--pact-dir=\/tmp' '--pact-file-write-mode=overwrite' '--host=localhost' '--port=7200'.\n[2018-04-18 22:33:17] INFO  WEBrick 1.3.1\n[2018-04-18 22:33:17] INFO  ruby 2.2.2 (2015-04-13) [x86_64-linux]\n[2018-04-18 22:33:17] INFO  WEBrick::HTTPServer#start: pid=11881 port=7200\n.                                                               \t1 \/ 1 (100%)[2018-04-18 22:33:17] INFO  going to shutdown ...\n[2018-04-18 22:33:18] INFO  WEBrick::HTTPServer#start done.\nI, [2018-04-18T22:33:17.859136 #11881]  INFO -- : Registered expected interaction GET \/2\/categories\nD, [2018-04-18T22:33:17.860161 #11881] DEBUG -- : {\n  \"description\": \"General Meetup Categories\",\n  \"providerState\": \"A GET request to return JSON using Meetups category api under version 2\",\n  \"request\": {\n\t\"method\": \"GET\",\n\t\"path\": \"\/2\/categories\",\n\t\"headers\": {\n  \t\"Content-Type\": \"application\/json\"\n\t}\n  },\n  \"response\": {\n\t\"status\": 200,\n\t\"headers\": {\n  \t\"Content-Type\": \"application\/json\"\n\t},\n\t\"body\": {\n  \t\"results\": {\n    \t\"json_class\": \"Pact::ArrayLike\",\n    \t\"contents\": {\n      \t\"name\": {\n        \t\"json_class\": \"Pact::Term\",\n        \t\"data\": {\n          \t\"generate\": \"Games\",\n          \t\"matcher\": {\"json_class\":\"Regexp\",\"o\":0,\"s\":\"[gbBG]\"}\n        \t}\n      \t},\n      \t\"sort_name\": \"Games\",\n      \t\"id\": 11,\n      \t\"shortname\": \"Games\"\n    \t},\n    \t\"min\": 1\n  \t}\n\t}\n  }\n}\nI, [2018-04-18T22:33:17.870039 #11881]  INFO -- : Received request GET \/2\/categories\nD, [2018-04-18T22:33:17.870314 #11881] DEBUG -- : {\n  \"path\": \"\/2\/categories\",\n  \"query\": \"\",\n  \"method\": \"get\",\n  \"headers\": {\n\t\"Content-Type\": \"application\/json\",\n\t\"Host\": \"localhost:7200\",\n\t\"User-Agent\": \"GuzzleHttp\/6.3.2 curl\/7.55.1 PHP\/7.1.16\",\n\t\"Version\": \"HTTP\/1.1\"\n  }\n}\nI, [2018-04-18T22:33:17.870853 #11881]  INFO -- : Found matching response for GET \/2\/categories\nD, [2018-04-18T22:33:17.871244 #11881] DEBUG -- : {\n  \"status\": 200,\n  \"headers\": {\n\t\"Content-Type\": \"application\/json\"\n  },\n  \"body\": {\n\t\"results\": {\n  \t\"json_class\": \"Pact::ArrayLike\",\n  \t\"contents\": {\n    \t\"name\": {\n      \t\"json_class\": \"Pact::Term\",\n      \t\"data\": {\n        \t\"generate\": \"Games\",\n        \t\"matcher\": {\n          \t\"json_class\": \"Regexp\",\n          \t\"o\": 0,\n          \t\"s\": \"[gbBG]\"\n        \t}\n      \t}\n    \t},\n    \t\"sort_name\": \"Games\",\n    \t\"id\": 11,\n    \t\"shortname\": \"Games\"\n  \t},\n  \t\"min\": 1\n\t}\n  }\n}\nI, [2018-04-18T22:33:17.884214 #11881]  INFO -- : Verifying - interactions matched\nI, [2018-04-18T22:33:17.936178 #11881]  INFO -- : Verifying - interactions matched\nI, [2018-04-18T22:33:17.941931 #11881]  INFO -- : Writing pact for ExampleAPI to \/tmp\/exampleone-exampleapi.json\nProcess exited with code 0.\nPACT_BROKER_URI environment variable was not set. Skipping PACT file upload.\n\nTime: 2.76 seconds, Memory: 6.00MB\n\nOK (1 test, 3 assertions)\n<\/pre>\n<p>Como pode ser visto na resposta dos testes, ao final \u00e9 gerado um arquivo json com o contrato, como este:<\/p>\n<pre class=\"lang:php decode:true \">{\n  \"consumer\": {\n\t\"name\": \"ExampleOne\"\n  },\n  \"provider\": {\n\t\"name\": \"ExampleAPI\"\n  },\n  \"interactions\": [\n\t{\n  \t\"description\": \"General Meetup Categories\",\n  \t\"providerState\": \"A GET request to return JSON using Meetups category api under version 2\",\n  \t\"request\": {\n    \t\"method\": \"GET\",\n    \t\"path\": \"\/2\/categories\",\n    \t\"headers\": {\n      \t\"Content-Type\": \"application\/json\"\n    \t}\n  \t},\n  \t\"response\": {\n    \t\"status\": 200,\n    \t\"headers\": {\n      \t\"Content-Type\": \"application\/json\"\n    \t},\n    \t\"body\": {\n      \t\"results\": [\n        \t{\n          \t\"name\": \"Games\",\n          \t\"sort_name\": \"Games\",\n          \t\"id\": 11,\n          \t\"shortname\": \"Games\"\n        \t}\n      \t]\n    \t},\n    \t\"matchingRules\": {\n      \t\"$.body.results\": {\n        \t\"min\": 1\n      \t},\n      \t\"$.body.results[*].*\": {\n        \t\"match\": \"type\"\n      \t},\n      \t\"$.body.results[*].name\": {\n        \t\"match\": \"regex\",\n        \t\"regex\": \"[gbBG]\"\n      \t}\n    \t}\n  \t}\n\t}\n  ],\n  \"metadata\": {\n\t\"pactSpecification\": {\n  \t\"version\": \"2.0.0\"\n\t}\n  }\n}\n<\/pre>\n<p>Neste artigo vimos um exemplo de como pode ser implementado testes de contrato, mais alguns exemplos podem ser encontrado nos seguintes reposit\u00f3rios <a href=\"https:\/\/github.com\/pact-foundation\/pact-php\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/pact-foundation\/pact-php<\/a> e <a href=\"https:\/\/github.com\/fernandodebrando\/pact-php-example\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/fernandodebrando\/pact-php-example<\/a>.<\/p>\n<h2>Conclus\u00e3o &#8211; Testes de contratos para microsservi\u00e7os em PHP<\/h2>\n<p>A evolu\u00e7\u00e3o dos sistemas para microsservi\u00e7os resultou com que novos problemas entrassem em foco, <strong>fazendo com que m\u00e9todos alternativos de testes ganhassem mais destaque al\u00e9m dos testes de contratos tradicionais.<\/strong> Apesar de muitas empresas terem experi\u00eancias ruins na migra\u00e7\u00e3o para este tipo de arquitetura, existem vantagens bem relevantes em rela\u00e7\u00e3o a aderir ao uso deste tipo de tecnologia, como <em>builds<\/em> e entregas mais r\u00e1pidas e modularidade das API\u2019s. Se quiser saber mais sobre microsservi\u00e7os, no artigo <a href=\"https:\/\/king.host\/blog\/2018\/02\/novo-shopping-cart-kinghost\/\">Microsservi\u00e7os: Distribuindo servi\u00e7os cr\u00edticos ao neg\u00f3cio<\/a> falo sobre um case que a equipe que eu trabalho na KingHost desenvolveu a respeito do novo shopping cart da empresa, abordando vantagens, desvantagens, dificuldades e como migramos nosso sistema monol\u00edtico.<\/p>\n<p><a href=\"https:\/\/materiaiseducativos.kinghost.net\/programacao-php-tendencias-webinar\/?utm_source=lab&amp;utm_medium=post&amp;utm_term=&amp;utm_content=php-testes-contrato&amp;utm_campaign=content-marketing\" target=\"_blank\" rel=\"noopener\"><img fetchpriority=\"high\" decoding=\"async\" class=\"aligncenter wp-image-18148 size-large\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/06\/2018-06-15-banner-webinar-php-780x195.png\" alt=\"\" width=\"780\" height=\"195\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/06\/2018-06-15-banner-webinar-php-780x195.png 780w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/06\/2018-06-15-banner-webinar-php-300x75.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/06\/2018-06-15-banner-webinar-php-768x192.png 768w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/06\/2018-06-15-banner-webinar-php-400x100.png 400w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/06\/2018-06-15-banner-webinar-php.png 800w\" sizes=\"(max-width: 780px) 100vw, 780px\" \/><\/a><\/p>\n<p>E voc\u00ea, qual a sua opini\u00e3o sobre testes de contrato, j\u00e1 utilizou alguma vez? <strong>Deixe um coment\u00e1rio abaixo<\/strong>. Caso queira ficar por dentro de outros conte\u00fados relacionados, visite nosso <a href=\"https:\/\/king.host\/blog\/\" target=\"_blank\" rel=\"noopener\">Blog da KingHost<\/a>. Obrigado pela leitura.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Este artigo ir\u00e1 abordar testes de contrato para microsservi\u00e7os com Pact em PHP. O desenvolvimento de sistemas sempre modificou-se rapidamente ao decorrer do tempo. Novos paradigmas de programa\u00e7\u00e3o foram criados, novos tipos de linguagem, metodologias e processos. A arquitetura dos softwares tamb\u00e9m n\u00e3o poderia ficar fora, come\u00e7ando com uma arquitetura monol\u00edtica, passando para uma arquitetura [&hellip;]<\/p>\n","protected":false},"author":285,"featured_media":13485,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1179,8],"tags":[1374],"class_list":["post-13446","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-desenvolvimento","category-tecnologia","tag-php"],"_links":{"self":[{"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/posts\/13446","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/users\/285"}],"replies":[{"embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/comments?post=13446"}],"version-history":[{"count":9,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/posts\/13446\/revisions"}],"predecessor-version":[{"id":35808,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/posts\/13446\/revisions\/35808"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/media\/13485"}],"wp:attachment":[{"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/media?parent=13446"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/categories?post=13446"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/tags?post=13446"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}