Skip to content

Commit bae1bee

Browse files
common utils to support Microsoft teams in notifications (#428)
* 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]>
1 parent fad4525 commit bae1bee

File tree

14 files changed

+417
-15
lines changed

14 files changed

+417
-15
lines changed

src/main/kotlin/org/opensearch/commons/notifications/model/ConfigType.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ enum class ConfigType(val tag: String) {
5454
override fun toString(): String {
5555
return tag
5656
}
57+
},
58+
MICROSOFT_TEAMS("microsoft_teams") {
59+
override fun toString(): String {
60+
return tag
61+
}
5762
};
5863

5964
companion object {

src/main/kotlin/org/opensearch/commons/notifications/model/EventStatus.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ data class EventStatus(
4343
ConfigType.SLACK -> requireNotNull(deliveryStatus)
4444
ConfigType.EMAIL -> require(emailRecipientStatus.isNotEmpty())
4545
ConfigType.SNS -> requireNotNull(deliveryStatus)
46+
ConfigType.MICROSOFT_TEAMS -> requireNotNull(deliveryStatus)
4647
ConfigType.NONE -> log.info("Some config field not recognized")
4748
else -> {
4849
log.info("non-allowed config type for Status")
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package org.opensearch.commons.notifications.model
6+
7+
import org.opensearch.commons.notifications.NotificationConstants.URL_TAG
8+
import org.opensearch.commons.utils.logger
9+
import org.opensearch.commons.utils.validateUrl
10+
import org.opensearch.core.common.Strings
11+
import org.opensearch.core.common.io.stream.StreamInput
12+
import org.opensearch.core.common.io.stream.StreamOutput
13+
import org.opensearch.core.common.io.stream.Writeable
14+
import org.opensearch.core.xcontent.ToXContent
15+
import org.opensearch.core.xcontent.XContentBuilder
16+
import org.opensearch.core.xcontent.XContentParser
17+
import org.opensearch.core.xcontent.XContentParserUtils
18+
import java.io.IOException
19+
20+
/**
21+
* Data class representing MicrosoftTeams channel.
22+
*/
23+
data class MicrosoftTeams(
24+
val url: String
25+
) : BaseConfigData {
26+
27+
init {
28+
require(!Strings.isNullOrEmpty(url)) { "URL is null or empty" }
29+
validateUrl(url)
30+
}
31+
32+
companion object {
33+
private val log by logger(MicrosoftTeams::class.java)
34+
35+
/**
36+
* reader to create instance of class from writable.
37+
*/
38+
val reader = Writeable.Reader { MicrosoftTeams(it) }
39+
40+
/**
41+
* Parser to parse xContent
42+
*/
43+
val xParser = XParser { parse(it) }
44+
45+
/**
46+
* Creator used in REST communication.
47+
* @param parser XContentParser to deserialize data from.
48+
*/
49+
@JvmStatic
50+
@Throws(IOException::class)
51+
fun parse(parser: XContentParser): MicrosoftTeams {
52+
var url: String? = null
53+
54+
XContentParserUtils.ensureExpectedToken(
55+
XContentParser.Token.START_OBJECT,
56+
parser.currentToken(),
57+
parser
58+
)
59+
while (parser.nextToken() != XContentParser.Token.END_OBJECT) {
60+
val fieldName = parser.currentName()
61+
parser.nextToken()
62+
when (fieldName) {
63+
URL_TAG -> url = parser.text()
64+
else -> {
65+
parser.skipChildren()
66+
log.info("Unexpected field: $fieldName, while parsing MicrosoftTeams destination")
67+
}
68+
}
69+
}
70+
url ?: throw IllegalArgumentException("$URL_TAG field absent")
71+
return MicrosoftTeams(url)
72+
}
73+
}
74+
75+
/**
76+
* Constructor used in transport action communication.
77+
* @param input StreamInput stream to deserialize data from.
78+
*/
79+
constructor(input: StreamInput) : this(
80+
url = input.readString()
81+
)
82+
83+
/**
84+
* {@inheritDoc}
85+
*/
86+
override fun writeTo(output: StreamOutput) {
87+
output.writeString(url)
88+
}
89+
90+
/**
91+
* {@inheritDoc}
92+
*/
93+
override fun toXContent(builder: XContentBuilder?, params: ToXContent.Params?): XContentBuilder {
94+
builder!!
95+
return builder.startObject()
96+
.field(URL_TAG, url)
97+
.endObject()
98+
}
99+
}

src/main/kotlin/org/opensearch/commons/notifications/model/config/ConfigDataProperties.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.opensearch.commons.notifications.model.Chime
99
import org.opensearch.commons.notifications.model.ConfigType
1010
import org.opensearch.commons.notifications.model.Email
1111
import org.opensearch.commons.notifications.model.EmailGroup
12+
import org.opensearch.commons.notifications.model.MicrosoftTeams
1213
import org.opensearch.commons.notifications.model.SesAccount
1314
import org.opensearch.commons.notifications.model.Slack
1415
import org.opensearch.commons.notifications.model.SmtpAccount
@@ -36,7 +37,8 @@ internal object ConfigDataProperties {
3637
Pair(ConfigType.SNS, ConfigProperty(Sns.reader, Sns.xParser)),
3738
Pair(ConfigType.SES_ACCOUNT, ConfigProperty(SesAccount.reader, SesAccount.xParser)),
3839
Pair(ConfigType.EMAIL_GROUP, ConfigProperty(EmailGroup.reader, EmailGroup.xParser)),
39-
Pair(ConfigType.SMTP_ACCOUNT, ConfigProperty(SmtpAccount.reader, SmtpAccount.xParser))
40+
Pair(ConfigType.SMTP_ACCOUNT, ConfigProperty(SmtpAccount.reader, SmtpAccount.xParser)),
41+
Pair(ConfigType.MICROSOFT_TEAMS, ConfigProperty(MicrosoftTeams.reader, MicrosoftTeams.xParser))
4042
)
4143

4244
/**
@@ -62,6 +64,7 @@ internal object ConfigDataProperties {
6264
ConfigType.CHIME -> configData is Chime
6365
ConfigType.SNS -> configData is Sns
6466
ConfigType.SES_ACCOUNT -> configData is SesAccount
67+
ConfigType.MICROSOFT_TEAMS -> configData is MicrosoftTeams
6568
ConfigType.NONE -> true
6669
}
6770
}

src/test/kotlin/org/opensearch/commons/notifications/action/CreateNotificationConfigRequestTests.kt

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.opensearch.commons.notifications.model.Email
1515
import org.opensearch.commons.notifications.model.EmailGroup
1616
import org.opensearch.commons.notifications.model.EmailRecipient
1717
import org.opensearch.commons.notifications.model.MethodType
18+
import org.opensearch.commons.notifications.model.MicrosoftTeams
1819
import org.opensearch.commons.notifications.model.NotificationConfig
1920
import org.opensearch.commons.notifications.model.Slack
2021
import org.opensearch.commons.notifications.model.SmtpAccount
@@ -57,6 +58,16 @@ internal class CreateNotificationConfigRequestTests {
5758
isEnabled = true
5859
)
5960
}
61+
private fun createMicrosoftTeamsContentConfigObject(): NotificationConfig {
62+
val sampleMicrosoftTeams = MicrosoftTeams("https://domain.com/sample_microsoft_teams_url#1234567890")
63+
return NotificationConfig(
64+
"name",
65+
"description",
66+
ConfigType.MICROSOFT_TEAMS,
67+
configData = sampleMicrosoftTeams,
68+
isEnabled = true
69+
)
70+
}
6071

6172
private fun createEmailGroupContentConfigObject(): NotificationConfig {
6273
val sampleEmailGroup = EmailGroup(listOf(EmailRecipient("[email protected]")))
@@ -114,6 +125,20 @@ internal class CreateNotificationConfigRequestTests {
114125
assertNull(recreatedObject.validate())
115126
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
116127
}
128+
@Test
129+
fun `Create config serialize and deserialize transport object should be equal microsoft teams`() {
130+
val configRequest = CreateNotificationConfigRequest(
131+
createMicrosoftTeamsContentConfigObject()
132+
)
133+
val recreatedObject =
134+
recreateObject(configRequest) {
135+
CreateNotificationConfigRequest(
136+
it
137+
)
138+
}
139+
assertNull(recreatedObject.validate())
140+
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
141+
}
117142

118143
@Test
119144
fun `Create config serialize and deserialize transport object should be equal slack`() {
@@ -189,6 +214,15 @@ internal class CreateNotificationConfigRequestTests {
189214
assertNull(recreatedObject.validate())
190215
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
191216
}
217+
@Test
218+
fun `Create config serialize and deserialize using json object should be equal microsoft teams`() {
219+
val configRequest = CreateNotificationConfigRequest(
220+
createMicrosoftTeamsContentConfigObject()
221+
)
222+
val jsonString = getJsonString(configRequest)
223+
val recreatedObject = createObjectFromJsonString(jsonString) { CreateNotificationConfigRequest.parse(it) }
224+
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
225+
}
192226

193227
@Test
194228
fun `Create config serialize and deserialize using json object should be equal`() {
@@ -275,6 +309,32 @@ internal class CreateNotificationConfigRequestTests {
275309
val recreatedObject = createObjectFromJsonString(jsonString) { CreateNotificationConfigRequest.parse(it) }
276310
assertEquals(config, recreatedObject.notificationConfig)
277311
}
312+
@Test
313+
fun `Create config should deserialize json object using parser microsoft teams`() {
314+
val sampleMicrosoftTeams = MicrosoftTeams("https://domain.com/sample_microsoft_teams_url#1234567890")
315+
val config = NotificationConfig(
316+
"name",
317+
"description",
318+
ConfigType.MICROSOFT_TEAMS,
319+
configData = sampleMicrosoftTeams,
320+
isEnabled = true
321+
)
322+
323+
val jsonString = """
324+
{
325+
"config_id":"config_id1",
326+
"config":{
327+
"name":"name",
328+
"description":"description",
329+
"config_type":"microsoft_teams",
330+
"is_enabled":true,
331+
"microsoft_teams":{"url":"https://domain.com/sample_microsoft_teams_url#1234567890"}
332+
}
333+
}
334+
""".trimIndent()
335+
val recreatedObject = createObjectFromJsonString(jsonString) { CreateNotificationConfigRequest.parse(it) }
336+
assertEquals(config, recreatedObject.notificationConfig)
337+
}
278338

279339
@Test
280340
fun `Create config should deserialize json object using parser webhook`() {

src/test/kotlin/org/opensearch/commons/notifications/action/GetChannelListResponseTests.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,17 @@ internal class GetChannelListResponseTests {
6262
"description3",
6363
ConfigType.WEBHOOK
6464
)
65+
val sampleConfig4 = Channel(
66+
"config_id5",
67+
"name4",
68+
"description4",
69+
ConfigType.MICROSOFT_TEAMS
70+
)
6571
val searchResult = ChannelList(
6672
100,
6773
1000,
6874
TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO,
69-
listOf(sampleConfig1, sampleConfig2, sampleConfig3)
75+
listOf(sampleConfig1, sampleConfig2, sampleConfig3, sampleConfig4)
7076
)
7177
val getResponse = GetChannelListResponse(searchResult)
7278
val recreatedObject = recreateObject(getResponse) { GetChannelListResponse(it) }
@@ -108,11 +114,17 @@ internal class GetChannelListResponseTests {
108114
"description3",
109115
ConfigType.WEBHOOK
110116
)
117+
val sampleConfig4 = Channel(
118+
"config_id5",
119+
"name4",
120+
"description4",
121+
ConfigType.MICROSOFT_TEAMS
122+
)
111123
val searchResult = ChannelList(
112124
100,
113125
1000,
114126
TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO,
115-
listOf(sampleConfig1, sampleConfig2, sampleConfig3)
127+
listOf(sampleConfig1, sampleConfig2, sampleConfig3, sampleConfig4)
116128
)
117129
val getResponse = GetChannelListResponse(searchResult)
118130
val jsonString = getJsonString(getResponse)

src/test/kotlin/org/opensearch/commons/notifications/action/GetNotificationConfigResponseTests.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
1010
import org.junit.jupiter.api.Test
1111
import org.opensearch.commons.notifications.model.Chime
1212
import org.opensearch.commons.notifications.model.ConfigType
13+
import org.opensearch.commons.notifications.model.MicrosoftTeams
1314
import org.opensearch.commons.notifications.model.NotificationConfig
1415
import org.opensearch.commons.notifications.model.NotificationConfigInfo
1516
import org.opensearch.commons.notifications.model.NotificationConfigSearchResult
@@ -79,11 +80,23 @@ internal class GetNotificationConfigResponseTests {
7980
Instant.now(),
8081
sampleConfig2
8182
)
83+
val sampleConfig3 = NotificationConfig(
84+
"name",
85+
"description",
86+
ConfigType.MICROSOFT_TEAMS,
87+
configData = MicrosoftTeams("https://domain.com/sample_url#1234567890")
88+
)
89+
val configInfo3 = NotificationConfigInfo(
90+
"config_id3",
91+
Instant.now(),
92+
Instant.now(),
93+
sampleConfig3
94+
)
8295
val searchResult = NotificationConfigSearchResult(
8396
100,
8497
1000,
8598
TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO,
86-
listOf(configInfo1, configInfo2)
99+
listOf(configInfo1, configInfo2, configInfo3)
87100
)
88101
val searchResponse = GetNotificationConfigResponse(searchResult)
89102
val recreatedObject = recreateObject(searchResponse) { GetNotificationConfigResponse(it) }
@@ -142,11 +155,23 @@ internal class GetNotificationConfigResponseTests {
142155
createdTimeMs,
143156
sampleConfig2
144157
)
158+
val sampleConfig3 = NotificationConfig(
159+
"name",
160+
"description",
161+
ConfigType.MICROSOFT_TEAMS,
162+
configData = MicrosoftTeams("https://domain.com/sample_url#1234567890")
163+
)
164+
val configInfo3 = NotificationConfigInfo(
165+
"config_id3",
166+
lastUpdatedTimeMs,
167+
createdTimeMs,
168+
sampleConfig3
169+
)
145170
val searchResult = NotificationConfigSearchResult(
146171
100,
147172
1000,
148173
TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO,
149-
listOf(configInfo1, configInfo2)
174+
listOf(configInfo1, configInfo2, configInfo3)
150175
)
151176
val searchResponse = GetNotificationConfigResponse(searchResult)
152177
val jsonString = getJsonString(searchResponse)

src/test/kotlin/org/opensearch/commons/notifications/action/UpdateNotificationConfigRequestTests.kt

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.opensearch.commons.notifications.model.Email
1515
import org.opensearch.commons.notifications.model.EmailGroup
1616
import org.opensearch.commons.notifications.model.EmailRecipient
1717
import org.opensearch.commons.notifications.model.MethodType
18+
import org.opensearch.commons.notifications.model.MicrosoftTeams
1819
import org.opensearch.commons.notifications.model.NotificationConfig
1920
import org.opensearch.commons.notifications.model.Slack
2021
import org.opensearch.commons.notifications.model.SmtpAccount
@@ -35,7 +36,16 @@ internal class UpdateNotificationConfigRequestTests {
3536
isEnabled = true
3637
)
3738
}
38-
39+
private fun createMicrosoftTeamsContentConfigObject(): NotificationConfig {
40+
val sampleMicrosoftTeams = MicrosoftTeams("https://domain.com/sample_microsoft_teams_url#1234567890")
41+
return NotificationConfig(
42+
"name",
43+
"description",
44+
ConfigType.MICROSOFT_TEAMS,
45+
configData = sampleMicrosoftTeams,
46+
isEnabled = true
47+
)
48+
}
3949
private fun createSlackContentConfigObject(): NotificationConfig {
4050
val sampleSlack = Slack("https://domain.com/sample_slack_url#1234567890")
4151
return NotificationConfig(
@@ -109,6 +119,15 @@ internal class UpdateNotificationConfigRequestTests {
109119
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
110120
assertEquals("config_id", recreatedObject.configId)
111121
}
122+
@Test
123+
fun `Update config serialize and deserialize transport object should be equal Microsoft Teams`() {
124+
val configRequest = UpdateNotificationConfigRequest("config_id", createMicrosoftTeamsContentConfigObject())
125+
val recreatedObject =
126+
recreateObject(configRequest) { UpdateNotificationConfigRequest(it) }
127+
assertNull(recreatedObject.validate())
128+
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
129+
assertEquals("config_id", recreatedObject.configId)
130+
}
112131

113132
@Test
114133
fun `Update config serialize and deserialize transport object should be equal Slack`() {
@@ -168,6 +187,14 @@ internal class UpdateNotificationConfigRequestTests {
168187
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
169188
assertEquals("config_id", recreatedObject.configId)
170189
}
190+
@Test
191+
fun `Update config serialize and deserialize using json object should be equal microsoft Teams`() {
192+
val configRequest = UpdateNotificationConfigRequest("config_id", createMicrosoftTeamsContentConfigObject())
193+
val jsonString = getJsonString(configRequest)
194+
val recreatedObject = createObjectFromJsonString(jsonString) { UpdateNotificationConfigRequest.parse(it) }
195+
assertEquals(configRequest.notificationConfig, recreatedObject.notificationConfig)
196+
assertEquals("config_id", recreatedObject.configId)
197+
}
171198

172199
@Test
173200
fun `Update config serialize and deserialize using json object should be equal slack`() {

0 commit comments

Comments
 (0)