Skip to content

Commit e909a0e

Browse files
committed
Modified deserialization cases and added tests
1 parent 09729c7 commit e909a0e

File tree

5 files changed

+546
-97
lines changed

5 files changed

+546
-97
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.core.notifications
5+
6+
import com.fasterxml.jackson.core.JsonParser
7+
import com.fasterxml.jackson.core.JsonToken
8+
import com.fasterxml.jackson.databind.DeserializationContext
9+
import com.fasterxml.jackson.databind.JsonDeserializer
10+
import com.fasterxml.jackson.databind.JsonMappingException
11+
import com.fasterxml.jackson.databind.JsonNode
12+
13+
class OperationConditionDeserializer : JsonDeserializer<NotificationExpression.OperationCondition>() {
14+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.OperationCondition = when (parser.currentToken) {
15+
JsonToken.VALUE_STRING -> {
16+
// Handle direct string value
17+
NotificationExpression.OperationCondition(parser.valueAsString)
18+
}
19+
else -> throw JsonMappingException(parser, "Cannot deserialize OperatingCondition")
20+
}
21+
}
22+
23+
class ComparisonConditionDeserializer : JsonDeserializer<NotificationExpression.ComparisonCondition>() {
24+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.ComparisonCondition {
25+
val op = OperationConditionDeserializer().deserialize(parser, ctxt)
26+
return NotificationExpression.ComparisonCondition(op.value)
27+
}
28+
}
29+
30+
class NotEqualsConditionDeserializer : JsonDeserializer<NotificationExpression.NotEqualsCondition>() {
31+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.NotEqualsCondition {
32+
val op = OperationConditionDeserializer().deserialize(parser, ctxt)
33+
return NotificationExpression.NotEqualsCondition(op.value)
34+
}
35+
}
36+
class GreaterThanConditionDeserializer : JsonDeserializer<NotificationExpression.GreaterThanCondition>() {
37+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.GreaterThanCondition {
38+
val op = OperationConditionDeserializer().deserialize(parser, ctxt)
39+
return NotificationExpression.GreaterThanCondition(op.value)
40+
}
41+
}
42+
class GreaterThanOrEqualsConditionDeserializer : JsonDeserializer<NotificationExpression.GreaterThanOrEqualsCondition>() {
43+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.GreaterThanOrEqualsCondition {
44+
val op = OperationConditionDeserializer().deserialize(parser, ctxt)
45+
return NotificationExpression.GreaterThanOrEqualsCondition(op.value)
46+
}
47+
}
48+
class LessThanConditionDeserializer : JsonDeserializer<NotificationExpression.LessThanCondition>() {
49+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.LessThanCondition {
50+
val op = OperationConditionDeserializer().deserialize(parser, ctxt)
51+
return NotificationExpression.LessThanCondition(op.value)
52+
}
53+
}
54+
class LessThanOrEqualsConditionDeserializer : JsonDeserializer<NotificationExpression.LessThanOrEqualsCondition>() {
55+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.LessThanOrEqualsCondition {
56+
val op = OperationConditionDeserializer().deserialize(parser, ctxt)
57+
return NotificationExpression.LessThanOrEqualsCondition(op.value)
58+
}
59+
}
60+
class ComplexOperationConditionDeserializer : JsonDeserializer<NotificationExpression.ComplexOperationCondition>() {
61+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.ComplexOperationCondition {
62+
val node = parser.codec.readTree<JsonNode>(parser)
63+
if (!node.isArray) {
64+
throw JsonMappingException(parser, "anyOf/noneOf must contain an array of values")
65+
}
66+
val values = node.map { it.asText() }
67+
return NotificationExpression.ComplexOperationCondition(values)
68+
}
69+
}
70+
class AnyOfConditionDeserializer : JsonDeserializer<NotificationExpression.AnyOfCondition>() {
71+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.AnyOfCondition {
72+
val op = ComplexOperationConditionDeserializer().deserialize(parser, ctxt)
73+
return NotificationExpression.AnyOfCondition(op.value)
74+
}
75+
}
76+
77+
class NoneOfConditionDeserializer : JsonDeserializer<NotificationExpression.NoneOfCondition>() {
78+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.NoneOfCondition {
79+
val op = ComplexOperationConditionDeserializer().deserialize(parser, ctxt)
80+
return NotificationExpression.NoneOfCondition(op.value)
81+
}
82+
}
83+
84+
class ComplexConditionDeserializer : JsonDeserializer<NotificationExpression.ComplexCondition>() {
85+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.ComplexCondition {
86+
val node = parser.codec.readTree<JsonNode>(parser)
87+
if (!node.isArray) {
88+
throw JsonMappingException(parser, "or/and must contain an array of values")
89+
}
90+
return NotificationExpression.ComplexCondition(node.toNotificationExpressions(parser))
91+
}
92+
}
93+
class OrConditionDeserializer : JsonDeserializer<NotificationExpression.OrCondition>() {
94+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.OrCondition {
95+
val op = ComplexConditionDeserializer().deserialize(parser, ctxt)
96+
return NotificationExpression.OrCondition(op.expectedValueList)
97+
}
98+
}
99+
100+
class AndConditionDeserializer : JsonDeserializer<NotificationExpression.AndCondition>() {
101+
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NotificationExpression.AndCondition {
102+
val op = ComplexConditionDeserializer().deserialize(parser, ctxt)
103+
return NotificationExpression.AndCondition(op.expectedValueList)
104+
}
105+
}
106+
107+
class NotConditionDeserializer : JsonDeserializer<NotificationExpression.NotCondition>() {
108+
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): NotificationExpression.NotCondition {
109+
val node = p.codec.readTree<JsonNode>(p)
110+
val parser = node.traverse(p.codec)
111+
parser.nextToken()
112+
113+
return NotificationExpression.NotCondition(parser.readValueAs(NotificationExpression::class.java))
114+
}
115+
}
116+
117+
private fun JsonNode.toNotificationExpressions(p: JsonParser): List<NotificationExpression> = this.map { element ->
118+
val parser = element.traverse(p.codec)
119+
parser.nextToken()
120+
parser.readValueAs(NotificationExpression::class.java)
121+
}

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/notifications/NotificationFormatUtils.kt

Lines changed: 94 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
package software.aws.toolkits.jetbrains.core.notifications
55

66
import com.fasterxml.jackson.annotation.JsonProperty
7+
import com.fasterxml.jackson.annotation.JsonSubTypes
8+
import com.fasterxml.jackson.annotation.JsonTypeInfo
9+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
710

811
data class NotificationsList(
912
@JsonProperty("schema")
1013
val schema: Schema,
1114
@JsonProperty("notifications")
12-
val notifications: List<NotificationData>,
15+
val notifications: List<NotificationData>?,
1316
)
1417

1518
data class Schema(
@@ -84,7 +87,7 @@ data class NotificationDisplayCondition(
8487
@JsonProperty("extension")
8588
val extension: List<ExtensionType>?,
8689
@JsonProperty("authx")
87-
val authx: List<AuthxType>,
90+
val authx: List<AuthxType>?,
8891
)
8992

9093
data class ComputeType(
@@ -108,24 +111,95 @@ data class ExtensionType(
108111
val version: NotificationExpression?,
109112
)
110113

111-
open class NotificationExpression
112-
113-
open class NotificationOperation : NotificationExpression()
114-
115-
data class NotCondition(
116-
@JsonProperty("not")
117-
val expectedValue: NotificationExpression,
118-
) : NotificationExpression()
119-
120-
data class OrCondition(
121-
@JsonProperty("or")
122-
val expectedValueList: List<NotificationExpression>,
123-
) : NotificationExpression()
124-
125-
data class AndCondition(
126-
@JsonProperty("and")
127-
val expectedValueList: List<NotificationExpression>,
128-
) : NotificationExpression()
114+
@JsonTypeInfo(
115+
use = JsonTypeInfo.Id.NAME,
116+
include = JsonTypeInfo.As.WRAPPER_OBJECT
117+
)
118+
@JsonSubTypes(
119+
JsonSubTypes.Type(value = NotificationExpression.ComparisonCondition::class, name = "=="),
120+
JsonSubTypes.Type(value = NotificationExpression.NotEqualsCondition::class, name = "!="),
121+
JsonSubTypes.Type(value = NotificationExpression.GreaterThanCondition::class, name = ">"),
122+
JsonSubTypes.Type(value = NotificationExpression.GreaterThanOrEqualsCondition::class, name = ">="),
123+
JsonSubTypes.Type(value = NotificationExpression.LessThanCondition::class, name = "<"),
124+
JsonSubTypes.Type(value = NotificationExpression.LessThanOrEqualsCondition::class, name = "<="),
125+
JsonSubTypes.Type(value = NotificationExpression.AnyOfCondition::class, name = "anyOf"),
126+
JsonSubTypes.Type(value = NotificationExpression.NotCondition::class, name = "not"),
127+
JsonSubTypes.Type(value = NotificationExpression.OrCondition::class, name = "or"),
128+
JsonSubTypes.Type(value = NotificationExpression.AndCondition::class, name = "and"),
129+
JsonSubTypes.Type(value = NotificationExpression.NoneOfCondition::class, name = "noneOf")
130+
)
131+
sealed class NotificationExpression {
132+
@JsonDeserialize(using = NotConditionDeserializer::class)
133+
data class NotCondition(
134+
val expectedValue: NotificationExpression,
135+
) : NotificationExpression()
136+
137+
@JsonDeserialize(using = OrConditionDeserializer::class)
138+
data class OrCondition(
139+
val expectedValueList: List<NotificationExpression>,
140+
) : NotificationExpression()
141+
142+
@JsonDeserialize(using = AndConditionDeserializer::class)
143+
data class AndCondition(
144+
val expectedValueList: List<NotificationExpression>,
145+
) : NotificationExpression()
146+
147+
@JsonDeserialize(using = ComplexConditionDeserializer::class)
148+
data class ComplexCondition(
149+
val expectedValueList: List<NotificationExpression>,
150+
) : NotificationExpression()
151+
152+
// General class for comparison operators
153+
@JsonDeserialize(using = OperationConditionDeserializer::class)
154+
data class OperationCondition(
155+
val value: String,
156+
) : NotificationExpression()
157+
158+
@JsonDeserialize(using = ComplexOperationConditionDeserializer::class)
159+
data class ComplexOperationCondition(
160+
val value: List<String>,
161+
) : NotificationExpression()
162+
163+
@JsonDeserialize(using = ComparisonConditionDeserializer::class)
164+
data class ComparisonCondition(
165+
val value: String,
166+
) : NotificationExpression()
167+
168+
@JsonDeserialize(using = NotEqualsConditionDeserializer::class)
169+
data class NotEqualsCondition(
170+
val value: String,
171+
) : NotificationExpression()
172+
173+
@JsonDeserialize(using = GreaterThanConditionDeserializer::class)
174+
data class GreaterThanCondition(
175+
val value: String,
176+
) : NotificationExpression()
177+
178+
@JsonDeserialize(using = GreaterThanOrEqualsConditionDeserializer::class)
179+
data class GreaterThanOrEqualsCondition(
180+
val value: String,
181+
) : NotificationExpression()
182+
183+
@JsonDeserialize(using = LessThanConditionDeserializer::class)
184+
data class LessThanCondition(
185+
val value: String,
186+
) : NotificationExpression()
187+
188+
@JsonDeserialize(using = LessThanOrEqualsConditionDeserializer::class)
189+
data class LessThanOrEqualsCondition(
190+
val value: String,
191+
) : NotificationExpression()
192+
193+
@JsonDeserialize(using = AnyOfConditionDeserializer::class)
194+
data class AnyOfCondition(
195+
val value: List<String>,
196+
) : NotificationExpression()
197+
198+
@JsonDeserialize(using = NoneOfConditionDeserializer::class)
199+
data class NoneOfCondition(
200+
val value: List<String>,
201+
) : NotificationExpression()
202+
}
129203

130204
data class AuthxType(
131205
@JsonProperty("feature")
@@ -139,43 +213,3 @@ data class AuthxType(
139213
@JsonProperty("ssoscopes")
140214
val ssoScopes: NotificationExpression?,
141215
)
142-
143-
data class ComparisonCondition(
144-
@JsonProperty("==")
145-
val expectedValue: String,
146-
) : NotificationOperation()
147-
148-
data class NotEqualsCondition(
149-
@JsonProperty("!=")
150-
val expectedValue: String,
151-
) : NotificationOperation()
152-
153-
data class GreaterThanCondition(
154-
@JsonProperty(">")
155-
val expectedValue: String,
156-
) : NotificationOperation()
157-
158-
data class GreaterThanOrEqualsCondition(
159-
@JsonProperty(">=")
160-
val expectedValue: String,
161-
) : NotificationOperation()
162-
163-
data class LessThanCondition(
164-
@JsonProperty("<")
165-
val expectedValue: String,
166-
) : NotificationOperation()
167-
168-
data class LessThanOrEqualsCondition(
169-
@JsonProperty("<=")
170-
val expectedValue: String,
171-
) : NotificationOperation()
172-
173-
data class InCondition(
174-
@JsonProperty("anyOf")
175-
val expectedValueList: List<String>,
176-
) : NotificationOperation()
177-
178-
data class NotInCondition(
179-
@JsonProperty("noneOf")
180-
val expectedValueList: List<String>,
181-
) : NotificationOperation()

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/notifications/RulesEngine.kt

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ object RulesEngine {
3636

3737
private fun matchesCompute(notificationCompute: ComputeType, actualCompute: String, actualArchitecture: String): Boolean {
3838
val type = notificationCompute.type?.let { evaluateNotificationExpression(it, actualCompute) } ?: true
39-
val architecture = notificationCompute.architecture?.let { evaluateNotificationExpression(it, actualArchitecture) } ?: return true
39+
val architecture = notificationCompute.architecture?.let { evaluateNotificationExpression(it, actualArchitecture) } ?: true
4040
return type && architecture
4141
}
4242

@@ -89,32 +89,27 @@ object RulesEngine {
8989
}
9090

9191
private fun evaluateNotificationExpression(notificationExpression: NotificationExpression, value: String): Boolean = when (notificationExpression) {
92-
is NotificationOperation -> performNotifOp(notificationExpression, value)
93-
is NotCondition -> performNotOp(notificationExpression, value)
94-
is OrCondition -> performOrOp(notificationExpression, value)
95-
is AndCondition -> performAndOp(notificationExpression, value)
92+
is NotificationExpression.NotCondition -> performNotOp(notificationExpression, value)
93+
is NotificationExpression.OrCondition -> performOrOp(notificationExpression, value)
94+
is NotificationExpression.AndCondition -> performAndOp(notificationExpression, value)
95+
is NotificationExpression.ComparisonCondition -> notificationExpression.value == value
96+
is NotificationExpression.NotEqualsCondition -> notificationExpression.value != value
97+
is NotificationExpression.GreaterThanCondition -> value > notificationExpression.value
98+
is NotificationExpression.LessThanCondition -> value < notificationExpression.value
99+
is NotificationExpression.GreaterThanOrEqualsCondition -> value >= notificationExpression.value
100+
is NotificationExpression.LessThanOrEqualsCondition -> value <= notificationExpression.value
101+
is NotificationExpression.AnyOfCondition -> notificationExpression.value.contains(value)
102+
is NotificationExpression.NoneOfCondition -> !notificationExpression.value.contains(value)
96103
else -> true
97104
}
98105

99-
private fun performNotifOp(notificationOperation: NotificationOperation, actualValue: String): Boolean = when (notificationOperation) {
100-
is ComparisonCondition -> notificationOperation.expectedValue == actualValue
101-
is NotEqualsCondition -> notificationOperation.expectedValue != actualValue
102-
is GreaterThanCondition -> actualValue > notificationOperation.expectedValue
103-
is LessThanCondition -> actualValue < notificationOperation.expectedValue
104-
is GreaterThanOrEqualsCondition -> actualValue >= notificationOperation.expectedValue
105-
is LessThanOrEqualsCondition -> actualValue <= notificationOperation.expectedValue
106-
is InCondition -> notificationOperation.expectedValueList.contains(actualValue)
107-
is NotInCondition -> !notificationOperation.expectedValueList.contains(actualValue)
108-
else -> true
109-
}
110-
111-
private fun performNotOp(notificationOperation: NotCondition, actualValue: String): Boolean =
106+
private fun performNotOp(notificationOperation: NotificationExpression.NotCondition, actualValue: String): Boolean =
112107
!evaluateNotificationExpression(notificationOperation.expectedValue, actualValue)
113108

114-
private fun performOrOp(notificationOperation: OrCondition, actualValue: String): Boolean =
109+
private fun performOrOp(notificationOperation: NotificationExpression.OrCondition, actualValue: String): Boolean =
115110
notificationOperation.expectedValueList.any { evaluateNotificationExpression(it, actualValue) }
116111

117-
private fun performAndOp(notificationOperation: AndCondition, actualValue: String): Boolean =
112+
private fun performAndOp(notificationOperation: NotificationExpression.AndCondition, actualValue: String): Boolean =
118113
notificationOperation.expectedValueList.all { evaluateNotificationExpression(it, actualValue) }
119114
}
120115

@@ -134,12 +129,6 @@ fun getCurrentSystemAndConnectionDetails(): SystemDetails {
134129
return SystemDetails(computeType, computeArchitecture, osType, osVersion, ideType, ideVersion, pluginVersionMap)
135130
}
136131

137-
data class AuthDetails(
138-
val qConnection: ActiveConnection,
139-
val codeCatalystConnection: ActiveConnection,
140-
val explorerConnection: ActiveConnection,
141-
)
142-
143132
data class FeatureAuthDetails(
144133
val connectionType: String,
145134
val region: String,
@@ -196,7 +185,7 @@ private fun getConnectionDetailsForToolkit(project: Project): FeatureAuthDetails
196185
)
197186
}
198187

199-
private fun getConnectionDetailsForFeature(project: Project, featureId: BearerTokenFeatureSet): FeatureAuthDetails? {
188+
fun getConnectionDetailsForFeature(project: Project, featureId: BearerTokenFeatureSet): FeatureAuthDetails? {
200189
val connection = checkBearerConnectionValidity(project, featureId)
201190
if (connection.activeConnectionBearer == null) return null
202191
val authType = when (connection.connectionType) {

0 commit comments

Comments
 (0)