-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added feature support for microsoft teams webhoo Signed-off-by: danielkyalo599 <[email protected]> * Added feature support for microsoft teams webhook ,removed valid webhooks Signed-off-by: danielkyalo599 <[email protected]> * Added feature support for Microsoft teams webhook Signed-off-by: danielkyalo599 <[email protected]> * Refactored feature support for ms teams and added unit and integTest Signed-off-by: danielkyalo599 <[email protected]> * fix build in core Signed-off-by: zhichao-aws <[email protected]> * fix core-spi build Signed-off-by: zhichao-aws <[email protected]> * fix notifications main code Signed-off-by: zhichao-aws <[email protected]> * fix mappings, add IT Signed-off-by: zhichao-aws <[email protected]> * add auto upgrade mapping logic Signed-off-by: zhichao-aws <[email protected]> * put load mapping to initialize step Signed-off-by: zhichao-aws <[email protected]> * add schema_version field Signed-off-by: zhichao-aws <[email protected]> * add integ test Signed-off-by: zhichao-aws <[email protected]> * adjust with auto upgrade mapping logic Signed-off-by: zhichao-aws <[email protected]> * add bwc Signed-off-by: zhichao-aws <[email protected]> * modify bwc Signed-off-by: zhichao-aws <[email protected]> * modify bwc Signed-off-by: zhichao-aws <[email protected]> * resolve comments Signed-off-by: zhichao-aws <[email protected]> * add license header Signed-off-by: zhichao-aws <[email protected]> * fix microsoft teams sample url in IT to adapt url validation Signed-off-by: zhichao-aws <[email protected]> --------- Signed-off-by: danielkyalo599 <[email protected]> Signed-off-by: zhichao-aws <[email protected]> Co-authored-by: danielkyalo599 <[email protected]>
- Loading branch information
1 parent
d36b9c0
commit 8c8a6b6
Showing
28 changed files
with
575 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
...in/kotlin/org/opensearch/notifications/spi/model/destination/MicrosoftTeamsDestination.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.opensearch.notifications.spi.model.destination | ||
|
||
/** | ||
* This class holds the contents of a Microsoft Teams destination | ||
*/ | ||
class MicrosoftTeamsDestination( | ||
url: String, | ||
) : WebhookDestination(url, DestinationType.MICROSOFT_TEAMS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
...t/kotlin/org/opensearch/notifications/core/destinations/MicrosoftTeamsDestinationTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.opensearch.notifications.core.destinations | ||
|
||
import io.mockk.every | ||
import io.mockk.mockk | ||
import io.mockk.mockkStatic | ||
import org.apache.hc.client5.http.classic.methods.HttpPost | ||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient | ||
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse | ||
import org.apache.hc.core5.http.io.entity.StringEntity | ||
import org.easymock.EasyMock | ||
import org.junit.jupiter.api.Assertions | ||
import org.junit.jupiter.api.Assertions.assertEquals | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.assertThrows | ||
import org.junit.jupiter.params.ParameterizedTest | ||
import org.junit.jupiter.params.provider.Arguments | ||
import org.junit.jupiter.params.provider.MethodSource | ||
import org.opensearch.core.rest.RestStatus | ||
import org.opensearch.notifications.core.NotificationCoreImpl | ||
import org.opensearch.notifications.core.client.DestinationHttpClient | ||
import org.opensearch.notifications.core.transport.DestinationTransportProvider | ||
import org.opensearch.notifications.core.transport.WebhookDestinationTransport | ||
import org.opensearch.notifications.spi.model.DestinationMessageResponse | ||
import org.opensearch.notifications.spi.model.MessageContent | ||
import org.opensearch.notifications.spi.model.destination.ChimeDestination | ||
import org.opensearch.notifications.spi.model.destination.DestinationType | ||
import org.opensearch.notifications.spi.model.destination.MicrosoftTeamsDestination | ||
import java.net.MalformedURLException | ||
import java.util.stream.Stream | ||
|
||
internal class MicrosoftTeamsDestinationTests { | ||
companion object { | ||
@JvmStatic | ||
fun escapeSequenceToRaw(): Stream<Arguments> = | ||
Stream.of( | ||
Arguments.of("\n", """\n"""), | ||
Arguments.of("\t", """\t"""), | ||
Arguments.of("\b", """\b"""), | ||
Arguments.of("\r", """\r"""), | ||
Arguments.of("\"", """\""""), | ||
) | ||
} | ||
|
||
@BeforeEach | ||
fun setup() { | ||
// Stubbing isHostInDenylist() so it doesn't attempt to resolve hosts that don't exist in the unit tests | ||
mockkStatic("org.opensearch.notifications.spi.utils.ValidationHelpersKt") | ||
every { org.opensearch.notifications.spi.utils.isHostInDenylist(any(), any()) } returns false | ||
} | ||
|
||
@Test | ||
fun `test MicrosoftTeams message null entity response`() { | ||
val mockHttpClient: CloseableHttpClient = EasyMock.createMock(CloseableHttpClient::class.java) | ||
|
||
// The DestinationHttpClient replaces a null entity with "{}". | ||
val expectedWebhookResponse = DestinationMessageResponse(RestStatus.OK.status, "{}") | ||
|
||
val httpResponse = mockk<CloseableHttpResponse>() | ||
EasyMock.expect(mockHttpClient.execute(EasyMock.anyObject(HttpPost::class.java))).andReturn(httpResponse) | ||
|
||
every { httpResponse.code } returns RestStatus.OK.status | ||
every { httpResponse.entity } returns null | ||
EasyMock.replay(mockHttpClient) | ||
|
||
val httpClient = DestinationHttpClient(mockHttpClient) | ||
val webhookDestinationTransport = WebhookDestinationTransport(httpClient) | ||
DestinationTransportProvider.destinationTransportMap = mapOf(DestinationType.MICROSOFT_TEAMS to webhookDestinationTransport) | ||
|
||
val title = "test MicrosoftTeams" | ||
val messageText = "Message gughjhjlkh Body emoji test: :) :+1: " + | ||
"link test: http://sample.com email test: [email protected] All member callout: " + | ||
"@All All Present member callout: @Present" | ||
val url = "https://abc/com" | ||
|
||
val destination = MicrosoftTeamsDestination(url) | ||
val message = MessageContent(title, messageText) | ||
|
||
val actualMicrosoftTeamsResponse: DestinationMessageResponse = NotificationCoreImpl.sendMessage(destination, message, "ref") | ||
|
||
assertEquals(expectedWebhookResponse.statusText, actualMicrosoftTeamsResponse.statusText) | ||
assertEquals(expectedWebhookResponse.statusCode, actualMicrosoftTeamsResponse.statusCode) | ||
} | ||
|
||
@Test | ||
fun `test MicrosoftTeams message empty entity response`() { | ||
val mockHttpClient: CloseableHttpClient = EasyMock.createMock(CloseableHttpClient::class.java) | ||
val expectedWebhookResponse = DestinationMessageResponse(RestStatus.OK.status, "{}") | ||
|
||
val httpResponse = mockk<CloseableHttpResponse>() | ||
EasyMock.expect(mockHttpClient.execute(EasyMock.anyObject(HttpPost::class.java))).andReturn(httpResponse) | ||
every { httpResponse.code } returns RestStatus.OK.status | ||
every { httpResponse.entity } returns StringEntity("") | ||
EasyMock.replay(mockHttpClient) | ||
|
||
val httpClient = DestinationHttpClient(mockHttpClient) | ||
val webhookDestinationTransport = WebhookDestinationTransport(httpClient) | ||
DestinationTransportProvider.destinationTransportMap = mapOf(DestinationType.MICROSOFT_TEAMS to webhookDestinationTransport) | ||
|
||
val title = "test MicrosoftTeams" | ||
val messageText = "{\"Content\":\"Message gughjhjlkh Body emoji test: :) :+1: " + | ||
"link test: http://sample.com email test: [email protected] All member callout: " + | ||
"@All All Present member callout: @Present\"}" | ||
val url = "https://abc/com" | ||
|
||
val destination = MicrosoftTeamsDestination(url) | ||
val message = MessageContent(title, messageText) | ||
|
||
val actualMicrosoftTeamsResponse: DestinationMessageResponse = NotificationCoreImpl.sendMessage(destination, message, "ref") | ||
|
||
assertEquals(expectedWebhookResponse.statusText, actualMicrosoftTeamsResponse.statusText) | ||
assertEquals(expectedWebhookResponse.statusCode, actualMicrosoftTeamsResponse.statusCode) | ||
} | ||
|
||
@Test | ||
fun `test MicrosoftTeams message non-empty entity response`() { | ||
val responseContent = "It worked!" | ||
val mockHttpClient: CloseableHttpClient = EasyMock.createMock(CloseableHttpClient::class.java) | ||
val expectedWebhookResponse = DestinationMessageResponse(RestStatus.OK.status, responseContent) | ||
|
||
val httpResponse = mockk<CloseableHttpResponse>() | ||
EasyMock.expect(mockHttpClient.execute(EasyMock.anyObject(HttpPost::class.java))).andReturn(httpResponse) | ||
every { httpResponse.code } returns RestStatus.OK.status | ||
every { httpResponse.entity } returns StringEntity(responseContent) | ||
EasyMock.replay(mockHttpClient) | ||
|
||
val httpClient = DestinationHttpClient(mockHttpClient) | ||
val webhookDestinationTransport = WebhookDestinationTransport(httpClient) | ||
DestinationTransportProvider.destinationTransportMap = mapOf(DestinationType.MICROSOFT_TEAMS to webhookDestinationTransport) | ||
|
||
val title = "test MicrosoftTeams" | ||
val messageText = "{\"Content\":\"Message gughjhjlkh Body emoji test: :) :+1: " + | ||
"link test: http://sample.com email test: [email protected] All member callout: " + | ||
"@All All Present member callout: @Present\"}" | ||
val url = "https://abc/com" | ||
|
||
val destination = MicrosoftTeamsDestination(url) | ||
val message = MessageContent(title, messageText) | ||
|
||
val actualMicrosoftTeamsResponse: DestinationMessageResponse = NotificationCoreImpl.sendMessage(destination, message, "ref") | ||
|
||
assertEquals(expectedWebhookResponse.statusText, actualMicrosoftTeamsResponse.statusText) | ||
assertEquals(expectedWebhookResponse.statusCode, actualMicrosoftTeamsResponse.statusCode) | ||
} | ||
|
||
@Test | ||
fun `test url missing should throw IllegalArgumentException with message`() { | ||
val exception = Assertions.assertThrows(IllegalArgumentException::class.java) { | ||
MicrosoftTeamsDestination("") | ||
} | ||
assertEquals("url is null or empty", exception.message) | ||
} | ||
|
||
@Test | ||
fun testUrlInvalidMessage() { | ||
assertThrows<MalformedURLException> { | ||
ChimeDestination("invalidUrl") | ||
} | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("escapeSequenceToRaw") | ||
fun `test build webhook request body for microsoft teams should have title included and prevent escape`( | ||
escapeSequence: String, | ||
rawString: String | ||
) { | ||
val httpClient = DestinationHttpClient() | ||
val title = "test MicrosoftTeams" | ||
val messageText = "line1${escapeSequence}line2" | ||
val url = "https://abc/com" | ||
val expectedRequestBody = """{"text":"$title\n\nline1${rawString}line2"}""" | ||
val destination = MicrosoftTeamsDestination(url) | ||
val message = MessageContent(title, messageText) | ||
val actualRequestBody = httpClient.buildRequestBody(destination, message) | ||
assertEquals(expectedRequestBody, actualRequestBody) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.