{"id":829,"date":"2020-03-30T15:00:00","date_gmt":"2020-03-30T15:00:00","guid":{"rendered":"http:\/\/bullyrooks.com\/index.php\/2020\/04\/10\/simple-spring-boot-service-to-kubernetes-application-step-15-34e1bba8351b\/"},"modified":"2021-02-04T02:05:47","modified_gmt":"2021-02-04T02:05:47","slug":"simple-spring-boot-service-to-kubernetes-application-step-15-34e1bba8351b","status":"publish","type":"post","link":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-15-34e1bba8351b\/","title":{"rendered":"Messaging and Event Driven Design"},"content":{"rendered":"\n<p class=\"graf graf--p graf-after--h3 graf--trailing\" id=\"2f92\">In order publish messages, we need a message broker and add some logic to use the message broker. We\u2019re going to use a cloud based SaaS service as a message broker and use spring cloud stream to interact with it.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf--leading wp-block-heading\" id=\"2bfb\">Setup the Message&nbsp;Broker<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"69b7\">We\u2019ll need a message broker in order to publish and we can stand up a free one at <a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/www.cloudamqp.com\/\" target=\"_blank\" rel=\"noopener\" data-href=\"https:\/\/www.cloudamqp.com\/\">cloudamqp<\/a>. Go ahead and register an account. Once that is done, create a new instance. We\u2019re going to create a free instance called <code class=\"markup--code markup--p-code\">medium<\/code> in whatever region you want. When that is done, click on the instance to get the details. We\u2019re going to need the host, user and password from here.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf-after--p wp-block-heading\" id=\"3ae1\">Write the Publisher Logic<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"a504\">Create a new package called <code class=\"markup--code markup--p-code\">com.brianrook.medium.customer.messaging<\/code> and in that package create 2 more sub packages for <code class=\"markup--code markup--p-code\">mapper <\/code>and <code class=\"markup--code markup--p-code\">message<\/code>. First create the message payload in <code class=\"markup--code markup--p-code\">message <\/code>using a class called <code class=\"markup--code markup--p-code\">CustomerCreatedMessage<\/code>:<\/p>\n\n\n\n<pre id=\"82de\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>package com.brianrook.medium.customer.messaging.message;\n\nimport lombok.Data;\n\n@Data\npublic class CustomerCreatedMessage {\n    private Long customerId;\n    private String firstName;\n    private String lastName;\n    private String phoneNumber;\n    private String email;\n}<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"c094\">Create a mapper interface under <code class=\"markup--code markup--p-code\">mapper<\/code> called <code class=\"markup--code markup--p-code\">CustomerMessageMapper <\/code>with this content:<\/p>\n\n\n\n<pre id=\"1eda\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>package com.brianrook.medium.customer.messaging.mapper;\n\nimport com.brianrook.medium.customer.messaging.message.CustomerCreatedMessage;\nimport com.brianrook.medium.customer.service.model.Customer;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n@Mapper\npublic interface CustomerMessageMapper {\n    CustomerMessageMapper <em class=\"markup--em markup--pre-em\">INSTANCE <\/em>= Mappers.<em class=\"markup--em markup--pre-em\">getMapper<\/em>(CustomerMessageMapper.class);\n\n    CustomerCreatedMessage customerToCustomerCreatedMessage(Customer customer);\n}<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"ec74\">in <code class=\"markup--code markup--p-code\">com.brianrook.medium.customer.config<\/code> create<code class=\"markup--code markup--p-code\">CustomerCreateBinding<\/code>:<\/p>\n\n\n\n<pre id=\"4332\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>package com.brianrook.medium.customer.messaging;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface CustomerCreateBinding {\n\n    @Output(\"customerCreateChannel\")\n    MessageChannel customerCreate();\n}<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"3634\">and in <code class=\"markup--code markup--p-code\">com.brianrook.medium.customer.messaging <\/code>create <code class=\"markup--code markup--p-code\">CustomerCreatePublisher<\/code><\/p>\n\n\n\n<pre id=\"a24d\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>package com.brianrook.medium.customer.messaging;\n\nimport com.brianrook.medium.customer.messaging.mapper.CustomerMessageMapper;\nimport com.brianrook.medium.customer.messaging.message.CustomerCreatedMessage;\nimport com.brianrook.medium.customer.service.model.Customer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.stereotype.Component;\n\n@Component\n@EnableBinding(CustomerCreateBinding.class)\npublic class CustomerCreatePublisher {\n\n    @Autowired\n    CustomerCreateBinding customerCreateBinding;\n\n    public void publishCustomerCreate(Customer customer) {\n        CustomerCreatedMessage customerMessage = CustomerMessageMapper.<em class=\"markup--em markup--pre-em\">INSTANCE<\/em>.customerToCustomerCreatedMessage(customer);\n        Message&lt;CustomerCreatedMessage&gt; msg = MessageBuilder.<em class=\"markup--em markup--pre-em\">withPayload<\/em>(customerMessage).build();\n\n        customerCreateBinding.customerCreate().send(msg);\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"6aa3\">and hook it into our service CustomerService:<\/p>\n\n\n\n<pre id=\"3592\" class=\"wp-block-preformatted graf graf--pre graf-after--p\">...<\/pre>\n\n\n\n<pre id=\"5486\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>@Autowired\nCustomerCreatePublisher customerCreatePublisher;<\/code><\/pre>\n\n\n\n<pre id=\"12ef\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"e053\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>public Customer saveCustomer(Customer customer) {\n    if (customerExists(customer))\n    {\n        throw new CreateCustomerException(String.<em class=\"markup--em markup--pre-em\">format<\/em>(\"customer with email: %s already exists\", customer.getEmail()));\n    }\n    Customer savedCustomer = persistCustomer(customer);\n    customerCreatePublisher.publishCustomerCreate(savedCustomer);\n\n    return savedCustomer;\n}<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"4c1f\">And finally we\u2019ll need to configure our application, add this configuration to <code class=\"markup--code markup--p-code\">application.yaml<\/code>:<\/p>\n\n\n\n<pre id=\"ac94\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>spring:<\/code><\/pre>\n\n\n\n<pre id=\"d574\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"6682\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>  cloud:\n    stream:\n      bindings:\n        output:\n          destination: queue.customer.create\n          content-type: application\/json\n  rabbitmq:\n    addresses: amqp:\/\/shrimp.rmq.cloudamqp.com\/c***j<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote graf graf--blockquote graf-after--pre is-layout-flow wp-block-quote-is-layout-flow\" id=\"8678\"><p>Here we\u2019re telling spring boot to create a rabbit destination on the instance and where to connect to. Make sure to strip out the username and password from the url and replace with your own instance name.<\/p><\/blockquote>\n\n\n\n<p class=\"graf graf--p graf-after--blockquote\" id=\"997b\">We also need to add the rabbit binder libraries to the <code class=\"markup--code markup--p-code\">pom.xml<\/code><\/p>\n\n\n\n<pre id=\"18e0\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>&lt;dependency&gt;\n   &lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n   &lt;artifactId&gt;spring-cloud-starter-stream-rabbit&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n&lt;dependency&gt;\n   &lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n   &lt;artifactId&gt;spring-cloud-stream-binder-rabbit&lt;\/artifactId&gt;\n&lt;\/dependency&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"graf graf--h3 graf-after--pre wp-block-heading\" id=\"b419\">Testing<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"7a64\">Update <code class=\"markup--code markup--p-code\">CustomerControllerTest<\/code> to test that we can publish this message.<\/p>\n\n\n\n<pre id=\"411f\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"4b97\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>@Autowired\nMessageCollector messageCollector;\n@Autowired\nAppointmentCreateBinding appointmentCreateBinding;<\/code><\/pre>\n\n\n\n<pre id=\"5501\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"3c60\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>@Test\npublic void testAddCustomerSuccess() throws URISyntaxException, JsonProcessingException {<\/code><\/pre>\n\n\n\n<pre id=\"986c\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"a902\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>    \/\/validate AMQP\n    Message&lt;String&gt; publishedMessage = (Message&lt;String&gt;)messageCollector\n            .forChannel(customerCreateBinding.customerCreate())\n            .poll();\n<em class=\"markup--em markup--pre-em\">assertThat<\/em>(publishedMessage).isNotNull();\n    ObjectMapper om = new ObjectMapper().registerModule(new JavaTimeModule());\n\n    CustomerCreatedMessage appointmentMessage = om.readValue(\n            publishedMessage.getPayload(),\n            CustomerCreatedMessage.class);\n<em class=\"markup--em markup--pre-em\">assertThat<\/em>(appointmentMessage.getCustomerId()).isGreaterThan(0l);\n}<\/code><\/pre>\n\n\n\n<pre id=\"9229\" class=\"wp-block-preformatted graf graf--pre graf-after--pre\">...<\/pre>\n\n\n\n<pre id=\"26c9\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>@Test\npublic void testAddCustomerConflict() throws URISyntaxException {<\/code><\/pre>\n\n\n\n<pre id=\"cbc1\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>...\n\n    \/\/validate AMQP\n<em class=\"markup--em markup--pre-em\">assertThat<\/em>(messageCollector\n            .forChannel(customerCreateBinding.customerCreate())\n            .isEmpty()).isTrue();\n}<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote graf graf--blockquote graf-after--pre is-layout-flow wp-block-quote-is-layout-flow\" id=\"31b9\"><p>Here we\u2019re using the spring application context to hook into the spring bindings used to talk to our AMQP broker. We\u2019re essentially wiring in before the logic to publish a message is executed and pulling the messages out of the pipeline before they get sent. We can also confirm that messages are not sent in the case of errors.<\/p><\/blockquote>\n\n\n\n<p class=\"graf graf--p graf-after--blockquote\" id=\"f9d0\">We\u2019ve also added significant functionality so lets update our application version using semantic versioning. Update the <code class=\"markup--code markup--p-code\">pom.xml<\/code>:<\/p>\n\n\n\n<pre id=\"5484\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>&lt;groupId&gt;com.brianrook.medium&lt;\/groupId&gt;\n&lt;artifactId&gt;medium-customer&lt;\/artifactId&gt;\n&lt;version&gt;0.1.0-SNAPSHOT&lt;\/version&gt;\n&lt;name&gt;medium-customer&lt;\/name&gt;<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"a183\">I was also having trouble with the <code class=\"markup--code markup--p-code\">LoggingAspect <\/code>trying to log system actions on startup, so I changed the pointcut configuration to look like this, in order to limit what was visible to aspectj:<\/p>\n\n\n\n<pre id=\"030d\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>@Around(\"within(com.brianrook.medium.customer.controller. .*) \" +\n        \"&amp;&amp; within(com.brianrook.medium.customer.service. .*) \" +\n        \"&amp;&amp; within(com.brianrook.medium.customer.messaging. .*) \" +\n        \"&amp;&amp; within(com.brianrook.medium.customer.dao. .*) ) \")<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"5b41\">We can also startup the application locally if we add the following configuration to the application startup config:<\/p>\n\n\n\n<pre id=\"a3a9\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>-Dspring.profiles.active=herokudb\n-Dspring.datasource.username=&lt;db username&gt;\n-Dspring.datasource.password=&lt;db password&gt;\n-Dspring.rabbitmq.username=&lt;cloudamqp username&gt;\n-Dspring.rabbitmq.password=&lt;cloudamqp password&gt;<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"e7ad\">If we save a customer, we should be able to see a message published in the cloudamqp manager console.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/02\/1PYJQ6b60eZ2OmiluC3yhkg.png?w=960\" alt=\"\" data-recalc-dims=\"1\"\/><\/figure>\n\n\n\n<h3 class=\"graf graf--h3 graf-after--figure wp-block-heading\" id=\"54be\">Kubernetes Configuration<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"e5df\">This configuration is only valid for running locally for tests though. We need to configure our running application. We can do that by adding to our helm <code class=\"markup--code markup--p-code\">values.yaml<\/code> file and uploading another secrets file.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"474b\">values.yaml<\/p>\n\n\n\n<pre id=\"30c1\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"8836\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>content: |-\n  spring:<\/code><\/pre>\n\n\n\n<pre id=\"0623\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"d7d7\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>    cloud:\n      stream:\n        bindings:\n          output:\n            destination: queue.customer.create\n            content-type: application\/json\n    rabbitmq:\n      addresses: amqp:\/\/shrimp.rmq.cloudamqp.com\/c***j<\/code><\/pre>\n\n\n\n<pre id=\"baa9\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"18a5\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>secretsToEnv:<\/code><\/pre>\n\n\n\n<pre id=\"2a82\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"4f81\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>  - name: SPRING_RABBITMQ_PASSWORD\n    valueFrom:\n      secretKeyRef:\n        name: amqp-secrets\n        key: SPRING_RABBITMQ_PASSWORD\n  - name: SPRING_RABBITMQ_USERNAME\n    valueFrom:\n      secretKeyRef:\n        name: amqp-secrets\n        key: SPRING_RABBITMQ_USERNAME<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"9131\">Upload your secrets file<\/p>\n\n\n\n<pre id=\"4bd3\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>kubectl create secret generic amqp-secrets --from-literal=SPRING_RABBITMQ_PASSWORD=&lt;cloud amqp password&gt; --from-literal=SPRING_RABBITMQ_USERNAME=&lt;cloud amqp user\/vhost&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"graf graf--h3 graf-after--pre wp-block-heading\" id=\"17b7\">Build and&nbsp;Commit<\/h3>\n\n\n\n<pre id=\"3942\" class=\"wp-block-code graf graf--pre graf-after--h3\"><code>git checkout -b customer-updates\nmvn clean install\ngit add .\ngit commit -m \"new functionality and publishing\"\ngit push --set-upstream origin customer-updates\ngit checkout master\ngit merge customer-updates\ngit push<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"41a1\">Now lets let the build pipeline generate a new chart version and use the updated chart to deploy to minikube<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"faae\">After the build completes go to the helm chart registry and locate the latest version<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/02\/1PYJQ6b60eZ2OmiluC3yhkg.png?w=960\" alt=\"\" data-recalc-dims=\"1\"\/><\/figure>\n\n\n\n<p class=\"graf graf--p graf-after--figure\" id=\"0a38\">In this case we want 5<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"d7a8\">take a look at helm to see what is currently deployed<\/p>\n\n\n\n<pre id=\"b9a3\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>$ helm list\nNAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION\nmedium  default         2               2020-04-04 13:56:58.2124564 -0600 MDT   deployed        medium-customer-3       0.0.1<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"87f6\">update the repositories<\/p>\n\n\n\n<pre id=\"b090\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>helm repo update<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"46e8\">now list our repo contents<\/p>\n\n\n\n<pre id=\"1ba3\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>$ helm search repo codefresh\nNAME                            CHART VERSION   APP VERSION     DESCRIPTION\ncodefresh\/medium-customer       5               0.0.1           A Helm chart for Kubernetes<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"3e0e\">we can see that the chart versions match, so we pulled down the latest built version. Lets deploy it.<\/p>\n\n\n\n<pre id=\"a30b\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>helm upgrade medium codefresh\/medium-customer --version=5<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"86eb\">we should be able to verify any of the new functionality we added<\/p>\n\n\n\n<pre id=\"7cd2\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>GET \/customer\/all HTTP\/1.1\nHost: 172.17.144.73:30001\nContent-Type: application\/json\nContent-Type: text\/plain<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<div class=\"entry-summary\">\nIn order publish messages, we need a message broker and add some&hellip;\n<\/div>\n<div class=\"link-more\"><a href=\"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-15-34e1bba8351b\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &ldquo;Messaging and Event Driven Design&rdquo;<\/span>&hellip;<\/a><\/div>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41],"tags":[94,95,90,29,79,80,50,91,93,92],"course":[40],"class_list":["post-829","post","type-post","status-publish","format-standard","hentry","category-software-development","tag-amqp","tag-cloudamqp","tag-event-driven-architecture","tag-git","tag-helm","tag-kubernetes","tag-maven","tag-messaging","tag-pubsub","tag-rabbitmq","course-spring-with-kubernetes","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/829","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/comments?post=829"}],"version-history":[{"count":3,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/829\/revisions"}],"predecessor-version":[{"id":905,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/829\/revisions\/905"}],"wp:attachment":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/media?parent=829"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/categories?post=829"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/tags?post=829"},{"taxonomy":"course","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/course?post=829"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}