Skip to content

Commit

Permalink
feat: update content type overrides to allow setting the content type…
Browse files Browse the repository at this point in the history
… to json #1314
  • Loading branch information
Ronald Holshausen committed Mar 6, 2021
1 parent 4e5c05b commit 03fbfe0
Show file tree
Hide file tree
Showing 12 changed files with 65 additions and 32 deletions.
2 changes: 1 addition & 1 deletion consumer/groovy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ For example:

By default, bodies will be handled based on their content types. For binary contents, the bodies will be base64
encoded when written to the Pact file and then decoded again when the file is loaded. You can change this with
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|binary`. For instance, setting
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|json|binary`. For instance, setting
`pact.content_type.override.application.pdf=text` will treat PDF bodies as a text type and not encode/decode them.

## Changing the directory pact files are written to
Expand Down
2 changes: 1 addition & 1 deletion consumer/junit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ The `and` and `or` methods take a variable number of matchers (varargs).

By default, bodies will be handled based on their content types. For binary contents, the bodies will be base64
encoded when written to the Pact file and then decoded again when the file is loaded. You can change this with
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|binary`. For instance, setting
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|binary|json`. For instance, setting
`pact.content_type.override.application.pdf=text` will treat PDF bodies as a text type and not encode/decode them.

### Matching on paths
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,10 @@ object MatchingConfig {
val clazz = Class.forName(matcher).kotlin
(clazz.objectInstance ?: clazz.createInstance()) as BodyMatcher?
} else {
val override = System.getProperty("pact.content_type.override.$contentType")
if (override != null) {
val matcherOverride = bodyMatchers.entries.find { override.matches(Regex(it.key)) }?.value
if (matcherOverride != null) {
val clazz = Class.forName(matcherOverride).kotlin
(clazz.objectInstance ?: clazz.createInstance()) as BodyMatcher?
} else {
null
}
} else {
null
when (System.getProperty("pact.content_type.override.$contentType")) {
"json" -> JsonBodyMatcher
"text" -> PlainTextBodyMatcher()
else -> null
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package au.com.dius.pact.core.matchers

import spock.lang.Specification
import spock.lang.Unroll
import spock.util.environment.RestoreSystemProperties

@RestoreSystemProperties
class MatchingConfigSpec extends Specification {

def setupSpec() {
System.setProperty('pact.content_type.override.application/x-thrift', 'json')
System.setProperty('pact.content_type.override.application/x-other', 'text')
}

@Unroll
Expand All @@ -24,6 +27,6 @@ class MatchingConfigSpec extends Specification {
'application/json-rpc' | 'au.com.dius.pact.core.matchers.JsonBodyMatcher'
'application/jsonrequest' | 'au.com.dius.pact.core.matchers.JsonBodyMatcher'
'application/x-thrift' | 'au.com.dius.pact.core.matchers.JsonBodyMatcher'
'application/x-other' | 'au.com.dius.pact.core.matchers.PlainTextBodyMatcher'
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,19 @@ class ContentType(val contentType: MediaType?) {

fun isJson(): Boolean {
return if (contentType != null) {
val override = System.getProperty("pact.content_type.override.${contentType.baseType}")
if (override == null)
jsonRegex.matches(contentType.subtype.toLowerCase())
else
jsonRegex.matches(override)
when (System.getProperty("pact.content_type.override.${contentType.baseType}")) {
"json" -> true
else -> jsonRegex.matches(contentType.subtype.toLowerCase())
}
} else false
}

fun isXml(): Boolean = if (contentType != null) xmlRegex.matches(contentType.subtype.toLowerCase()) else false
fun isXml(): Boolean = if (contentType != null) {
when (System.getProperty("pact.content_type.override.${contentType.baseType}")) {
"xml" -> true
else -> xmlRegex.matches(contentType.subtype.toLowerCase())
}
} else false

fun isOctetStream(): Boolean = if (contentType != null)
contentType.baseType.toString() == "application/octet-stream"
Expand Down Expand Up @@ -61,6 +65,7 @@ class ContentType(val contentType: MediaType?) {
val type = contentType.type
val baseType = superType.type
val override = System.getProperty("pact.content_type.override.$type.${contentType.subtype}")
?: System.getProperty("pact.content_type.override.$type/${contentType.subtype}")
when {
override.isNotEmpty() -> override == "binary"
type == "text" || baseType == "text" -> false
Expand Down
15 changes: 10 additions & 5 deletions core/model/src/main/kotlin/au/com/dius/pact/core/model/HttpPart.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,23 @@ abstract class HttpPart {
private const val CONTENT_TYPE = "Content-Type"

@JvmStatic
fun extractBody(json: JsonValue.Object, contentType: ContentType): OptionalBody {
@JvmOverloads
fun extractBody(
json: JsonValue.Object,
contentType: ContentType,
decoder: Base64.Decoder = Base64.getDecoder()
): OptionalBody {
return when (val b = json["body"]) {
is JsonValue.Null -> OptionalBody.nullBody()
is JsonValue.StringValue -> decodeBody(b.asString(), contentType)
else -> decodeBody(b.serialise(), contentType)
is JsonValue.StringValue -> decodeBody(b.asString(), contentType, decoder)
else -> decodeBody(b.serialise(), contentType, decoder)
}
}

private fun decodeBody(body: String, contentType: ContentType): OptionalBody {
private fun decodeBody(body: String, contentType: ContentType, decoder: Base64.Decoder): OptionalBody {
return when {
contentType.isBinaryType() || contentType.isMultipart() -> try {
OptionalBody.body(Base64.getDecoder().decode(body), contentType)
OptionalBody.body(decoder.decode(body), contentType)
} catch (ex: IllegalArgumentException) {
logger.warn(ex) { "Expected body for content type $contentType to be base64 encoded" }
OptionalBody.body(body.toByteArray(contentType.asCharset()), contentType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@ package au.com.dius.pact.core.model

import spock.lang.Specification
import spock.lang.Unroll
import spock.util.environment.RestoreSystemProperties

import java.nio.charset.Charset

@SuppressWarnings('UnnecessaryBooleanExpression')
@RestoreSystemProperties
class ContentTypeSpec extends Specification {

def setupSpec() {
System.setProperty('pact.content_type.override.application/x-thrift', 'json')
System.setProperty('pact.content_type.override.application/x-other', 'text')
System.setProperty('pact.content_type.override.application/x-bin', 'binary')
System.setProperty('pact.content_type.override.application/x-ml', 'xml')
}

@Unroll
def '"#value" is json -> #result'() {
expect:
result ? contentType.json : !contentType.json
result == contentType.json

where:

Expand All @@ -27,14 +32,15 @@ class ContentTypeSpec extends Specification {
'application/hal+json' || true
'application/HAL+JSON' || true
'application/x-thrift' || true
'application/x-other' || false

contentType = new ContentType(value)
}

@Unroll
def '"#value" is xml -> #result'() {
expect:
result ? contentType.xml : !contentType.xml
result == contentType.xml

where:

Expand All @@ -45,6 +51,8 @@ class ContentTypeSpec extends Specification {
'application/xml' || true
'application/stuff+xml' || true
'application/STUFF+XML' || true
'application/x-ml' || true
'application/x-thrift' || false

contentType = new ContentType(value)
}
Expand Down Expand Up @@ -89,6 +97,7 @@ class ContentTypeSpec extends Specification {
'text/csv' || false
'multipart/form-data' || true
'application/x-www-form-urlencoded' || false
'application/x-bin' || true

contentType = new ContentType(value)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package au.com.dius.pact.core.model

import au.com.dius.pact.core.support.json.JsonValue
import spock.lang.Issue
import spock.lang.Specification
import spock.lang.Unroll
import spock.util.environment.RestoreSystemProperties

import java.nio.charset.Charset

Expand Down Expand Up @@ -61,4 +63,20 @@ class HttpPartSpec extends Specification {
HttpPart.extractBody(json, ContentType.fromString('application/zip'))
.valueAsString() == 'hello'
}

@Issue('#1314')
@RestoreSystemProperties
def 'takes into account content type overrides'() {
given:
def json = new JsonValue.Object([body: new JsonValue.StringValue('{}'.chars)])
System.setProperty('pact.content_type.override.application/x-thrift', 'json')
def decoder = Mock(Base64.Decoder)

when:
def result = HttpPart.extractBody(json, ContentType.fromString('application/x-thrift'), decoder)

then:
0 * decoder.decode(_)
result.valueAsString() == '{}'
}
}
2 changes: 1 addition & 1 deletion provider/gradle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ The following project properties can be specified with `-Pproperty=value` on the
|`pact.verifier.disableUrlPathDecoding`|Disables decoding of request paths|
|`pact.pactbroker.httpclient.usePreemptiveAuthentication`|Enables preemptive authentication with the pact broker when set to `true`|
|`pact.provider.tag`|Sets the provider tag to push before publishing verification results (can use a comma separated list)|
|`pact.content_type.override.<TYPE>.<SUBTYPE>=<VAL>` where `<VAL>` may be `text` or `binary`|Overrides the handling of a particular content type [4.1.3+]|
|`pact.content_type.override.<TYPE>.<SUBTYPE>=<VAL>` where `<VAL>` may be `text`, `json` or `binary`|Overrides the handling of a particular content type [4.1.3+]|

## Specifying the provider hostname at runtime

Expand Down
2 changes: 1 addition & 1 deletion provider/junit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ having been verified due to a configuration error. **

By default, bodies will be handled based on their content types. For binary contents, the bodies will be base64
encoded when written to the Pact file and then decoded again when the file is loaded. You can change this with
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|binary`. For instance, setting
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|json|binary`. For instance, setting
`pact.content_type.override.application.pdf=text` will treat PDF bodies as a text type and not encode/decode them.

## Test target
Expand Down
2 changes: 1 addition & 1 deletion provider/junit5/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ null values for any of the injected parameters.

By default, bodies will be handled based on their content types. For binary contents, the bodies will be base64
encoded when written to the Pact file and then decoded again when the file is loaded. You can change this with
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|binary`. For instance, setting
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|json|binary`. For instance, setting
`pact.content_type.override.application.pdf=text` will treat PDF bodies as a text type and not encode/decode them.

# Pending Pact Support (version 4.1.0 and later)
Expand Down
4 changes: 2 additions & 2 deletions provider/maven/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ The following plugin properties can be specified with `-Dproperty=value` on the
|`pact.verifier.disableUrlPathDecoding`|Disables decoding of request paths|
|`pact.pactbroker.httpclient.usePreemptiveAuthentication`|Enables preemptive authentication with the pact broker when set to `true`|
|`pact.consumer.tags`|Overrides the tags used when publishing pacts [version 4.0.7+]|
|`pact.content_type.override.<TYPE>.<SUBTYPE>=text\|binary`|Overrides the handling of a particular content type [version 4.1.3+]|
|`pact.content_type.override.<TYPE>.<SUBTYPE>=text\|json\|binary`|Overrides the handling of a particular content type [version 4.1.3+]|

Example in the configuration section:

Expand Down Expand Up @@ -752,7 +752,7 @@ For example:

By default, bodies will be handled based on their content types. For binary contents, the bodies will be base64
encoded when written to the Pact file and then decoded again when the file is loaded. You can change this with
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|binary`. For instance, setting
an override property: `pact.content_type.override.<TYPE>.<SUBTYPE>=text|json|binary`. For instance, setting
`pact.content_type.override.application.pdf=text` will treat PDF bodies as a text type and not encode/decode them.

# Publishing verification results to a Pact Broker
Expand Down

0 comments on commit 03fbfe0

Please sign in to comment.