All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
run.qontract.core.HttpHeadersPattern.kt Maven / Gradle / Ivy
Go to download
A Contract Testing Tool that leverages Gherkin to describe APIs in a human readable and machine enforceable manner
package run.qontract.core
import run.qontract.core.pattern.*
import run.qontract.core.value.StringValue
data class HttpHeadersPattern(val pattern: Map = emptyMap(), val ancestorHeaders: Map? = null) {
fun matches(headers: Map, resolver: Resolver): Result {
val result = headers to resolver to
::matchEach otherwise
::handleError toResult
::returnResult
return when (result) {
is Result.Failure -> result.breadCrumb("HEADERS")
else -> result
}
}
private fun matchEach(parameters: Pair, Resolver>): MatchingResult, Resolver>> {
val (headers, resolver) = parameters
val headersWithRelevantKeys = when {
ancestorHeaders != null -> withoutIgnorableHeaders(headers, ancestorHeaders)
else -> withoutContentTypeGeneratedByQontract(headers, pattern)
}
val missingKey = resolver.findMissingKey(pattern, headersWithRelevantKeys.mapValues { StringValue(it.value) }, ignoreUnexpectedKeys)
if(missingKey != null) {
return MatchFailure(missingKeyToResult(missingKey, "header"))
}
this.pattern.forEach { (key, pattern) ->
val keyWithoutOptionality = withoutOptionality(key)
val sampleValue = headersWithRelevantKeys[keyWithoutOptionality]
when {
sampleValue != null -> try {
val result = resolver.matchesPattern(keyWithoutOptionality, pattern, attempt(breadCrumb = keyWithoutOptionality) { parseOrString(pattern, sampleValue, resolver) } )
if (result is Result.Failure) {
return MatchFailure(result.breadCrumb(keyWithoutOptionality))
}
} catch(e: ContractException) {
return MatchFailure(e.failure())
} catch(e: Throwable) {
return MatchFailure(Result.Failure(e.localizedMessage, breadCrumb = keyWithoutOptionality))
}
!key.endsWith("?") ->
return MatchFailure(missingKeyToResult(MissingKeyError(key), "header").breadCrumb(key))
}
}
return MatchSuccess(parameters)
}
private fun withoutIgnorableHeaders(headers: Map, ancestorHeaders: Map): Map {
return headers.filterKeys { key ->
val keyWithoutOptionality = withoutOptionality(key)
ancestorHeaders.containsKey(keyWithoutOptionality) || ancestorHeaders.containsKey("$keyWithoutOptionality?")
}
}
private fun withoutContentTypeGeneratedByQontract(headers: Map, pattern: Map): Map {
val contentTypeHeader = "Content-Type"
return when {
contentTypeHeader in headers && contentTypeHeader !in pattern && "$contentTypeHeader?" !in pattern -> headers.minus(contentTypeHeader)
else -> headers
}
}
fun generate(resolver: Resolver): Map {
return attempt(breadCrumb = "HEADERS") {
pattern.mapValues { (key, pattern) ->
attempt(breadCrumb = key) {
resolver.generate(key, pattern).toStringValue()
}
}
}
}
fun newBasedOn(row: Row, resolver: Resolver): List =
forEachKeyCombinationIn(pattern, row) { pattern ->
newBasedOn(pattern, row, resolver)
}.map { HttpHeadersPattern(it.mapKeys { withoutOptionality(it.key) }) }
fun encompasses(other: HttpHeadersPattern, thisResolver: Resolver, otherResolver: Resolver): Result {
val myRequiredKeys = pattern.keys.filter { !isOptional(it) }
val otherRequiredKeys = other.pattern.keys.filter { !isOptional(it) }
return checkMissingHeaders(myRequiredKeys, otherRequiredKeys).ifSuccess {
val otherWithoutOptionality = other.pattern.mapKeys { withoutOptionality(it.key) }
val thisWithoutOptionality = pattern.filterKeys { withoutOptionality(it) in otherWithoutOptionality }.mapKeys { withoutOptionality(it.key) }
val valueResults =
thisWithoutOptionality.keys.asSequence().map { key ->
Pair(key, thisWithoutOptionality.getValue(key).encompasses(resolvedHop(otherWithoutOptionality.getValue(key), otherResolver), thisResolver, otherResolver))
}
valueResults.find { it.second is Result.Failure }.let { result ->
result?.second?.breadCrumb(result.first) ?: Result.Success()
}
}.breadCrumb("HEADER")
}
private fun checkMissingHeaders(myRequiredKeys: List, otherRequiredKeys: List): Result =
when(val missingFixedKey = myRequiredKeys.find { it !in otherRequiredKeys }) {
null -> Result.Success()
else -> missingKeyToResult(MissingKeyError(missingFixedKey), "header").breadCrumb(missingFixedKey)
}
}
private fun parseOrString(pattern: Pattern, sampleValue: String, resolver: Resolver) =
try {
pattern.parse(sampleValue, resolver)
} catch (e: Throwable) {
StringValue(sampleValue)
}