Skip to content

Commit 2c55200

Browse files
Parse optional trailing query parameters off subjects (#49) (#52)
AntPatternNamespace had no notion of an optional trailing query string, which (a) polluted the last path variable when a subject carried a `?query`, and (b) left consumers no way to read query parameters. util: - Strip an optional trailing `?query` before match/extractPathVariables so path variables are never polluted (fixes the bug). - Add extractQueryParameters(subject): parses `?a=b&c=d` into a map (empty when absent), via a shared pure parseQueryString helper (URL-decoding, empty values, last-wins on repeated keys). - URL-decode path variables too, for consistency with the Spring DataSourceMessageHandler which already decodes destination variables. NOTE: this changes query-less subjects like /X/EUR%2FUSD to yield EUR/USD instead of EUR%2FUSD. - Expose a shared CHARSET constant; DataSourceServerBootstrap sources its bootstrapCharset from it. reactive DSL: - Thread the parsed query map to all supplier shapes via a Request / ChannelRequest object (path, pathVariables, queryParameters[, receive]) instead of positional params, avoiding the same-typed adjacent-map footgun and making future additions non-breaking. - RequestSupplier / ChannelRequestSupplier are receiver-style SAMs, so Kotlin gets `{ this.path }` ergonomics with no overload ambiguity while Java implements a clean `invoke(request)`. - Retain the previous positional shapes as @deprecated PathVariablesSupplier / PathVariablesChannelSupplier overloads for source back-compat. Public API: additive to util; the reactive supplier interfaces are reshaped (deprecated overloads retained) — a major bump. .api snapshots regenerated.
1 parent 5b1e75d commit 2c55200

24 files changed

Lines changed: 676 additions & 83 deletions

File tree

buildSrc/src/main/kotlin/Active.kt

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import com.squareup.kotlinpoet.AnnotationSpec
12
import com.squareup.kotlinpoet.ClassName
23
import com.squareup.kotlinpoet.CodeBlock
34
import com.squareup.kotlinpoet.FunSpec
@@ -40,7 +41,7 @@ object Active : FunctionProvider {
4041
val publisherPatternSupplierParameter =
4142
ParameterSpec.builder(
4243
"supplier",
43-
pathVariablesSupplierClassName.parameterizedBy(
44+
requestSupplierClassName.parameterizedBy(
4445
publisherType.className.parameterizedBy(projectedParameterTypeName),
4546
),
4647
)
@@ -61,7 +62,7 @@ object Active : FunctionProvider {
6162
.addParameter(publisherPatternSupplierParameter)
6263
.addCode(
6364
"""
64-
%N.%N(%N, %N) { %N.asFlow(%N(it, %N.extractPathVariables(it))) }
65+
%N.%N(%N, %N) { %N.asFlow(with(%N) { %T(it, %N.extractPathVariables(it), %N.extractQueryParameters(it)).invoke() }) }
6566
"""
6667
.trimIndent(),
6768
binderProperty,
@@ -70,6 +71,8 @@ object Active : FunctionProvider {
7071
antNamespaceParameter,
7172
adapterProperty,
7273
publisherPatternSupplierParameter,
74+
requestClassName,
75+
antNamespaceParameter,
7376
antNamespaceParameter,
7477
)
7578
.build()
@@ -101,6 +104,66 @@ object Active : FunctionProvider {
101104
)
102105
.build()
103106

107+
val deprecatedSupplierParameter =
108+
ParameterSpec.builder(
109+
"supplier",
110+
pathVariablesSupplierClassName.parameterizedBy(
111+
publisherType.className.parameterizedBy(projectedParameterTypeName),
112+
),
113+
)
114+
.build()
115+
116+
val deprecatedAnnotation =
117+
AnnotationSpec.builder(Deprecated::class)
118+
.addMember(
119+
"%S",
120+
"Use the supplier overload taking a Request, which also carries query parameters.",
121+
)
122+
.build()
123+
124+
val suppressDeprecationAnnotation =
125+
AnnotationSpec.builder(Suppress::class).addMember("%S", "DEPRECATION").build()
126+
127+
val byNamespaceDeprecatedFunction =
128+
FunSpec.builder("namespace")
129+
.addAnnotation(deprecatedAnnotation)
130+
.addAnnotation(suppressDeprecationAnnotation)
131+
.addAnnotation(JvmOverloads::class)
132+
.addParameter(antNamespaceParameter)
133+
.addParameter(configureParameter)
134+
.addParameter(deprecatedSupplierParameter)
135+
.addCode(
136+
"%N(%N, %N, %T { %N(path, pathVariables) })",
137+
byNamespaceFunction,
138+
antNamespaceParameter,
139+
configureParameter,
140+
requestSupplierClassName,
141+
deprecatedSupplierParameter,
142+
)
143+
.build()
144+
145+
val byPatternDeprecatedFunction =
146+
FunSpec.builder("pattern")
147+
.addAnnotation(deprecatedAnnotation)
148+
.addAnnotation(suppressDeprecationAnnotation)
149+
.addAnnotation(JvmOverloads::class)
150+
.addParameter(patternParameter)
151+
.addParameter(configureParameter)
152+
.addParameter(deprecatedSupplierParameter)
153+
.addCode(
154+
"""
155+
val namespace = %T(%N)
156+
%N(namespace, %N, %N)
157+
"""
158+
.trimIndent(),
159+
antPatternNamespaceClassName,
160+
patternParameter,
161+
byNamespaceDeprecatedFunction,
162+
configureParameter,
163+
deprecatedSupplierParameter,
164+
)
165+
.build()
166+
104167
val byPathFunction =
105168
FunSpec.builder("path")
106169
.addAnnotation(JvmOverloads::class)
@@ -112,7 +175,7 @@ object Active : FunctionProvider {
112175
"""
113176
val namespace = %T(%N)
114177
check(namespace.isExact) { %S }
115-
%N(namespace, %N) { _, _ -> %N }
178+
%N(namespace, %N) { %N }
116179
"""
117180
.trimIndent(),
118181
antPatternNamespaceClassName,
@@ -151,7 +214,9 @@ object Active : FunctionProvider {
151214
return Functions(
152215
listOf(
153216
byNamespaceFunction,
217+
byNamespaceDeprecatedFunction,
154218
byPatternFunction,
219+
byPatternDeprecatedFunction,
155220
byPathFunction,
156221
byPathValueFunction,
157222
),
@@ -169,14 +234,14 @@ object Active : FunctionProvider {
169234
Bind a [%N] to a [%N] of data publishers.
170235
171236
For example, if a [%T] for /fx/ is bound then when a request for /fx/gbpusd is received
172-
the [%N] will be called with /fx/gbpusd as the %N parameter to return a [%T] which will be subscribed to
237+
the [%N] will be called with a [%T] whose path is /fx/gbpusd to return a [%T] which will be subscribed to
173238
in order to provide data.
174-
239+
175240
@param %N A namespace for matching subjects for which this [%N] will be invoked.
176241
@param %N A configuration block that can be used to set up the behaviour of this publisher.
177242
@param %N This will be invoked on each incoming request. It should parse the `subject` and provide a
178243
%T capable of supplying data in response.
179-
244+
180245
@see %T
181246
@see %T
182247
@see %T
@@ -186,7 +251,7 @@ object Active : FunctionProvider {
186251
flowSupplierParameter,
187252
prefixNamespaceClassName,
188253
flowSupplierParameter,
189-
pathParameter,
254+
requestClassName,
190255
publisherType,
191256
antNamespaceParameter,
192257
flowSupplierParameter,
@@ -210,21 +275,21 @@ object Active : FunctionProvider {
210275
Bind an ant pattern to a [%N] of data publishers.
211276
212277
For example, if [%N] is provided as /fx/* then when a request for /fx/gbpusd is received
213-
the [%N] will be called with /fx/gbpusd as the %N parameter to return a [%T] which will be subscribed to
278+
the [%N] will be called with a [%T] whose path is /fx/gbpusd to return a [%T] which will be subscribed to
214279
in order to provide data.
215-
280+
216281
@param %N The ant pattern for matching subjects for which this [%N] will be invoked.
217282
@param %N A configuration block that can be used to set up the behaviour of this publisher.
218283
@param %N This will be invoked on each incoming request. It should parse the `subject` and provide a
219284
%T capable of supplying data in response.
220-
285+
221286
@see %T
222287
"""
223288
.trimIndent(),
224289
flowSupplierParameter,
225290
patternParameter,
226291
flowSupplierParameter,
227-
pathParameter,
292+
requestClassName,
228293
publisherType,
229294
patternParameter,
230295
flowSupplierParameter,

buildSrc/src/main/kotlin/ActiveContainer.kt

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import com.squareup.kotlinpoet.AnnotationSpec
12
import com.squareup.kotlinpoet.ClassName
23
import com.squareup.kotlinpoet.FunSpec
34
import com.squareup.kotlinpoet.MemberName
@@ -46,7 +47,7 @@ object ActiveContainer : FunctionProvider {
4647
val publisherPatternSupplierParameter =
4748
ParameterSpec.builder(
4849
"supplier",
49-
pathVariablesSupplierClassName.parameterizedBy(
50+
requestSupplierClassName.parameterizedBy(
5051
publisherType.className.parameterizedBy(containerEventTypeName),
5152
),
5253
)
@@ -60,7 +61,7 @@ object ActiveContainer : FunctionProvider {
6061
.addParameter(publisherPatternSupplierParameter)
6162
.addCode(
6263
"""
63-
%N.%N(%N, %N) { path -> %N.asFlow(%N(path, %N.extractPathVariables(path))) }
64+
%N.%N(%N, %N) { path -> %N.asFlow(with(%N) { %T(path, %N.extractPathVariables(path), %N.extractQueryParameters(path)).invoke() }) }
6465
"""
6566
.trimIndent(),
6667
binderProperty,
@@ -69,6 +70,8 @@ object ActiveContainer : FunctionProvider {
6970
antNamespaceParameter,
7071
adapterProperty,
7172
publisherPatternSupplierParameter,
73+
requestClassName,
74+
antNamespaceParameter,
7275
antNamespaceParameter,
7376
)
7477
.build()
@@ -93,6 +96,66 @@ object ActiveContainer : FunctionProvider {
9396
)
9497
.build()
9598

99+
val deprecatedSupplierParameter =
100+
ParameterSpec.builder(
101+
"supplier",
102+
pathVariablesSupplierClassName.parameterizedBy(
103+
publisherType.className.parameterizedBy(containerEventTypeName),
104+
),
105+
)
106+
.build()
107+
108+
val deprecatedAnnotation =
109+
AnnotationSpec.builder(Deprecated::class)
110+
.addMember(
111+
"%S",
112+
"Use the supplier overload taking a Request, which also carries query parameters.",
113+
)
114+
.build()
115+
116+
val suppressDeprecationAnnotation =
117+
AnnotationSpec.builder(Suppress::class).addMember("%S", "DEPRECATION").build()
118+
119+
val byNamespaceDeprecatedFunction =
120+
FunSpec.builder("namespace")
121+
.addAnnotation(deprecatedAnnotation)
122+
.addAnnotation(suppressDeprecationAnnotation)
123+
.addAnnotation(JvmOverloads::class)
124+
.addParameter(antNamespaceParameter)
125+
.addParameter(configureParameter)
126+
.addParameter(deprecatedSupplierParameter)
127+
.addCode(
128+
"%N(%N, %N, %T { %N(path, pathVariables) })",
129+
primaryFunction,
130+
antNamespaceParameter,
131+
configureParameter,
132+
requestSupplierClassName,
133+
deprecatedSupplierParameter,
134+
)
135+
.build()
136+
137+
val byPatternDeprecatedFunction =
138+
FunSpec.builder("pattern")
139+
.addAnnotation(deprecatedAnnotation)
140+
.addAnnotation(suppressDeprecationAnnotation)
141+
.addAnnotation(JvmOverloads::class)
142+
.addParameter(patternParameter)
143+
.addParameter(configureParameter)
144+
.addParameter(deprecatedSupplierParameter)
145+
.addCode(
146+
"""
147+
val namespace = %T(%N)
148+
%N(namespace, %N, %N)
149+
"""
150+
.trimIndent(),
151+
antPatternNamespaceClassName,
152+
patternParameter,
153+
byNamespaceDeprecatedFunction,
154+
configureParameter,
155+
deprecatedSupplierParameter,
156+
)
157+
.build()
158+
96159
val byPathFunction =
97160
FunSpec.builder("path")
98161
.addAnnotation(JvmOverloads::class)
@@ -103,7 +166,7 @@ object ActiveContainer : FunctionProvider {
103166
"""
104167
val namespace = %T(%N)
105168
check(namespace.isExact) { %S }
106-
%N(namespace, %N) { _, _ -> %N }
169+
%N(namespace, %N) { %N }
107170
"""
108171
.trimIndent(),
109172
antPatternNamespaceClassName,
@@ -142,7 +205,9 @@ object ActiveContainer : FunctionProvider {
142205
return Functions(
143206
listOf(
144207
primaryFunction,
208+
byNamespaceDeprecatedFunction,
145209
byPatternFunction,
210+
byPatternDeprecatedFunction,
146211
byPathFunction,
147212
byPathValueFunction,
148213
),

0 commit comments

Comments
 (0)