io.specmatic.core.HttpQueryParamPattern.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of specmatic-core Show documentation
Show all versions of specmatic-core Show documentation
Turn your contracts into executable specifications. Contract Driven Development - Collaboratively Design & Independently Deploy MicroServices & MicroFrontends.
package io.specmatic.core
import io.specmatic.core.pattern.*
import io.specmatic.core.utilities.URIUtils
import io.specmatic.core.value.JSONArrayValue
import io.specmatic.core.value.StringValue
import java.net.URI
const val QUERY_PARAMS_BREADCRUMB = "QUERY-PARAMS"
data class HttpQueryParamPattern(val queryPatterns: Map, val additionalProperties: Pattern? = null) {
fun generate(resolver: Resolver): List> {
return attempt(breadCrumb = "QUERY-PARAMS") {
queryPatterns.map { it.key.removeSuffix("?") to it.value }.flatMap { (parameterName, pattern) ->
attempt(breadCrumb = parameterName) {
val generatedValue = resolver.withCyclePrevention(pattern) { it.generate(parameterName, pattern) }
if(generatedValue is JSONArrayValue) {
generatedValue.list.map { parameterName to it.toString() }
}
else {
listOf(parameterName to generatedValue.toString())
}
}
}.let { queryParamPairs ->
if(additionalProperties == null)
queryParamPairs
else
queryParamPairs.plus(randomString(5) to additionalProperties.generate(resolver).toStringLiteral())
}
}
}
fun newBasedOn(
row: Row,
resolver: Resolver
): Sequence>> {
val createdBasedOnExamples = attempt(breadCrumb = QUERY_PARAMS_BREADCRUMB) {
val queryParams = queryPatterns.let {
if(additionalProperties != null)
it.plus(randomString(5) to additionalProperties)
else
it
}
val combinations = forEachKeyCombinationIn(
row.withoutOmittedKeys(queryParams, resolver.defaultExampleResolver),
row
) { entry ->
newMapBasedOn(entry, row, resolver)
}
combinations.map { pattern ->
pattern.update {
it.mapKeys { withoutOptionality(it.key) }
}
}
}
return createdBasedOnExamples
}
fun addComplimentaryPatterns(basePatterns: Sequence>>, row: Row, resolver: Resolver): Sequence>> {
return addComplimentaryPatterns(basePatterns, queryPatterns, additionalProperties, row, resolver)
}
fun matches(httpRequest: HttpRequest, resolver: Resolver): Result {
val queryParams = if(additionalProperties != null) {
httpRequest.queryParams.withoutMatching(queryPatterns.keys, additionalProperties, resolver)
} else {
httpRequest.queryParams
}
val keyErrors =
resolver.findKeyErrorList(queryPatterns, queryParams.asMap().mapValues { StringValue(it.value) })
val keyErrorList: List = keyErrors.map {
it.missingKeyToResult("query param", resolver.mismatchMessages).breadCrumb(it.name)
}
// 1. key is optional and request does not have the key as well
// 2. key is mandatory and request does not have the key as well -> Result.Failure
// 3. key in request but not in groupedPatternPairs -> Result.Failure
// 4. key in request
// A. key value pattern is an array
// B. key value pattern is a scalar (not an array)
// C. multiple pattern patternPairGroup with the same key
// We don't need unmatched values when:
// 1. Running contract tests
// 2. Backward compatibility
// 3. Stub
// A. Setting expectation
// B. Matching incoming request to a stub without expectations
// Where we need unmatched values:
// Matching incoming request to stubbed out API
val results: List = queryPatterns.mapNotNull { (key, parameterPattern) ->
val requestValues = queryParams.getValues(withoutOptionality(key))
if (requestValues.isEmpty()) return@mapNotNull null
val keyWithoutOptionality = withoutOptionality(key)
val requestValuesList = JSONArrayValue(requestValues.map {
StringValue(it)
})
resolver.matchesPattern(keyWithoutOptionality, parameterPattern, requestValuesList).breadCrumb(keyWithoutOptionality)
}
val failures = keyErrorList.plus(results).filterIsInstance()
return if (failures.isNotEmpty())
Result.Failure.fromFailures(failures).breadCrumb(QUERY_PARAMS_BREADCRUMB)
else
Result.Success()
}
fun newBasedOn(resolver: Resolver): Sequence