{"id":677,"date":"2022-10-31T12:29:46","date_gmt":"2022-10-31T12:29:46","guid":{"rendered":"https:\/\/infobip.com\/developers\/?p=677"},"modified":"2023-09-11T14:33:26","modified_gmt":"2023-09-11T14:33:26","slug":"automating-deployment-of-ksql-architecture","status":"publish","type":"post","link":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture","title":{"rendered":"Automating Deployment of KSQL Architecture&nbsp;"},"content":{"rendered":"\n<p>It&#8217;s no secret that automation is essential to streamlined development. Nowadays, we typically collaborate across teams and time zones, which requires a structured deployment process to stay organized. <\/p>\n\n\n\n<p>That&#8217;s why we decided to build a proof-of-concept using KSQL for data transformation and document our journey. While exploring the abilities of Confluent technologies was exciting, developing a build automation pipeline proved to be a challenge. This post will outline our solution for managing the deployment of KSQL streams and tables.&nbsp;<\/p>\n\n\n\n<p>To integrate with our existing CI\/CD, we used Jenkins pipelines to run Ansible playbooks, but these concepts can be applied to other configuration tools.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Setting up and tearing down<\/strong>&nbsp;<\/h2>\n\n\n\n<p>Our project manipulated data from several input topics, creating a sequence of streams and tables to calculate results. Each query was built on previous ones, so if a change was made to the KSQL syntax (e.g. modifying a time window, or changing the type of join), then the entire sequence had to be torn down and rebuilt from scratch.&nbsp;<\/p>\n\n\n\n<p>To illustrate this concept, consider a simple example with just two queries. The first creates a stream from an existing input topic, and the second re-partitions the stream:&nbsp;<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-sql\" data-lang=\"SQL\"><code>CREATE STREAM PERSON_IDENTIFIER (first VARCHAR, last VARCHAR) \n    WITH (KAFKA_TOPIC=&#39;person_topic&#39;, VALUE_FORMAT=&#39;JSON&#39;, KEY=&#39;last&#39;); \nCREATE STREAM PERSON_IDENTIFIER_FIRST \n    AS SELECT * FROM PERSON_IDENTIFIER PARTITION BY first; <\/code><\/pre><\/div>\n\n\n\n<p>These queries can be placed in a .sql file to run using KSQL&#8217;s RUN SCRIPT command.&nbsp;<\/p>\n\n\n\n<p>Tearing down looks similar, except the streams must be dropped in the opposite order, because the second depends on the first:&nbsp;<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-sql\" data-lang=\"SQL\"><code>DROP STREAM IF EXISTS PERSON_IDENTIFIER_FIRST DELETE TOPIC; \nDROP STREAM IF EXISTS PERSON_IDENTIFIER; <\/code><\/pre><\/div>\n\n\n\n<p>In an Ansible playbook, copy the setup and teardown scripts to the host, then run them using RUN SCRIPT.&nbsp;<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-sql\" data-lang=\"SQL\"><code>- name: &quot;Tear down and set up KSQL queries&quot; \n  hosts: &quot;{{ hostname }}&quot; \n  any_errors_fatal: True \n\n  tasks: \n    - name: &quot;Copy teardown script file to host&quot; \n      copy: \n        src: \/dir\/ksql-teardown-commands.sql \n        dest: &quot;\/dir\/ksql-teardown-commands.sql&quot; \n\n    - name: &quot;Run teardown commands in KSQL&quot; \n      shell: &quot;.\/current\/bin\/ksql http:\/\/{{ hostname }}:8088 &lt;&lt;&lt; \\&quot;RUN SCRIPT &#39;\/dir\/ksql-teardown-commands.sql&#39;;\\&quot;&quot; \n\n    - name: &quot;Copy setup script file to host&quot; \n      copy: \n        src: \/dir\/ksql-setup-commands.sql \n        dest: &quot;\/dir\/ksql-setup-commands.sql&quot;  \n\n    - name: &quot;Run setup commands in KSQL&quot; \n      shell: &quot;.\/current\/bin\/ksql http:\/\/{{ hostname }}:8088 &lt;&lt;&lt; \\&quot;RUN SCRIPT &#39;\/dir\/ksql-setup-commands.sql&#39;;\\&quot;&quot; <\/code><\/pre><\/div>\n\n\n\n<p>This playbook can be run from the command line or triggered using an automation server such as Jenkins.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Terminating the queries<\/strong>&nbsp;<\/h2>\n\n\n\n<p>The above commands handle the destruction and reconstruction of streams, but they fail to terminate the underlying queries. <\/p>\n\n\n\n<p>Attempting to drop PERSON_IDENTIFIER will result in an error because the CSAS query used to construct PERSON_IDENTIFIER_FIRST is still running. <\/p>\n\n\n\n<p>As of Confluent 5.3, it is not possible to automatically terminate a query when a stream or table is dropped, nor is bulk termination supported. The simplest automated solution is to fetch the queries and terminate them one by one:&nbsp;<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-sql\" data-lang=\"SQL\"><code>- name: &quot;Terminate running queries on all hosts in group&quot; \n  hosts: &quot;{{ hostname }}&quot; \n  any_errors_fatal: True  \n\n  tasks: \n    # Fetch and terminate currently running queries \n    - name: &quot;Get currently running queries via REST&quot; \n      uri: \n        url: &quot;http:\/\/{{ hostname }}:8088\/ksql&quot; \n        method: POST \n        body: &quot;{\\&quot;ksql\\&quot;: \\&quot;SHOW QUERIES;\\&quot;}&quot; \n        headers: \n          Content-Type: &quot;application\/vnd.ksql.v1+json; charset=utf-8&quot; \n        return_content: yes \n        body_format: json \n      register: all_queries  \n\n    - name: &quot;Parse query ids&quot; \n      set_fact: \n        query_ids: &quot;{{ all_queries | to_json | from_json | json_query(query) | list }}&quot; \n      vars: \n        query: &quot;json[0].queries[?starts_with(id,&#39;CSAS_PERSON_&#39;) || starts_with(id,&#39;CTAS_PERSON_&#39;)].id&quot; \n      delegate_to: 127.0.0.1  \n\n    - name: &quot;Terminate queries by id&quot; \n      shell: &quot;.\/current\/bin\/ksql http:\/\/{{ hostname }}:8088 &lt;&lt;&lt; \\&quot;TERMINATE {{ item }};\\&quot;&quot; \n      with_items: &quot;{{ query_ids }}&quot; <\/code><\/pre><\/div>\n\n\n\n<p>We use REST once again, this time fetching all of the currently running queries, then filtering for the ones associated with this project (i.e. beginning with CSAS_PERSON_ or CTAS_PERSON_). Then, the TERMINATE command is issued for each query.&nbsp;<\/p>\n\n\n\n<p>If a query fails to terminate, the error will not be detected by Ansible since the command was run in shell. As we only want to proceed if all relevant queries are terminated, we repeat the process of fetching and filtering, triggering the playbook to fail if the filtered list is not empty:&nbsp;<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-sql\" data-lang=\"SQL\"><code># Pause for 5 seconds to allow the queries to finish terminating \n    - pause: \n        seconds: 5 \n\n    # Confirm that all PERSON queries were terminated \n    - name: &quot;Get remaining queries via REST&quot; \n      uri: \n        url: &quot;http:\/\/{{ hostname }}:8088\/ksql&quot; \n        method: POST \n        body: &quot;{\\&quot;ksql\\&quot;: \\&quot;SHOW QUERIES;\\&quot;}&quot; \n        headers: \n          Content-Type: &quot;application\/vnd.ksql.v1+json; charset=utf-8&quot; \n        return_content: yes \n        body_format: json \n      register: remaining_queries  \n\n    - name: &quot;Parse remaining query ids&quot; \n      set_fact: \n        remaining_ids: &quot;{{ remaining_queries | to_json | from_json | json_query(remaining_query) | list }}&quot; \n      vars: \n        remaining_query: &quot;json[0].queries[?starts_with(id,&#39;CSAS_PERSON_&#39;) || starts_with(id,&#39;CTAS_PERSON_&#39;)].id&quot; \n      delegate_to: 127.0.0.1  \n\n    - fail: \n        msg: &quot;Some queries were not successfully terminated: {{ remaining_ids }}&quot; \n      when: remaining_ids|length != 0 <\/code><\/pre><\/div>\n\n\n\n<p>Once query termination completes successfully, the streams can then be dropped and rebuilt. New streams and tables can be added to the setup .sql file, and the existing ones can be modified, as long as the corresponding teardown .sql file is kept up-to-date. Redeployment will rebuild the KSQL environment to reflect the changes.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Other solutions<\/strong>&nbsp;<\/h2>\n\n\n\n<p>There are other approaches to bulk query termination (see <a href=\"https:\/\/rmoff.net\/2019\/03\/25\/terminate-all-ksql-queries\/\" target=\"_blank\" rel=\"noreferrer noopener\">Robin Moffatt&#8217;s post<\/a> for an interesting one), but this method worked best with our existing automation tools.&nbsp;<\/p>\n\n\n\n<p>Our sequence of KSQL syntax became increasingly complex as we added streams and tables, so maintaining an organized method of setup and teardown was essential. Had this project been more than just a proof of concept, we would have sought out a more refined solution, but the steps above worked well to support our rapid development process.&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s no secret that automation is essential to streamlined [&hellip;]<\/p>\n","protected":false},"author":10,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[28,253,249,252],"tags":[260,47],"coauthors":[133],"class_list":["post-677","post","type-post","status-publish","format-standard","hentry","category-blog-post","category-developer-experience","category-devops-and-security","category-tools","tag-architecture","tag-data"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.6 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Automating Deployment of KSQL Architecture&nbsp; - Infobip Developers Hub<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Automating Deployment of KSQL Architecture&nbsp; - Infobip Developers Hub\" \/>\n<meta property=\"og:description\" content=\"It&#8217;s no secret that automation is essential to streamlined [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture\" \/>\n<meta property=\"og:site_name\" content=\"Infobip Developers Hub\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/infobip\/\" \/>\n<meta property=\"article:published_time\" content=\"2022-10-31T12:29:46+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-09-11T14:33:26+00:00\" \/>\n<meta name=\"author\" content=\"Infobip Devs\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@InfobipDev\" \/>\n<meta name=\"twitter:site\" content=\"@InfobipDev\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Infobip Devs\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture\"},\"author\":{\"name\":\"Infobip Devs\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/e04a9012051f81b6a2f6976e156f6ce0\"},\"headline\":\"Automating Deployment of KSQL Architecture&nbsp;\",\"datePublished\":\"2022-10-31T12:29:46+00:00\",\"dateModified\":\"2023-09-11T14:33:26+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture\"},\"wordCount\":609,\"publisher\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#organization\"},\"keywords\":[\"architecture\",\"data\"],\"articleSection\":[\"Blog Post\",\"Developer Experience\",\"DevOps and Security\",\"Tools\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture\",\"url\":\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture\",\"name\":\"Automating Deployment of KSQL Architecture&nbsp; - Infobip Developers Hub\",\"isPartOf\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#website\"},\"datePublished\":\"2022-10-31T12:29:46+00:00\",\"dateModified\":\"2023-09-11T14:33:26+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.infobip.com\/developers\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Automating Deployment of KSQL Architecture&nbsp;\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#website\",\"url\":\"https:\/\/www.infobip.com\/developers\/\",\"name\":\"Infobip Developers Hub\",\"description\":\"Build meaningful customer relationships across any channel\",\"publisher\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.infobip.com\/developers\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#organization\",\"name\":\"Infobip Developers Hub\",\"url\":\"https:\/\/www.infobip.com\/developers\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png\",\"contentUrl\":\"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png\",\"width\":696,\"height\":696,\"caption\":\"Infobip Developers Hub\"},\"image\":{\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/infobip\/\",\"https:\/\/x.com\/InfobipDev\",\"https:\/\/www.youtube.com\/channel\/UCUPSTy53VecI5GIir3J3ZbQ\",\"https:\/\/github.com\/infobip-community\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/e04a9012051f81b6a2f6976e156f6ce0\",\"name\":\"Infobip Devs\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/image\/f61078713800c4e479dce745cd206b82\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f29655934347a5aa15879f9af9ac1f05b87167e0f7ed4074a04132eaa9f631c4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f29655934347a5aa15879f9af9ac1f05b87167e0f7ed4074a04132eaa9f631c4?s=96&d=mm&r=g\",\"caption\":\"Infobip Devs\"},\"url\":\"https:\/\/www.infobip.com\/developers\/blog\/author\/infobip-devs\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Automating Deployment of KSQL Architecture&nbsp; - Infobip Developers Hub","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture","og_locale":"en_US","og_type":"article","og_title":"Automating Deployment of KSQL Architecture&nbsp; - Infobip Developers Hub","og_description":"It&#8217;s no secret that automation is essential to streamlined [&hellip;]","og_url":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture","og_site_name":"Infobip Developers Hub","article_publisher":"https:\/\/www.facebook.com\/infobip\/","article_published_time":"2022-10-31T12:29:46+00:00","article_modified_time":"2023-09-11T14:33:26+00:00","author":"Infobip Devs","twitter_card":"summary_large_image","twitter_creator":"@InfobipDev","twitter_site":"@InfobipDev","twitter_misc":{"Written by":"Infobip Devs","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture#article","isPartOf":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture"},"author":{"name":"Infobip Devs","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/e04a9012051f81b6a2f6976e156f6ce0"},"headline":"Automating Deployment of KSQL Architecture&nbsp;","datePublished":"2022-10-31T12:29:46+00:00","dateModified":"2023-09-11T14:33:26+00:00","mainEntityOfPage":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture"},"wordCount":609,"publisher":{"@id":"https:\/\/www.infobip.com\/developers\/#organization"},"keywords":["architecture","data"],"articleSection":["Blog Post","Developer Experience","DevOps and Security","Tools"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture","url":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture","name":"Automating Deployment of KSQL Architecture&nbsp; - Infobip Developers Hub","isPartOf":{"@id":"https:\/\/www.infobip.com\/developers\/#website"},"datePublished":"2022-10-31T12:29:46+00:00","dateModified":"2023-09-11T14:33:26+00:00","breadcrumb":{"@id":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.infobip.com\/developers\/blog\/automating-deployment-of-ksql-architecture#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.infobip.com\/developers\/"},{"@type":"ListItem","position":2,"name":"Automating Deployment of KSQL Architecture&nbsp;"}]},{"@type":"WebSite","@id":"https:\/\/www.infobip.com\/developers\/#website","url":"https:\/\/www.infobip.com\/developers\/","name":"Infobip Developers Hub","description":"Build meaningful customer relationships across any channel","publisher":{"@id":"https:\/\/www.infobip.com\/developers\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.infobip.com\/developers\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.infobip.com\/developers\/#organization","name":"Infobip Developers Hub","url":"https:\/\/www.infobip.com\/developers\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/","url":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png","contentUrl":"https:\/\/www.infobip.com\/developers\/wp-content\/uploads\/2023\/03\/Infobip_logo_favicon.png","width":696,"height":696,"caption":"Infobip Developers Hub"},"image":{"@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/infobip\/","https:\/\/x.com\/InfobipDev","https:\/\/www.youtube.com\/channel\/UCUPSTy53VecI5GIir3J3ZbQ","https:\/\/github.com\/infobip-community"]},{"@type":"Person","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/e04a9012051f81b6a2f6976e156f6ce0","name":"Infobip Devs","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.infobip.com\/developers\/#\/schema\/person\/image\/f61078713800c4e479dce745cd206b82","url":"https:\/\/secure.gravatar.com\/avatar\/f29655934347a5aa15879f9af9ac1f05b87167e0f7ed4074a04132eaa9f631c4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f29655934347a5aa15879f9af9ac1f05b87167e0f7ed4074a04132eaa9f631c4?s=96&d=mm&r=g","caption":"Infobip Devs"},"url":"https:\/\/www.infobip.com\/developers\/blog\/author\/infobip-devs"}]}},"_links":{"self":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts\/677","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/users\/10"}],"replies":[{"embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/comments?post=677"}],"version-history":[{"count":7,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts\/677\/revisions"}],"predecessor-version":[{"id":2015,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/posts\/677\/revisions\/2015"}],"wp:attachment":[{"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/media?parent=677"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/categories?post=677"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/tags?post=677"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.infobip.com\/developers\/wp-json\/wp\/v2\/coauthors?post=677"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}