{"id":1339,"date":"2022-02-27T15:05:48","date_gmt":"2022-02-27T22:05:48","guid":{"rendered":"https:\/\/bullyrooks.com\/?p=1339"},"modified":"2022-02-27T15:05:48","modified_gmt":"2022-02-27T22:05:48","slug":"kube-cloud-pt5-create-an-event-publisher","status":"publish","type":"post","link":"https:\/\/bullyrooks.com\/index.php\/2022\/02\/27\/kube-cloud-pt5-create-an-event-publisher\/","title":{"rendered":"Kube Cloud Pt5 | Create an Event Publisher"},"content":{"rendered":"\n<p>We&#8217;re going to be updating cloud-application to remove the mongodb repository logic and replace it with an event publisher.<\/p>\n\n\n\n<p>Create a new branch from main<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git checkout -b migrate<\/code><\/pre>\n\n\n\n<p>Update the pom.xml first, remove the mongodb dependencies<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>        &lt;dependency>\r\n            &lt;groupId>org.springframework.boot&lt;\/groupId>\r\n            &lt;artifactId>spring-boot-starter-data-mongodb&lt;\/artifactId>\r\n        &lt;\/dependency>\n...\n        &lt;dependency>\n            &lt;groupId>org.testcontainers&lt;\/groupId>\n            &lt;artifactId>mongodb&lt;\/artifactId>\n            &lt;version>1.16.3&lt;\/version>\n            &lt;scope>test&lt;\/scope>\n        &lt;\/dependency><\/code><\/pre>\n\n\n\n<p>and add the spring cloud streaming dependencies and the kafka binder<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>        &lt;!-- Streams and Kafka -->\r\n        &lt;dependency>\r\n            &lt;groupId>org.springframework.cloud&lt;\/groupId>\r\n            &lt;artifactId>spring-cloud-stream&lt;\/artifactId>\r\n        &lt;\/dependency>\r\n        &lt;dependency>\r\n            &lt;groupId>org.springframework.cloud&lt;\/groupId>\r\n            &lt;artifactId>spring-cloud-stream&lt;\/artifactId>\r\n            &lt;type>test-jar&lt;\/type>\r\n            &lt;classifier>test-binder&lt;\/classifier>\r\n            &lt;scope>test&lt;\/scope>\r\n        &lt;\/dependency>\r\n        &lt;dependency>\r\n            &lt;groupId>org.springframework.cloud&lt;\/groupId>\r\n            &lt;artifactId>spring-cloud-stream-binder-kafka&lt;\/artifactId>\r\n        &lt;\/dependency><\/code><\/pre>\n\n\n\n<p>What we&#8217;re doing is adding the streaming functionality and support test frameworks, but also adding the platform specific binding classes needed for kafka.  Streaming is generic and will work across multiple platforms (like kafka and rabbitmq).  However, in order to actually work against confluent, we need the kafka adapters (the binding classes).<\/p>\n\n\n\n<p>Next, I globally refactored my <code>Message<\/code> class to be called <code>MessageModel<\/code> so that I wouldn&#8217;t conflict with the Message classes that spring cloud stream provides.  I&#8217;ll be using that class name going forward.<\/p>\n\n\n\n<p>Next, make a new <code>MessageEvent <\/code>object under <code>com.bullyrooks.cloud_application.messaging.dto<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.bullyrooks.cloud_application.messaging.dto;\r\n\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class MessageEvent {\r\n    private String messageId;\r\n    private String firstName;\r\n    private String lastName;\r\n    private String message;\r\n}<\/code><\/pre>\n\n\n\n<p>This is going to be the dto used to transfer the event inside the message (as a json payload)<\/p>\n\n\n\n<p>we also need a mapper.  Create <code>MessageEventMapper <\/code>in <code>com.bullyrooks.cloud_application.messaging.mapper<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.bullyrooks.cloud_application.messaging.mapper;\r\n\r\nimport com.bullyrooks.cloud_application.messaging.dto.MessageEvent;\r\nimport com.bullyrooks.cloud_application.service.model.MessageModel;\r\nimport org.mapstruct.Mapper;\r\nimport org.mapstruct.factory.Mappers;\r\n\r\n@Mapper\r\npublic interface MessageEventMapper {\r\n    MessageEventMapper INSTANCE = Mappers.getMapper(MessageEventMapper.class);\r\n\r\n    MessageEvent modelToEvent(MessageModel model);\r\n\r\n    MessageModel eventToModel(MessageEvent returnEvent);\r\n}<\/code><\/pre>\n\n\n\n<p>Next, lets delete the packages and classes in <code>com.bullyrooks.cloud_application_repository<\/code> (or save them for later since we&#8217;re going to move them to the new service)<\/p>\n\n\n\n<p>Next, we&#8217;re going to update the service class (<code>MessageService<\/code>) to publish an event instead of storing in the repository<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\r\n...\n    \/\/new attribute to inject\n    StreamBridge streamBridge;\n...\n    @Autowired\r\n    public MessageService(MessageGeneratorClient messageGeneratorClient,\r\n                          StreamBridge streamBridge,\r\n                          MeterRegistry logzioMeterRegistry){\r\n        this.messageGeneratorClient = messageGeneratorClient;\r\n        this.streamBridge = streamBridge;\r\n        this.logzioMeterRegistry = logzioMeterRegistry;\r\n        initCounters();\r\n    }\r\n...\n    public MessageModel saveMessage(MessageModel messageModel) {\r\n\r\n        msgCount.increment();\r\n        if (StringUtils.isEmpty(messageModel.getMessage())) {\r\n\r\n            genMsgCount.increment();\r\n            log.info(\"No message, retrieve from message generator\");\r\n            MessageResponseDTO dto = messageGeneratorClient.getMessage();\r\n            messageModel.setMessage(dto.getMessage());\r\n            genMsgSuccess.increment();\r\n            log.info(\"retrieved message: {}\", messageModel.getMessage());\r\n        }\r\n\r\n        log.info(\"publishing event: {}\", messageModel);\r\n        streamBridge.send(\"message.created\",\r\n                MessageEventMapper.INSTANCE.modelToEvent(messageModel));\r\n        return messageModel;\r\n    }<\/code><\/pre>\n\n\n\n<p>Now we have to add the configuration so that our topics and binders can connect to confluent.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>spring:\r\n  application:\r\n    name: cloud-application\r\n  cloud:\r\n    stream:\r\n      function:\r\n        bindings:\r\n          messageEvent-out-0:\r\n            destination: message.created\r\n  kafka:\r\n    properties:\r\n      sasl.mechanism: PLAIN\r\n      bootstrap.servers: pkc-2396y.us-east-1.aws.confluent.cloud:9092\r\n      security.protocol: SASL_SSL<\/code><\/pre>\n\n\n\n<p>we&#8217;re missing a key piece here which is the security credentials, so when you run make sure to add a -D property to configure them:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"503\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-39-1024x537.png?resize=960%2C503&#038;ssl=1\" alt=\"\" class=\"wp-image-1340\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-39.png?resize=1024%2C537&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-39.png?resize=300%2C157&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-39.png?resize=768%2C403&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-39.png?w=1047&amp;ssl=1 1047w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>And the correct credentials can be found on confluent.  There&#8217;s an option available when you create the cluster, I think.  But they can be recreated this way:<\/p>\n\n\n\n<p>From cluster overview, choose configure a client<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"426\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-40-1024x454.png?resize=960%2C426&#038;ssl=1\" alt=\"\" class=\"wp-image-1341\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-40.png?resize=1024%2C454&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-40.png?resize=300%2C133&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-40.png?resize=768%2C341&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-40.png?w=1244&amp;ssl=1 1244w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>Doesn&#8217;t matter which client, but we can pick java and you should get a screen like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"554\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-41-1024x591.png?resize=960%2C554&#038;ssl=1\" alt=\"\" class=\"wp-image-1342\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-41.png?resize=1024%2C591&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-41.png?resize=300%2C173&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-41.png?resize=768%2C443&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-41.png?w=1062&amp;ssl=1 1062w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>If you pick create kafka cluster api key, you should be given the option to get a new api key:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"542\" height=\"531\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-42.png?resize=542%2C531&#038;ssl=1\" alt=\"\" class=\"wp-image-1343\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-42.png?w=542&amp;ssl=1 542w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-42.png?resize=300%2C294&amp;ssl=1 300w\" sizes=\"auto, (max-width: 542px) 100vw, 542px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>You can copy\/past the key and secret into your configuration from here.  Make sure to have the leading\/training <code>\"<\/code> (double quote) and surround the key\/secret with the <code>'<\/code> (single quote) as these will help escape any control characters.<\/p>\n\n\n\n<p>Now we need to update the configuration so that our tests will work, add this to the application-test.yaml<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>spring:\r\n  cloud:\r\n    stream:\r\n      bindings:\r\n        messageEvent-out-0:\r\n          destination: message.created <\/code><\/pre>\n\n\n\n<p>and next the actual test.  Add this annotation to allow spring to inject a test binder.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">@Import(TestChannelBinderConfiguration.class)<\/pre>\n\n\n\n<p>Add this field so that we can access the test binder topic<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    @Autowired\r\n    OutputDestination outputDestination;<\/code><\/pre>\n\n\n\n<p>remove the repository injection and asserts and replace with the topic assertions.  This is what the final test should look like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.bullyrooks.cloud_application.controller;\r\n\r\nimport com.bullyrooks.cloud_application.controller.dto.CreateMessageRequestDTO;\r\nimport com.bullyrooks.cloud_application.controller.dto.CreateMessageResponseDTO;\r\nimport com.bullyrooks.cloud_application.message_generator.client.MessageGeneratorClient;\r\nimport com.bullyrooks.cloud_application.message_generator.client.dto.MessageResponseDTO;\r\nimport com.bullyrooks.cloud_application.messaging.dto.MessageEvent;\r\nimport com.fasterxml.jackson.databind.ObjectMapper;\r\nimport com.github.javafaker.Faker;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.apache.commons.lang3.StringUtils;\r\nimport org.junit.jupiter.api.Test;\r\nimport org.junit.jupiter.api.extension.ExtendWith;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;\r\nimport org.springframework.boot.test.context.SpringBootTest;\r\nimport org.springframework.boot.test.mock.mockito.MockBean;\r\nimport org.springframework.boot.web.server.LocalServerPort;\r\nimport org.springframework.cloud.stream.binder.test.OutputDestination;\r\nimport org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;\r\nimport org.springframework.context.annotation.Import;\r\nimport org.springframework.http.ResponseEntity;\r\nimport org.springframework.messaging.Message;\r\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\r\nimport org.springframework.web.client.RestTemplate;\r\nimport org.springframework.web.util.UriComponents;\r\nimport org.springframework.web.util.UriComponentsBuilder;\r\n\r\nimport java.io.IOException;\r\nimport java.nio.charset.StandardCharsets;\r\n\r\nimport static org.junit.jupiter.api.Assertions.assertEquals;\r\nimport static org.junit.jupiter.api.Assertions.assertTrue;\r\nimport static org.mockito.Mockito.when;\r\n\r\n@ExtendWith(SpringExtension.class)\r\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\r\n@Slf4j\r\n@AutoConfigureMockMvc\r\n@Import(TestChannelBinderConfiguration.class)\r\npublic class MessageControllerTest {\r\n\r\n    private final String MESSAGE_PATH = \"\/message\";\r\n\r\n    @LocalServerPort\r\n    int randomServerPort;\r\n\r\n    @MockBean\r\n    MessageGeneratorClient messageGeneratorClient;\r\n\r\n    @Autowired\r\n    OutputDestination outputDestination;\r\n\r\n    Faker faker = new Faker();\r\n\r\n    @Test\r\n    void testSaveMessage() throws IOException {\r\n        Long userId = 1l;\r\n\r\n        \/\/given\r\n        CreateMessageRequestDTO request = CreateMessageRequestDTO\r\n                .builder()\r\n                .firstName(faker.name().firstName())\r\n                .lastName(faker.name().lastName())\r\n                .message(faker.gameOfThrones().quote())\r\n                .build();\r\n\r\n        \/\/when\r\n        RestTemplate restTemplate = new RestTemplate();\r\n        String baseUrl = \"http:\/\/localhost:\" + randomServerPort + MESSAGE_PATH;\r\n        UriComponents builder = UriComponentsBuilder.fromHttpUrl(baseUrl)\r\n                .build();\r\n\r\n        ResponseEntity&lt;CreateMessageResponseDTO> result = restTemplate.postForEntity(\r\n                builder.toUri(), request, CreateMessageResponseDTO.class);\r\n\r\n        \/\/then\r\n        CreateMessageResponseDTO dto = result.getBody();\r\n        assertEquals(request.getFirstName(), dto.getFirstName());\r\n        assertEquals(request.getLastName(), dto.getLastName());\r\n        assertEquals(request.getMessage(), dto.getMessage());\r\n\r\n        Message&lt;byte&#91;]> receievedMessage = outputDestination.receive(1000,\"message.created\");\r\n        String messageStr = new String(receievedMessage.getPayload(), StandardCharsets.UTF_8);\r\n        log.info(\"message string: {}\", messageStr);\r\n        ObjectMapper mapper = new ObjectMapper();\r\n        MessageEvent event = mapper.reader().readValue(messageStr, MessageEvent.class);\r\n        assertEquals(request.getFirstName(), event.getFirstName());\r\n        assertEquals(request.getLastName(), event.getLastName());\r\n        assertEquals(request.getMessage(), event.getMessage());\r\n\r\n    }\r\n    @Test\r\n    void testGetReturnsMessageIfMissing() throws InterruptedException, IOException {\r\n        Long userId = 1l;\r\n\r\n        \/\/given\r\n        CreateMessageRequestDTO request = CreateMessageRequestDTO\r\n                .builder()\r\n                .firstName(faker.name().firstName())\r\n                .lastName(faker.name().lastName())\r\n                .build();\r\n\r\n        when(messageGeneratorClient.getMessage()).thenReturn(\r\n                MessageResponseDTO.builder()\r\n                .message(faker.gameOfThrones().quote())\r\n                .build());\r\n\r\n        \/\/when\r\n        RestTemplate restTemplate = new RestTemplate();\r\n        String baseUrl = \"http:\/\/localhost:\" + randomServerPort + MESSAGE_PATH;\r\n        UriComponents builder = UriComponentsBuilder.fromHttpUrl(baseUrl)\r\n                .build();\r\n\r\n        ResponseEntity&lt;CreateMessageResponseDTO> result = restTemplate.postForEntity(\r\n                builder.toUri(), request, CreateMessageResponseDTO.class);\r\n\r\n        \/\/then\r\n        CreateMessageResponseDTO dto = result.getBody();\r\n        assertEquals(request.getFirstName(), dto.getFirstName());\r\n        assertEquals(request.getLastName(), dto.getLastName());\r\n        assertTrue(StringUtils.isNotBlank(dto.getMessage()));\r\n\r\n        Message&lt;byte&#91;]> receievedMessage = outputDestination.receive(1000,\"message.created\");\r\n        String messageStr = new String(receievedMessage.getPayload(), StandardCharsets.UTF_8);\r\n        log.info(\"message string: {}\", messageStr);\r\n        ObjectMapper mapper = new ObjectMapper();\r\n        MessageEvent event = mapper.reader().readValue(messageStr, MessageEvent.class);\r\n        assertEquals(request.getFirstName(), event.getFirstName());\r\n        assertEquals(request.getLastName(), event.getLastName());\r\n        assertTrue(StringUtils.isNotBlank(event.getMessage()));\r\n    }\r\n\r\n}\r\nutDestination.receive(1000,\"message.created\");\r\n        String messageStr = new String(receievedMessage.getPayload(), StandardCharsets.UTF_8);\r\n        log.info(\"message string: {}\", messageStr);\r\n        ObjectMapper mapper = new ObjectMapper();\r\n        MessageEvent event = mapper.reader().readValue(messageStr, MessageEvent.class);\r\n        assertEquals(request.getFirstName(), event.getFirstName());\r\n        assertEquals(request.getLastName(), event.getLastName());\r\n        assertTrue(StringUtils.isNotBlank(event.getMessage()));\r\n    }\r\n\r\n}\r\n<\/code><\/pre>\n\n\n\n<p>You can see that we&#8217;re pulling a message off the topic, decoding it into a json message which we transform into an object before we run our asserts.<\/p>\n\n\n\n<p>The final thing we&#8217;ll need to do is create a new secret and environment variable in our helm deployment, so that we can connect to confluent when we deploy to okteto<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create secret generic confluent-secrets --from-literal=SPRING_KAFKA_PROPERTIES_SASL_JAAS_CONFIG=\"org.apache.kafka.common.security.plain.PlainLoginModule   required username='...'  password='...';\"<\/code><\/pre>\n\n\n\n<p>and add this environment configuration to the <code>deployment.yaml<\/code> file<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>          - name: SPRING_KAFKA_PROPERTIES_SASL_JAAS_CONFIG\r\n            valueFrom:\r\n              secretKeyRef:\r\n                name: confluent-secrets\r\n                key: SPRING_KAFKA_PROPERTIES_SASL_JAAS_CONFIG<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Testing<\/h2>\n\n\n\n<p>We should be able to confirm that we can publish messages now.  Navigate to the topic message page in confluent<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"637\" height=\"321\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-44.png?resize=637%2C321&#038;ssl=1\" alt=\"\" class=\"wp-image-1346\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-44.png?w=637&amp;ssl=1 637w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-44.png?resize=300%2C151&amp;ssl=1 300w\" sizes=\"auto, (max-width: 637px) 100vw, 637px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"516\" height=\"328\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-45.png?resize=516%2C328&#038;ssl=1\" alt=\"\" class=\"wp-image-1347\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-45.png?w=516&amp;ssl=1 516w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-45.png?resize=300%2C191&amp;ssl=1 300w\" sizes=\"auto, (max-width: 516px) 100vw, 516px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>Start up the application locally and hit the cloud-application endpoint with postman<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"651\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-43.png?resize=960%2C651&#038;ssl=1\" alt=\"\" class=\"wp-image-1345\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-43.png?w=1025&amp;ssl=1 1025w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-43.png?resize=300%2C203&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-43.png?resize=768%2C521&amp;ssl=1 768w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>You should see a message appear in the topic in confluent<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"308\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-46-1024x328.png?resize=960%2C308&#038;ssl=1\" alt=\"\" class=\"wp-image-1348\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-46.png?resize=1024%2C328&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-46.png?resize=300%2C96&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-46.png?resize=768%2C246&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-46.png?w=1224&amp;ssl=1 1224w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><figcaption>push up to the feature branch and if it builds successfully merge to main<\/figcaption><\/figure>\n\n\n\n<p>Run the test again against the cloud instance and confirm you can still see the message being produced<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"670\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-47-1024x715.png?resize=960%2C670&#038;ssl=1\" alt=\"\" class=\"wp-image-1353\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-47.png?resize=1024%2C715&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-47.png?resize=300%2C210&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-47.png?resize=768%2C536&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-47.png?w=1041&amp;ssl=1 1041w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"317\" src=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-48-1024x338.png?resize=960%2C317&#038;ssl=1\" alt=\"\" class=\"wp-image-1354\" srcset=\"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-48.png?resize=1024%2C338&amp;ssl=1 1024w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-48.png?resize=300%2C99&amp;ssl=1 300w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-48.png?resize=768%2C253&amp;ssl=1 768w, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-48.png?w=1243&amp;ssl=1 1243w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" data-recalc-dims=\"1\" \/><\/figure>\n","protected":false},"excerpt":{"rendered":"<div class=\"entry-summary\">\nWe&#8217;re going to be updating cloud-application to remove the mongodb repository logic&hellip;\n<\/div>\n<div class=\"link-more\"><a href=\"https:\/\/bullyrooks.com\/index.php\/2022\/02\/27\/kube-cloud-pt5-create-an-event-publisher\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &ldquo;Kube Cloud Pt5 | Create an Event Publisher&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":[189,90,192,190,43,194],"course":[188],"class_list":["post-1339","post","type-post","status-publish","format-standard","hentry","category-software-development","tag-confluent","tag-event-driven-architecture","tag-events","tag-kafka","tag-spring-boot","tag-spring-cloud-stream","course-kube-cloud-pt5-kafka-events","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":1356,"url":"https:\/\/bullyrooks.com\/index.php\/2022\/02\/27\/kube-cloud-pt5-create-an-event-consumer\/","url_meta":{"origin":1339,"position":0},"title":"Kube Cloud Pt5 | Create an Event Consumer","author":"Bullyrook","date":"February 27, 2022","format":false,"excerpt":"Now that we've got messages being published to kafka, we are going to need to build our consumer that receives those events and stores them into the mongo database. Go ahead and create a new repository called message-repository according to the microservice startup course here. This is the pom.xml that\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-49.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-49.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image-49.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1069,"url":"https:\/\/bullyrooks.com\/index.php\/2021\/07\/23\/spring-boot-lambda-implementation\/","url_meta":{"origin":1339,"position":1},"title":"Spring Boot Lambda Implementation","author":"Bullyrook","date":"July 23, 2021","format":false,"excerpt":"Now we're going to add some code. I'm going to follow my ports and adapters method of building a DTO and value object that I've used previously. Yes, its a bit of overkill for this project (especially a hello world example), but if you're using this course as a springboard\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-4.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-4.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2021\/07\/image-4.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1229,"url":"https:\/\/bullyrooks.com\/index.php\/2022\/01\/23\/kube-cloud-pt2-automated-testing-with-testcontainers\/","url_meta":{"origin":1339,"position":2},"title":"Kube Cloud Pt2 | Automated Testing with TestContainers","author":"Bullyrook","date":"January 23, 2022","format":false,"excerpt":"In the last section we implemented service endpoint that stored data in a mongodb backend. In this session we're going to build a component test to automatically verify that functionality. TestContainer Overview TestContainers allow us to mock external resources with a docker based implementation. This is similar to \"regular\" mocking\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1258,"url":"https:\/\/bullyrooks.com\/index.php\/2022\/02\/13\/kube-cloud-pt3-rest-interaction\/","url_meta":{"origin":1339,"position":3},"title":"Kube Cloud Pt3 | REST Interaction","author":"Bullyrook","date":"February 13, 2022","format":false,"excerpt":"Now that we've got a new service, we're going to make it discoverable via kubernetes and call it from the cloud application service. Enable Kubernetes Features Let's start a new branch in our cloud_application project $ git checkout -b kube Switched to a new branch 'kube' Edit the pom.xml and\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/02\/image.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":1220,"url":"https:\/\/bullyrooks.com\/index.php\/2022\/01\/23\/cloud-kube-pt2-setting-up-a-datastore\/","url_meta":{"origin":1339,"position":4},"title":"Cloud Kube Pt2 | Setting Up a Datastore","author":"Bullyrook","date":"January 23, 2022","format":false,"excerpt":"The first part of these courses was all about setting up an build and deploy pipeline so that we could automate building a helm chart and deploying to our cloud hosted environment at Okteto. In this course we're going to start adding more functionality so that we can demonstrate best\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/01\/image-38.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/01\/image-38.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/bullyrooks.com\/wp-content\/uploads\/2022\/01\/image-38.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":827,"url":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-1-d67f80487848\/","url_meta":{"origin":1339,"position":5},"title":"Setup: IDE and New Project","author":"Bullyrook","date":"March 30, 2020","format":false,"excerpt":"\u00a0Its very easy to get a spring boot microservice up and running. You can read any variety of articles on medium (or other service) on how to do that. However, there\u2019s little about how to do some of the more complicated things that you need in order to support a\u2026","rel":"","context":"In &quot;Software Development&quot;","block_context":{"text":"Software Development","link":"https:\/\/bullyrooks.com\/index.php\/category\/software-development\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/1339","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=1339"}],"version-history":[{"count":4,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/1339\/revisions"}],"predecessor-version":[{"id":1355,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/1339\/revisions\/1355"}],"wp:attachment":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/media?parent=1339"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/categories?post=1339"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/tags?post=1339"},{"taxonomy":"course","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/course?post=1339"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}