Skip to content

Commit

Permalink
common utils to support Microsoft teams in notifications (#428)
Browse files Browse the repository at this point in the history
* Added common utils for microsoft teams

Signed-off-by: danielkyalo599 <[email protected]>

* Added configType ,eventstatus,configDataProperties and msTeams files

Signed-off-by: dankyalo599 <[email protected]>

* Added ConfigType,EventStatus,ConfigDataProperties and MicrosoftTeams

Signed-off-by: dankyalo599 <[email protected]>

* fix build

Signed-off-by: zhichao-aws <[email protected]>

* fix build, add more test

Signed-off-by: zhichao-aws <[email protected]>

* change strings import

Signed-off-by: zhichao-aws <[email protected]>

* fix after core

Signed-off-by: zhichao-aws <[email protected]>

---------

Signed-off-by: danielkyalo599 <[email protected]>
Signed-off-by: dankyalo599 <[email protected]>
Signed-off-by: zhichao-aws <[email protected]>
Co-authored-by: danielkyalo599 <[email protected]>
  • Loading branch information
zhichao-aws and dankyalo599 committed Aug 18, 2023
1 parent fad4525 commit bae1bee
Show file tree
Hide file tree
Showing 14 changed files with 417 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ enum class ConfigType(val tag: String) {
override fun toString(): String {
return tag
}
},
MICROSOFT_TEAMS("microsoft_teams") {
override fun toString(): String {
return tag
}
};

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ data class EventStatus(
ConfigType.SLACK -> requireNotNull(deliveryStatus)
ConfigType.EMAIL -> require(emailRecipientStatus.isNotEmpty())
ConfigType.SNS -> requireNotNull(deliveryStatus)
ConfigType.MICROSOFT_TEAMS -> requireNotNull(deliveryStatus)
ConfigType.NONE -> log.info("Some config field not recognized")
else -> {
log.info("non-allowed config type for Status")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
package org.opensearch.commons.notifications.model

import org.opensearch.commons.notifications.NotificationConstants.URL_TAG
import org.opensearch.commons.utils.logger
import org.opensearch.commons.utils.validateUrl
import org.opensearch.core.common.Strings
import org.opensearch.core.common.io.stream.StreamInput
import org.opensearch.core.common.io.stream.StreamOutput
import org.opensearch.core.common.io.stream.Writeable
import org.opensearch.core.xcontent.ToXContent
import org.opensearch.core.xcontent.XContentBuilder
import org.opensearch.core.xcontent.XContentParser
import org.opensearch.core.xcontent.XContentParserUtils
import java.io.IOException

/**
* Data class representing MicrosoftTeams channel.
*/
data class MicrosoftTeams(
val url: String
) : BaseConfigData {

init {
require(!Strings.isNullOrEmpty(url)) { "URL is null or empty" }
validateUrl(url)
}

companion object {
private val log by logger(MicrosoftTeams::class.java)

/**
* reader to create instance of class from writable.
*/
val reader = Writeable.Reader { MicrosoftTeams(it) }

/**
* Parser to parse xContent
*/
val xParser = XParser { parse(it) }

/**
* Creator used in REST communication.
* @param parser XContentParser to deserialize data from.
*/
@JvmStatic
@Throws(IOException::class)
fun parse(parser: XContentParser): MicrosoftTeams {
var url: String? = null

XContentParserUtils.ensureExpectedToken(
XContentParser.Token.START_OBJECT,
parser.currentToken(),
parser
)
while (parser.nextToken() != XContentParser.Token.END_OBJECT) {
val fieldName = parser.currentName()
parser.nextToken()
when (fieldName) {
URL_TAG -> url = parser.text()
else -> {
parser.skipChildren()
log.info("Unexpected field: $fieldName, while parsing MicrosoftTeams destination")
}
}
}
url ?: throw IllegalArgumentException("$URL_TAG field absent")
return MicrosoftTeams(url)
}
}

/**
* Constructor used in transport action communication.
* @param input StreamInput stream to deserialize data from.
*/
constructor(input: StreamInput) : this(
url = input.readString()
)

/**
* {@inheritDoc}
*/
override fun writeTo(output: StreamOutput) {
output.writeString(url)
}

/**
* {@inheritDoc}
*/
override fun toXContent(builder: XContentBuilder?, params: ToXContent.Params?): XContentBuilder {
builder!!
return builder.startObject()
.field(URL_TAG, url)
.endObject()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.opensearch.commons.notifications.model.Chime
import org.opensearch.commons.notifications.model.ConfigType
import org.opensearch.commons.notifications.model.Email
import org.opensearch.commons.notifications.model.EmailGroup
import org.opensearch.commons.notifications.model.MicrosoftTeams
import org.opensearch.commons.notifications.model.SesAccount
import org.opensearch.commons.notifications.model.Slack
import org.opensearch.commons.notifications.model.SmtpAccount
Expand Down Expand Up @@ -36,7 +37,8 @@ internal object ConfigDataProperties {
Pair(ConfigType.SNS, ConfigProperty(Sns.reader, Sns.xParser)),
Pair(ConfigType.SES_ACCOUNT, ConfigProperty(SesAccount.reader, SesAccount.xParser)),
Pair(ConfigType.EMAIL_GROUP, ConfigProperty(EmailGroup.reader, EmailGroup.xParser)),
Pair(ConfigType.SMTP_ACCOUNT, ConfigProperty(SmtpAccount.reader, SmtpAccount.xParser))
Pair(ConfigType.SMTP_ACCOUNT, ConfigProperty(SmtpAccount.reader, SmtpAccount.xParser)),
Pair(ConfigType.MICROSOFT_TEAMS, ConfigProperty(MicrosoftTeams.reader, MicrosoftTeams.xParser))
)

/**
Expand All @@ -62,6 +64,7 @@ internal object ConfigDataProperties {
ConfigType.CHIME -> configData is Chime
ConfigType.SNS -> configData is Sns
ConfigType.SES_ACCOUNT -> configData is SesAccount
ConfigType.MICROSOFT_TEAMS -> configData is MicrosoftTeams
ConfigType.NONE -> true
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.opensearch.commons.notifications.model.Email
import org.opensearch.commons.notifications.model.EmailGroup
import org.opensearch.commons.notifications.model.EmailRecipient
import org.opensearch.commons.notifications.model.MethodType
import org.opensearch.commons.notifications.model.MicrosoftTeams
import org.opensearch.commons.notifications.model.NotificationConfig
import org.opensearch.commons.notifications.model.Slack
import org.opensearch.commons.notifications.model.SmtpAccount
Expand Down Expand Up @@ -57,6 +58,16 @@ internal class CreateNotificationConfigRequestTests {
isEnabled = true
)
}
private fun createMicrosoftTeamsContentConfigObject(): NotificationConfig {
val sampleMicrosoftTeams = MicrosoftTeams("https://domain.com/sample_microsoft_teams_url#1234567890")
return NotificationConfig(
"name",
"description",
ConfigType.MICROSOFT_TEAMS,
configData = sampleMicrosoftTeams,
isEnabled = true
)
}

private fun createEmailGroupContentConfigObject(): NotificationConfig {
val sampleEmailGroup = EmailGroup(listOf(EmailRecipient("[email protected]")))
Expand Down Expand Up @@ -114,6 +125,20 @@ internal class CreateNotificationConfigRequestTests {
assertNull(recreatedObject.validate())
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
}
@Test
fun `Create config serialize and deserialize transport object should be equal microsoft teams`() {
val configRequest = CreateNotificationConfigRequest(
createMicrosoftTeamsContentConfigObject()
)
val recreatedObject =
recreateObject(configRequest) {
CreateNotificationConfigRequest(
it
)
}
assertNull(recreatedObject.validate())
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
}

@Test
fun `Create config serialize and deserialize transport object should be equal slack`() {
Expand Down Expand Up @@ -189,6 +214,15 @@ internal class CreateNotificationConfigRequestTests {
assertNull(recreatedObject.validate())
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
}
@Test
fun `Create config serialize and deserialize using json object should be equal microsoft teams`() {
val configRequest = CreateNotificationConfigRequest(
createMicrosoftTeamsContentConfigObject()
)
val jsonString = getJsonString(configRequest)
val recreatedObject = createObjectFromJsonString(jsonString) { CreateNotificationConfigRequest.parse(it) }
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
}

@Test
fun `Create config serialize and deserialize using json object should be equal`() {
Expand Down Expand Up @@ -275,6 +309,32 @@ internal class CreateNotificationConfigRequestTests {
val recreatedObject = createObjectFromJsonString(jsonString) { CreateNotificationConfigRequest.parse(it) }
assertEquals(config, recreatedObject.notificationConfig)
}
@Test
fun `Create config should deserialize json object using parser microsoft teams`() {
val sampleMicrosoftTeams = MicrosoftTeams("https://domain.com/sample_microsoft_teams_url#1234567890")
val config = NotificationConfig(
"name",
"description",
ConfigType.MICROSOFT_TEAMS,
configData = sampleMicrosoftTeams,
isEnabled = true
)

val jsonString = """
{
"config_id":"config_id1",
"config":{
"name":"name",
"description":"description",
"config_type":"microsoft_teams",
"is_enabled":true,
"microsoft_teams":{"url":"https://domain.com/sample_microsoft_teams_url#1234567890"}
}
}
""".trimIndent()
val recreatedObject = createObjectFromJsonString(jsonString) { CreateNotificationConfigRequest.parse(it) }
assertEquals(config, recreatedObject.notificationConfig)
}

@Test
fun `Create config should deserialize json object using parser webhook`() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,17 @@ internal class GetChannelListResponseTests {
"description3",
ConfigType.WEBHOOK
)
val sampleConfig4 = Channel(
"config_id5",
"name4",
"description4",
ConfigType.MICROSOFT_TEAMS
)
val searchResult = ChannelList(
100,
1000,
TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO,
listOf(sampleConfig1, sampleConfig2, sampleConfig3)
listOf(sampleConfig1, sampleConfig2, sampleConfig3, sampleConfig4)
)
val getResponse = GetChannelListResponse(searchResult)
val recreatedObject = recreateObject(getResponse) { GetChannelListResponse(it) }
Expand Down Expand Up @@ -108,11 +114,17 @@ internal class GetChannelListResponseTests {
"description3",
ConfigType.WEBHOOK
)
val sampleConfig4 = Channel(
"config_id5",
"name4",
"description4",
ConfigType.MICROSOFT_TEAMS
)
val searchResult = ChannelList(
100,
1000,
TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO,
listOf(sampleConfig1, sampleConfig2, sampleConfig3)
listOf(sampleConfig1, sampleConfig2, sampleConfig3, sampleConfig4)
)
val getResponse = GetChannelListResponse(searchResult)
val jsonString = getJsonString(getResponse)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.opensearch.commons.notifications.model.Chime
import org.opensearch.commons.notifications.model.ConfigType
import org.opensearch.commons.notifications.model.MicrosoftTeams
import org.opensearch.commons.notifications.model.NotificationConfig
import org.opensearch.commons.notifications.model.NotificationConfigInfo
import org.opensearch.commons.notifications.model.NotificationConfigSearchResult
Expand Down Expand Up @@ -79,11 +80,23 @@ internal class GetNotificationConfigResponseTests {
Instant.now(),
sampleConfig2
)
val sampleConfig3 = NotificationConfig(
"name",
"description",
ConfigType.MICROSOFT_TEAMS,
configData = MicrosoftTeams("https://domain.com/sample_url#1234567890")
)
val configInfo3 = NotificationConfigInfo(
"config_id3",
Instant.now(),
Instant.now(),
sampleConfig3
)
val searchResult = NotificationConfigSearchResult(
100,
1000,
TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO,
listOf(configInfo1, configInfo2)
listOf(configInfo1, configInfo2, configInfo3)
)
val searchResponse = GetNotificationConfigResponse(searchResult)
val recreatedObject = recreateObject(searchResponse) { GetNotificationConfigResponse(it) }
Expand Down Expand Up @@ -142,11 +155,23 @@ internal class GetNotificationConfigResponseTests {
createdTimeMs,
sampleConfig2
)
val sampleConfig3 = NotificationConfig(
"name",
"description",
ConfigType.MICROSOFT_TEAMS,
configData = MicrosoftTeams("https://domain.com/sample_url#1234567890")
)
val configInfo3 = NotificationConfigInfo(
"config_id3",
lastUpdatedTimeMs,
createdTimeMs,
sampleConfig3
)
val searchResult = NotificationConfigSearchResult(
100,
1000,
TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO,
listOf(configInfo1, configInfo2)
listOf(configInfo1, configInfo2, configInfo3)
)
val searchResponse = GetNotificationConfigResponse(searchResult)
val jsonString = getJsonString(searchResponse)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.opensearch.commons.notifications.model.Email
import org.opensearch.commons.notifications.model.EmailGroup
import org.opensearch.commons.notifications.model.EmailRecipient
import org.opensearch.commons.notifications.model.MethodType
import org.opensearch.commons.notifications.model.MicrosoftTeams
import org.opensearch.commons.notifications.model.NotificationConfig
import org.opensearch.commons.notifications.model.Slack
import org.opensearch.commons.notifications.model.SmtpAccount
Expand All @@ -35,7 +36,16 @@ internal class UpdateNotificationConfigRequestTests {
isEnabled = true
)
}

private fun createMicrosoftTeamsContentConfigObject(): NotificationConfig {
val sampleMicrosoftTeams = MicrosoftTeams("https://domain.com/sample_microsoft_teams_url#1234567890")
return NotificationConfig(
"name",
"description",
ConfigType.MICROSOFT_TEAMS,
configData = sampleMicrosoftTeams,
isEnabled = true
)
}
private fun createSlackContentConfigObject(): NotificationConfig {
val sampleSlack = Slack("https://domain.com/sample_slack_url#1234567890")
return NotificationConfig(
Expand Down Expand Up @@ -109,6 +119,15 @@ internal class UpdateNotificationConfigRequestTests {
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
assertEquals("config_id", recreatedObject.configId)
}
@Test
fun `Update config serialize and deserialize transport object should be equal Microsoft Teams`() {
val configRequest = UpdateNotificationConfigRequest("config_id", createMicrosoftTeamsContentConfigObject())
val recreatedObject =
recreateObject(configRequest) { UpdateNotificationConfigRequest(it) }
assertNull(recreatedObject.validate())
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
assertEquals("config_id", recreatedObject.configId)
}

@Test
fun `Update config serialize and deserialize transport object should be equal Slack`() {
Expand Down Expand Up @@ -168,6 +187,14 @@ internal class UpdateNotificationConfigRequestTests {
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
assertEquals("config_id", recreatedObject.configId)
}
@Test
fun `Update config serialize and deserialize using json object should be equal microsoft Teams`() {
val configRequest = UpdateNotificationConfigRequest("config_id", createMicrosoftTeamsContentConfigObject())
val jsonString = getJsonString(configRequest)
val recreatedObject = createObjectFromJsonString(jsonString) { UpdateNotificationConfigRequest.parse(it) }
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
assertEquals("config_id", recreatedObject.configId)
}

@Test
fun `Update config serialize and deserialize using json object should be equal slack`() {
Expand Down
Loading

0 comments on commit bae1bee

Please sign in to comment.