Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.x] common utils to support Microsoft teams in notifications #512

Merged
merged 1 commit into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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