diff --git a/README.md b/README.md index 3e8cff9..cbbe6f0 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,41 @@ public NexmoClient.Builder customNexmoBuilder() { ``` > Note that you must include your credentials as shown in this example. This builder completely replaces the automatically configured one. +## Listen for Incoming Events + +The starter will register a few different routes for accepting webhooks from Nexmo. You can hook into these events by creating an event listener. + +To listen for incoming SMS events, for example: + +```java +import com.nexmo.client.incoming.MessageEvent; +import com.nexmo.starter.events.SmsMessageReceivedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +@Component +class SmsEventListener { + @EventListener + public void onIncomingSms(SmsMessageReceivedEvent event) { + MessageEvent message = event.getMessage(); + } +} +``` + +By default, the SMS event webhook is configured on the `/webhooks/sms` path. This can be customized via the `application.properties`: + +```properties +nexmo.webhooks.incoming-sms-endpoint=/foo/bar +``` + +This webhook will need to be configured in the [Nexmo Dashboard](https://dashboard.nexmo.com). + +You can also disable the webhook controllers: + +```properties +nexmo.webhooks.disabled=true +``` + ## Customize Nexmo Client Version By default, the Nexmo Spring Boot Starter will transitively define Nexmo Client to the latest version at its release. You can override this by adding a dependency on the Nexmo Client, bringing in `4.2.0` for example: diff --git a/nexmo-spring-boot-autoconfigure/pom.xml b/nexmo-spring-boot-autoconfigure/pom.xml index ff715b9..65a5346 100644 --- a/nexmo-spring-boot-autoconfigure/pom.xml +++ b/nexmo-spring-boot-autoconfigure/pom.xml @@ -64,6 +64,13 @@ true + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + true + + com.nexmo client @@ -95,6 +102,13 @@ 3.12.2 test + + + com.nhaarman.mockitokotlin2 + mockito-kotlin + 2.1.0 + test + diff --git a/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/NexmoAutoConfiguration.kt b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/NexmoAutoConfiguration.kt index c916664..2a3f2a7 100644 --- a/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/NexmoAutoConfiguration.kt +++ b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/NexmoAutoConfiguration.kt @@ -44,37 +44,35 @@ import org.springframework.context.annotation.Lazy @Configuration @ConditionalOnClass(NexmoClient::class) @EnableConfigurationProperties(NexmoCredentialsProperties::class) -open class NexmoAutoConfiguration( - private val nexmoProperties: NexmoCredentialsProperties -) { +open class NexmoAutoConfiguration(private val nexmoCredentialsProperties: NexmoCredentialsProperties) { @Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "nexmo.creds", value = ["api-key", "secret"]) @Lazy @NotNull open fun nexmoBuilder(): NexmoClient.Builder { - if (nexmoProperties.privateKeyContents.isNotBlank() && nexmoProperties.privateKeyPath.isNotBlank()) { + if (nexmoCredentialsProperties.privateKeyContents.isNotBlank() && nexmoCredentialsProperties.privateKeyPath.isNotBlank()) { throw IllegalArgumentException("Found both private key path and private key contents. Only one option can be used at a time.") } val builder = NexmoClient.builder() - .apiKey(nexmoProperties.apiKey) - .apiSecret(nexmoProperties.secret) + .apiKey(nexmoCredentialsProperties.apiKey) + .apiSecret(nexmoCredentialsProperties.secret) - if (nexmoProperties.privateKeyContents.isNotBlank()) { - builder.privateKeyContents(nexmoProperties.privateKeyContents) + if (nexmoCredentialsProperties.privateKeyContents.isNotBlank()) { + builder.privateKeyContents(nexmoCredentialsProperties.privateKeyContents) } - if (nexmoProperties.privateKeyPath.isNotBlank()) { - builder.privateKeyPath(nexmoProperties.privateKeyPath) + if (nexmoCredentialsProperties.privateKeyPath.isNotBlank()) { + builder.privateKeyPath(nexmoCredentialsProperties.privateKeyPath) } - if (nexmoProperties.applicationId.isNotBlank()) { - builder.applicationId(nexmoProperties.applicationId) + if (nexmoCredentialsProperties.applicationId.isNotBlank()) { + builder.applicationId(nexmoCredentialsProperties.applicationId) } - if (nexmoProperties.signature.isNotBlank()) { - builder.signatureSecret(nexmoProperties.signature) + if (nexmoCredentialsProperties.signature.isNotBlank()) { + builder.signatureSecret(nexmoCredentialsProperties.signature) } return builder diff --git a/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/NexmoProperties.kt b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/NexmoCredentialsProperties.kt similarity index 100% rename from nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/NexmoProperties.kt rename to nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/NexmoCredentialsProperties.kt diff --git a/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/events/SmsMessageReceivedEvent.kt b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/events/SmsMessageReceivedEvent.kt new file mode 100644 index 0000000..8e1f06e --- /dev/null +++ b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/events/SmsMessageReceivedEvent.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011-2019 Nexmo Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.nexmo.starter.events + +import com.nexmo.client.incoming.MessageEvent +import org.springframework.context.ApplicationEvent + +class SmsMessageReceivedEvent(source: Any, val message: MessageEvent) : ApplicationEvent(source) diff --git a/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/webhooks/NexmoWebhookAutoConfiguration.kt b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/webhooks/NexmoWebhookAutoConfiguration.kt new file mode 100644 index 0000000..581e1e2 --- /dev/null +++ b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/webhooks/NexmoWebhookAutoConfiguration.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011-2019 Nexmo Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.nexmo.starter.webhooks + +import com.nexmo.client.NexmoClient +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.ApplicationEventPublisher +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +@EnableConfigurationProperties(NexmoWebhookProperties::class) +@ConditionalOnClass(NexmoClient::class) +open class NexmoWebhookAutoConfiguration(private val nexmoWebhookProperties: NexmoWebhookProperties) { + @Bean + @ConditionalOnProperty( + prefix = "nexmo.webhooks", + value = ["disabled"], + havingValue = "false", + matchIfMissing = true + ) + open fun smsMessageReceivedController(applicationEventPublisher: ApplicationEventPublisher) = + SmsMessageReceivedController(applicationEventPublisher) +} diff --git a/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/webhooks/NexmoWebhookProperties.kt b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/webhooks/NexmoWebhookProperties.kt new file mode 100644 index 0000000..5611961 --- /dev/null +++ b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/webhooks/NexmoWebhookProperties.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011-2019 Nexmo Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.nexmo.starter.webhooks + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "nexmo.webhooks") +data class NexmoWebhookProperties( + /** + * Set to true to disable the incoming webhook controllers from being automatically registered. + */ + var disabled: Boolean = false, + + /** + * Incoming SMS Endpoint + */ + var incomingSmsEndpoint: String = SmsMessageReceivedController.DEFAULT_ENDPOINT +) diff --git a/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/webhooks/SmsMessageReceivedController.kt b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/webhooks/SmsMessageReceivedController.kt new file mode 100644 index 0000000..88c2aa3 --- /dev/null +++ b/nexmo-spring-boot-autoconfigure/src/main/kotlin/com/nexmo/starter/webhooks/SmsMessageReceivedController.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011-2019 Nexmo Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.nexmo.starter.webhooks + +import com.fasterxml.jackson.databind.ObjectMapper +import com.nexmo.client.incoming.MessageEvent +import com.nexmo.starter.events.SmsMessageReceivedEvent +import org.springframework.context.ApplicationEventPublisher +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.* + +@RequestMapping +@ResponseStatus(HttpStatus.OK) +class SmsMessageReceivedController(private val applicationEventPublisher: ApplicationEventPublisher) { + /** + * Handle incoming SMS webhooks where the data is sent via a get request. + * + * Nexmo HTTP Method: GET + */ + @GetMapping("\${nexmo.webhooks.incoming-sms-endpoint:$DEFAULT_ENDPOINT}") + fun get(@RequestParam parameters: Map) { + val messageEvent = MessageEvent.fromJson(ObjectMapper().writeValueAsString(parameters)) + applicationEventPublisher.publishEvent(SmsMessageReceivedEvent(this, messageEvent)) + } + + /** + * Handle incoming SMS webhooks where the data is sent via a post request using form parameters. + * + * Nexmo HTTP Method: POST + */ + @PostMapping( + "\${nexmo.webhooks.incoming-sms-endpoint:$DEFAULT_ENDPOINT}", + consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8"] + ) + fun post(@RequestParam parameters: Map) { + val messageEvent = MessageEvent.fromJson(ObjectMapper().writeValueAsString(parameters)) + applicationEventPublisher.publishEvent(SmsMessageReceivedEvent(this, messageEvent)) + } + + /** + * Handle incoming SMS webhooks where the data is sent via a post request and the body is JSON + * + * Nexmo HTTP Method: POST-JSON + */ + @PostMapping( + "\${nexmo.webhooks.incoming-sms-endpoint:$DEFAULT_ENDPOINT}", + consumes = [MediaType.APPLICATION_JSON_VALUE] + ) + fun post(@RequestBody json: String) { + val messageEvent = MessageEvent.fromJson(json) + applicationEventPublisher.publishEvent(SmsMessageReceivedEvent(this, messageEvent)) + } + + companion object { + const val DEFAULT_ENDPOINT = "/webhooks/sms" + } +} diff --git a/nexmo-spring-boot-autoconfigure/src/test/kotlin/com/nexmo/starter/webhooks/NexmoWebhookAutoConfigurationTest.kt b/nexmo-spring-boot-autoconfigure/src/test/kotlin/com/nexmo/starter/webhooks/NexmoWebhookAutoConfigurationTest.kt new file mode 100644 index 0000000..b1b4173 --- /dev/null +++ b/nexmo-spring-boot-autoconfigure/src/test/kotlin/com/nexmo/starter/webhooks/NexmoWebhookAutoConfigurationTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011-2019 Nexmo Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.nexmo.starter.webhooks + +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import org.springframework.boot.autoconfigure.AutoConfigurations +import org.springframework.boot.test.context.runner.ApplicationContextRunner + +class NexmoWebhookAutoConfigurationTest { + val contextRunner = ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(NexmoWebhookAutoConfiguration::class.java) + ) + + @Test + fun `when webhooks are disabled the incoming sms controller is not in the container`() { + contextRunner.withPropertyValues( + "nexmo.webhooks.disabled=true" + ).run { + assertThat(it).doesNotHaveBean(SmsMessageReceivedController::class.java) + } + } + + @Test + fun `when webhooks are not disabled the incoming sms controller is in in the container`() { + contextRunner.withPropertyValues( + "nexmo.webhooks.disabled=false" + ).run { + assertThat(it).hasSingleBean(SmsMessageReceivedController::class.java) + } + } + + @Test + fun `when webhooks disabled property is missing the incoming sms controller is in the container`() { + contextRunner.withPropertyValues().run { + assertThat(it).hasSingleBean(SmsMessageReceivedController::class.java) + } + } +} diff --git a/nexmo-spring-boot-autoconfigure/src/test/kotlin/com/nexmo/starter/webhooks/SmsMessageReceivedControllerTest.kt b/nexmo-spring-boot-autoconfigure/src/test/kotlin/com/nexmo/starter/webhooks/SmsMessageReceivedControllerTest.kt new file mode 100644 index 0000000..ec3f5d3 --- /dev/null +++ b/nexmo-spring-boot-autoconfigure/src/test/kotlin/com/nexmo/starter/webhooks/SmsMessageReceivedControllerTest.kt @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2011-2019 Nexmo Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.nexmo.starter.webhooks + +import com.nexmo.client.incoming.MessageType +import com.nexmo.starter.events.SmsMessageReceivedEvent +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.verify +import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.mock +import org.springframework.context.ApplicationEventPublisher +import java.text.SimpleDateFormat +import java.util.* + +class SmsMessageReceivedControllerTest { + lateinit var controller: SmsMessageReceivedController + lateinit var applicationEventPublisher: ApplicationEventPublisher + + @Before + fun setUp() { + applicationEventPublisher = mock(ApplicationEventPublisher::class.java) + controller = SmsMessageReceivedController(applicationEventPublisher) + } + + @Test + fun `when the get method is called with a map of url parameters then an sms received event is fired`() { + val captor = argumentCaptor() + + controller.get( + mapOf( + "msisdn" to "msisdn", + "to" to "to", + "text" to "text", + "type" to "TEXT", + "concat" to "true", + "concat-part" to "1", + "concat-ref" to "2", + "concat-total" to "3", + "data" to "data", + "keyword" to "keyword", + "messageId" to "message-id", + "message-timestamp" to "2019-06-21 14:14:01", + "nonce" to "nonce", + "udh" to "udh" + ) + ) + + verify(applicationEventPublisher).publishEvent(captor.capture()) + val event = captor.firstValue + assertEquals("msisdn", event.message.msisdn) + assertEquals("to", event.message.to) + assertEquals("text", event.message.text) + assertEquals(MessageType.TEXT, event.message.type) + assertTrue(event.message.concat) + assertEquals(1, event.message.concatPart) + assertEquals(2, event.message.concatRef) + assertEquals(3, event.message.concatTotal) + assertEquals("data", event.message.data) + assertEquals("keyword", event.message.keyword) + assertEquals("message-id", event.message.messageId) + assertEquals("nonce", event.message.nonce) + assertEquals("udh", event.message.udh) + + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + dateFormat.timeZone = TimeZone.getTimeZone("UTC") + assertEquals(dateFormat.parse("2019-06-21 14:14:01"), event.message.messageTimestamp) + } + + @Test + fun `when the post method is called with a map of form parameters then an sms received event is fired`() { + val captor = argumentCaptor() + + controller.post( + mapOf( + "msisdn" to "msisdn", + "to" to "to", + "text" to "text", + "type" to "TEXT", + "concat" to "true", + "concat-part" to "1", + "concat-ref" to "2", + "concat-total" to "3", + "data" to "data", + "keyword" to "keyword", + "messageId" to "message-id", + "message-timestamp" to "2019-06-21 14:14:01", + "nonce" to "nonce", + "udh" to "udh" + ) + ) + + verify(applicationEventPublisher).publishEvent(captor.capture()) + val event = captor.firstValue + assertEquals("msisdn", event.message.msisdn) + assertEquals("to", event.message.to) + assertEquals("text", event.message.text) + assertEquals(MessageType.TEXT, event.message.type) + assertTrue(event.message.concat) + assertEquals(1, event.message.concatPart) + assertEquals(2, event.message.concatRef) + assertEquals(3, event.message.concatTotal) + assertEquals("data", event.message.data) + assertEquals("keyword", event.message.keyword) + assertEquals("message-id", event.message.messageId) + assertEquals("nonce", event.message.nonce) + assertEquals("udh", event.message.udh) + + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + dateFormat.timeZone = TimeZone.getTimeZone("UTC") + assertEquals(dateFormat.parse("2019-06-21 14:14:01"), event.message.messageTimestamp) + } + + @Test + fun `when the post method is called with a json parameters then an sms received event is fired`() { + val captor = argumentCaptor() + + controller.post( + "{\"msisdn\":\"msisdn\",\"to\":\"to\",\"text\":\"text\",\"type\":\"TEXT\",\"concat\":\"true\",\"concat-part\":\"1\",\"concat-ref\":\"2\",\"concat-total\":\"3\",\"data\":\"data\",\"keyword\":\"keyword\",\"messageId\":\"message-id\",\"message-timestamp\":\"2019-06-21 14:14:01\",\"nonce\":\"nonce\",\"udh\":\"udh\"}" + ) + + verify(applicationEventPublisher).publishEvent(captor.capture()) + val event = captor.firstValue + assertEquals("msisdn", event.message.msisdn) + assertEquals("to", event.message.to) + assertEquals("text", event.message.text) + assertEquals(MessageType.TEXT, event.message.type) + assertTrue(event.message.concat) + assertEquals(1, event.message.concatPart) + assertEquals(2, event.message.concatRef) + assertEquals(3, event.message.concatTotal) + assertEquals("data", event.message.data) + assertEquals("keyword", event.message.keyword) + assertEquals("message-id", event.message.messageId) + assertEquals("nonce", event.message.nonce) + assertEquals("udh", event.message.udh) + + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + dateFormat.timeZone = TimeZone.getTimeZone("UTC") + assertEquals(dateFormat.parse("2019-06-21 14:14:01"), event.message.messageTimestamp) + } +} diff --git a/nexmo-spring-boot-test-application/src/main/kotlin/com/nexmo/starter/SmsMessageReceivedEventListener.kt b/nexmo-spring-boot-test-application/src/main/kotlin/com/nexmo/starter/SmsMessageReceivedEventListener.kt new file mode 100644 index 0000000..6be80f4 --- /dev/null +++ b/nexmo-spring-boot-test-application/src/main/kotlin/com/nexmo/starter/SmsMessageReceivedEventListener.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011-2019 Nexmo Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.nexmo.starter + +import com.nexmo.starter.events.SmsMessageReceivedEvent +import org.springframework.context.event.EventListener +import org.springframework.stereotype.Component + +@Component +class SmsMessageReceivedEventListener { + @EventListener + fun onSmsReceivedEvent(messageReceivedEvent: SmsMessageReceivedEvent) { + val message = messageReceivedEvent.message + println("(${message.msisdn}) -> ${message.to} ${message.text}") + } +}