Skip to content

Commit 2e14424

Browse files
authored
Add support for @RequestParam annotation (#2487)
1 parent e28d3bb commit 2e14424

File tree

2 files changed

+108
-32
lines changed

2 files changed

+108
-32
lines changed

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ val iterableClassId = java.lang.Iterable::class.id
307307
val mapClassId = java.util.Map::class.id
308308
val collectionClassId = java.util.Collection::class.id
309309

310+
val listClassId = List::class.id
311+
310312
val baseStreamClassId = java.util.stream.BaseStream::class.id
311313
val streamClassId = java.util.stream.Stream::class.id
312314
val intStreamClassId = java.util.stream.IntStream::class.id

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/SpringModelUtils.kt

Lines changed: 106 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ object SpringModelUtils {
122122
private val requestMappingClassId = ClassId("org.springframework.web.bind.annotation.RequestMapping")
123123
private val pathVariableClassId = ClassId("org.springframework.web.bind.annotation.PathVariable")
124124
private val requestBodyClassId = ClassId("org.springframework.web.bind.annotation.RequestBody")
125+
private val requestParamClassId = ClassId("org.springframework.web.bind.annotation.RequestParam")
125126
private val uriComponentsBuilderClassId = ClassId("org.springframework.web.util.UriComponentsBuilder")
126127
private val mediaTypeClassId = ClassId("org.springframework.http.MediaType")
127128
private val mockHttpServletResponseClassId = ClassId("org.springframework.mock.web.MockHttpServletResponse")
@@ -283,15 +284,17 @@ object SpringModelUtils {
283284
@Suppress("UNCHECKED_CAST")
284285
val classRequestMappingAnnotation: Annotation? =
285286
methodId.classId.jClass.getAnnotation(requestMappingClassId.jClass as Class<out Annotation>)
286-
val cassRequestPath = classRequestMappingAnnotation?.let { getRequestPathOrNull(it) }.orEmpty()
287+
val classRequestPath = classRequestMappingAnnotation?.let { getRequestPathOrNull(it) }.orEmpty()
287288

288-
val requestPath = cassRequestPath + (getRequestPathOrNull(requestMappingAnnotation) ?: return null)
289+
val requestPath = classRequestPath + (getRequestPathOrNull(requestMappingAnnotation) ?: return null)
289290

290291
val pathVariablesModel = createPathVariablesModel(methodId, arguments, idGenerator)
291292

292-
val urlTemplateModel = createUrlTemplateModel(pathVariablesModel, requestPath, idGenerator)
293+
val requestParamsModel = createRequestParamsModel(methodId, arguments, idGenerator)
293294

294-
val requestBuilderModel = UtAssembleModel(
295+
val urlTemplateModel = createUrlTemplateModel(pathVariablesModel, requestParamsModel, requestPath, idGenerator)
296+
297+
var requestBuilderModel = UtAssembleModel(
295298
id = idGenerator(),
296299
classId = mockHttpServletRequestBuilderClassId,
297300
modelName = "requestBuilder",
@@ -405,16 +408,7 @@ object SpringModelUtils {
405408
arguments: List<UtModel>,
406409
idGenerator: () -> Int
407410
): UtAssembleModel {
408-
val pathVariables = mutableMapOf<String, UtModel>()
409-
410-
methodId.method.parameters.zip(arguments).forEach { (param, arg) ->
411-
@Suppress("UNCHECKED_CAST") val pathVariableAnnotation =
412-
param.getAnnotation(pathVariableClassId.jClass as Class<out Annotation>) ?: return@forEach
413-
val name = (pathVariableClassId.jClass.getMethod("name").invoke(pathVariableAnnotation) as? String).orEmpty()
414-
.ifEmpty { pathVariableClassId.jClass.getMethod("value").invoke(pathVariableAnnotation) as? String }.orEmpty()
415-
.ifEmpty { param.name }
416-
pathVariables[name] = arg
417-
}
411+
val pathVariables = collectArgumentsWithAnnotationModels(methodId, pathVariableClassId, arguments)
418412

419413
// TODO filter out `null` and `Optional.empty()` values of `arg`
420414
return UtAssembleModel(
@@ -442,15 +436,73 @@ object SpringModelUtils {
442436
)
443437
}
444438

439+
private fun createRequestParamsModel(
440+
methodId: MethodId,
441+
arguments: List<UtModel>,
442+
idGenerator: () -> Int
443+
): List< Pair<UtPrimitiveModel, UtAssembleModel> > {
444+
val requestParams = collectArgumentsWithAnnotationModels(methodId, requestParamClassId, arguments)
445+
446+
// TODO filter out `null` and `Optional.empty()` values of `arg`
447+
return requestParams.map { (name, value) ->
448+
Pair(UtPrimitiveModel(name),
449+
UtAssembleModel(
450+
id = idGenerator(),
451+
classId = listClassId,
452+
modelName = "queryParams",
453+
instantiationCall = UtExecutableCallModel(
454+
instance = null,
455+
executable = constructorId(java.util.ArrayList::class.id),
456+
params = emptyList()
457+
),
458+
modificationsChainProvider = {
459+
listOf(
460+
UtExecutableCallModel(
461+
instance = this,
462+
executable = methodId(
463+
classId = listClassId,
464+
name = "add",
465+
returnType = booleanClassId,
466+
arguments = arrayOf(Object::class.id),
467+
),
468+
params = listOf(value)
469+
)
470+
)
471+
}
472+
)
473+
)
474+
}
475+
}
476+
477+
private fun collectArgumentsWithAnnotationModels(
478+
methodId: MethodId,
479+
annotationClassId: ClassId,
480+
arguments: List<UtModel>
481+
): MutableMap<String, UtModel> {
482+
val argumentsModels = mutableMapOf<String, UtModel>()
483+
484+
methodId.method.parameters.zip(arguments).forEach { (param, arg) ->
485+
@Suppress("UNCHECKED_CAST") val paramAnnotation =
486+
param.getAnnotation(annotationClassId.jClass as Class<out Annotation>) ?: return@forEach
487+
val name = (annotationClassId.jClass.getMethod("name").invoke(paramAnnotation) as? String).orEmpty()
488+
.ifEmpty { annotationClassId.jClass.getMethod("value").invoke(paramAnnotation) as? String }.orEmpty()
489+
.ifEmpty { param.name }
490+
argumentsModels[name] = arg
491+
}
492+
493+
return argumentsModels
494+
}
495+
445496
private fun createUrlTemplateModel(
446497
pathVariablesModel: UtAssembleModel,
498+
requestParamModel: List<Pair<UtPrimitiveModel, UtAssembleModel>>,
447499
requestPath: String,
448500
idGenerator: () -> Int
449501
): UtModel {
450502
val requestPathModel = UtPrimitiveModel(requestPath)
451-
return if (pathVariablesModel.modificationsChain.isEmpty()) requestPathModel
503+
return if (pathVariablesModel.modificationsChain.isEmpty() && requestParamModel.isEmpty()) requestPathModel
452504
else {
453-
val uriBuilderFromPath = UtAssembleModel(
505+
var uriBuilderFromPath = UtAssembleModel(
454506
id = idGenerator(),
455507
classId = uriComponentsBuilderClassId,
456508
modelName = "uriBuilderFromPath",
@@ -465,27 +517,49 @@ object SpringModelUtils {
465517
params = listOf(requestPathModel),
466518
)
467519
)
468-
val uriBuilderWithPathVariables = UtAssembleModel(
469-
id = idGenerator(),
470-
classId = uriComponentsBuilderClassId,
471-
modelName = "uriBuilderWithPathVariables",
472-
instantiationCall = UtExecutableCallModel(
473-
instance = uriBuilderFromPath,
474-
executable = MethodId(
475-
classId = uriComponentsBuilderClassId,
476-
name = "uriVariables",
477-
parameters = listOf(Map::class.java.id),
478-
returnType = uriComponentsBuilderClassId
479-
),
480-
params = listOf(pathVariablesModel),
520+
521+
if(pathVariablesModel.modificationsChain.isNotEmpty()) {
522+
uriBuilderFromPath = UtAssembleModel(
523+
id = idGenerator(),
524+
classId = uriComponentsBuilderClassId,
525+
modelName = "uriBuilderWithPathVariables",
526+
instantiationCall = UtExecutableCallModel(
527+
instance = uriBuilderFromPath,
528+
executable = MethodId(
529+
classId = uriComponentsBuilderClassId,
530+
name = "uriVariables",
531+
parameters = listOf(Map::class.java.id),
532+
returnType = uriComponentsBuilderClassId
533+
),
534+
params = listOf(pathVariablesModel),
535+
)
481536
)
482-
)
483-
UtAssembleModel(
537+
}
538+
539+
requestParamModel.forEach { (name, value) ->
540+
uriBuilderFromPath = UtAssembleModel(
541+
id = idGenerator(),
542+
classId = uriComponentsBuilderClassId,
543+
modelName = "uriBuilderWithRequestParam",
544+
instantiationCall = UtExecutableCallModel(
545+
instance = uriBuilderFromPath,
546+
executable = MethodId(
547+
classId = uriComponentsBuilderClassId,
548+
name = "queryParam",
549+
parameters = listOf(stringClassId, collectionClassId),
550+
returnType = uriComponentsBuilderClassId
551+
),
552+
params = listOf(name, value),
553+
)
554+
)
555+
}
556+
557+
return UtAssembleModel(
484558
id = idGenerator(),
485559
classId = stringClassId,
486560
modelName = "uriString",
487561
instantiationCall = UtExecutableCallModel(
488-
instance = uriBuilderWithPathVariables,
562+
instance = uriBuilderFromPath,
489563
executable = MethodId(
490564
classId = uriComponentsBuilderClassId,
491565
name = "toUriString",

0 commit comments

Comments
 (0)